Читаем Linux: Полное руководство онлайн бесплатно
if (unregister_chrdev(Major, DEV_NAME) < 0){
printk("My module: cannot to unregister device\n");
}
printk("My module: device unregistered\n");
return;
}
При загрузке модуля вы увидите следующее сообщение:
My module: device registered, major number = 255
Конечно, кроме этого сообщения будут и другие, но нас они не интересуют. Почему именно это сообщение так важно для нас? В первой части сообщения говорится, что наше устройство успешно зарегистрировано, а во второй сообщается старший номер устройства, который мы будем использовать для создания устройств /dev/device0 и /dev/device1.
Вы не забыли, что нам еще нужно создать два устройства типа device, чтобы программы могли работать с ними? Перейдите в каталог /dev и от имени суперпользователя выполните команды:
# mknod device с 255 0
# mknod device с 255 1
Здесь 255 — это старший номер устройства (у вас он будет другим), 0 и 1 — младшие номера устройств. После выполнения данных команд будут созданы два файла устройств — /dev/device0 и /dev/device1.
После регистрации устройства функцией register_chrdev() мы пытаемся захватить диапазон портов. Для этого предназначена функция request_region(), но перед ее вызовом мы должны убедиться, что нужный нам диапазон не используется (функция check_region()). Затем, если нужно, мы резервируем память для нашего устройства. Для резервирования памяти используется функция request_mem_region(), а для проверки возможности захвата памяти предназначена функция check_mem_region(). После успешной регистрации памяти можно попытаться захватить IRQ — функция request_irq().
Предположим, что на каком-то этапе регистрации модуля произошла ошибка. Если мы не смогли зарегистрировать порты ввода/вывода, вряд ли имеет смысл продолжать работу. Если же ошибка произошла при резервировании памяти, то перед завершением работы модуля нам нужно освободить порты ввода/вывода, которые мы зарегистрировали на предыдущем этапе. Аналогично поступаем при ошибке захвата IRQ — освобождаем порты и память. Функции release_mem_region(), release_region() и free_irq() используются для освобождения памяти, портов и IRQ соответственно.
Обратите внимание: мы написали драйвер так, что он захватывает порты и память от имени одного устройства — DEV_NAME. В реальности все гораздо сложнее: нужно захватывать ресурсы для каждого устройства данного типа. К тому же придется предусмотреть поиск устройств модулем: в нашем случае мы знаем, что устройств только два, но у конечного пользователя таких устройств может быть больше или меньше, поэтому наш модуль не будет универсален, если он будет поддерживать только два устройства.
Наш модуль пока еще не может называться «драйвером» в прямом смысле этого слова: устройство-то он регистрирует, но не позволяет выполнить ни одной операции с ним — ведь структура file_operations пуста.
Кроме структуры file_operations нам еще понадобится структура для хранения информации о состоянии устройства, а так как устройств у нас два, то нужен также массив структур для хранения состояния каждого устройства. Индексами массива будут младшие номера устройств.
// Структура для хранения состояния устройства
struct device_state {
int dev_open; // 1 - устройство открыто, 0 - закрыто
ssize_t byte_read; // Количество прочитанных
// из устройства байтов
ssize_t byte_write; // Количество записанных байтов
};
// Массив для хранения информации о состоянии устройств
static struct device_state state[2];
В принципе, можно обойтись и без кода поиска устройств — без него модуль будет проще (а значит, надежнее), да и работать он будет быстрее. Обойти поиск устройств можно следующим образом. Мы не знаем, сколько устройств типа device будет у конечного пользователя. Поэтому вместо массива state (он будет описан ниже) нужно использовать динамический список, который будет содержать информацию о каждом устройстве типа device. При загрузке модуля список будет содержать всего один элемент — для устройства /dev/device0. Если устройств этого типа в системе нет вообще, будем просто считать, что устройство device0 закрыто, а при попытке обращения к нему будем сообщать, что оно занято. По мере поступления запросов программ на открытие других устройств /dev/deviceX будем добавлять в наш список новые элементы.




