9.5.1 Интерфейс кадрового буфера Linux |
Предыдущая Содержание Следующая |
Кадровый буфер на Linux реализован как интерфейс символьного устройства. Это означает, что приложения делают стандартные системные вызовы, такие как open(), read(), write() и ioctl() на указанном имени устройства. Устройство с кадровым буфером доступно в пространстве пользователя как /dev/fb[0-31]. В Таблице 9.2 перечислены интерфейсы и их операции. Первые две операции в списке являются общими для любого другого устройства. Третья, mmap, это то, что делает интерфейс кадрового буфера уникальным. Сейчас немного отойдём от основой темы и обсудим возможности системного вызова mmap().
Таблица 9.2 Интерфейс кадрового буфера
Мощь mmap
Драйверы являются частью ядра и, следовательно, работают в памяти ядра, в то время как приложения находятся на стороне пользователя и работают в пользовательской памяти. Единственным интерфейсом, доступным для взаимодействия между драйверами и приложениями являются файловые операции (fops), такие как open, read, write и ioctl. Рассмотрим простую операцию записи. Вызов write происходит от пользовательского процесса, с данными, размещёнными в пользовательском буфере (выделенном в памяти пользовательского пространства), и переданными драйверу. Драйвер выделяет буфер в пространстве ядра и копирует пользовательский буфер в буфер ядра с помощью функции ядра copy_from_user и выполняет с буфером необходимые действия. В случае драйверов с кадровым буфером необходимо скопировать/DMA его для вывода в память кадрового буфера. Если приложение вынуждено писать с определённым смещением, становится необходимо делать вызов seek(), а за ним write() Рисунок 9.7 подробно показывает разные этапы в ходе операции записи.
Рисунок 9.7 Поиск/запись с повтором.
Теперь рассмотрим графическое приложение. Оно должно писать данные по всей области экрана. Возможно, придётся обновить какой-то один прямоугольник или даже весь экран, или иногда просто помигать курсором. Каждое выполнение seek(), а затем write() стоит дорого и отнимает много времени. Для использования в таких приложениях интерфейс fops предлагает API mmap(). Если драйвер в своей структуре fops реализует mmap(), пользовательское приложение может напрямую получить в пользовательском пространстве отображённый на память эквивалент аппаратного адреса кадрового буфера. Для драйверов класса кадрового буфера реализация mmap() является обязательной (* Обратите внимание, что это не требуется в операционных системах без MMU, таких как VxWorks или uClinux, потому что всё адресное пространство памяти - это единая область. В модели flataddresing любой процесс может обратиться к любой области памяти, независимо ядро это, или пользователя.). Рисунок 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.
▪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 пиксель!
| ||||||||
Предыдущая Содержание Следующая |