A user might occasionally want to change the interface configuration at run time. If, for example, the IRQ number can’t be probed for, the only way to have it properly configured is through a trial-and-error technique. A user-space program can retrieve the device’s current configuration or set a new configuration by invoking ioctl on an open socket. The ifconfig application, for instance, uses ioctl to set the I/O port for an interface.
We saw earlier how one of the methods defined for network interface is set_config. The method is used to set or change some interface features at run time.
When a program asks for the current configuration, the kernel
extracts the information from struct device
without notifying
the driver; on the other hand, when a new configuration is passed to
the interface, the set_config method is called so the driver can
check the values being passed and take appropriate action.
The driver method responds to the following prototype:
int (*set_config)(struct device *dev, struct ifmap *map);
The map
argument points to a copy of the structure passed
by the user program; the copy is already in kernel space, so
the driver doesn’t need to call memcpy_from_fs.
The fields of struct ifmap
are:
unsigned long mem_start;
,
unsigned long mem_end;
,
unsigned short base_addr;
,
unsigned char irq;
,
unsigned char dma;
These fields correspond to the fields in
struct device
.
unsigned char port;
This field corresponds to if_port
, as found
in dev
. The meaning of map->port
is
device-specific.
The set_config device method is called when a
process issues ioctl(SIOCSIFMAP)
(Socket I/O Control Set
InterFace MAP) for the device. The process should issue
ioctl(SIOCGIFMAP)
(Socket I/O Control Get InterFace MAP)
before trying to force new values on, so the driver will just look for
mismatches between struct dev
and struct ifmap
. Any fields in map
that are not used by the
driver can be skipped. For instance, a network device not using DMA
ignores map->dma
.
The snull implementation is designed to show how the driver can behave with respect to configuration changes. None of the fields has any physical sense for the snull driver. But for the sake of illustration, the code prohibits changes to the I/O address, allows changes to the IRQ number, and ignores other options in order to show how the changes are acknowledged, refused, or ignored.
int snull_config(struct device *dev, struct ifmap *map) { if (dev->flags & IFF_UP) /* can't act on a running interface */ return -EBUSY; /* Don't allow changing the I/O address */ if (map->base_addr != dev->base_addr) { printk(KERN_WARNING "snull: Can't change I/O address "); return -EOPNOTSUPP; } /* Allow changing the IRQ */ if (map->irq != dev->irq) { dev->irq = map->irq; /* request_irq() is delayed to open-time */ } /* ignore other fields */ return 0; }
The return value of the method is used as the return value for the
outstanding ioctl system call, and -EOPNOTSUPP
is
returned for drivers that don’t implement set_config.
If you are curious about how the interface configuration is
accessed from user space, look in misc-progs/netifconfig.c
,
which can be used to play with set_config. Here is the output
from a sample run:
morgana.root#./netifconfig sn0
sn0: mem=0x0-0x0, io=0x0, irq=0, dma=0, port=0 morgana.root#./netifconfig sn0 irq=4
./netifconfig: ioctl(SIOCSIFMAP): Device or resource busy morgana.root#ifconfig sn0 down
morgana.root#./netifconfig sn0 irq=4 tell
sn0: mem=0x0-0x0, io=0x0, irq=4, dma=0, port=0 morgana.root#./netifconfig eth0
eth0: mem=0x0-0x0, io=0x300, irq=5, dma=0, port=0 morgana.root#./netifconfig eth0 io=0x400
./netifconfig: ioctl(SIOCSIFMAP): Operation not supported on transport endpoint
18.217.144.32