Linux Device Drivers (3rd Edition) [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Linux Device Drivers (3rd Edition) [Electronic resources] - نسخه متنی

Jonathan Corbet, Greg Kroah-Hartman, Alessandro Rubini

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








17.2. Connecting to the Kernel


We start looking at the structure of network
drivers by dissecting the snull source. Keeping
the source code for several drivers handy might help you follow the
discussion and to see how real-world Linux network drivers operate.
As a place to start, we suggest loopback.c,
plip.c, and e100.c, in
order of increasing complexity. All these files live in
drivers/net, within the kernel source tree.


17.2.1. Device Registration


When a driver module is loaded into
a

running kernel, it requests resources and offers facilities;
there's nothing new in that. And
there's also nothing new in the way resources are
requested. The driver should probe for its device and its hardware
location (I/O ports and IRQ line)but not register
themas described in Section 10.2.
The way a network driver
is registered by its module initialization function is different from
char and block drivers. Since there is no equivalent of major and
minor numbers for network interfaces, a network driver does not
request such a number. Instead, the driver inserts a data structure
for each newly detected interface into a global list of network
devices.


Each
interface is described by a struct net_device
item, which is defined in
<linux/netdevice.h>. The
snull driver keeps pointers to two of these
structures (for sn0 and sn1) in
a simple array:

struct net_device *snull_devs[2];

The net_device structure, like many other kernel
structures, contains a kobject and is, therefore, reference-counted
and exported via sysfs. As with other such structures, it must be
allocated dynamically. The kernel function provided to perform this
allocation is alloc_netdev, which has the
following prototype:

struct net_device *alloc_netdev(int sizeof_priv, 
const char *name,
void (*setup)(struct net_device *));

Here, sizeof_priv is the size of the
driver's "private
data" area; with network devices, that area is
allocated along with the net_device structure. In
fact, the two are allocated together in one large chunk of memory,
but driver authors should pretend that they don't
know that. name is the name of this interface, as
is seen by user space; this name can have a
printf-style %d in it. The
kernel replaces the %d with the next available
interface number. Finally, setup is a pointer to
an initialization function that is called to set up the rest of
the net_device
structure. We get to the initialization function shortly, but, for
now, suffice it to say that snull allocates its
two device structures in
this way:

snull_devs[0] = alloc_netdev(sizeof(struct snull_priv), "sn%d",
snull_init);
snull_devs[1] = alloc_netdev(sizeof(struct snull_priv), "sn%d",
snull_init);
if (snull_devs[0] = = NULL || snull_devs[1] = = NULL)
goto out;

As always, we must check the return value to ensure that the
allocation succeeded.

The networking subsystem provides a number of helper functions
wrapped around alloc_netdev for various types of
interfaces. The most common is alloc_etherdev,
which is defined in <linux/etherdevice.h>:

struct net_device *alloc_etherdev(int sizeof_priv);

This function allocates a network device using
eth%d for the name argument. It provides its own
initialization function (ether_setup) that sets
several net_device fields with appropriate values
for Ethernet devices. Thus, there is no driver-supplied
initialization function for alloc_etherdev; the
driver should simply do its required initialization directly after a
successful allocation. Writers of drivers for other types of devices
may want to take advantage of one of the other helper functions, such
as alloc_fcdev (defined in
<linux/fcdevice.h>) for fiber-channel
devices, alloc_fddidev
(<linux/fddidevice.h>) for FDDI devices,
or alloc_trdev
(<linux/trdevice.h>) for token ring
devices.

snull could use
alloc_etherdev without trouble; we chose to use
alloc_netdev instead, as a way of demonstrating
the lower-level interface and to give us control over the name
assigned to the interface.

Once the net_device
structure has been initialized, completing
the process is just a matter of passing the structure to
register_netdev. In snull,
the call looks as follows:

for (i = 0; i < 2;  i++)
if ((result = register_netdev(snull_devs[i])))
printk("snull: error %i registering device \"%s\"\n",
result, snull_devs[i]->name);

The usual cautions apply here: as soon as you call
register_netdev, your driver may be called to
operate on the device. Thus, you should not register the device until
everything has been completely initialized.


17.2.2. Initializing Each Device


We have looked at the
allocation

and registration of net_device structures, but we
passed over the intermediate step of completely initializing that
structure. Note that struct net_device is always
put together at runtime; it cannot be set up at compile time in the
same manner as a file_operations or
block_device_operations structure. This
initialization must be complete before
calling

register_netdev. The
net_device structure is large and complicated;
fortunately, the kernel takes care of some Ethernet-wide defaults
through the
ether_setup

function (which is called by alloc_etherdev).

Since snull uses
alloc_netdev
,
it has a separate initialization function. The core of this function
(snull_init) is as follows:

ether_setup(dev); /* assign some of the fields */
dev->open = snull_open;
dev->stop = snull_release;
dev->set_config = snull_config;
dev->hard_start_xmit = snull_tx;
dev->do_ioctl = snull_ioctl;
dev->get_stats = snull_stats;
dev->rebuild_header = snull_rebuild_header;
dev->hard_header = snull_header;
dev->tx_timeout = snull_tx_timeout;
dev->watchdog_timeo = timeout;
/* keep the default flags, just add NOARP */
dev->flags |= IFF_NOARP;
dev->features |= NETIF_F_NO_CSUM;
dev->hard_header_cache = NULL; /* Disable caching */


The above code is a fairly routine
initialization of the net_device structure; it is
mostly a matter of storing pointers to our various driver functions.
The single unusual feature of the code is setting
IFF_NOARP in the flags. This specifies that the
interface cannot use the Address Resolution Protocol (ARP). ARP
is a low-level Ethernet protocol; its job is to turn IP addresses
into Ethernet
medium
access control (MAC) addresses. Since the
"remote" systems simulated by
snull do not really exist, there is nobody
available to answer ARP requests for them. Rather than complicate
snull with the addition of an ARP
implementation, we chose to mark the interface as being unable to
handle that protocol. The assignment to
hard_header_cache is there for a similar reason:
it disables the caching of the (nonexistent) ARP replies on this
interface. This topic is discussed in detail in Section 17.11 later in this chapter.

The initialization code also sets a
couple of fields (tx_timeout and
watchdog_timeo) that relate to the handling of
transmission timeouts. We cover this topic thoroughly in the section
Section 17.5.2.

We look now
at one more struct net_device field,
priv. Its role is similar to that of the
private_data pointer that we used for char
drivers. Unlike fops->private_data, this
priv pointer is allocated along with the
net_device structure. Direct access to the
priv field is also discouraged, for performance
and flexibility reasons. When a driver needs to get access to the
private data pointer, it should use the netdev_priv
function. Thus, the snull driver is
full of declarations such as:

struct snull_priv *priv = netdev_priv(dev);

The snull module declares a
snull_priv data structure to be used for
priv:

struct snull_priv {
struct net_device_stats stats;
int status;
struct snull_packet *ppool;
struct snull_packet *rx_queue; /* List of incoming packets */
int rx_int_enabled;
int tx_packetlen;
u8 *tx_packetdata;
struct sk_buff *skb;
spinlock_t lock;
};


The
structure includes, among other things, an instance of
struct net_device_stats, which
is the standard place to hold interface statistics. The following
lines in snull_init allocate and initialize
dev->priv:

priv = netdev_priv(dev);
memset(priv, 0, sizeof(struct snull_priv));
spin_lock_init(&priv->lock);
snull_rx_ints(dev, 1); /* enable receive interrupts */


17.2.3. Module Unloading


Nothing special happens

when
the module is unloaded. The module cleanup function simply
unregisters the interfaces, performs whatever internal cleanup is
required, and releases the net_device structure
back to the system:

void snull_cleanup(void)
{
int i;
for (i = 0; i < 2; i++) {
if (snull_devs[i]) {
unregister_netdev(snull_devs[i]);
snull_teardown_pool(snull_devs[i]);
free_netdev(snull_devs[i]);
}
}
return;
}

The call to
unregister_netdev

removes the interface from the system;
free_netdev returns the
net_device structure to the kernel. If a reference
to that structure exists somewhere, it may continue to exist, but
your driver need not care about that. Once you have unregistered the
interface, the kernel no longer calls its methods.

Note that our internal cleanup (done in
snull_teardown_pool) cannot happen until the
device has been unregistered. It must, however, happen before we
return the net_device structure to the system;
once we have called
free_netdev
,
we cannot make any further references to the device or our private
area.


    / 202