Консольные драйверы |
Предыдущая Содержание Следующая |
Консоль является устройством, которое показывает сообщения printk(), генерируемые ядром. Если посмотреть на Рисунок 12.5, то можно увидеть, что консольные драйверы лежат в двух ярусах: верхний уровень составляют такие драйверы, как драйвер виртуального терминала, консольный драйвер печати и, например, драйвер консоли USB_UART (обсуждается в ближайшее время), а низкий уровень - драйверы, которые несут ответственность за дополнительные операции. Следовательно, есть две основных определённых структуры интерфейса, используемые консольными драйверами. Высокоуровневые консольные драйверы вращаются вокруг структуры console, которая определяет такие основные операции, как setup() и write(). Низкоуровневые драйверы сконцентрированы на структуре consw, которая определяет дополнительные операции, такие, как установка свойств курсора, переключение консоли, гашение, изменение размеров и установка информации палитры. Эти структуры определяются в include/linux/console.h следующим образом:
1.struct console { 2.struct consw {
Как вы уже могли догадаться, глядя на Рисунок 12.5, большинство консольных устройств требуют драйверов обоих уровней, работающих в тандеме. Во многих ситуациях драйвером консоли верхнего уровня является vt. В ПК-совместимых системах, консольный драйвер VGA (vgacon) является обычно низкоуровневым консольным драйвером, тогда как на встроенных устройствах низкоуровневым драйвером часто является консольный драйвер кадрового буфер (fbcon). Из-за косвенности, предлагаемой абстракцией кадрового буфера, fbcon, в отличие от других низкоуровневых консольных драйверов, является аппаратно-независимым.
Давайте кратко рассмотрим архитектуру обоих уровней консольных драйверов:
•Драйвер верхнего уровня заполняет структуру console заданными точками входа и регистрирует её в ядре с помощью register_console(). Отмена регистрации осуществляется с использованием unregister_console(). Это драйвер, который взаимодействует с printk(). Входные точки, принадлежащие этому драйверу, вызываются службы связанного низкоуровневого драйвера консоли. •Низкоуровневый драйвер консоли заполняет структуру consw заданными точками входа и регистрирует её в ядре с помощью register_con_driver(). Отмена регистрации осуществляется с помощью unregister_con_driver(). Когда система поддерживает несколько драйверов консоли, чтобы зарегистрировать себя, драйвер может вместо этого вызвать take_over_console() и взять на себя обслуживание существующей консоли. give_up_console() выполняет обратное. Для обычных дисплеев низкоуровневые драйверы взаимодействуют с консольным драйвером верхнего уровня vt и символьным (текстовым) драйвером vc_screen, который позволяет получить доступ к памяти консоли. Некоторым простым консолям, таким, построчно-печатающие устройства и USB_UART, обсуждаемые далее, необходим только высокоуровневый драйвер консоли.
Драйвер fbcon в ядре 2.6 также поддерживает поворот консоли. Дисплейные панели на КПК и мобильных телефонах обычно монтируются в портретной ориентации, в то время как автомобильные панели и IP телефоны являются примерами систем, где дисплейная панель находится в альбомном режиме. Иногда из-за экономических или других факторов встроенные устройства могут потребовать альбомный LCD, смонтированный в режиме портрета, или наоборот. В таких ситуациях удобна поддержка поворота консоли. Поскольку fbcon аппаратно-независим, реализация поворота консоли также универсальна. Для включения поворота консоли включите при конфигурации ядра CONFIG_FRAMEBUFFER_CONSOLE_ROTATION и добавьте в командную строчку ядра fbcon=rotate:X, где X равно 0 для нормальной ориентации, 1 для поворота на 90 градусов, 2 для поворота на 180 градусов, и 3 для поворота на 270 градусов. Пример устройства: Снова сотовый телефонЧтобы узнать, как писать консольные драйверы, давайте вновь обратимся к сотовому телефону с Linux, который мы использовали в Главе 6. Наша задача в этом разделе заключается в разработке драйвера консоли, который работает в мобильном телефоне поверх USB_UART. Для удобства на Рисунке 12.7 воспроизводится сотовый телефон из Рисунка 6.5 Главы 6. Давайте напишем консольный драйвер, который выдаёт сообщения printk() наружу через USB_UART. Сообщения забираются ПК и отображаются для пользователя с помощью сессии, эмулирующей терминал.
Рисунок 12.7. Консоль через USB_UART.
Распечатка 12.3 содержит реализацию консольного драйвера, который работает через USB_UART-ы. Структура usb_uart_port[] и некоторые определения, используемые драйвером USB_UART в Главе 6, также включены в эту распечатку, чтобы создать полноценный драйвер. Действия драйвера объясняют комментарии распечатки.
Рисунок 12.5 показывает положение нашего примера консольного драйвера USB_UART в подсистеме видео в Linux. Как вы можете видеть, USB_UART - это простое устройство, которому необходим только высокоуровневый консольный драйвер.
Распечатка 12.3. Консоль через USB_UART
Код: #include <linux/console.h> #include <linux/serial_core.h> #include <asm/io.h>
#define USB_UART_PORTS 2 /* Этот сотовый телефон имеет 2 порта USB_UART */ /* Каждый USB_UART имеет 3-х байтовый набор регистров, состоящий из UU_STATUS_REGISTER по смещению 0, UU_READ_DATA_REGISTER по смещению 1, и UU_WRITE_DATA_REGISTER по смещению 2, как показано Таблице 1 Главы 6, "Драйверы последовательный устройств" */ #define USB_UART1_BASE 0xe8000000 /* Базовый адрес памяти для USB_UART1 */ #define USB_UART2_BASE 0xe9000000 /* Базовый адрес памяти для USB_UART2 */ #define USB_UART_REGISTER_SPACE 0x3 /* Назначение битов регистра состояния */ #define USB_UART_TX_FULL 0x20 #define USB_UART_RX_EMPTY 0x10 #define USB_UART_STATUS 0x0F
#define USB_UART1_IRQ 3 #define USB_UART2_IRQ 4 #define USB_UART_CLK_FREQ 16000000 #define USB_UART_FIFO_SIZE 32
/* Параметры каждого из поддерживаемых портов USB_UART */ static struct uart_port usb_uart_port[] = { { .mapbase = (unsigned int)USB_UART1_BASE, .iotype = UPIO_MEM, /* Отображённая память */ .irq = USB_UART1_IRQ, /* IRQ */ .uartclk = USB_UART_CLK_FREQ, /* Частота в Гц */ .fifosize = USB_UART_FIFO_SIZE, /* Размер FIFO */ .flags = UPF_BOOT_AUTOCONF, /* Флаг порта UART */ .line = 0, /* Номер линии UART */ }, { .mapbase = (unsigned int)USB_UART2_BASE, .iotype = UPIO_MEM, /* Отображённая память */ .irq = USB_UART2_IRQ, /* IRQ */ .uartclk = USB_UART_CLK_FREQ, /* Частота в Гц */ .fifosize = USB_UART_FIFO_SIZE, /* Размер FIFO */ .flags = UPF_BOOT_AUTOCONF, /* Флаг порта UART */ .line = 1, /* Номер линии UART */ } };
/* Запись символа в порт USB_UART */ static void usb_uart_putc(struct uart_port *port, unsigned char c) { /* Ждём, пока появится место в TX FIFO этого USB_UART. Проверяем это опрашивая бит USB_UART_TX_FULL регистра состояния */ while (__raw_readb(port->membase) & USB_UART_TX_FULL);
/* Записываем символ в порт данных */ __raw_writeb(c, (port->membase+1)); }
/* Консольная запись */ static void usb_uart_console_write(struct console *co, const char *s, u_int count) { int i;
/* Пишем каждый символ */ for (i = 0; i < count; i++, s++) { usb_uart_putc(&usb_uart_port[co->index], *s); } }
/* Получение параметров коммуникации */ static void __init usb_uart_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits) { /* Читаем текущие настройки (возможно, установленные через bootloader) или возвращаем настройки по умолчанию для чётности, числа бит данных и скорости передачи */ *parity = 'n'; *bits = 8; *baud = 115200; }
/* Установка параметров консоли для коммуникации */ static int __init usb_uart_console_setup(struct console *co, char *options) { struct uart_port *port; int baud, bits, parity, flow;
/* Проверяем номер порта и получаем индекс соответствующей структуры */ if (co->index == -1 || co->index >= USB_UART_PORTS) { co->index = 0; } port = &usb_uart_port[co->index];
/* Используем функции, предоставляемые уровнем serial для обработки параметров */ if (options) { uart_parse_options(options, &baud, &parity, &bits, &flow); } else { usb_uart_console_get_options(port, &baud, &parity, &bits); } return uart_set_options(port, co, baud, parity, bits, flow); }
/* Заполняем структуру консоли */ static struct console usb_uart_console = { .name = "ttyUU", /* Имя консоли */ .write = usb_uart_console_write, /* Как делать printk в консоли */ .device = uart_console_device, /* Предоставлена ядром serial */ .setup = usb_uart_console_setup, /* Как настроить консоль */ .flags = CON_PRINTBUFFER, /* Флаг по умолчанию */ .index = -1, /* Инициализация в неправильное значение */ };
/* Инициализация консоли */ static int __init usb_uart_console_init(void) { /* ... */ /* Регистрация этой консоли */ register_console(&usb_uart_console); return 0; }
console_initcall(usb_uart_console_init); /* Метка инициализации консоли */
После того, как драйвер будет собран как часть ядра, вы сможете активировать его с помощью добавления в командную строку ядра console=ttyUUX (где X равен 0 или 1). Логотип при загрузкеПопулярной функцией, предлагаемой подсистемой кадрового буфера является отображение при загрузке логотипа. Для отображения логотипа при конфигурации ядра включите CONFIG_LOGO и выберите доступный логотип. Вы также можете добавить своё изображение логотипа в каталог drivers/video/logo/.
Широко используемым форматом изображения для логотипа загрузки является CLUT224, который поддерживает 224 цветов. Работа в таком формате похожа на псевдо-палитры, описанные в разделе "Цветовые режимы". Изображение в CLUT224 является файлом языка Си, содержащим две структуры:
•CLUT (Color Look Up Table, Справочная таблица цветов), которая является символьным массивом из 224 наборов RGB (тем самым имеет размер 224*3 байт). Каждый 3-х байтовый элемент CLUT представляет собой комбинацию красного, зеленого и синего цветов. •Массив данных, каждый байт которого является индексом в CLUT. Индексы имеют значения от 32 до 255 (поддерживая тем самым 224 цветов). Индекс 32 относится к первому элементу в CLUT. Код работы с логотипом (в drivers/video/fbmem.c) создаёт пиксельные данные кадрового буфера из наборов CLUT, соответствующих каждому индексу в массиве данных. Визуальное отображение информации осуществляется с использованием метода низкоуровневого драйвера кадрового буфера fb_imageblit(), как рассказывалось в разделе "Методы ускорения".
Другими поддерживаемыми форматами логотипа являются 16-ти цветный vga16 и черно-белый mono. Скрипты для конвертации стандартных файлов в Переносимую пиксельную карту, Portable Pixel Map (PPM) доступны на верхнем уровне каталога scripts/.
Если устройство кадрового буфера также является консолью, под логотипом прокручиваются сообщения при загрузке. Вы можете предпочесть отключить консольные сообщения на системе, готовой к поставке (добавив в командную строку ядра console=/dev/null), и показывать в качестве загрузочного логотипа собственно подготовленное изображение "заставка" в формата CLUT224. |
Предыдущая Содержание Следующая |