[LinuxFocus-icon]
Домой  |  Карта  |  Индекс  |  Поиск

Новости | Архивы | Ссылки | Про LF
эта страница доступна на следующих языках: English  ChineseGB  Deutsch  Francais  Italiano  Russian  Turkce  

[Photo of the Author]
автор Dr. B.Thangaraju
<balasubramanian.thangaraju(at)wipro.com>

Об авторе:
Dr. B. Thangaraju получил диплом професора физики в Университете Bharathidasan и 5 лет занимался исследованиями в Индийской Академии наук. B. Thangaraju опубликовал более 10 научных статей в международных журналах. Он представлял свои исследовательские данные более чем на 7 международных и национальных конференциях

В настоящий время он работает менеджером в Talent Transformation, Wipro Technologies, в Индии. Занимается исследованиями, изучением и упорядочиванием знаний в области Linux Kernel, Device Drivers (драйвера устройств) and Real Time Linux.

Перевод на Русский:
kira <kirunchik(at)tut.by>

Содержание:

 

Выделение портов для драйверов в Linux

[Illustration]

Резюме:

Написание драйвера трудная, но интересная задача. Как только модуль драйвера подключается с помощь функции init_module(), должны быть выделены ресурсы для данного оборудования. Одним из основных ресурсов оборудования является I/O port (порт ввода/вывода). Разработчик должен быть внимательным при указании диапазона портов для своего оборудования. Прежде всего, драйверу необходимо проверить порты: используются ли они либо свободны, только после этого выделяется дипапазон портов для оборудования. Когда модуль удаляется из кернела, порты должны быть освобождены. Эта статья описывает сложности правильного выделения портов для устройств в Линуксе.

_________________ _________________ _________________

 

Введение

Основная цель разработчика драйверов - выделение ресурсов для устройств, таких как I/O порты, память и прерывания. Эта статья пытается объяснить основы подсистемного ввода-вывода и важность выделения ресурсов, особенно I/O портов. Она также разъясняет, как опрашивать, обращаться и освобождать адреса портов для устройств.

Основные элементы аппаратной части компьютера такие как порты, шины и контролеры устройств, входят в состав разнообразных устройств ввода-вывода. Драйвера представляют собой универсальный интерфейс доступа к устройствам для подсистемы ввода-вывода, также как системные вызовы служат стандартным интерфейсом общения приложений и операционных систем. Существует множество устройств подключенных к компьютеру, например, носители информации, такие как диски, ленточные накопители, CD-ROM и дисководы , устройства ввода-вывода: клавиатура, мышка, монитор, устройства обмена информацией: сетевые карты и модемы. Несмотря на большое количество различных устройств, необходимо понять только несколько основных принципов того, как устанавливаются устройства и как они контролируются программным обеспечением.

 

Основные принципы

Устройство состоит из двух частей: электрической (контролер устройства) и механической. Контролер связан с системой через системную шину. Обычно, каждому контролеру выделяется уникальный (не конфликтующий с другими) диапазон портов, которые состоят из четырех наборов регистров. Это регистр статуса (status), контроля (control),ввода данных (data-in) и вывода данных (data-out). Хост считывая биты из регистра статуса, определяет закончено ли выполнение текущей команды, либо готовность устройства к записи, чтению, или определяет тип ошибки. Регистр контроля служит для передачи команд или изменения режима работы устройства. Регистры ввода-вывода используются для передачи данных.

Таким образом, основным интерфейсом общения между процессором и устройством служит набор регистров контроля и статуса. Когда процессор выполняет программу и получает инструкции связанные с устройством, он их выполняет отправляя команды соответствующему устройству. Контролер оборудования выполняет запрошенное действие и изменяет соответствующие биты в статус регистре, а затем ждет. Функцией процессора является периодическая проверка статуса устройства, пока он не обнаружит завершение выполнения команды. Например, драйвер параллельного порта (используемого принтером) обычно проверяет готов ли принтер принять вывод (на печать) и если принтер не готов , то драйвер повторит свой запрос через какое-то время (за которое процессор будет заниматься другими полезными задачами) и так до того момента когда принтер будет готов. Этот механизм улучшает быстродействие системы, т.к в противном случае система ждет готовности устройства не занимаясь другой полезной работой.

Регистры имеют четко определенные адреса в области ввода-вывода. В основном эти адреса присваиваются во время загрузки системы, используя при этом набор параметров определенных в файле конфигурации. Набор адресов должен быть указан для каждого устройства, при их статическом подключении. Это значит, что кернел содержит драйвера для существующих устройств, I/O порты выделенные для устройств обычно находятся в директории proc. Информацию об адресах используемых в данный момент можно получить с помощью команды $cat /proc/interrupts. Первая колонка этого вывода показывает набор портов, а вторая - устройство использующее их. В некоторых операционных системах есть возможность загрузки модулей устройств динамически, т.е когда система работает. Таким образом, любое новое устройство можно подключить к уже работающей системе и контролировать, а так же получать к доступ к нему, с помощью динамически подключаемого модуля драйвера.

Понятие драйвера устройства довольно абстрактно, это низший уровень программного обеспечения, который работает на компьютере, т.к драйвер непосредственно связан с аппаратными особенностями устройства. Каждый драйвер управляет одним устройством (символьным или блочным) или сетью. При запросе приложением определенного устройства кернел обращается к соответствующему драйверу. Драйвер в свою очередь передает команду устройству. Драйвер устройства - набор функций, таких как open, close, read, write, ioctl, llseek и т.д. Когда вы подключаете модуль, то вызывается функция init_module( ), когда удаляете модуль, вызывается функция cleanup_module( ).

Когда устройство зарегистрировано с помощь init_module( ), для устройства самой функцией выделяются ресурсы такие как I/O порты, память и прерывание, необходимые драйверу для корректной работы. При указании неправильного адреса в памяти для данного устройства, кернел выдаст ошибку segmentation fault. Но в случае I/O портов, кернел не выдаст никакой ошибки типа wrong I/O port, однако обращение к уже использующемуся порту, принадлежащему какому-либо устройству, погубит вашу систему. При удалении модуля, надо удалить и устройство, т.е освободить major number и ресурсы функцией cleanup_module( ).

Основная работа драйверов устройств заключается в чтении и записи в I/O порты. Поэтому драйвер должен использовать уникальные порты. Для этого ему необходимо, прежде всего, проверить используется ли адрес порта в данный момент или нет. Как только драйвер убедится, что адреса не используются, он может запросить кернел закрепить эти порты за его устройством.

 

Выделение портов

Теперь мы рассмотрим, как выделить и освободить ресурсы с помощью функций кернела. Данный пример был испробован на Linux 2.4 kernel, но возможна работа в других версиях Linux и Unix систем.

Первое - проверяем диапазон портов:

int check_region (unsigned long start, unsigned long len);

возвращает значение равное 0, если набор портов свободен, или меньше 0 или код ошибки ( -EBUSY или -EINVAL) если значение уже используется. Функция использует два аргумента: start начало исследуемого диапазона и len - количество портов.

Если порт свободен, он может быть закреплен за устройством функцией request_region.

struct resource *request_region (unsigned long start, unsigned long len, char *name);

Первые два аргумента такие же как у предыдущей функции параметр name - имя устройства, которое было указано в адресе. Функция возвращает указатель на структуру, которая для описания ресурсов устройства. Структура описана в <linux/ioport.h>. Она имеет следующий формат:

struct resource {
        const char *name;
        unsigned long start, end;
        unsigned long flags;
        struct resource *parent, *sibiling, *child;
};
Когда модуль удаляется из кернела порты следует освободить, осуществляется это с помощью функции release_region ( ) в cleanup_module ( ). Синтаксис функции следующий:

void release_region ( unsigned long start, unsigned long len);

Объяснения двух аргументов идентичны предыдущим. Три функции описанные выше на самом деле макросы описанные в <linux/ioport.h>.  

Пример

Данная программа является примером выделения и освобождения портов для динамически подключаемых устройств.
#include <linux/fs.h.>
#include <linux/ioport.h.>

struct file_operations fops;
unsigned long start, len;

int init_module (void)
{
 int status;
 start = 0xff90;
 len   = 0x90;

 register_chrdev(254,"ваше устройство",&fops);

 status =  check_region (start, len);
 if (status == 0) {
     printk ("Доступны порты в диапазоне.\n");
     request_region(start,len,"ваше устройство");
 } else {
     printk ("Порты уже используются. Попробуйте другой диапазон.\n");
     return (status);
 }
 return 0;
}

void cleanup_module (void)
{
 release_region(start, len);
 printk (" порты освобождены удачно\n");
 unregister_chrdev(254,"ваше устройство");}
 printk (" ваше устройство удалено\n");
}

Для простоты в этом примере пропущена проверка ошибок. Когда порты выделены это можно проверить командой :
$cat /proc/ioports  

Функции кернела для работы с I/O портами

В Linux есть различные функции для записи и чтения I/O портов. Порты могут быть 8, 16 or 32 битной разрядности. Заголовочный файл <asm/io.h> определяет функции доступа к портам ввода-вывода. Для чтения (inx) и записи(outx) 8, 16 и 32 битных портов используют следующие функции:



__u8 inb (unsigned int port);
void outb (__u8 data, unsigned int port);

__u16 inw (unsigned int port);
void outw(__u16 data, unsigned int port);

__u32 inl (unsigned int prot);
void outl (__u32 data, unsigned int port);


Для передачи данных ввиде строк, используются следующие функции:


void insb(unsigned int port, void *addr, unsigned long count);
void outsb(unsigned int port, void *addr, unsigned long count);


addr адрес памяти из которого или в который передаются данные, count - количество объектов для передачи. Данные читаются или записываются в единственный "port".


void insw(unsigned int port, void *addr, unsigned long count);
void outsw(unsigned int port, void *addr, unsigned long count);

чтение или запись 16 битных значений в 16 битный порт.


void insl(unsigned int port, void *addr, unsigned long count);
void outsl(unsigned int port, void *addr, unsigned long count);

чтение или запись 32 битных значений в 32 битный порт.  

Благодарности

Автор благодарен Mr. Jayasurya V, менеджеру Wipro Technologies, за критику.  

Ссылки

 

Страница отзывов

У каждой заметки есть страница отзывов. На этой странице вы можете оставить свой комментарий или просмотреть комментарии других читателей :
 talkback page 

Webpages maintained by the LinuxFocus Editor team
© Dr. B.Thangaraju, FDL
LinuxFocus.org
Translation information:
en --> -- : Dr. B.Thangaraju <balasubramanian.thangaraju(at)wipro.com>
en --> ru: kira <kirunchik(at)tut.by>

2002-11-16, generated by lfparser version 2.31