17.4. Opening and Closing
Our driver can probe for the
interface at module load time
or at kernel boot. Before the interface can carry packets, however,
the kernel must open it and assign an address to it. The kernel opens
or closes an interface in response to the
ifconfig command.
When ifconfig is used to assign an address to
the interface, it performs two tasks. First, it assigns the address
by means of ioctl(SIOCSIFADDR) (Socket I/O Control
Set Interface Address). Then it sets the IFF_UP
bit in dev->flag by means of
ioctl(SIOCSIFFLAGS) (Socket I/O Control Set
Interface Flags) to turn the interface on.
As far as the device is concerned,
ioctl(SIOCSIFADDR) does nothing. No driver
function is invoked—the task is device independent, and the
kernel performs it. The latter command
(ioctl(SIOCSIFFLAGS)), however, calls the
open method for the device.
Similarly, when the interface is shut down,
ifconfig uses
ioctl(SIOCSIFFLAGS) to clear
IFF_UP, and the stop method
is called.
Both device methods return 0 in case of success
and the usual negative value in case of error.
As far as the actual code is concerned, the driver has to perform
many of the same tasks as the char and block drivers do.
open requests any system resources it needs and
tells the interface to come up; stop shuts down
the interface and releases system resources. Network drivers must
perform some additional steps at open time,
however.
First, the hardware (MAC) address needs to
be copied from the hardware device to
dev->dev_addr before the interface can
communicate with the outside world. The hardware address can then be
copied to the device at open time. The snull
software interface assigns it from within open;
it just fakes a hardware number using an ASCII string of length
ETH_ALEN, the length of Ethernet hardware
addresses.
The open method
should also start the interface's transmit queue
(allowing it to accept packets for transmission) once it is ready to
start sending data. The kernel provides a function to start the
queue:
void netif_start_queue(struct net_device *dev);
The open code for snull
looks like the following:
int snull_open(struct net_device *dev)
{
/* request_region( ), request_irq( ), .... (like fops->open) */
/*
* Assign the hardware address of the board: use "\0SNULx", where
* x is 0 or 1. The first byte is '\0' to avoid being a multicast
* address (the first byte of multicast addrs is odd).
*/
memcpy(dev->dev_addr, "\0SNUL0", ETH_ALEN);
if (dev = = snull_devs[1])
dev->dev_addr[ETH_ALEN-1]++; /* \0SNUL1 */
netif_start_queue(dev);
return 0;
}
As you can see, in the absence of real hardware, there is little to
do in the open method. The same is true of the
stop method; it just reverses the operations of
open. For this reason, the function implementing
stop is often called close
or release.
int snull_release(struct net_device *dev)
{
/* release ports, irq and such -- like fops->close */
netif_stop_queue(dev); /* can't transmit any more */
return 0;
}
The
function:
void netif_stop_queue(struct net_device *dev);
is the opposite of netif_start_queue; it marks
the device as being unable to transmit any more packets. The function
must be called when the interface is closed (in the
stop method) but can also be used to temporarily stop
transmission, as explained in the next section.
|