Ранее я уже говорил о том, что XWindow и разработка модулей ядра есть вещи несовместимые. Это все так, но иногда возникает необходимость выдачи сообщений от модуля на любой tty. [13]
В качестве одного из вариантов можно предложить следующее: используя указатель на текущий исполняемый процесс -- current, получить структуру tty. Затем извлечь из этой структуры указатель на функцию вывода строки и использовать ее для выдачи сообщений.
Пример 9-1. print_string.c
/* * print_string.c - отправляет вывод на tty терминала, независимо от того * X11 это, или telnet, или что-то еще. Делается это путем вывода строки на tty, * ассоциированный с текущим процессом. */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/sched.h> /* определение current */ #include <linux/tty.h> /* определение tty */ #include <linux/version.h> /* макрос LINUX_VERSION_CODE */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Peter Jay Salzman"); static void print_string(char *str) { struct tty_struct *my_tty; /* * Начиная с версии 2.6.6, структура tty перекочевала в структуру signal */ #if ( LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,5) ) /* * tty текущего процесса */ my_tty = current->tty; #else /* * tty текущего процесса, для ядер 2.6.6 и выше */ my_tty = current->signal->tty; #endif /* * если my_tty == NULL, то текущий процесс не имеет tty на который можно * было бы что нибудь вывести (например, демон). * В этом случае нам ничего не нужно делать. */ if (my_tty != NULL) { /* * my_tty->driver -- структура, которая хранит указатели на функции-обработчики, * одна из которых (write) используется для вывода строк на tty. * * Первый параметр функции -- tty, на который осуществляется вывод, * поскольку эта функция обычно используется для вывода на все * tty одного и того же типа. * Второй параметр -- флаг расположения строки * если строка находится в пространстве ядра, флаг равен false (0) * если в пространстве пользователя, то true (не ноль). * Третий параметр -- указатель на строку. * Четвертый параметр -- длина строки. */ ((my_tty->driver)->write) (my_tty, /* Собственно tty */ 0, /* Строка в пространстве ядра */ str, /* Сама строка */ strlen(str)); /* Длина строки */ /* * tty изначально был аппаратным устройством, который (обычно) * ограничивался стандартом ASCII, в котором перевод строки * включал в себя два символа -- "возврат каретки" и "перевод строки". * В Unix, символ ASCII -- "перевод строки" заменял оба этих символа, * поэтому нам придется использовать для перевода строки * оба символа. * * Это одна из причин различий между текстовыми файлами Unix и * MS Windows. CP/M и ее "наследницы", например MS-DOS и * MS Windows, строго придерживались стандарта ASCII. */ ((my_tty->driver)->write) (my_tty, 0, "\015\012", 2); } } static int __init print_string_init(void) { print_string("The module has been inserted. Hello world!"); return 0; } static void __exit print_string_exit(void) { print_string("The module has been removed. Farewell world!"); } module_init(print_string_init); module_exit(print_string_exit);
При определенных условиях у вас может возникнуть желание дать вашему модулю более простой и более прямолинейный способ взаимодействия с внешним миром. Изменение состояния светодиодных индикаторов клавиатуры может быть одним из вариантов привлечения внимания пользователя или отображения некоторого состояния. Светодиодные индикаторы присутствуют на любой клавиатуре, они всегда находятся в поле зрения, они не нуждаются в установке, и их "подмаргивание" достаточно ненавязчиво, по сравнению с выводом на tty или в файл.
Следующий исходный код иллюстрирует модуль ядра, который после загрузки начинает мигать индикаторами клавиатуры.
Пример 9-2. kbleds.c
/* * kbleds.c - Мигание индикаторами на клавиатуре. */ #include <linux/module.h> #include <linux/config.h> #include <linux/init.h> #include <linux/tty.h> /* определение fg_console, MAX_NR_CONSOLES */ #include <linux/kd.h> /* определение KDSETLED */ #include <linux/console_struct.h> /* определение vc_cons */ MODULE_DESCRIPTION("Пример module illustrating the use of Keyboard LEDs."); MODULE_AUTHOR("Daniele Paolo Scarpazza"); MODULE_LICENSE("GPL"); struct timer_list my_timer; struct tty_driver *my_driver; char kbledstatus = 0; #define BLINK_DELAY HZ/5 #define ALL_LEDS_ON 0x07 #define RESTORE_LEDS 0xFF /* * Функция my_timer_func мигает индикаторами на клавиатуре периодически вызывая * ioctl() драйвера клавиатуры с командой KDSETLED. Дополнительную информацию, * по командам ioctl виртуального терминала, вы найдете в: * /usr/src/linux/drivers/char/vt_ioctl.c, function vt_ioctl(). * * Дополнительный аргумент команды KDSETLED -- значение 7 (перевод в режим * LED_SHOW_IOCTL -- управление индикаторами через ioctl), значение 0xFF -- * (любое значение, большее 7, перевод в режим LED_SHOW_FLAGS -- * отображение фактического состояния клавиатуры). Дополнительная информация: * /usr/src/linux/drivers/char/keyboard.c, function setledstate(). * */ static void my_timer_func(unsigned long ptr) { int *pstatus = (int *)ptr; if (*pstatus == ALL_LEDS_ON) *pstatus = RESTORE_LEDS; else *pstatus = ALL_LEDS_ON; (my_driver->ioctl) (vc_cons[fg_console].d->vc_tty, NULL, KDSETLED, *pstatus); my_timer.expires = jiffies + BLINK_DELAY; add_timer(&my_timer); } static int __init kbleds_init(void) { int i; printk(KERN_INFO "kbleds: loading\n"); printk(KERN_INFO "kbleds: fgconsole is %x\n", fg_console); for (i = 0; i < MAX_NR_CONSOLES; i++) { if (!vc_cons[i].d) break; printk(KERN_INFO "poet_atkm: console[geshifilter-questionmarkphp]i/%i] #%i, tty %lx\n", i, MAX_NR_CONSOLES, vc_cons[i].d->vc_num, (unsigned long)vc_cons[i].d->vc_tty); } printk(KERN_INFO "kbleds: finished scanning consoles\n"); my_driver = vc_cons[fg_console].d->vc_tty->driver; printk(KERN_INFO "kbleds: tty driver magic %x\n", my_driver->magic); /* * Инициировать таймер */ init_timer(&my_timer); my_timer.function = my_timer_func; my_timer.data = (unsigned long)&kbledstatus; my_timer.expires = jiffies + BLINK_DELAY; add_timer(&my_timer); return 0; } static void __exit kbleds_cleanup(void) { printk(KERN_INFO "kbleds: unloading...\n"); del_timer(&my_timer); (my_driver->ioctl) (vc_cons[fg_console].d->vc_tty, NULL, KDSETLED, RESTORE_LEDS); } module_init(kbleds_init); module_exit(kbleds_cleanup);