9.5.1 Интерфейс кадрового буфера Linux

Предыдущая  Содержание  Следующая V*D*V

Кадровый буфер на Linux реализован как интерфейс символьного устройства. Это означает, что приложения делают стандартные системные вызовы, такие как open(), read(), write() и ioctl() на указанном имени устройства. Устройство с кадровым буфером доступно в пространстве пользователя как /dev/fb[0-31]. В Таблице 9.2 перечислены интерфейсы и их операции. Первые две операции в списке являются общими для любого другого устройства. Третья, mmap, это то, что делает интерфейс кадрового буфера уникальным. Сейчас немного отойдём от основой темы и обсудим возможности системного вызова mmap().

 

Таблица 9.2 Интерфейс кадрового буфера

 

Интерфейс

Операция

Обычный ввод/вывод

open, read, write с /dev/fb

ioctl

Команды для настройки видеорежима, запроса информации о чипсете, и так далее

mmap

Отображение области памяти видеобуфера в память программ

 

Мощь mmap

 

Драйверы являются частью ядра и, следовательно, работают в памяти ядра, в то время как приложения находятся на стороне пользователя и работают в пользовательской памяти. Единственным интерфейсом, доступным для взаимодействия между драйверами и приложениями являются файловые операции (fops), такие как open, read, write и ioctl. Рассмотрим простую операцию записи. Вызов write происходит от пользовательского процесса, с данными, размещёнными в пользовательском буфере (выделенном в памяти пользовательского пространства), и переданными драйверу. Драйвер выделяет буфер в пространстве ядра и копирует пользовательский буфер в буфер ядра с помощью функции ядра copy_from_user и выполняет с буфером необходимые действия. В случае драйверов с кадровым буфером необходимо скопировать/DMA его для вывода в память кадрового буфера. Если приложение вынуждено писать с определённым смещением, становится необходимо делать вызов seek(), а за ним write() Рисунок 9.7 подробно показывает разные этапы в ходе операции записи.

 

Рисунок 9.7 Поиск/запись с повтором.

Рисунок 9.7 Поиск/запись с повтором.

 

Теперь рассмотрим графическое приложение. Оно должно писать данные по всей области экрана. Возможно, придётся обновить какой-то один прямоугольник или даже весь экран, или иногда просто помигать курсором. Каждое выполнение seek(), а затем write() стоит дорого и отнимает много времени. Для использования в таких приложениях интерфейс fops предлагает API mmap(). Если  драйвер в своей структуре fops реализует mmap(), пользовательское приложение может напрямую получить в пользовательском пространстве отображённый на память эквивалент аппаратного адреса кадрового буфера. Для драйверов класса кадрового буфера реализация mmap() является обязательной (* Обратите внимание, что это не требуется в операционных системах без MMU, таких как VxWorks или uClinux, потому что всё адресное пространство памяти - это единая область. В модели flataddresing любой процесс может обратиться к любой области памяти, независимо ядро это, или пользователя.). Рисунок 9.8 показывает различные шаги при использовании mmap.

 

Рисунок 9.8 Запись через mmap.

Рисунок 9.8 Запись через mmap.

 

Все приложения, работающие с кадровым буфером, просто вызывают open() для /dev/fb, делая необходимой ioctl(), чтобы установить разрешение экрана, размерность пикселя, частоту обновления и так далее, и, наконец, вызывают mmap(). Реализация mmap в драйвере просто отображает полный буфер кадра видеооборудования. В результате приложение получает указатель на память кадрового буфера. Любые изменения, сделанные в этой области памяти, прямо отражаются на экране.

 

Чтобы понять, чем же интерфейс кадрового буфера лучше, мы настроим драйвер кадрового буфера на рабочем столе Linux и напишем простое приложение, использующее кадровый буфер.

 

Установка драйвера кадрового буфера

 

Ядро Linux должно быть загружено с опцией кадрового буфера, чтобы использовать кадровый буфер консоли и проинициализировать драйвер кадрового буфера. Передайте при запуске ядра параметр командной строки vga=0x761 (* Для достижения того же результата можно отредактировать конфигурационные файлы загрузчика /etc/lilo.conf или grub.conf). Число представляет собой режим разрешения  экрана карты; для получения полной информации о том, что означает это число, обратитесь к Documentation/fb.txt в каталоге исходных текстов ядра Linux. Если ядро грузится с активированным режимом кадрового буфера, вы сразу же заметите при загрузке в верхней левой части экрана известный образ пингвина. Кроме того, распечатки ядра будут отображаться шрифтами с высоким разрешением. Для дальнейшего подтверждения просто сделайте для любого файла cat, направив вывод в /dev/fb0. Вы увидите на экране мусор. Если это не сработало, вам, возможно, придётся скомпилировать поддержку кадрового буфера в ядре, а затем повторить попытку. Для получения дополнительной помощи по настройке устройства с кадровым буфером смотрите раздел frame buffer HOWTO, доступный на http://www.tldp.org/.

 

Теперь мы обсудим структуры данных и команды ioctl, которые обеспечивают интерфейс кадрового буфера для пространства пользователя. Обычные графические устройства имеют поддержку нескольких разрешений экрана и режимов. Например, одно устройство может иметь следующие настраиваемые режимы.

 

Режим 1: 640 × 480, цвет 24 бита, RGB 888

Режим 2: 320 × 480, цвет 16 бит, RGB 565

Режим 3: 640 × 480, монохромный, индексный

 

Драйвер устройства сообщает об установленных параметрах в определённом режиме с помощью структуры fb_fix_screeninfo. Иными словами, структура fb_fix_screeninfo определяет зафиксированные или неизменные свойства видеокарты, когда началась работа в том или ином разрешении экрана/режиме.

 

struct fb_fix_screeninfo {

    char id[16];              /*Идентификационная строка, например "ATI Radeon 360"*/

    unsigned long smem_start; /*Начало памяти кадрового буфера*/

    __u32 smem_len;           /*Размер памяти кадрового буфера*/

    __u32 type;               /*один из многих FB_TYPE_XXX*/

    __u32 visual;             /*один из FB_VISUAL_XXX*/

    …

    …

    __u32 line_length;        /*длина строки в байтах*/

    …

    …

};

 

smem_start - это физический начальный адрес памяти кадрового буфера размером smem_len. Поля type и visual указывают формат пикселя и режим цвета. Для чтения структуры fb_fix_screeninfo используется ioctl FBIOGET_FSCREENINFO.

 

fb_fix_screeninfo fix;

ioctl(FrameBufferFD, FBIOGET_FSCREENINFO, &fix)

 

Структура fb_var_screeninfo содержит изменяемые параметры графического режима. Можно использовать эту структуру и  установить необходимый графический режим. Важными членами структуры являются:

 

struct fb_var_screeninfo {

    __u32 xres;         /*видимое разрешение по X, Y*/

    __u32 yres;

    __u32 xres_virtual; /*виртуальное разрешение*/

    __u32 yres_virtual;

    __u32 xoffset;      /*смещение от виртуального к видимому разрешению*/

    __u32 yoffset;

    __u32 bits_per_pixel; /*Число битов в пикселе*/

    __u32 grayscale;    /*!= 0 Уровни серого вместо цветов*/

    …

    …

    struct fb_bitfield red;    /*битовое поле в памяти к/б в режиме true color*/

    struct fb_bitfield green;

    struct fb_bitfield blue;

    struct fb_bitfield transp; /*уровень прозрачности*/

    __u32 nonstd;              /*!= 0 Нестандартный формат пикселя*/

    …

    …

};

 

Вышеупомянутая структура fb_bitfield детализирует размер и смещение битов для каждого цвета пикселя.

 

struct fb_bitfield {

    __u32 offset;    /*начало битового поля*/

    __u32 length;    /*размер битового поля*/

    __u32 msb_right; /*!= 0 : Старший бит справа*/

};

 

Напомним, что разные цветовые режимы, которые были обсуждены, это RGB565, RGB888, и так далее, и элементы fb_bitfield представляют собой то же самое. Например, один пиксель RGB 565 имеет размер 2 байта и имеет формат, показанный на Рисунке 9.9.

 

Рисунок 9.9 Формат пикселя RGB565.

Рисунок 9.9 Формат пикселя RGB565.

 

red.length = 5, red.offset = 16 → 32 оттенка чистого красного

green.length = 6 и green.offset = 11 → 64 оттенка чистого зелёного

blue.length = 5 и blue.offset = 5 → 32 оттенка чистого синего

размер пикселя 16 бит или 2 байта → 65536 всего оттенков.

 

Поля xres и yres представляют собой разрешение видимого экрана по X и Y. Поля xres_virtual и yres_virtual указывают виртуальное разрешение видеокарты, которые могут быть больше, чем видимые координаты. Например, рассмотрим случай, когда видимое разрешение по Y равно 480, а виртуальное разрешение по Y равно 960. Поскольку видны только 480 линий, необходимо указать, какие 480 линий из 960 будут видимы на экране. Для этих целей используются поля xoffset и yoffset. В нашем примере, если yoffset равно 0, то первые 480 строк будут видны или, если yoffset равно 480, видны последние 480 линий. Изменяя только значение смещения программисты могут осуществлять двойную буферизацию и переключение страниц в их графических приложениях (* Двойная буферизация и переключение страниц - это техники графики, в которых изображение отрисовывается в закадровый (невидимый) буфер. Отрисовка изображения на экране выполняется простым переключением указателя).

 

ioctl с FBIOGET_VSCREENINFO возвращает структуру fb_var_screenifno, а ioctl с FBIOSET_VSCREENINFO передаёт настроенную структуру fb_var_screeninfo.

 

fb_var_screeninfo var;

/* Читаем изменяемую информацию */

ioctl(FrameBufferFD, FBIOGET_VCREENINFO, &var);

 

/* Устанавливаем разрешение экрана 1024x768 */

var.xres = 1024; var.yres = 768;

ioctl(FrameBufferFD, FBIOSET_VCREENINFO, &var);

 

Следующей важной структурой в программировании кадрового буфера является структура fb_cmap.

 

struct fb_cmap {

    __u32 start;   /* Первая запись */

    __u32 len;     /* Число записей */

    __u16 *red;    /* Значения цветов */

    __u16 *green;

    __u16 *blue;

    __u16 *transp; /* прозрачность, может быть NULL */

};

 

Каждое поле цвета red, green и blue является массивом из значений цвета длиной len. Таким образом, эта структура представляет собой таблицу карты цветов или CLUT, где значение цвета для любого индекса n получается поиском в таблице по red[n], green[n], blue[n], где start <= n < len. Поле transp используется для указания уровня прозрачности, если это необходимо и не является обязательным (* Поле прозрачности также называют альфа-каналом. Мы добавляем к списку известных режимов новый режим, 32-х битный режим RGBA8888, где A означает альфа-канал (8 бит)).

 

ioctl FBIOGETCMAP используется для чтения из существующей таблицы карты цветов, а ioctl FBIOPUTCMAP программирует/загружает новую таблицу карты цветов/CLUT. (* Загрузка новой CLUT или таблицы цветов упоминается как программирование палитры.)

 

/* Читаем текущую таблицу цветов */

fb_cmap cmap;

/* Инициализируем структура данных cmap */

allocate_cmap(&cmap, 256);

ioctl(FrameBufferFD, FBIOGETCMAP, &cmap);

 

/* Изменяем записи cmap и загружаем 8-ми битовую индексную таблицу цветов */

#define RGB(r, g, b) ((r<<red_offset)|

                    (g << green_offset)|

                    (b << blue_offset))

 

/* Устанавливаем смещение для режима RGB332 */

red_offset = 5; green_offset = 2; blue_offset = 0;

for(r=0;r<3;r++) {

    for(g=0;j<3;g++) {

        for(b=0;b<2;b++) {

            q=RGB(r, g, b);

            cmap.red[q]=r;

            cmap.green[q]=g;

            cmap.blue[q]=b;

        }

    }

}

 

/* Наконец, загружаем нашу CLUT */

ioctl(FrameBufferFD, FBIOPUTCMAP, &cmap);

 

Теперь мы готовы написать нашу программу Hello world для работы с кадровым буфером. Распечатка 9.1 ставит в центре экрана один белый пиксель.

 

Первый шаг достаточно очевиден: открыть устройство с кадровым буфером /dev/fb0 с O_RDWR (чтение/запись). Вторым шагом является чтение структур с информацией об устройстве. Устройство с кадровым буфером имеет две важные структуры: постоянную и переменную. Постоянная информация предназначена только для чтения: читается с помощью ioctl FBIOGET_FSCREENINFO.  Структура fb_fix_screeninfo содержит идентификационную информацию об устройстве с кадровым буфером, информацию о пиксельном формате, поддерживаемом этим устройством и адреса для отображения памяти кадрового буфера. Переменная информация экрана читается с использованием ioctl FBIOGET_VSCREENINFO и записывается с помощью ioctl FBIOSET_VSCREENINFO. Структура fb_var_screeninfo описывает геометрию и временные параметры текущего видеорежима. Следующий шаг - отобразить аппаратный буфер в пространство процесса нашего приложения используя системный вызов mmap().

 

Теперь мы готовы установить наш пиксель. Позаботимся о различных битовых полях и схемах упаковки пикселей. Число бит и смещения для отдельных цветовых каналов предоставляет структура fb_var_screeninfo. Наконец, используя свойство линейного  кадрового буфера (пиксель (х, у) = (line_width * у) + х), мы ставим единственный пиксель. Так много, чтобы установить всего 1 пиксель!

 

Предыдущая  Содержание  Следующая