Linux Device Drivers 3 / Драйверы устройств Линукс (третье издание): 17.2. Соединение с ядром

Оглавление
Английский оригинал Перевод на русский

Сожалеем, но текст оригинала доступен только зарегистрированным пользователям.

17.2. Соединение с ядром

hardorsoft 26.10.10 в 01:26

0

Мы начнем изучение структуры сетевого драйвера, на примере
snull.
Наличие исходных кодов нескольких драйверов, помогут вам
следить за обсуждением, и видеть, как работают драйвера Linux. Для
начала мы рекомендуем рассмотреть, в порядке возрастания сложности,
файлы loopback.c, plip.c и e100.c. Их можно найти в дереве исходных
кодов ядра drivers/net.

hardorsoft 26.10.10 в 01:26

0

17.2.1. Регистрация устройства

hardorsoft 26.10.10 в 01:27

0

Удобно, когда модуль драйвера загружается в работающее ядро, но
на это нужно потратить ресурсы. В этом нет ничего нового. Драйвер
должен протестировать устройство и найти его размещение (определить
порты ввода/вывода и прерывания), но не регистрировать их, как описано
в главе 10 «Инсталляция обработчиков прерываний». Сетевой драйвер
выполняет эти регистрации в функции инициализации модуля, и этим он
отличается от символьных и блочных устройств. Помимо этого имеется
еще одно отличие сетевого интерфейса – сетевой драйвер не запрашивает
старший и младший номера устройства. Вместо этого драйвер создает
новую структуру данных для каждого нового обнаруженного интерфейса в
глобальном списке сетевых устройств.

hardorsoft 26.10.10 в 01:27

0

Каждый интерфейс описывается структурой net_device, которая
определена в файле <linux></linux>netdevice.h>. snull драйвер хранит
указатели на две такие структуры (для интерфейса sn0 и sn1) в простом
массиве:

hardorsoft 26.10.10 в 01:27

0

struct net_device *snull_devs[2];

hardorsoft 26.10.10 в 01:27

0

Структура net_device, подобно многим другим структурам ядра,
содержит kobject, и, следовательно, счетчик ссылок, и экспортируется
через sysfs. Как и другие такие структуры должна создаваться
динамически. Для этого ядро предоставляет функцию:

hardorsoft 26.10.10 в 01:27

0

struct net_device *alloc_netdev(int sizeof_priv,
const char *name,
void (*setup)(struct net_device *));
Здесь sizeof_priv – размер пространства «внутренних данных»
драйвера. Эта область размещается вместе со структурой net_device.
Фактически вместо 2-х выделений памяти производится 1 большое, но
драйвер делает вид, что не знает об этом. Name – имя интерфейса, каким
оно будет в пространстве пользователя. Это имя может иметь printf-стиль
– содержать «%d». Ядро заменяет «%d» доступным номером интерфейса.
setup – указатель на функцию инициализации, которая используется для
настройки структуры net_device. Мы кратко рассмотрели функцию
инициализации, и этого пока достаточно. Итак, snull требует выделения
2-х структур для устройств:

hardorsoft 26.10.10 в 01:28

0

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;
Как всегда, мы должны проверить возвращаемое значение, чтобы
убедиться, что выделение памяти прошло успешно.

hardorsoft 26.10.10 в 01:28

0

Для alloc_netdev, сетевая подсистема предоставляет
вспомогательные функции-обертки, чтобы использовать их для различных
типов интерфейсов. Наиболее общая - alloc_etherdev, определена в
#include /linux/etherdevice.h&gt;:

hardorsoft 26.10.10 в 01:28

0

struct net_device *alloc_etherdev(int sizeof_priv);

hardorsoft 26.10.10 в 01:29

0

Эта функция выделяет память под структуру сетевого устройства,
используя eth%d в качестве имени аргумента. А так же производит
инициализацию (с помощью функции ether_setup) некоторых полей
net_device, со значениями подходящими для Ethernet-устройств. Таким
образом упрощается инициализация, после динамического выделения
памяти. Разработчики драйверов других типов устройств, возможно,
захотят использовать еще какую-либо вспомогательную функцию, как
например, alloc_fcdev (определенную в <linux></linux>fcdevice.h>) для
оптоволоконных устройств, alloc_fddidev (<linux></linux>fddidevice.h>) для
FDDI устройств (Fiber Distributed Data Interface - волоконно-оптический
распределенный информационный интерфейс), или alloc_trdev
(<linux></linux>trdevice.h>) для сетевых устройств с маркерным доступом (token
ring).

hardorsoft 26.10.10 в 01:29

0

В snull можно было бы использовать alloc_etherdev, но мы будем
использовать alloc_netdev, чтобы продемонстрировать низкоуровневый
интерфейс, и назначить имя интерфейса.

hardorsoft 26.10.10 в 01:31

0

После инициализации структуры net_device, необходимо передать
их в функцию register_netdev, как показано ниже:

hardorsoft 26.10.10 в 01:31

0

for (i = 0; i &lt; 2; i++)
if ((result = register_netdev(snull_devs[i])))
printk("snull: error %i registering device \"%s\"\n",
result, snull_devs[i]->name);
Будьте внимательны, как только вы вызвали register_netdev, ваш
драйвер может быть использован устройством. Не вызывайте
register_netdev, до тех пор, пока полностью не закончите
инициализацию.

hardorsoft 26.10.10 в 01:32

0

17.2.2. Инициализация каждого устройства

hardorsoft 26.10.10 в 01:32

0

Мы рассмотрели создание и регистрацию структур net_device.
Теперь сделаем промежуточный шаг инициализации этих структур.
Структура net_device создается во время выполнения, и не может быть
создана на этапе компиляции, так же как структуры file_operations
или block_device_operations.
Инициализация должна быть выполнена до вызова register_netdev.
Структура net_device большая и сложная, к счастью для Ethernet-
устройств, ядро заполняет ее большую часть при вызове функции
ether_setup (котрая вызывается из alloc_etherdev).

hardorsoft 26.10.10 в 01:32

0

После использования в snull функции alloc_netdev, остановимся
на функции инициализации. Концепция этой функции(snull_init),
приведена ниже:

hardorsoft 26.10.10 в 01:33

0

ether_setup(dev); /* Инициализация некоторых полей */
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;
/* добавим флаг NOARP, к флагам по умолчанию */
dev->flags |= IFF_NOARP;
dev->features |= NETIF_F_NO_CSUM;
dev->hard_header_cache = NULL; /* запретим кеширование */

hardorsoft 26.10.10 в 01:33

0

В начале происходит инициализация структуры net_device. В
основном настраиваются указатели на методы нашего драйвера.
Единственная не обычная особенность этого кода – установка флага
IFF_NOARP. Дело в том, что интерфейс не может использовать протокол
определения адресов ARP (Address Resolution Protocol). ARP –
низкоуровневый протокол Ethernet. Его задача заключается в
определении MAC-адреса по IP-адресу (medium access control). Поскольку
удаленные системы, моделируемые snull, реально не существуют, некому
ответить на запрос ARP. Вместо дополнительной реализации ARP в snull ,
мы просто помечаем, что не можем работать с этим протоколом. По той
же причине происходит присвоение hard_header_cache = NULL. Это
присвоение запрещает кэширование несуществующих ARP ответов в этом
интерфейсе. Даная тема обсуждается в подразделе «Определение MAC
адресов» этой главы.

hardorsoft 26.10.10 в 01:33

0

Код инициализации так же устанавливает два поля tx_timeout и
watchdog_timeo, эти поля содержат обработчики таймаутов передачи. Эта
рассматривается в подразделе «Таймауты передачи».

hardorsoft 26.10.10 в 01:34

0

Рассмотрим еще одно поле структуры net_device - priv. Ее
назначение аналогично указателю private_data, который мы
использовали для символьных устройств. Отличие от fops->private_data,
в том, что под priv выделяется память одновременно с выделением под
net_device. Прямой доступ к полю priv не одобряется, это сделано в
целях повышения производительности и гибкости. Для доступа к этому
полю драйвер snull должен использовать функцию netdev_priv:

hardorsoft 26.10.10 в 01:34

0

struct snull_priv *priv = netdev_priv(dev);

hardorsoft 26.10.10 в 01:34

0

для поля Priv, модуль snull объявляет структуру snull_priv:

hardorsoft 26.10.10 в 01:34

0

struct snull_priv {
struct net_device_stats stats;
int status;
struct snull_packet *ppool;
struct snull_packet *rx_queue; /* список входящих пакетов */
int rx_int_enabled;
int tx_packetlen;
u8 *tx_packetdata;
struct sk_buff *skb;
spinlock_t lock;
};

hardorsoft 26.10.10 в 01:34

0

Здесь, помимо всего прочего, содержится экземпляр структуры
net_device_stats, которая является стандартным хранилищем
статистических данных интерфейса. Следующие строки инициализируют
dev->priv:

hardorsoft 26.10.10 в 01:35

0

priv = netdev_priv(dev);
memset(priv, 0, sizeof(struct snull_priv));
spin_lock_init(&priv->lock);
snull_rx_ints(dev, 1); /* Разрешение прерываний */

hardorsoft 26.10.10 в 01:35

0

17.2.3. Выгрузка модуля

hardorsoft 26.10.10 в 01:35

0

При выгрузке модуля ничего особенного не происходит. Функция
выгрузки отменяет регистрацию интерфейса, выполняет внутренние
операции по выгрузке, и освобождает структуру net_device.

hardorsoft 26.10.10 в 01:35

0

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

hardorsoft 26.10.10 в 01:36

0

Вызов unregister_netdev удаляет интерфейс из системы.
free_netdev освобождает выделенную память. Если ссылки на эту
структуру существуют где-нибудь еще, они могут продолжать
использоваться, и ваш драйвер не должен об этом беспокоиться. После
отмены регистрации, ядро не станет вызывать методы интерфейса.

hardorsoft 26.10.10 в 01:36

0

Имейте в виду, что внутренние операции по выгрузке (выполняемые
в snull_teardown_pool), должны производиться после отмены
регистрации, и до выполнения free_netdev.

hardorsoft 26.10.10 в 01:36

0
Минутку...