- Помогите разобраться с драйвером!, skb7, 17:40 , 06-Дек-13 (1)
> чтобы драйвер компилировался, как модуль ядра1. Есть 2 способа скомпилировать драйвер как модуль ядра: in-tree и out-of-tree. Т.е. внутри дерева исходников ядра или снаружи. Вам сначала нужно решить, какой способ вы хотите использовать (уточнить у препода?). В зависимости от этого будут разные Makefile. Моё предположение -- вам нужно использовать out-of-tree способ, т.к. он проще. a) in-tree * Создаете в drivers/ (в исходниках ядра) свой каталог, например "masha". - Туда кладете свой .c файл драйвера; назовём его "masha.c". Для начала можете забить туда этот код (для проверки), потом добавите код драйвера клавиатуры:
#include <linux/init.h> #include <linux/module.h>static int masha_init(void) { pr_alert("Hello there\n"); return 0; } static void masha_exit(void) { } MODULE_LICENSE("GPL"); module_init(masha_init); module_exit(masha_exit);
- Там же создаете Kconfig файл примерно следующего содержания:
menuconfig MASHA tristate "Masha keyboard support" default m help Masha keyboard support this long line is just to trick kernel git hook system this long line is just to trick kernel git hook system this long line is just to trick kernel git hook system this long line is just to trick kernel git hook system this long line is just to trick kernel git hook system this long line is just to trick kernel git hook systemconfig MASHA_KEYBOARD tristate "Masha keyboard" depends on MASHA != n default m help bla bla
- И еще там же создаете Makefile такого содержания:
obj-$(CONFIG_MASHA_KEYBOARD) += masha.o
* в drivers/Kconfig добавляете:
source "drivers/masha/Kconfig"
* в drivers/Makefile добавляете:
obj-$(CONFIG_MASHA) += masha/
* Собираете свой модуль таким образом (выполняете эти команды из корня дерева исходников ядра)
$ make Image -j4 $ make M=drivers/masha
- после этого увидите .ko файл в drivers/masha -- это и будет ваш собранный модуль.
b) out-of-tree
* Тут всё проще. создаете в произвольном месте (не в каталоге исходников ядра) свой каталог (например /home/masha/projects/masha-kernel-module/), и в нем будет всего 2 файла: masha.c и Makefile. Исходник будет такой же, как и в случае in-the-tree, но Makefile будет примерно следующего содержания:
ifeq ($(KERNELRELEASE),) KERNELDIR ?= /lib/modules/$(shell uname -r)/buildmodule: $(MAKE) -C $(KERNELDIR) M=$(PWD) C=1 modules clean: $(MAKE) -C $(KERNELDIR) M=$(PWD) C=1 clean .PHONY: module clean else obj-m := masha.o endif
где KERNELDIR -- путь к корню дерева исходников ядра; если у вас своё дерево -- эту переменную надо изменить соответствующим образом.
* дальше выполняете из этого каталога:
$ make module
и получаете .ko файл -- это и есть ваш модуль. Как работает этот Makefile не буду объяснять, это долго.
2. Когда у вас получиться собрать "Hello world" модуль (код его я привел выше, см. "masha.c") -- вам нужно будет попросту вставить вместо того кода свой код, который вам дал преподаватель. Насколько я вижу, вам останется только дописать туда соответствующие макросы module_init() и module_exit() для уже готовых там функций init_module() и cleanup_module(). Да, еще наверное сигнатуру этих функций придется изменить так, как это в моем примере показано.
> и работал правильно на новых версиях (3.2, например).
Когда сделаете всё что написано выше -- попробуйте собрать на новом ядре ваш старый код и посмотреть, какие ошибки/предупреждения покажет компилятор. Починив эти ошибки, скорее всего получите работающий код > Даже не знаю,с чего начать Я сторонник мнения, что любую сложную проблему можно решить, разбив её на небольшие, решаемые части. Это называется планирование. 1. Для старого ядра сделайте проект "Hello world" модуля (код я привел выше), наверное это будет out-of-tree проект. Добейтесь, чтобы он собирался и на выходе получался .ko файл. 2. Вместо кода "Hello world" модуля вставьте код, который вам дал преподаватель. Добавьте module_init() и module_exit() макросы, как это было сделано у меня в "Hello world" модуле, изменив сигнатуры соответствующих функций должным образом. Добейтесь чтобы собиралось всё. 3. Протестируйте работу вашего модуля (команда insmod загружает модуль, команда dmesg показывает вывод ядра). Для этого надо разобраться, как модуль работает и как увидеть результат его работ. Я не особо разбирался, но вроде как этот недо-драйвер просто вешается на шаренное прерывание клавиатуры и выводит в журнал ядра скан-код нажатой клавиши, и нажата она или отжата; кстати, убедитесь что в оригинальном драйвере клавиатуры это прерывание сделано шаренным, иначе ничего работать не будет. И да, я бы переделал workqueue на threaded_irq, так проще было бы. Убедитесь, что всё работает. 4. Теперь попробуйте собрать модуль с новым ядром. Разберитесь с ошибками/предупреждениями компилятора и почините их. 5. Когда сможете собрать модуль для нового ядра -- проверьте его работу на новом ядре. Если совсем ничего не понятно, советую к прочтению: [1] http://rus-linux.net/MyLDP/BOOKS/drivers/linux-device-driver... [2] http://lwn.net/Kernel/LDD3/
- Помогите разобраться с драйвером!, Гагарина Машка, 21:14 , 06-Дек-13 (2)
Спасибо за такой развернутый ответ, но вопрос, в общем-то, заключался именно в изменении драйвера-модуля и подстройки его под новую версию ядра) я не знаю,как поменять код,чтобы он правильно работал. а собирать их я умею)
- Помогите разобраться с драйвером!, pavlinux, 22:45 , 06-Дек-13 (3)
> Спасибо за такой развернутый ответ, но вопрос, в общем-то, заключался именно в > изменении драйвера-модуля и подстройки его под новую версию ядра) > я не знаю,как поменять код,чтобы он правильно работал. > а собирать их я умею) Вам рабочий или компилируемый? Рабочий - сложнее, так как у 90% народа USB-клавы, и подобная фича делается через evdev
- Помогите разобраться с драйвером!, pavlinux, 07:19 , 08-Дек-13 (23)
Тут правоверный код! #include <linux/kernel.h> #include <linux/module.h> #include <linux/workqueue.h> #include <linux/interrupt.h> #include <linux/slab.h> /* kzalloc */ #include <asm/io.h>struct scanwork { struct work_struct tsk; int sccode; unsigned char status; }; static struct scanwork *sw; static struct workqueue_struct *my_workqueue; static void got_char(struct work_struct *tp) { struct scanwork *ssw = container_of(tp, struct scanwork, tsk); printk(KERN_INFO "Scan Code %x %s.\n", ssw->sccode & 0x7F, ssw->sccode & 0x80 ? "Released" : "Pressed"); } static irqreturn_t irq_handler(int irq, void *dev_id) { static int initialised = 0; struct scanwork *sw = dev_id; sw->status = inb(0x64); sw->sccode = inb(0x60); if (initialised == 0) { INIT_WORK(&sw->tsk, got_char); initialised = 1; } else { PREPARE_WORK(&sw->tsk, got_char); } queue_work(my_workqueue, &sw->tsk); return IRQ_HANDLED; } static int __init init_kbd(void) { my_workqueue = create_workqueue("Wrkque"); sw = kzalloc(sizeof(struct scanwork), GFP_KERNEL); return request_irq(1, irq_handler, IRQF_SHARED, "test", sw); } static void __exit cleanup_kbd(void) { free_irq(1, sw); kfree(sw); } module_init(init_kbd); module_exit(cleanup_kbd); MODULE_LICENSE("GPL");
- Помогите разобраться с драйвером!, skb7, 16:59 , 08-Дек-13 (25)
> Тут правоверный код!1. Почему int sccode, если достаточно char? 2. Зачем читать и хранить статус, если он не используется? 3. Зачем создавать свой workqueue, если можно использовать глобальный workqueue (через функцию schedule_work() вместо queue_work())? 4. Можно использовать DECLARE_WORK() вместо INIT_WORK() и PREPARE_WORK() Если уже делать BH через wq, то можно сделать проще (код проверен):
#include <linux/kernel.h> #include <linux/module.h> #include <linux/workqueue.h> #include <linux/interrupt.h> #include <asm/io.h>static void kbd2_do_work(struct work_struct *work); static char scancode; static DECLARE_WORK(kbd2_work, kbd2_do_work); static void kbd2_do_work(struct work_struct *work) { pr_info("Scan Code %x %s\n", scancode & 0x7F, scancode & 0x80 ? "Released" : "Pressed"); } static irqreturn_t kbd2_isr(int irq, void *dev_id) { scancode = inb(0x60); schedule_work(&kbd2_work); return IRQ_HANDLED; } static int __init kbd2_init(void) { return request_irq(1, kbd2_isr, IRQF_SHARED, "kbd2", &scancode); } static void __exit kbd2_cleanup(void) { free_irq(1, &scancode); } module_init(kbd2_init); module_exit(kbd2_cleanup); MODULE_LICENSE("GPL");
Ну и всё же magic numbers -- зло, я за замену их на осмысленные дефайны. И вообще, если задача портировать код на новое ядро -- то правильно это было бы сделать именно с использованием threaded IRQ, как я это показал ниже.
- Помогите разобраться с драйвером!, pavlinux, 17:55 , 08-Дек-13 (27)
>> Тут правоверный код! > 1. Почему > 2. Зачем > 3. Зачем Патамуша см. начальный код.
- Помогите разобраться с драйвером!, skb7, 21:04 , 08-Дек-13 (30)
>>> Тут правоверный код! >> 1. Почему >> 2. Зачем >> 3. Зачем > Патамуша см. начальный код.Если ваша цель была портировать код с минимальными правками -- то не сходится: зачем тогда вы добавили kzalloc() и свою структуру? С другой стороны, попытка придерживаться старого кода при портировании -- это вообще нонсенс. Попробуйте портировать например драйвер датчика какой-нибудь, используя работу с sysfs напрямую. Он даже будет работать. Только не примут его в mainline, потому что уже на IIO датчики пишут, это позволяет в 2 раза меньше кода клепать. Вот и тут тоже самое: не нужно пытаться оставить всё как есть -- это неправильный подход при портировании. Нужно разобраться, как код работает, а потом изменить его с учетом текущего положения дел в ядре. Некоторые флаги депрекейтятся, некоторые фреймворки заменяются другими и т.д. Всё это надо учитывать при портировании. В идеале после портирования код должен выглядеть так, как если бы вы его написали для текущей версии ядра.
- Помогите разобраться с драйвером!, pavlinux, 18:05 , 08-Дек-13 (28) –1
> И вообще, если задача портировать код на новое ядро -- то правильно это было бы сделать именно с использованием threaded IRQ, как я это показал ниже.Правильно - input_dev *, input_register_device, serio_write, input_event, input_report_key, ... А для кейлоггера - все варианты тут представленные очень оверхедные, нужон asm()
- Помогите разобраться с драйвером!, skb7, 20:54 , 08-Дек-13 (29)
>> И вообще, если задача портировать код на новое ядро -- то правильно это было бы сделать именно с использованием threaded IRQ, как я это показал ниже. > Правильно - input_dev *, input_register_device, serio_write, input_event, input_report_key, > ... > А для кейлоггера - все варианты тут представленные очень оверхедные, нужон asm() Насколько я понимаю, суть задачи -- научиться работать с прерыванием -- реквестить его и обрабатывать, а также делать BH. Клава тут просто в качестве источника прерывания используется. Драйвер клавы-то уже написан. Это ж просто учебная задачка на прерывания. Вот почему я говорю, что threaded IRQ тут подходит как нельзя лучше. Он во-первых позволяет сосредоточиться на главной теме задания (и избавиться от кучи оверхеда в виде выделений памяти, ворков и прочик container_of), а во-вторых это то, как в новых ядрах делается нижняя половина прерывания.
- Помогите разобраться с драйвером!, pavlinux, 03:33 , 09-Дек-13 (31)
> Насколько я понимаю, суть задачи -- научиться работать с прерыванием Неа. Суть, во том, что init_module() и cleanup_module() заменили на module_init() и module_exit(). (может SA_SHIRQ на IRQF_SHARED). Думаю на трояк если модуль скомпилиться, на 4 если будет глюкать, и на 5 если за работает. :)
- Помогите разобраться с драйвером!, skb7, 06:19 , 07-Дек-13 (8) +1
> Спасибо за такой развернутый ответ, но вопрос, в общем-то, заключался именно в > изменении драйвера-модуля и подстройки его под новую версию ядра) > я не знаю,как поменять код,чтобы он правильно работал. > а собирать их я умею) Раз пошла такая пьянка -- режь последний огурец :) Код проверен и работает. #include <linux/kernel.h> #include <linux/module.h> #include <linux/interrupt.h> #include <asm/io.h>MODULE_LICENSE("GPL"); #define KBD_IRQ 1 /* IRQ number for keyboard (i8042) */ #define KBD_DATA 0x60 /* I/O port for keyboard data */ #define KBD_SCAN_CODE_MASK 0x7f #define KBD_STATUS_MASK 0x80 static char scancode; static irqreturn_t kbd2_isr(int irq, void *data) { char *sc = (char *)data; *sc = inb(KBD_DATA); return IRQ_WAKE_THREAD; } static irqreturn_t kbd2_thread(int irq, void *data) { char sc = *(char *)data; pr_alert("### Scan Code %#x %s\n", sc & KBD_SCAN_CODE_MASK, sc & KBD_STATUS_MASK ? "Released" : "Pressed"); return IRQ_HANDLED; } static int __init kbd2_init(void) { return request_threaded_irq(KBD_IRQ, kbd2_isr, kbd2_thread, IRQF_SHARED, "masha", &scancode); } static void __exit kbd2_clean(void) { free_irq(KBD_IRQ, &scancode); } module_init(kbd2_init); module_exit(kbd2_clean);
Проверить модуль можно так:
$ sudo insmod masha.ko $ sudo tail -f /var/log/syslog | grep "Scan Code"
После этого понабирайте что-нибудь на PS/2 клавиатуре, вы должны увидеть что-то типа:
7Dec 7 04:20:27 joe-pc kernel: [ 3730.257933] ### Scan Code 0x8 Pressed Dec 7 04:20:27 joe-pc kernel: [ 3730.351589] ### Scan Code 0x8 Released 8Dec 7 04:20:27 joe-pc kernel: [ 3730.595211] ### Scan Code 0x9 Pressed Dec 7 04:20:27 joe-pc kernel: [ 3730.677482] ### Scan Code 0x9 Released 9Dec 7 04:20:28 joe-pc kernel: [ 3731.042015] ### Scan Code 0xa Pressed Dec 7 04:20:28 joe-pc kernel: [ 3731.132826] ### Scan Code 0xa Released 0Dec 7 04:20:29 joe-pc kernel: [ 3732.655725] ### Scan Code 0xb Pressed Dec 7 04:20:29 joe-pc kernel: [ 3732.743720] ### Scan Code 0xb Released
Преимущества по сравнению с оригинальной версией: 1. Использован механизм threaded IRQ вместо создания нижней половины прерывания с помощью workqueue, что позволило сократить код. 2. Не убивается прерывание, запрошенное драйвером клавиатуры, т.к. в драйвере клавиатуры это прерывание обозначено как shared. Таким образом, функционирование клавиатуры не нарушается и компьютер не нужно перезагружать после подгрузки (insmod) этого модуля.
Что было сделано для портирования драйвера (на новую версию ядра): механизм workqueue заменен на новый механизм threaded IRQ. Можно почитать про него здесь: http://lwn.net/Articles/302043/ Что было сделано для того, чтобы ваш пример собирался как модуль: добавлен этот код:
module_init(kbd2_init); module_exit(kbd2_clean);
- Помогите разобраться с драйвером!, linina, 08:55 , 29-Июн-15 (44)
Надеюсь, Вы еще обитаете на этом форуме скажите, по какой причине может быть такое, что ничего не выводится в syslog во время тестирования этого кода?
- Помогите разобраться с драйвером!, ubob, 17:41 , 16-Июл-15 (45)
> скажите, по какой причине может быть такое, что ничего не выводится в > syslog во время тестирования этого кода?посмотрите в dmesg
- Помогите разобраться с драйвером!, pavlinux, 23:10 , 06-Дек-13 (4)
Ну держы и Машка!
#include <linux/kernel.h> #include <linux/module.h> #include <linux/workqueue.h> #include <linux/interrupt.h> #include <linux/slab.h> #include <asm/io.h>MODULE_LICENSE("GPL"); static void got_char(struct work_struct *); static irqreturn_t irq_handler(int, void *); static void __exit cleanup_kbd(void); static int __init init_kbd(void); struct scanwork { struct work_struct tsk; int sccode; unsigned char status; }; static struct workqueue_struct *my_workqueue; static void got_char(struct work_struct *tp) { struct scanwork *sw = container_of(tp, struct scanwork, tsk); printk(KERN_INFO "Scan Code %x %s.\n", sw->sccode & 0x7F, sw->sccode & 0x80 ? "Released" : "Pressed"); } static irqreturn_t irq_handler(int irq, void *dev_id) { static int initialised = 0; struct scanwork *sw = kzalloc(sizeof(struct scanwork), GFP_KERNEL); sw->status = inb(0x64); /* накуя это нужно? */ sw->sccode = inb(0x60); if (initialised == 0) { INIT_WORK(&sw->tsk, got_char); initialised = 1; } else { PREPARE_WORK(&sw->tsk, got_char); } schedule_work(&sw->tsk); return IRQ_RETVAL(IRQ_HANDLED); } static int __init init_kbd(void) { my_workqueue = create_workqueue("PAVLINUX WAS HERE"); free_irq(1, NULL); return request_irq(1, irq_handler, IRQF_SHARED, "test_keyboard_irq_handler", irq_handler); } static void __exit cleanup_kbd(void) { free_irq(1, irq_handler); } module_init(init_kbd); module_exit(cleanup_kbd);
- Помогите разобраться с драйвером!, skb7, 04:41 , 07-Дек-13 (6) +1
У вас там 2 грубых ошибки в одной строке.
struct scanwork *sw = kzalloc(sizeof(struct scanwork), GFP_KERNEL);
1. Утечка памяти. Этот код будет несколько раз вызываться на каждое нажатие клавиши и память будет течь, у вас она там не высвобождается нигде.
2. В атомарном контексте происходит выделение памяти используя GFP_KERNEL. А ведь этим вы говорите, что kzalloc() может спать. А спать в атомарном контексте (в прерывании в данном случае) ну никак нельзя. Будет вот такая ошибка у вас постоянно возникать:
BUG: scheduling while atomic: swapper/1/0/0x10010000 [44877.249899] Call Trace: [44877.249900] <IRQ> [<ffffffff8138068b>] ? __schedule_bug+0x42/0x4f [44877.249907] [<ffffffff813849de>] ? __schedule+0x85/0x532 [44877.249911] [<ffffffff810606f5>] ? __cond_resched+0x1d/0x26 [44877.249914] [<ffffffff81384ed4>] ? _cond_resched+0x10/0x18 [44877.249917] [<ffffffff810f9ea3>] ? kmem_cache_alloc_trace+0x25/0xcc [44877.249921] [<ffffffffa1024040>] ? irq_handler+0x16/0x7b [masha]
Правильно было бы использовать GFP_ATOMIC. А еще правильнее -- не выделять память в каждом прерывании. Ну и вообще, ваш модуль не работает -- никакого вывода с него нет. sw->status = inb(0x64); /* накуя это нужно? */
А и не нужно. Очевидно же, что взяли чей-то пример и покромсали. Остались сопли. Можно смело удалять эту строку, это вычитывание статусного регистра клавиатуры из 16-битного адресного пространства x86. Дальше в коде нигде не используется, т.е. не нужен.
Еще я считаю, что некрасиво делать free_irq(), чтобы прибить прерывание драйвера клавиатуры. Намного правильнее было бы сделать шаренным прерывание в драйвере клавиатуры, а затем пересобрать и загрузить новый драйвер клавиатуры.
Ну и последнее -- зачем такие мучения с workqueue в качестве bottom half? Почему не использовать threaded IRQ, которое есть в новых ядрах? Сразу код в 2 раза меньше становится.
- Помогите разобраться с драйвером!, skb7, 05:00 , 07-Дек-13 (7) +1
> Еще я считаю, что некрасиво делать free_irq(), чтобы прибить прерывание драйвера клавиатуры. > Намного правильнее было бы сделать шаренным прерывание в драйвере клавиатуры, а > затем пересобрать и загрузить новый драйвер клавиатуры.Более того, если посмотреть в drivers/input/serio/i8042.c, можно увидеть, что это прерывание и так уже шаренное:
error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED, "i8042", i8042_platform_device);
Так что по идее можно не делать free_irq(), и всё будет работать.
- Помогите разобраться с драйвером!, skb7, 06:21 , 07-Дек-13 (9) +1
- Помогите разобраться с драйвером!, pavlinux, 15:44 , 07-Дек-13 (12)
> Так что по идее можно не делать free_irq(), и всё будет работать. Угу, а если предыдущий был c IRQF_DISABLED
- Помогите разобраться с драйвером!, skb7, 16:07 , 07-Дек-13 (15)
>> Так что по идее можно не делать free_irq(), и всё будет работать. > Угу, а если предыдущий был c IRQF_DISABLED Да нет же, я проверил, что он в драйвере клавиатуры SHARED. Более того, смотрим в /include/linux/interrupt.h: IRQF_DISABLED - keep irqs disabled when calling the action handler. DEPRECATED. This flag is a NOOP and scheduled to be removed
В любом случае, когда вы пытаетесь сделать такое:
free_irq(1, NULL);
то (по крайней мере на новой версии ядра) получите:
[ 779.065079] Trying to free already-free IRQ 1 [ 779.065142] Call Trace: [ 779.065147] [<ffffffff8103bb5f>] ? warn_slowpath_common+0x5b/0x70 [ 779.065150] [<ffffffff8103bc0a>] ? warn_slowpath_fmt+0x47/0x49 [ 779.065153] [<ffffffff81099ce0>] ? __free_irq+0x91/0x155 [ 779.065155] [<ffffffff81099e06>] ? free_irq+0x62/0x87 [ 779.065167] [<ffffffffa003800c>] ? kbd2_init+0xc/0x1000 [masha] [ 779.065189] ---[ end trace f688fc88f1374d89 ]---
то есть так делать нельзя.
- Помогите разобраться с драйвером!, pavlinux, 15:32 , 07-Дек-13 (10)
> Ну и вообще, ваш модуль не работает -- никакого вывода с него нет.Конечно, что я идиот, тестировать модули с прерываниями на своём компе. Я его даже не загружал :) стухтуру можно сделать глобальной, казаллок() всунуть в init, фри() в ехит(), в нужном месте обнулять... Короча, причёсывать - домашнее задание. И так 95% сделали.
- Помогите разобраться с драйвером!, skb7, 16:14 , 07-Дек-13 (16)
>> Ну и вообще, ваш модуль не работает -- никакого вывода с него нет. > Конечно, что я идиот, тестировать модули с прерываниями на своём компе. Я > его даже не загружал :) > стухтуру можно сделать глобальной, казаллок() всунуть в init, фри() в ехит(), > в нужном месте обнулять... > Короча, причёсывать - домашнее задание. И так 95% сделали.Поздно. Я выше привет 100% рабочий модуль сделал и проверил на своём компе. Наверное не нужно было изначально давать готовое решение. Мне в инсте таких заданий не давали, пускай бы сама лучше разобралась, спрашивая по ходу дела, оно полезнее было бы. А так получилось что мы просто показали какие мы молодцы. Ну да, молодцы, "hello world" модуль написали.
- Помогите разобраться с драйвером!, pavlinux, 23:11 , 07-Дек-13 (19)
> Поздно. Я выше привет 100% рабочий модуль сделал и Ну это завал на зачете, однозначо.
- Помогите разобраться с драйвером!, skb7, 00:48 , 08-Дек-13 (20)
>> Поздно. Я выше привет 100% рабочий модуль сделал и > Ну это завал на зачете, однозначо.Моих пояснений достаточно, чтобы хм... удовлетворить любого преподавателя? В любом случае, автомат еще никто не отменял :)
- Помогите разобраться с драйвером!, pavlinux, 01:22 , 08-Дек-13 (21)
>>> Поздно. Я выше привет 100% рабочий модуль сделал и >> Ну это завал на зачете, однозначо. > Моих пояснений достаточно, чтобы хм... удовлетворить любого преподавателя? В любом случае, > автомат еще никто не отменял :) А почему идет обращение к не инициализированной переменной scancode? А что такое нижняя половина? И почему нижняя, а не верхняя? Какие достоинства и недостатки workqueue против threaded irq? Для чего нужны __init и __exit, и чё это ваще такое... Короче завалить можно, было бы желание.
- Помогите разобраться с драйвером!, skb7, 05:16 , 08-Дек-13 (22)
> А почему идет обращение к не инициализированной переменной scancode? > А что такое нижняя половина? И почему нижняя, а не верхняя? > Какие достоинства и недостатки workqueue против threaded irq? > Для чего нужны __init и __exit, и чё это ваще такое... Я на эти вопросы конечно могу ответить сходу, но пусть она сама поищет, если интересно ;) > Короче завалить можно, было бы желание.
Конечно можно. Вне зависимости от того, сама она делала это задание или нашла готовое. Не хотели, чтобы у неё было готовое решение? Так зачем вы ей его дали? Я лишь написал свой вариант решения, когда готовое уже было. Изначально я и хотел, чтобы она сама делала. Короче разговор ни о чём.
- Помогите разобраться с драйвером!, pavlinux, 15:34 , 07-Дек-13 (11)
> Ну и последнее -- зачем такиеЗадача была - минимум изменений, для работы на ядре 3.2 Я всунул структуру в контейнер и заменил SA_SHIRQ на IRQF_SHARED. Кстати, queue_work(my_workqueue, &task); можно оставить.
- Помогите разобраться с драйвером!, skb7, 15:56 , 07-Дек-13 (14)
>> Ну и последнее -- зачем такие > Задача была - минимум изменений, для работы на ядре 3.2 > Я всунул структуру в контейнер и заменил SA_SHIRQ на IRQF_SHARED. > Кстати, queue_work(my_workqueue, &task); можно оставить.Да нет, про минимум там ничего не сказано: > Задание состоит в том, чтобы изменить код, написанный под старое второе ядро, так, чтобы > драйвер компилировался, как модуль ядра, и работал правильно на новых версиях (3.2, > например). Как по мне, логично было бы переделать на threaded IRQ, он для этого и придуман.
- Помогите разобраться с драйвером!, pavlinux, 15:56 , 07-Дек-13 (13)
> > sw->status = inb(0x64); /* накуя это > нужно? */ > > А и не нужно. Очевидно же, что взяли чей-то пример и покромсали. > Остались сопли. Можно смело удалять эту строку, это вычитывание статусного регистра > клавиатуры из 16-битного адресного пространства x86. Дальше в коде нигде не > используется, т.е. не нужен.По феншую этот статус нужно считать, проверить, а дальше по результатам, что-то типа: sw->status = inb(0x64); ... if (sw->status != 0x14) goto err_exit;
Во! The keyboard controller status registerThe keyboard controller has an 8-bit status register. It can be inspected by the CPU by reading port 0x64. (Typically, it has the value 0x14: keyboard not locked, self-test completed.) Bit 7: Parity error 0: OK. 1: Parity error with last byte. Bit 6: Timeout 0: OK. 1: Timeout. On PS/2 systems: General timeout. On AT systems: Timeout on transmission from keyboard to keyboard controller. Possibly parity error (in which case both bits 6 and 7 are set). Bit 5: Auxiliary output buffer full On PS/2 systems: Bit 0 tells whether a read from port 0x60 will be valid. If it is valid, this bit 5 tells what data will be read from port 0x60. 0: Keyboard data. 1: Mouse data. On AT systems: 0: OK. 1: Timeout on transmission from keyboard controller to keyboard. This may indicate that no keyboard is present. Bit 4: Keyboard lock 0: Locked. 1: Not locked. Bit 3: Command/Data 0: Last write to input buffer was data (written via port 0x60). 1: Last write to input buffer was a command (written via port 0x64). (This bit is also referred to as Address Line A2.) Bit 2: System flag Set to 0 after power on reset. Set to 1 after successful completion of the keyboard controller self-test (Basic Assurance Test, BAT). Can also be set by command (see below). Bit 1: Input buffer status 0: Input buffer empty, can be written. 1: Input buffer full, don't write yet. Bit 0: Output buffer status 0: Output buffer empty, don't read yet. 1: Output buffer full, can be read. (In the PS/2 situation bit 5 tells whether the available data is from keyboard or mouse.) This bit is cleared when port 0x60 is read.
- Помогите разобраться с драйвером!, skb7, 16:29 , 07-Дек-13 (17)
>The keyboard controller has an 8-bit status register. It can be inspected by the CPU > by reading port 0x64. > (Typically, it has the value 0x14: keyboard not locked, self-test completed.)Да, всё верно. Эти биты кстати уже объявлены в drivers/input/serio/i8042.h:
/* * Status register bits. */ #define I8042_STR_PARITY 0x80 #define I8042_STR_TIMEOUT 0x40 #define I8042_STR_AUXDATA 0x20 #define I8042_STR_KEYLOCK 0x10 #define I8042_STR_CMDDAT 0x08 #define I8042_STR_MUXERR 0x04 #define I8042_STR_IBF 0x02 #define I8042_STR_OBF 0x01
Регистры объявлены в drivers/input/serio/i8042-io.h:
/* * Register numbers. */ #define I8042_COMMAND_REG 0x64 #define I8042_STATUS_REG 0x64 #define I8042_DATA_REG 0x60
И дальше в drivers/input/serio/i8042.c статус проверяется в прерывании:
static irqreturn_t i8042_interrupt(int irq, void *dev_id) { ... str = i8042_read_status(); ... if (str & I8042_STR_MUXERR) { dbg("MUX error, status is x, data is x\n", str, data);
А на KEYLOCK статус проверяется один раз в ините:
static int i8042_controller_init(void) { .../* * Handle keylock. */ spin_lock_irqsave(&i8042_lock, flags); if (~i8042_read_status() & I8042_STR_KEYLOCK) { if (i8042_unlock) i8042_ctr |= I8042_CTR_IGNKEYLOCK; else pr_warn("Warning: Keylock active\n"); } spin_unlock_irqrestore(&i8042_lock, flags); }
- Помогите разобраться с драйвером!, pavlinux, 00:34 , 07-Дек-13 (5)
- Помогите разобраться с драйвером!, BatFox, 10:44 , 23-Дек-13 (32)
Прочла всё, что вы тут написали. Решила проблему по-своему – просто изменила обработчик так, чтобы при вызове он напрямую обращался к функции вывода символа в системный журнал. Вод код #include <linux/kernel.h> /* We're doing kernel work */ #include <linux/module.h> /* Specifically, a module */ #include <linux/sched.h> #include <linux/workqueue.h> #include <linux/interrupt.h> /* We want an interrupt */ #include <asm/io.h>#define MY_WORK_QUEUE_NAME "WQsched.c" #define SA_SHIRQ IRQF_SHARED //static struct workqueue_struct *my_workqueue; static void got_char(void *scancode) { printk(KERN_INFO "Scan Code %x %s.\n", (int)*((char *)scancode) & 0x7F, *((char *)scancode) & 0x80 ? "Released" : "Pressed"); } irqreturn_t irq_handler(int irq, void *dev_id, struct pt_regs *regs) { //static int initialised = 0; static unsigned char scancode; //static struct work_struct task; unsigned char status; status = inb(0x64); scancode = inb(0x60); /*if (initialised == 0) { INIT_WORK(&task, got_char, &scancode); initialised = 1; } else { PREPARE_WORK(&task, got_char, &scancode); } queue_work(my_workqueue, &task);*/ got_char(&scancode); return IRQ_HANDLED; } int init_module() { //my_workqueue = create_workqueue(MY_WORK_QUEUE_NAME); free_irq(1, NULL); return request_irq(1, /* The number of the keyboard IRQ on PCs */ irq_handler, /* our handler */ SA_SHIRQ, "test_keyboard_irq_handler", (void *)(irq_handler)); }
void cleanup_module() { free_irq(1, NULL); } MODULE_LICENSE("GPL"); Он проверен и работает. Лабу я сдала)
- Помогите разобраться с драйвером!, pavlinux, 02:00 , 25-Дек-13 (33)
> Он проверен и работает.Каким боком? Если функция обработчика должна иметь два аргумента: (int, void *); У тебя тут ещё структура pt_regs, которую уже в ядре 2.6.20 не стали использовать. irqreturn_t irq_handler(int irq, void *dev_id, struct pt_regs *regs) int init_module() void cleanup_module() Эти две тоже в 2.6.20 заменили, точнее расширили аттрибутами __init и __exit ... а задача была >> и работал правильно на новых версиях (3.2, например). . > Лабу я сдала) Скажем так, препод на тебя забил (как вариант - добрый и понимающий). :)
- Помогите разобраться с драйвером!, skb7, 05:14 , 25-Дек-13 (35)
> Скажем так, препод на тебя забил (как вариант - добрый и понимающий). Ну, вспоминая ваши слова: > Думаю на трояк если модуль скомпилиться, на 4 если будет глюкать, и на 5 если за работает. :) то таки 5 :) ибо на новом ядре компилится и работает. я бы сказал 4 -- потому что схитрила и выкинула WQ (хотя тут надо смотреть, какое было задание), плюс портирование кривое. Но таки догадалась, как можно сделать, и таки работает.
- Помогите разобраться с драйвером!, pavlinux, 14:20 , 25-Дек-13 (38)
>> Скажем так, препод на тебя забил (как вариант - добрый и понимающий). > Ну, вспоминая ваши слова: >> Думаю на трояк если модуль скомпилиться, на 4 если будет глюкать, и на 5 если за работает. :) > то таки 5 :) ибо на новом ядре компилится и работает. я > бы сказал 4 -- потому что схитрила и выкинула WQ (хотя > тут надо смотреть, какое было задание), плюс портирование кривое. Но таки > догадалась, как можно сделать, и таки работает.rmmod kod ------------[ cut here ]------------ WARNING: CPU: 0 PID: 2659 at kernel/irq/manage.c:1342 __free_irq+0x84/0x1b0() Trying to free already-free IRQ 1 Modules linked in: kod(O-) usbhid ehci_pci uhci_hcd ehci_hcd usbcore usb_common intel_agp vmxnet3 intel_gtt CPU: 0 PID: 2659 Comm: rmmod Tainted: G W O 3.12.3-rt4 #1 Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 07/31/2013 00000009 c14332ca f0ca1f0c c1038255 c150244c f0ca1f24 00000a63 c14ffff8 0000053e c106ed74 c106ed74 00000000 f60060c0 00000001 00000000 c1038313 00000009 f0ca1f0c c150244c f0ca1f24 c106ed74 c14ffff8 0000053e c150244c Call Trace: [<c14332ca>] ? dump_stack+0x48/0x78 [<c1038255>] ? warn_slowpath_common+0x85/0xb0 [<c106ed74>] ? __free_irq+0x84/0x1b0 [<c106ed74>] ? __free_irq+0x84/0x1b0 [<c1038313>] ? warn_slowpath_fmt+0x33/0x40 [<c106ed74>] ? __free_irq+0x84/0x1b0 [<c106eedd>] ? free_irq+0x3d/0xa0 [<c1083394>] ? SyS_delete_module+0x134/0x240 [<c10c9ab3>] ? __fput+0x123/0x1d0 [<c1058708>] ? lg_local_lock+0x8/0x20 [<c104e8c6>] ? task_work_run+0x76/0xa0 [<c14375c4>] ? sysenter_do_call+0x12/0x2c ---[ end trace 0000000000000003 ]--- BUG: unable to handle kernel paging request at f9498000 IP: [<f9498000>] 0xf9497fff *pde = 34b73067 *pte = 00000000 task: f3ebc9a0 ti: f3f86000 task.ti: f3f86000 EIP: 0060:[<f9498000>] EFLAGS: 00010246 CPU: 0 EIP is at 0xf9498000 EAX: 00000001 EBX: f3f18a00 ECX: f3ebc9a0 EDX: f9498000 ESI: f60060c0 EDI: f60060c0 EBP: f3ebc9a0 ESP: f3f87f34 DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068 CR0: 8005003b CR2: f9498000 CR3: 33f13000 CR4: 000007d0 Stack: c106f1b0 f3f18a00 f3ebc9a0 f60060c0 c106f477 32f37042 00000000 c106f190 00000000 00000000 00000000 c106f0a0 f0ca1d98 f3f18a00 c106f390 f3f86000 c1051568 00000000 f3ebf1c4 f3f18a00 00000000 c1000000 f3f87f8c f3f87f8c Call Trace: [<c106f1b0>] ? irq_forced_thread_fn+0x20/0x50 [<c106f477>] ? irq_thread+0xe7/0x130 [<c106f190>] ? irq_thread_fn+0x40/0x40 [<c106f0a0>] ? irq_finalize_oneshot+0xc0/0xc0 [<c106f390>] ? irq_thread_check_affinity+0x70/0x70 [<c1051568>] ? kthread+0x88/0x90 [<c1437537>] ? ret_from_kernel_thread+0x1b/0x28 [<c10514e0>] ? kthread_create_on_node+0xd0/0xd0 Code: Bad EIP value. EIP: [<f9498000>] 0xf9498000 SS:ESP 0068:f3f87f34 CR2: 00000000f9498000 ---[ end trace 0000000000000004 ]--- BUG: unable to handle kernel paging request at ffffffec IP: [<c1051696>] kthread_data+0x6/0x10 *pde = 01629067 *pte = 00000000 Oops: 0000 [#2] PREEMPT SMP Modules linked in: usbhid ehci_pci uhci_hcd ehci_hcd usbcore usb_common intel_agp vmxnet3 intel_gtt [last unloaded: kod] CPU: 0 PID: 2657 Comm: irq/1-test_keyb Tainted: G D W O 3.12.3-rt4 #1 Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 07/31/2013 task: f3ebc9a0 ti: f3f86000 task.ti: f3f86000 EIP: 0060:[<c1051696>] EFLAGS: 00010002 CPU: 0 EIP is at kthread_data+0x6/0x10 EAX: 00000000 EBX: c1633d68 ECX: 00000000 EDX: f3f87f5c ESI: f3ebc9a0 EDI: f3ebc9a0 EBP: 00000000 ESP: f3f87d88 DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068 CR0: 8005003b CR2: ffffffec CR3: 33f13000 CR4: 000007d0 Stack: c106f0b9 f3ebc9a0 c1035ff9 c1592c60 00000036 c1633d68 f3ebcd3c c104e8c1 00000000 00000009 f3ebc9a0 f9498000 c1039dc6 00000046 00000000 c1654ea2 00000046 00000009 00000000 f9498000 c1430108 00000000 00000001 00000000 Call Trace: [<c106f0b9>] ? irq_thread_dtor+0x19/0xb0 [<c1035ff9>] ? mm_release+0x89/0x120 [<c104e8c1>] ? task_work_run+0x71/0xa0 [<c1039dc6>] ? do_exit+0x676/0x8c0 [<c1430108>] ? printk+0x37/0x3b [<c10381c3>] ? print_oops_end_marker+0x33/0x40 [<c1005172>] ? oops_end+0x72/0xf0 [<c142f770>] ? no_context+0x16e/0x176 [<c142f893>] ? __bad_area_nosemaphore+0x11b/0x123 [<c1073868>] ? ktime_get+0x58/0xf0 [<c1030bc0>] ? vmalloc_sync_all+0x170/0x170 [<c142f8aa>] ? bad_area_nosemaphore+0xf/0x11 [<c10306d6>] ? __do_page_fault+0xc6/0x440 [<c1067c37>] ? dequeue_rt_stack+0x77/0x1c0 [<c1059b52>] ? finish_task_switch+0x42/0xb0 [<c143511b>] ? __schedule+0x1fb/0x4f0 [<c1030bc0>] ? vmalloc_sync_all+0x170/0x170 [<c143734a>] ? error_code+0x5a/0x60 [<c1030bc0>] ? vmalloc_sync_all+0x170/0x170 [<c106f1b0>] ? irq_forced_thread_fn+0x20/0x50 [<c106f477>] ? irq_thread+0xe7/0x130 [<c106f190>] ? irq_thread_fn+0x40/0x40 [<c106f0a0>] ? irq_finalize_oneshot+0xc0/0xc0 [<c106f390>] ? irq_thread_check_affinity+0x70/0x70 [<c1051568>] ? kthread+0x88/0x90 [<c1437537>] ? ret_from_kernel_thread+0x1b/0x28 [<c10514e0>] ? kthread_create_on_node+0xd0/0xd0 Code: eb a6 8d 76 00 64 a1 90 96 61 c1 8b 80 ac 01 00 00 8b 40 e4 c1 e8 02 83 e0 01 c3 8d 76 00 8d bc 27 00 00 00 00 8b 80 ac 01 00 00 <8b> 40 ec c3 8d b6 00 00 00 00 83 ec 04 8b 90 ac 01 00 00 b9 04 EIP: [<c1051696>] kthread_data+0x6/0x10 SS:ESP 0068:f3f87d88 CR2: 00000000ffffffec ---[ end trace 0000000000000005 ]--- Fixing recursive fault but reboot is needed!
- Помогите разобраться с драйвером!, skb7, 17:10 , 25-Дек-13 (42)
> rmmod kod > > Trying to free already-free IRQ 1 > Fixing recursive fault but reboot is needed! > Да, я об этом тоже писал. Ну тогда сойдёмся на четверке :)
- Помогите разобраться с драйвером!, skb7, 05:06 , 25-Дек-13 (34)
> Он проверен и работаетНевероятно, но факт. Специально проверил на своем ядре 3.11, до последнего не верил что заработает. Как минимум, за самостоятельное решение оценку вы заработали. Но покритиковать код надо. > Решила проблему по-своему – просто изменила обработчик так, чтобы при вызове он напрямую > обращался к функции вывода символа в системный журнал.
Оценку "отл." за такое надо вам поставить. "Обманула Товарища Лектора" то есть :) Вы делаете всю работу в хардварном прерывании, избавившись от bottom half. Чтобы не портировать код workqueue. Но: 1. Ведь BH там был не просто так: обработчик прерывания должен завершаться как можно быстрее, а printk, как и любая операция ввода/вывода, занимает весьма немало времени. Так что, как говорится, за изобретение ставлю пять, а за экзамен... 2. Более того, в этом же была суть задания -- спортировать весь код, а вы просто половину кода выкинули. > free_irq(1, NULL);
Будет матюкаться в журнал ядра (dmesg), что невозможно освободить незанятое прерывание. Я уже выше описывал, почему этого делать нельзя. Просто проверьте свой dmesg после insmod. > #define SA_SHIRQ IRQF_SHARED
Интересный способ портирования устаревших флагов... > int init_module() > void cleanup_module()
Забыли указать аргументы void в них, это важно. Ну и надо заметить, в новых ядрах принято использовать макросы module_init() и module_exit() для этого. > irqreturn_t irq_handler(int irq, void *dev_id, struct pt_regs *regs)
Посмотрите, какая сигнатура для ISR функций в новом ядре (88-я строка): https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.... В общем, с вашим подходом правильно было бы "спортировать" так:
#include <linux/kernel.h> #include <linux/module.h> #include <linux/interrupt.h> #include <asm/io.h>static irqreturn_t kbd2_isr(int irq, void *dev_id) { char scancode; scancode = inb(0x60); pr_info("Scan Code %x %s\n", scancode & 0x7F, scancode & 0x80 ? "Released" : "Pressed"); return IRQ_HANDLED; } static int __init kbd2_init(void) { return request_irq(1, kbd2_isr, IRQF_SHARED, "kbd2", NULL); } static void __exit kbd2_cleanup(void) { free_irq(1, NULL); } module_init(kbd2_init); module_exit(kbd2_cleanup); MODULE_LICENSE("GPL");
- Помогите разобраться с драйвером!, skb7, 05:30 , 25-Дек-13 (36)
По поводу ваших ошибок: просто читайте вывод сборки и исправляйте ворнинги. Вот например для вашего кода вывод сборки, с проверкой sparse (C=1) и cppcheck: $ makemake -C ... M=... C=1 modules make[1]: Entering directory `/usr/src/linux-headers-3.11-2-amd64' CHECK masha.c masha.c:43:17: warning: non-ANSI function declaration of function 'init_module' masha.c:55:21: warning: non-ANSI function declaration of function 'cleanup_module' masha.c:20:13: warning: symbol 'irq_handler' was not declared. Should it be static? masha.c:50:16: warning: incorrect type in argument 2 (different argument counts) masha.c:50:16: expected int enum irqreturn ( *[usertype] handler )( ... ) masha.c:50:16: got int enum irqreturn ( extern [toplevel] *<noident> )( ... ) CC [M] masha.o masha.c: In function ‘init_module’: masha.c:52:16: warning: passing argument 2 of ‘request_irq’ from incompatible pointer type [enabled by default] (void *)(irq_handler)); ^ In file included from masha.c:5:0: include/linux/interrupt.h:130:1: note: expected ‘irq_handler_t’ but argument is of type ‘enum irqreturn_t (*)(int, void *, struct pt_regs *)’ request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, ^ $ cppcheck --enable=all masha.c
Checking masha.c... [masha.c:28]: (style) Variable 'status' is assigned a value that is never used.
- Помогите разобраться с драйвером!, pavlinux, 14:46 , 25-Дек-13 (39) +1
Всё равно при rmmod глюкать будет, нужно функциям request_irq и free_irq вместо NULL передавать указатель на обработчик.. return request_irq(1, kbd2_isr, IRQF_SHARED, "kbd2", (void *)kbd2_isr); free_irq(1, (void *)kbd2_isr);
- Помогите разобраться с драйвером!, skb7, 17:04 , 25-Дек-13 (40)
> Всё равно при rmmod глюкать будет, нужно функциям request_irq и free_irq > вместо NULL > передавать указатель на обработчик.. > return request_irq(1, kbd2_isr, IRQF_SHARED, "kbd2", (void *)kbd2_isr); > free_irq(1, (void *)kbd2_isr); да, всё верно, не досмотрел
- Помогите разобраться с драйвером!, pavlinux, 13:39 , 25-Дек-13 (37)
|