Files
ipxe/src/net/netdev_settings.c
Michael Brown 66ea458125 [settings] Make "netX" settings block function as a symbolic link
Add a facility for settings blocks to act as symbolic links to other
settings blocks, and reimplement the "netX" virtual settings block
using this facility.

The primary advantage of this approach is that unscoped settings such
as ${mac} and ${filename} will now reflect the settings obtained from
the most recently opened network device: in most cases, this will mean
the settings obtained from the most recent DHCP attempt.  This should
improve conformance to the principle of least astonishment.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2013-07-13 15:11:45 +02:00

347 lines
9.5 KiB
C

/*
* Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 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.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <string.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/dhcp.h>
#include <ipxe/dhcpopts.h>
#include <ipxe/settings.h>
#include <ipxe/device.h>
#include <ipxe/netdevice.h>
#include <ipxe/init.h>
/** @file
*
* Network device configuration settings
*
*/
/** Network device named settings */
struct setting mac_setting __setting ( SETTING_NETDEV ) = {
.name = "mac",
.description = "MAC address",
.type = &setting_type_hex,
};
struct setting bustype_setting __setting ( SETTING_NETDEV ) = {
.name = "bustype",
.description = "Bus type",
.type = &setting_type_string,
};
struct setting busloc_setting __setting ( SETTING_NETDEV ) = {
.name = "busloc",
.description = "Bus location",
.type = &setting_type_uint32,
};
struct setting busid_setting __setting ( SETTING_NETDEV ) = {
.name = "busid",
.description = "Bus ID",
.type = &setting_type_hex,
};
struct setting chip_setting __setting ( SETTING_NETDEV ) = {
.name = "chip",
.description = "Chip",
.type = &setting_type_string,
};
/**
* Store MAC address setting
*
* @v netdev Network device
* @v data Setting data, or NULL to clear setting
* @v len Length of setting data
* @ret rc Return status code
*/
static int netdev_store_mac ( struct net_device *netdev,
const void *data, size_t len ) {
struct ll_protocol *ll_protocol = netdev->ll_protocol;
/* Record new MAC address */
if ( data ) {
if ( len != netdev->ll_protocol->ll_addr_len )
return -EINVAL;
memcpy ( netdev->ll_addr, data, len );
} else {
/* Reset MAC address if clearing setting */
ll_protocol->init_addr ( netdev->hw_addr, netdev->ll_addr );
}
return 0;
}
/**
* Fetch MAC address setting
*
* @v netdev Network device
* @v data Buffer to fill with setting data
* @v len Length of buffer
* @ret len Length of setting data, or negative error
*/
static int netdev_fetch_mac ( struct net_device *netdev, void *data,
size_t len ) {
if ( len > netdev->ll_protocol->ll_addr_len )
len = netdev->ll_protocol->ll_addr_len;
memcpy ( data, netdev->ll_addr, len );
return netdev->ll_protocol->ll_addr_len;
}
/**
* Fetch bus type setting
*
* @v netdev Network device
* @v data Buffer to fill with setting data
* @v len Length of buffer
* @ret len Length of setting data, or negative error
*/
static int netdev_fetch_bustype ( struct net_device *netdev, void *data,
size_t len ) {
static const char *bustypes[] = {
[BUS_TYPE_PCI] = "PCI",
[BUS_TYPE_ISAPNP] = "ISAPNP",
[BUS_TYPE_EISA] = "EISA",
[BUS_TYPE_MCA] = "MCA",
[BUS_TYPE_ISA] = "ISA",
};
struct device_description *desc = &netdev->dev->desc;
const char *bustype;
assert ( desc->bus_type < ( sizeof ( bustypes ) /
sizeof ( bustypes[0] ) ) );
bustype = bustypes[desc->bus_type];
assert ( bustypes != NULL );
strncpy ( data, bustype, len );
return strlen ( bustype );
}
/**
* Fetch bus location setting
*
* @v netdev Network device
* @v data Buffer to fill with setting data
* @v len Length of buffer
* @ret len Length of setting data, or negative error
*/
static int netdev_fetch_busloc ( struct net_device *netdev, void *data,
size_t len ) {
struct device_description *desc = &netdev->dev->desc;
uint32_t busloc;
busloc = cpu_to_be32 ( desc->location );
if ( len > sizeof ( busloc ) )
len = sizeof ( busloc );
memcpy ( data, &busloc, len );
return sizeof ( busloc );
}
/**
* Fetch bus ID setting
*
* @v netdev Network device
* @v data Buffer to fill with setting data
* @v len Length of buffer
* @ret len Length of setting data, or negative error
*/
static int netdev_fetch_busid ( struct net_device *netdev, void *data,
size_t len ) {
struct device_description *desc = &netdev->dev->desc;
struct dhcp_netdev_desc dhcp_desc;
dhcp_desc.type = desc->bus_type;
dhcp_desc.vendor = htons ( desc->vendor );
dhcp_desc.device = htons ( desc->device );
if ( len > sizeof ( dhcp_desc ) )
len = sizeof ( dhcp_desc );
memcpy ( data, &dhcp_desc, len );
return sizeof ( dhcp_desc );
}
/**
* Fetch chip setting
*
* @v netdev Network device
* @v data Buffer to fill with setting data
* @v len Length of buffer
* @ret len Length of setting data, or negative error
*/
static int netdev_fetch_chip ( struct net_device *netdev, void *data,
size_t len ) {
const char *chip = netdev->dev->driver_name;
strncpy ( data, chip, len );
return strlen ( chip );
}
/** A network device setting operation */
struct netdev_setting_operation {
/** Setting */
struct setting *setting;
/** Store setting (or NULL if not supported)
*
* @v netdev Network device
* @v data Setting data, or NULL to clear setting
* @v len Length of setting data
* @ret rc Return status code
*/
int ( * store ) ( struct net_device *netdev, const void *data,
size_t len );
/** Fetch setting
*
* @v netdev Network device
* @v data Buffer to fill with setting data
* @v len Length of buffer
* @ret len Length of setting data, or negative error
*/
int ( * fetch ) ( struct net_device *netdev, void *data, size_t len );
};
/** Network device settings */
static struct netdev_setting_operation netdev_setting_operations[] = {
{ &mac_setting, netdev_store_mac, netdev_fetch_mac },
{ &bustype_setting, NULL, netdev_fetch_bustype },
{ &busloc_setting, NULL, netdev_fetch_busloc },
{ &busid_setting, NULL, netdev_fetch_busid },
{ &chip_setting, NULL, netdev_fetch_chip },
};
/**
* Store value of network device setting
*
* @v settings Settings block
* @v setting Setting to store
* @v data Setting data, or NULL to clear setting
* @v len Length of setting data
* @ret rc Return status code
*/
static int netdev_store ( struct settings *settings, struct setting *setting,
const void *data, size_t len ) {
struct net_device *netdev = container_of ( settings, struct net_device,
settings.settings );
struct netdev_setting_operation *op;
unsigned int i;
/* Handle network device-specific settings */
for ( i = 0 ; i < ( sizeof ( netdev_setting_operations ) /
sizeof ( netdev_setting_operations[0] ) ) ; i++ ) {
op = &netdev_setting_operations[i];
if ( setting_cmp ( setting, op->setting ) == 0 ) {
if ( op->store ) {
return op->store ( netdev, data, len );
} else {
return -ENOTSUP;
}
}
}
return generic_settings_store ( settings, setting, data, len );
}
/**
* Fetch value of network device setting
*
* @v settings Settings block
* @v setting Setting to fetch
* @v data Buffer to fill with setting data
* @v len Length of buffer
* @ret len Length of setting data, or negative error
*/
static int netdev_fetch ( struct settings *settings, struct setting *setting,
void *data, size_t len ) {
struct net_device *netdev = container_of ( settings, struct net_device,
settings.settings );
struct netdev_setting_operation *op;
unsigned int i;
/* Handle network device-specific settings */
for ( i = 0 ; i < ( sizeof ( netdev_setting_operations ) /
sizeof ( netdev_setting_operations[0] ) ) ; i++ ) {
op = &netdev_setting_operations[i];
if ( setting_cmp ( setting, op->setting ) == 0 )
return op->fetch ( netdev, data, len );
}
return generic_settings_fetch ( settings, setting, data, len );
}
/**
* Clear network device settings
*
* @v settings Settings block
*/
static void netdev_clear ( struct settings *settings ) {
generic_settings_clear ( settings );
}
/** Network device configuration settings operations */
struct settings_operations netdev_settings_operations = {
.store = netdev_store,
.fetch = netdev_fetch,
.clear = netdev_clear,
};
/**
* Redirect "netX" settings block
*
* @v settings Settings block
* @ret settings Underlying settings block
*/
static struct settings * netdev_redirect ( struct settings *settings ) {
struct net_device *netdev;
/* Redirect to most recently opened network device */
netdev = last_opened_netdev();
if ( netdev ) {
return netdev_settings ( netdev );
} else {
return settings;
}
}
/** "netX" settings operations */
static struct settings_operations netdev_redirect_settings_operations = {
.redirect = netdev_redirect,
};
/** "netX" settings */
static struct settings netdev_redirect_settings = {
.refcnt = NULL,
.siblings = LIST_HEAD_INIT ( netdev_redirect_settings.siblings ),
.children = LIST_HEAD_INIT ( netdev_redirect_settings.children ),
.op = &netdev_redirect_settings_operations,
};
/** Initialise "netX" settings */
static void netdev_redirect_settings_init ( void ) {
int rc;
if ( ( rc = register_settings ( &netdev_redirect_settings, NULL,
"netX" ) ) != 0 ) {
DBG ( "Could not register netX settings: %s\n",
strerror ( rc ) );
return;
}
}
/** "netX" settings initialiser */
struct init_fn netdev_redirect_settings_init_fn __init_fn ( INIT_LATE ) = {
.initialise = netdev_redirect_settings_init,
};