28.4. Операции над устройством. Поиск устройств

We use cookies. Read the Privacy and Cookie Policy

28.4. Операции над устройством. Поиск устройств

Наш модуль пока еще не может называться «драйвером» в прямом смысле этого слова: устройство-то он регистрирует, но не позволяет выполнить ни одной операции с ним — ведь структура 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 будем добавлять в наш список новые элементы.

Если же вам все-таки хочется узнать конкретное количество устройств /dev/deviceX, установленных у пользователя, можно просто просмотреть содержимое каталога /dev и посчитать количество файлов device*.

Все готово для того, чтобы написать функцию открытия устройства.

Листинг 28.7. Функция открытия устройства

static int device_open(struct inode *inode, struct file *fp) {

 struct device_state *dev_state;

 printk("My module: try to open device with minor number %d ",

  MINOR(inode->i_rdev));

 devastate = &state[MINOR(inode->i_rdev)];

 if (dev_state->dev_open) {

  printk("Devise is busy ");

  return -EBUSY;

 }

 dev_state->dev_open = 1;

 dev_state->byte_read = 0;

 dev_state->byte_write = 0;

 MOD_INC_USE_COUNT;

 return 0;

}

Младший номер устройства мы получаем с помощью вызова MINOR (inode->i_rdev). Если устройство уже открыто, мы выводим сообщение: Devise is busy. В противном случае устанавливаем флаг открытия устройства, обнуляем byte_read и byte_write, а также увеличиваем счетчик использования данного модуля (MOD_INC_USE_COUNT).

Функция закрытия устройства сбрасывает флаг dev_open и уменьшает счетчик использования устройства.

Листинг 28.8. Функция закрытия устройства

static int device_close(struct inode *inode, struct file *fp) {

 struct device_state *dev_state;

 printk("My module: try to close device with minor number %d ",

  MINOR(inode->i_rdev));

 dev_state = &state[MINOR(inode->i_rdev)];

 if (!dev_state->dev_open) {

  printk("Device is not open ");

  return 0;

 }

 dev_state->dev_open=0;

 MOD_DEC_USE_COUNT;

 return 0;

}

Теперь нам нужно указать ядру, какие функции нужно использовать для открытия и закрытия устройства:

struct file_operations FO = {

open: device_open,

release: device_close

};

Полный код модуля устройства device вместе с функциями открытия и закрытия устройства, а также структурой file_operations приведен в следующем листинге:

Листинг 28.9. Модуль устройства device (module.с)

#define MODULE

#define __KERNEL__

#include <linux/module.h>

#include <linux/init.h>

#include <linux/kernel.h>

#include <linux/fs.h> // регистрация устройств

#include <linux/ioport.h> // работа с портами ввода/вывода

#include <linux/sched.h> // резервирование прерывания

// Имя нашего устройства

#define DEV_NAME "device"

// Порты ввода-вывода нашего устройства

#define PORT_START 0x2000

#define PORT_QTY 10

// Память нашего устройства

#define MEM_START 0x20000000

#define MEM_QTY 0x20

// Номер прерывания для нашего устройства

#define IRQ_NUM 9

MODULE_AUTHOR("Denis Kolisnichenko dhsilabs@mail.ru");

MODULE_DESCRIPTION("Linux kernel module");

// Старший номер файла устройства

static int Major;

// Структура file_operations - пока пустая,

// но вскоре мы ее напишем

struct file_operations FO {

 open:    device_open,

 release: device_close

};

// Структура для хранения состояния устройства

struct device_state {

 int dev_open; // 1 - устройство открыто, 0 — закрыто

 ssize_t byte_read; // Количество прочитанных байтов

                    // из устройства

 ssize_t byte_write; // Количество записанных байтов

};

// Массив для хранения информации о состоянии устройств

static struct device_state state[2];

// Обработчик прерывания

void irq_handler(int irq, void *dev_id, struct pt_regs

 *regs) {

 return;

}

int init_module() {

 // Регистрируем устройство

 printk("My module: starting... ") ;

 Major = register_chrdev(0, DEV_NAME, &F0);

 if (Major < 0) {

  // Устройство не зарегистрировано

  printk("My module: registration failed ");

  return Major;

 }

 printk("My module: device registered, major number = %d ",

  Major);

 // Резервирование портов ввода-вывода

 printk("My module: allocating io ports ");

 if (check_region(PORT_START, PORT_QTY)) {

  printk("My module: allocation io ports failed ");

  return -EBUSY;

 }

 request_region(PORT_START, PORT_QTY, DEV_NAME);

 printk("My module: io ports allocated ");

 // Резервирование памяти

 if (check_mem_region(MEM_START, MEM_QTY)) {

  printk(My module: memory allocation failed ");

  release_region(PORT_START, PQRT_QTY);

  return -EBUSY;

 }

 request_mem_region(MEM_START, MEM_QTY, DEV_NAME);

 printk("My module: memory allocated ");

 // Резервирование прерывания

 if (request_irq(IRQ_NUM, irq_handler, 0, DEV_NAME, NULL)) {

  printk("My module: IRQ allocation failed ");

  release_mem_region(MEM_START, MEM_QTY);

  release_region(PORT_START, PORT_QTY);

  return -EBUSY;

 }

 printk("My module: IRQ allocated ");

 return 0;

}

void cleanup_module() {

 // Освобождаем порты ввода-вывода

 release_region(PORT_START, PORT_QTY);

 printk("My module: release io ports ");

 // Освобождаем память

 release_mem_region(MEM_START, MEM_QTY);

 printk("My module: release memory ");

 // Освобождаем прерывание

 free_irq(IRQ_NUM, NULL);

 printk("My module: release irq ");

 // Отменяем регистрацию устройства

 if (unregister_chrdev(Major, DEV_NAME) < 0) {

  printk("My module: cannot to unregister device ");

 }

 printk("My module: device unregistered ");

 return;

}

static int device_open(struct inode *inode,

 struct file *fp) {

 struct device_state *dev_state;

 printk("My module: try to open device with minor number %d ",

  MINOR(inode->i_rdev));

 dev_state = &state[MINOR(inode->i_rdev)];

 if (dev__state->dev_open) {

  printk("Devise is busy ");

  return -EBUSY;

 }

 dev_state->dev_open = 1;

 dev_state->byte_read = 0;

 dev_state->byte_write = 0;

 MOD_INC_USE_COUNT;

 return 0;

}

static int device_close(struct inode *inode, struct file

 *fp) {

 struct device_state *dev_state;

 printk("My module: try to close device with minor number %d ",

  MINOR(inode->i_rdev));

 dev_state = &state[MINOR(inode->i_rdev)];

 if (!dev_state->dev_open) {

  printk("Device is not open ");

  return 0;

 }

 dev_state->dev_open = 0;

 MOD_DEC_USE_COUNT;

 return 0;

}

Теперь модуль для абстрактного устройства device готов. Вы можете написать небольшую программку, которая пыталась бы выполнить операции с нашим устройством: открыть его и закрыть — других операций мы не определили. Для определения других действий используется та же структура file_operations. Листинг 28.10 показывает, как она объявлена в файле /usr/src/linux-2.4/include/linux/fs.h.

Листинг 28.10. Фрагмент файла /usr/src/linux-2.4/include/linux/fs.h

struct file_operations {

 struct module *owner;

 loff_t (*llseek)(struct file*, loff_t, int);

 ssize_t (*read)(struct file*, char*, size_t, loff_t*);

 ssize_t (*write)(struct file*, const char*,

  size_t, loff_t*);

 int (*readdir)(struct file*, void*, filldir_t);

 unsigned int (*poll) (struct file*,

  struct poll_table_struct*);

 int (*ioctl)(struct inode*, struct file*, unsigned int,

  unsigned long);

 int (*mmap)(struct file*, struct vm_area_struct*);

 int (*open)(struct inode*, struct file*);

 int (*flush)(struct file*);

 int (*release)(struct inode*, struct file*);

 int (*fsync)(struct file*, struct dentry*,

  int datasync);

 int (*fasync)(int, struct file*, int);

 int (*lock)(struct file*, int, struct file_lock*);

 ssize_t (*readv)(struct file*, const struct iovec*,

  unsigned long, loff_t*);

 ssize_t (*writev)(struct file*, const struct iovec*,

  unsigned long, loff_t*);

 ssize_t (*sendpage)(struct file*, struct page*, int, size_t,

  loff_t*, int);

 unsigned long (*get_unmapped_area)(struct file*, unsigned long,

  unsigned long, unsigned long, unsigned long);

};

Как использовать структуру file_operations, думаю, ясно. Например, нам нужно описать обработчики записи и чтения устройства — функции device_write() и device_read():

struct file_operations FO = {

 open: device_open,

 release: device_close,

 read: device_read,

 write: device_write

};

Обработчики чтения и записи пишутся «по образу и подобию» обработчиков открытия и закрытия устройства, то есть сначала нам нужно определить младший номер с помощью вызова MINOR(), а затем произвести операцию с устройством.

Данный текст является ознакомительным фрагментом.