LINUX.ORG.RU

lkm, keyboard, method outb


0

1

В Linux Kernel Module Programming Guide упоминается пример, где сперва заменяют стандартный обработчик прерываний клавиатуры на свой, а затем играются с ней через порты (но там используется лишь inb() для чтения). Но мне по заданию необходимо, получив прерывание с символом «Q» (0x10) заменить его на «W» (0x11). Вопрос состоит в том, можно ли это осуществить с помощью метода outb_p(data,port) и если да, то почему при загрузке этого модуля при вводе «Q» я получаю всё тот же символ «Q» и сообщение: «Spurious NAK on isa0060/serio0. Some program might be trying access hardware directly».

Код таков:
#include <linux/kernel.h> /* Работа с ядром */
#include <linux/module.h> /* Необходимо для любого модуля */
#include <linux/interrupt.h> /* Определение irqreturn_t */
#include <asm/io.h> /* Функция inb(port) */

/*
* Обработчик прерываний от клавиатуры. Он считывает информацию с клавиатуры
* и в случае, если была нажата клавиша «Q» выводит вместо неё символ «W»
*/
irqreturn_t irq_handler(int irq, void *dev_id, struct pt_regs *regs)
{
unsigned char scancode;
unsigned char status;


   int value;

/*
* Прочитать состояние клавиатуры
*/
status = inb(0x64);
scancode = inb(0x60);

value = (int)scancode & 0x7f;
if ( value == 0x10 ){
status = inb(0x64);
outb_p(0x11, 0x60); //spurious NAK on isa0060/serio0. Some program might be trying access hardware directly
value = (int)inb(0x60) & 0x7f;
}
printk(«Scan Code %x %s.\n»,
   value,   
   //(int)scancode & 0x7f,
   scancode & 0x80 ? «Released»: «Pressed»);

return IRQ_HANDLED;
}
/*
* Инициализация модуля - регистрация обработчика прерывания
*/
int init_module()
{
/*
* Поскольку стандартный обработчик прерываний от клавиатуры не может
* сосуществовать с таким как наш, то придется запретить его
* (освободить IRQ) прежде, чем что либо сделать.
* Но поскольку мы не знаем где он находится в ядре, то мы лишены
* возможности переустановить его - поэтому компьютер придется перезагрузить
* после опробования этого примера.
*/
free_irq(1, NULL);
/*
* Подставить свой обработчик (irq_handler) на IRQ 1.
* IRQF_SHARED(SA_SHIRQ) означает, что мы допускаем возможность совместного
* обслуживания этого IRQ другими обработчиками.
*/
return request_irq(1, /* Номер IRQ */
irq_handler, /* наш обработчик */
   IRQF_SHARED,
   //=SA_SHIRQ,
«test_keyboard_irq_handler»,
(void *)(irq_handler));
}
/*
* Завершение работы
*/
void cleanup_module()
{
/*
* Эта функция добавлена лишь для полноты изложения.
* Она бессмысленна, поскольку нет способа
* восстановить стандартный обработчик прерываний от клавиатуры,
* поэтому необходимо выполнить перезагрузку системы.
*/
free_irq(1, NULL);
}
/*
* некоторые функции доступны только если модуль лицензирован под GPL
*/
MODULE_LICENSE(«GPL»);

Сам писал драйвера только под оффтопик, не знаю, как тут в модулях... Думаю, что после обработки прерывания нужно каким-то образом вызвать стандартный обработчик и передать ему прочитанное или измененное значение. Было бы странно писать обратно в железку то, что ты только что из нее прочитал...

nightrain ()
Ответ на: комментарий от nightrain

Как сообщает первоначальный автор, на то время не было возможности вернуть стандартный обработчик(2004 год), так как не известно, где в памяти он находится.
Согласен, что странно, но как тогда можно изменить то, что я получил? Я не очень понимаю последовательность. Ведь прежде прерывание попадает в мой обработчик, а уже затем символ печатается.Где и кем он печатается - это мне не понятно. Ведь я заменил обработчик на свой, который этого не делает.

bellum ()

Это сообщение говорит нам о том, что:

1)тебе не удалось удалить стандартный обработчик atkbd_interrupt

2)писать в тот порт, куда ты пытаешься, нельзя

ttnl ★★★★★ ()
Ответ на: комментарий от bellum

Одновременно. Погрепь исходники, и увидишь, что такое сообщение выводится только одной функцией — обработчиком прерываний.

ttnl ★★★★★ ()
Ответ на: комментарий от ttnl

Да, я понял. Спасибо. Но можно узнать, как полностью его(стандартный обработчик прерываний) выгрузить и возможно ли это?

bellum ()
Ответ на: комментарий от bellum

Наверное, как-то и можно, но делать это неправильно. Клавиатура, так сказать, работает через целую подсистему input, и не стоит отключать её таким топорным методом.

Боюсь, если решать твою задачу по-хорошему, она будет слишком сложная для обучения, чего собственно ты добиваешься. Советую начать с чего-нибудь попроще.

ttnl ★★★★★ ()
Ответ на: комментарий от ttnl

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

bellum ()
Ответ на: комментарий от ttnl

да, я смотрел его. но Вы бы могли навести меня на правильный путь в смысле механизма того, что мне нужно. та замена обработчика, что я произвожу, как я понимаю в ядрах версии 2.6 не работает. Какой же механизм нужно использовать теперь и осуществимо ли это вообще?

bellum ()
Ответ на: комментарий от bellum

под задачей я имею ввиду перехват и замену вводимого символа

bellum ()
Ответ на: комментарий от bellum

Вот смотри:

static int __init atkbd_init(void)
{
        dmi_check_system(atkbd_dmi_quirk_table);

        return serio_register_driver(&atkbd_drv);
}

static void __exit atkbd_exit(void)
{
        serio_unregister_driver(&atkbd_drv);
}

module_init(atkbd_init);
module_exit(atkbd_exit);

Клава регистрируется как последовательное устройство. Предусмотрена возможность полной выгрузки драйвера. Что, собственно, тебе и нужно сделать. Вместо него потом загрузишь свой. Правда, есть большая вероятность, что оригинальный драйвер не выгрузится, т.к. он будет в этот момент использоваться. Но попробовать стоит.

Второй способ, быдлокодерский. Динамически патчишь функцию atkbd_interrupt, в ее начале ставишь jump на свою функцию. Для этого тебе потребуются минимальные знания ассемблера.

Способ третий. Разбираешься в том, как регистрируются устройства, начиная с функции serio_register_driver, и пытаешься понять, как можно этим механизмом воспользоваться в своих целях. Это не так сложно, и этим тебе стоит заняться. Иначе можно будет подумать, что в Одессе диплом дают за просто так.

ttnl ★★★★★ ()
Ответ на: комментарий от ttnl

диплом бакалавра, так что запросы поменьше. и по сути мы не изучали вообще модули ядра линукс. у нас по линуксу пару лаб было и то по использованию распределённой памяти ) так что это был мой собственный выбор и, если бы чуть больше литературы, я был ещё более счастлив. спасибо за наводку.)

bellum ()
Ответ на: комментарий от bellum

Если стоит задача сдать предмет и забыть, как страшный сон, тогда патчь динамически. Это проще всего.

ttnl ★★★★★ ()
Ответ на: комментарий от ttnl

мне интересно это, но я же говорю, что брак литературы...

слушай. я давно сижу на линуксе, но никогда не читал чего-то целиком по нему, поэтому есть брак даже простых понятих. ты сказал, что есть функции загрузки и выгрузки модуля клавиатуры, имя которого «atkbd», как я понимаю,из структуры atkbd_drv:

1248 static struct serio_driver atkbd_drv = {
1249 .driver = {
1250 .name = «atkbd»,
1251 },
1252 .description = DRIVER_DESC,
1253 .id_table = atkbd_serio_ids,
1254 .interrupt = atkbd_interrupt,
1255 .connect = atkbd_connect,
1256 .reconnect = atkbd_reconnect,
1257 .disconnect = atkbd_disconnect,
1258 .cleanup = atkbd_cleanup,
1259 };

И под этим именем он должен быть загружен в систему (или я что-то путаю), но его там нет. И как вообще узнать, какой из загруженных модулей работает с клавиатурой? Я находил списки имён модулей, как то sunkbd и другие, но ничего этого во время работы системы, у меня не загружено. В чём подвох?

bellum ()
Ответ на: комментарий от bellum

>Случаем не появилось свежих мыслей? )

Я не думал над твоей задачей. Мне это не очень интересно.

ttnl ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.