176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This program is free software; you can redistribute it and/or
576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * modify it under the terms of the GNU General Public License as
676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * published by the Free Software Foundation; either version 2 of the
776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * License, or any later version.
876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This program is distributed in the hope that it will be useful, but
1076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * WITHOUT ANY WARRANTY; without even the implied warranty of
1176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * General Public License for more details.
1376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
1476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * You should have received a copy of the GNU General Public License
1576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * along with this program; if not, write to the Free Software
1676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
1776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
1876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
1976d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanFILE_LICENCE ( GPL2_OR_LATER );
2076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
2176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdint.h>
2276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdlib.h>
2376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdio.h>
2476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <byteswap.h>
2576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <string.h>
2676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <errno.h>
2776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/if_ether.h>
2876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/iobuf.h>
2976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/tables.h>
3076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/process.h>
3176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/init.h>
3276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/device.h>
3376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/errortab.h>
3476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/netdevice.h>
3576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
3676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** @file
3776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
3876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Network device management
3976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
4076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
4176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
4276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** List of network devices */
4376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct list_head net_devices = LIST_HEAD_INIT ( net_devices );
4476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
4576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** List of open network devices, in reverse order of opening */
4676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct list_head open_net_devices = LIST_HEAD_INIT ( open_net_devices );
4776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
4876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** Default link status code */
4976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define EUNKNOWN_LINK_STATUS EINPROGRESS
5076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
5176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** Human-readable message for the default link status */
5276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct errortab netdev_errors[] __errortab = {
5376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	{ EUNKNOWN_LINK_STATUS, "Unknown" },
5476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman};
5576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
5676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
5776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Mark network device as having link down
5876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
5976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
6076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
6176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanvoid netdev_link_down ( struct net_device *netdev ) {
6276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
6376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	switch ( netdev->link_rc ) {
6476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	case 0:
6576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	case -EUNKNOWN_LINK_STATUS:
6676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		netdev->link_rc = -ENOTCONN;
6776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		break;
6876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	default:
6976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Avoid clobbering a more detailed link status code,
7076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 * if one is already set.
7176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 */
7276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		break;
7376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
7476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
7576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
7676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
7776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Record network device statistic
7876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
7976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v stats		Network device statistics
8076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v rc		Status code
8176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
8276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void netdev_record_stat ( struct net_device_stats *stats, int rc ) {
8376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct net_device_error *error;
8476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct net_device_error *least_common_error;
8576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned int i;
8676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
8776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* If this is not an error, just update the good counter */
8876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( rc == 0 ) {
8976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		stats->good++;
9076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return;
9176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
9276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
9376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Update the bad counter */
9476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	stats->bad++;
9576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
9676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Locate the appropriate error record */
9776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	least_common_error = &stats->errors[0];
9876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	for ( i = 0 ; i < ( sizeof ( stats->errors ) /
9976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			    sizeof ( stats->errors[0] ) ) ; i++ ) {
10076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		error = &stats->errors[i];
10176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Update matching record, if found */
10276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if ( error->rc == rc ) {
10376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			error->count++;
10476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			return;
10576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
10676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if ( error->count < least_common_error->count )
10776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			least_common_error = error;
10876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
10976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
11076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Overwrite the least common error record */
11176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	least_common_error->rc = rc;
11276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	least_common_error->count = 1;
11376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
11476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
11576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
11676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Transmit raw packet via network device
11776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
11876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
11976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v iobuf		I/O buffer
12076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc		Return status code
12176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
12276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Transmits the packet via the specified network device.  This
12376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * function takes ownership of the I/O buffer.
12476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
12576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanint netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf ) {
12676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int rc;
12776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
12876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGC ( netdev, "NETDEV %p transmitting %p (%p+%zx)\n",
12976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	       netdev, iobuf, iobuf->data, iob_len ( iobuf ) );
13076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
13176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	list_add_tail ( &iobuf->list, &netdev->tx_queue );
13276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
13376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ! ( netdev->state & NETDEV_OPEN ) ) {
13476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		rc = -ENETUNREACH;
13576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		goto err;
13676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
13776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
13876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ( rc = netdev->op->transmit ( netdev, iobuf ) ) != 0 )
13976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		goto err;
14076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
14176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
14276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
14376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman err:
14476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev_tx_complete_err ( netdev, iobuf, rc );
14576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return rc;
14676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
14776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
14876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
14976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Complete network transmission
15076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
15176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
15276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v iobuf		I/O buffer
15376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v rc		Packet status code
15476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
15576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * The packet must currently be in the network device's TX queue.
15676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
15776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanvoid netdev_tx_complete_err ( struct net_device *netdev,
15876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			      struct io_buffer *iobuf, int rc ) {
15976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
16076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Update statistics counter */
16176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev_record_stat ( &netdev->tx_stats, rc );
16276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( rc == 0 ) {
16376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBGC ( netdev, "NETDEV %p transmission %p complete\n",
16476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		       netdev, iobuf );
16576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	} else {
16676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBGC ( netdev, "NETDEV %p transmission %p failed: %s\n",
16776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		       netdev, iobuf, strerror ( rc ) );
16876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
16976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
17076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Catch data corruption as early as possible */
17176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	assert ( iobuf->list.next != NULL );
17276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	assert ( iobuf->list.prev != NULL );
17376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
17476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Dequeue and free I/O buffer */
17576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	list_del ( &iobuf->list );
17676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	free_iob ( iobuf );
17776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
17876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
17976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
18076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Complete network transmission
18176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
18276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
18376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v rc		Packet status code
18476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
18576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Completes the oldest outstanding packet in the TX queue.
18676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
18776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanvoid netdev_tx_complete_next_err ( struct net_device *netdev, int rc ) {
18876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct io_buffer *iobuf;
18976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
19076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	list_for_each_entry ( iobuf, &netdev->tx_queue, list ) {
19176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		netdev_tx_complete_err ( netdev, iobuf, rc );
19276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return;
19376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
19476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
19576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
19676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
19776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Flush device's transmit queue
19876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
19976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
20076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
20176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void netdev_tx_flush ( struct net_device *netdev ) {
20276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
20376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Discard any packets in the TX queue */
20476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	while ( ! list_empty ( &netdev->tx_queue ) ) {
20576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		netdev_tx_complete_next_err ( netdev, -ECANCELED );
20676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
20776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
20876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
20976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
21076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Add packet to receive queue
21176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
21276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
21376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v iobuf		I/O buffer, or NULL
21476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
21576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * The packet is added to the network device's RX queue.  This
21676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * function takes ownership of the I/O buffer.
21776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
21876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanvoid netdev_rx ( struct net_device *netdev, struct io_buffer *iobuf ) {
21976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
22076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGC ( netdev, "NETDEV %p received %p (%p+%zx)\n",
22176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	       netdev, iobuf, iobuf->data, iob_len ( iobuf ) );
22276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
22376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Enqueue packet */
22476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	list_add_tail ( &iobuf->list, &netdev->rx_queue );
22576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
22676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Update statistics counter */
22776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev_record_stat ( &netdev->rx_stats, 0 );
22876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
22976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
23076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
23176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Discard received packet
23276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
23376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
23476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v iobuf		I/O buffer, or NULL
23576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v rc		Packet status code
23676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
23776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * The packet is discarded and an RX error is recorded.  This function
23876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * takes ownership of the I/O buffer.  @c iobuf may be NULL if, for
23976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * example, the net device wishes to report an error due to being
24076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * unable to allocate an I/O buffer.
24176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
24276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanvoid netdev_rx_err ( struct net_device *netdev,
24376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		     struct io_buffer *iobuf, int rc ) {
24476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
24576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGC ( netdev, "NETDEV %p failed to receive %p: %s\n",
24676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	       netdev, iobuf, strerror ( rc ) );
24776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
24876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Discard packet */
24976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	free_iob ( iobuf );
25076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
25176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Update statistics counter */
25276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev_record_stat ( &netdev->rx_stats, rc );
25376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
25476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
25576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
25676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Poll for completed and received packets on network device
25776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
25876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
25976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
26076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Polls the network device for completed transmissions and received
26176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * packets.  Any received packets will be added to the RX packet queue
26276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * via netdev_rx().
26376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
26476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanvoid netdev_poll ( struct net_device *netdev ) {
26576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
26676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( netdev->state & NETDEV_OPEN )
26776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		netdev->op->poll ( netdev );
26876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
26976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
27076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
27176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Remove packet from device's receive queue
27276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
27376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
27476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret iobuf		I/O buffer, or NULL
27576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
27676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Removes the first packet from the device's RX queue and returns it.
27776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Ownership of the packet is transferred to the caller.
27876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
27976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct io_buffer * netdev_rx_dequeue ( struct net_device *netdev ) {
28076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct io_buffer *iobuf;
28176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
28276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	list_for_each_entry ( iobuf, &netdev->rx_queue, list ) {
28376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		list_del ( &iobuf->list );
28476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return iobuf;
28576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
28676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return NULL;
28776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
28876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
28976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
29076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Flush device's receive queue
29176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
29276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
29376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
29476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void netdev_rx_flush ( struct net_device *netdev ) {
29576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct io_buffer *iobuf;
29676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
29776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Discard any packets in the RX queue */
29876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	while ( ( iobuf = netdev_rx_dequeue ( netdev ) ) ) {
29976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		netdev_rx_err ( netdev, iobuf, -ECANCELED );
30076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
30176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
30276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
30376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
30476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Free network device
30576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
30676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v refcnt		Network device reference counter
30776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
30876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void free_netdev ( struct refcnt *refcnt ) {
30976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct net_device *netdev =
31076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		container_of ( refcnt, struct net_device, refcnt );
31176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
31276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev_tx_flush ( netdev );
31376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev_rx_flush ( netdev );
31476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	clear_settings ( netdev_settings ( netdev ) );
31576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	free ( netdev );
31676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
31776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
31876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
31976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Allocate network device
32076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
32176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v priv_size		Size of private data area (net_device::priv)
32276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret netdev		Network device, or NULL
32376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
32476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Allocates space for a network device and its private data area.
32576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
32676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct net_device * alloc_netdev ( size_t priv_size ) {
32776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct net_device *netdev;
32876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	size_t total_len;
32976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
33076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	total_len = ( sizeof ( *netdev ) + priv_size );
33176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev = zalloc ( total_len );
33276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( netdev ) {
33376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		netdev->refcnt.free = free_netdev;
33476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		netdev->link_rc = -EUNKNOWN_LINK_STATUS;
33576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		INIT_LIST_HEAD ( &netdev->tx_queue );
33676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		INIT_LIST_HEAD ( &netdev->rx_queue );
33776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		netdev_settings_init ( netdev );
33876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		netdev->priv = ( ( ( void * ) netdev ) + sizeof ( *netdev ) );
33976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
34076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return netdev;
34176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
34276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
34376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
34476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Register network device
34576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
34676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
34776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc		Return status code
34876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
34976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Gives the network device a name and adds it to the list of network
35076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * devices.
35176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
35276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanint register_netdev ( struct net_device *netdev ) {
35376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	static unsigned int ifindex = 0;
35476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int rc;
35576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
35676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Create device name */
35776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	snprintf ( netdev->name, sizeof ( netdev->name ), "net%d",
35876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		   ifindex++ );
35976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
36076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Set initial link-layer address */
36176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev->ll_protocol->init_addr ( netdev->hw_addr, netdev->ll_addr );
36276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
36376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Register per-netdev configuration settings */
36476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ( rc = register_settings ( netdev_settings ( netdev ),
36576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					NULL ) ) != 0 ) {
36676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBGC ( netdev, "NETDEV %p could not register settings: %s\n",
36776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		       netdev, strerror ( rc ) );
36876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return rc;
36976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
37076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
37176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Add to device list */
37276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev_get ( netdev );
37376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	list_add_tail ( &netdev->list, &net_devices );
37476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGC ( netdev, "NETDEV %p registered as %s (phys %s hwaddr %s)\n",
37576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	       netdev, netdev->name, netdev->dev->name,
37676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	       netdev_addr ( netdev ) );
37776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
37876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
37976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
38076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
38176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
38276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Open network device
38376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
38476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
38576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc		Return status code
38676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
38776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanint netdev_open ( struct net_device *netdev ) {
38876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int rc;
38976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
39076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Do nothing if device is already open */
39176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( netdev->state & NETDEV_OPEN )
39276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return 0;
39376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
39476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGC ( netdev, "NETDEV %p opening\n", netdev );
39576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
39676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Open the device */
39776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ( rc = netdev->op->open ( netdev ) ) != 0 )
39876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return rc;
39976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
40076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Mark as opened */
40176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev->state |= NETDEV_OPEN;
40276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
40376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Add to head of open devices list */
40476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	list_add ( &netdev->open_list, &open_net_devices );
40576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
40676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
40776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
40876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
40976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
41076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Close network device
41176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
41276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
41376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
41476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanvoid netdev_close ( struct net_device *netdev ) {
41576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
41676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Do nothing if device is already closed */
41776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ! ( netdev->state & NETDEV_OPEN ) )
41876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return;
41976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
42076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGC ( netdev, "NETDEV %p closing\n", netdev );
42176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
42276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Close the device */
42376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev->op->close ( netdev );
42476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
42576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Flush TX and RX queues */
42676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev_tx_flush ( netdev );
42776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev_rx_flush ( netdev );
42876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
42976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Mark as closed */
43076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev->state &= ~NETDEV_OPEN;
43176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
43276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Remove from open devices list */
43376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	list_del ( &netdev->open_list );
43476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
43576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
43676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
43776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Unregister network device
43876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
43976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
44076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
44176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Removes the network device from the list of network devices.
44276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
44376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanvoid unregister_netdev ( struct net_device *netdev ) {
44476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
44576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Ensure device is closed */
44676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev_close ( netdev );
44776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
44876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Unregister per-netdev configuration settings */
44976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unregister_settings ( netdev_settings ( netdev ) );
45076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
45176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Remove from device list */
45276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	list_del ( &netdev->list );
45376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev_put ( netdev );
45476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGC ( netdev, "NETDEV %p unregistered\n", netdev );
45576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
45676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
45776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** Enable or disable interrupts
45876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
45976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
46076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v enable		Interrupts should be enabled
46176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
46276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanvoid netdev_irq ( struct net_device *netdev, int enable ) {
46376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev->op->irq ( netdev, enable );
46476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
46576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
46676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
46776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Get network device by name
46876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
46976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v name		Network device name
47076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret netdev		Network device, or NULL
47176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
47276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct net_device * find_netdev ( const char *name ) {
47376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct net_device *netdev;
47476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
47576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	list_for_each_entry ( netdev, &net_devices, list ) {
47676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if ( strcmp ( netdev->name, name ) == 0 )
47776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			return netdev;
47876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
47976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
48076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return NULL;
48176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
48276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
48376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
48476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Get network device by PCI bus:dev.fn address
48576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
48676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v bus_type		Bus type
48776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v location		Bus location
48876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret netdev		Network device, or NULL
48976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
49076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct net_device * find_netdev_by_location ( unsigned int bus_type,
49176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					      unsigned int location ) {
49276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct net_device *netdev;
49376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
49476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	list_for_each_entry ( netdev, &net_devices, list ) {
49576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if ( ( netdev->dev->desc.bus_type == bus_type ) &&
49676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		     ( netdev->dev->desc.location == location ) )
49776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			return netdev;
49876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
49976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
50076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return NULL;
50176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
50276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
50376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
50476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Get most recently opened network device
50576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
50676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret netdev		Most recently opened network device, or NULL
50776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
50876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct net_device * last_opened_netdev ( void ) {
50976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct net_device *netdev;
51076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
51176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	list_for_each_entry ( netdev, &open_net_devices, open_list ) {
51276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		assert ( netdev->state & NETDEV_OPEN );
51376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return netdev;
51476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
51576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
51676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return NULL;
51776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
51876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
51976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
52076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Transmit network-layer packet
52176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
52276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v iobuf		I/O buffer
52376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
52476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v net_protocol	Network-layer protocol
52576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v ll_dest		Destination link-layer address
52676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc		Return status code
52776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
52876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Prepends link-layer headers to the I/O buffer and transmits the
52976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * packet via the specified network device.  This function takes
53076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * ownership of the I/O buffer.
53176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
53276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanint net_tx ( struct io_buffer *iobuf, struct net_device *netdev,
53376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	     struct net_protocol *net_protocol, const void *ll_dest ) {
53476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ll_protocol *ll_protocol = netdev->ll_protocol;
53576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int rc;
53676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
53776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Force a poll on the netdevice to (potentially) clear any
53876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 * backed-up TX completions.  This is needed on some network
53976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 * devices to avoid excessive losses due to small TX ring
54076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 * sizes.
54176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 */
54276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	netdev_poll ( netdev );
54376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
54476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Add link-layer header */
54576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ( rc = ll_protocol->push ( netdev, iobuf, ll_dest, netdev->ll_addr,
54676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					net_protocol->net_proto ) ) != 0 ) {
54776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		free_iob ( iobuf );
54876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return rc;
54976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
55076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
55176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Transmit packet */
55276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return netdev_tx ( netdev, iobuf );
55376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
55476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
55576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
55676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Process received network-layer packet
55776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
55876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v iobuf		I/O buffer
55976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v netdev		Network device
56076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v net_proto		Network-layer protocol, in network-byte order
56176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v ll_source		Source link-layer address
56276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc		Return status code
56376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
56476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanint net_rx ( struct io_buffer *iobuf, struct net_device *netdev,
56576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	     uint16_t net_proto, const void *ll_source ) {
56676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct net_protocol *net_protocol;
56776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
56876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Hand off to network-layer protocol, if any */
56976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	for_each_table_entry ( net_protocol, NET_PROTOCOLS ) {
57076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if ( net_protocol->net_proto == net_proto )
57176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			return net_protocol->rx ( iobuf, netdev, ll_source );
57276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
57376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
57476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGC ( netdev, "NETDEV %p unknown network protocol %04x\n",
57576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	       netdev, ntohs ( net_proto ) );
57676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	free_iob ( iobuf );
57776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
57876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
57976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
58076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
58176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Single-step the network stack
58276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
58376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v process		Network stack process
58476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
58576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This polls all interfaces for received packets, and processes
58676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * packets from the RX queue.
58776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
58876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void net_step ( struct process *process __unused ) {
58976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct net_device *netdev;
59076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct io_buffer *iobuf;
59176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct ll_protocol *ll_protocol;
59276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	const void *ll_dest;
59376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	const void *ll_source;
59476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uint16_t net_proto;
59576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int rc;
59676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
59776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Poll and process each network device */
59876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	list_for_each_entry ( netdev, &net_devices, list ) {
59976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
60076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Poll for new packets */
60176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		netdev_poll ( netdev );
60276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
60376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Process at most one received packet.  Give priority
60476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 * to getting packets out of the NIC over processing
60576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 * the received packets, because we advertise a window
60676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 * that assumes that we can receive packets from the
60776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 * NIC faster than they arrive.
60876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 */
60976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if ( ( iobuf = netdev_rx_dequeue ( netdev ) ) ) {
61076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
61176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			DBGC ( netdev, "NETDEV %p processing %p (%p+%zx)\n",
61276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			       netdev, iobuf, iobuf->data,
61376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			       iob_len ( iobuf ) );
61476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
61576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			/* Remove link-layer header */
61676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			ll_protocol = netdev->ll_protocol;
61776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			if ( ( rc = ll_protocol->pull ( netdev, iobuf,
61876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman							&ll_dest, &ll_source,
61976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman							&net_proto ) ) != 0 ) {
62076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				free_iob ( iobuf );
62176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				continue;
62276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			}
62376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
62476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			net_rx ( iobuf, netdev, net_proto, ll_source );
62576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
62676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
62776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
62876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
62976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** Networking stack process */
63076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct process net_process __permanent_process = {
63176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	.list = LIST_HEAD_INIT ( net_process.list ),
63276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	.step = net_step,
63376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman};
634