美文网首页程序员
定时中断键盘灯闪烁

定时中断键盘灯闪烁

作者: 网路元素 | 来源:发表于2019-11-03 15:35 被阅读0次

在学习完《趣味定时器》后,是不是只在 dmesg 里看到效果很没劲,这次来点更实际的,我们让键盘灯闪起来。

对于定时器,就不多说了,接下来了解下键盘的基本内容。键盘在 Linux 里是属于TTY(TeleTYpe)设备,既然我们要用到键盘,那先找其通用驱动代码,在 Linux Kernel 源码drivers/tty/vt/keyboard.c 文件,我们找到需要的 LED 灯控制函数:

void setledstate(struct kbd_struct *kbd, unsigned int led);

该函数被在同一文件的 vt_do_kdskled()函数调用,而这函数又被 drivers/tty/vt/vt_ioctl.c 文件的 vt_ioctl()函数调用,这函数是 drivers/tty/vt/vt.c 里struct tty_operations con_ops 的 ioctl 成员,在同一文件 vty_init()函数里有如下语句:

tty_set_operations(console_driver, &con_ops);

将 console_driver->ops 赋值为&con_ops,而 console_driver 是如下类型:

struct tty_driver *console_driver;

该结构体在 include/linux/tty_driver.h 定义,其被 include/linux/tty.h 文件中的 struct tty_struct结构体包含,其又被同文件的 struct tty_port 结构体包含。而我们在 drivers/tty/vt/vt_ioctl.c 文件中有看到 vt_disallocate_all()函数里有 tty_port_destroy(&vc[i]->port);操作,那么找到 vc 就可以关联起来,在 include/linux/console_struct.h 文件中有 vc 的类型 struct vc_data 结构体定义,而在drivers/tty/vt/selection.c 文件中的 set_selection()函数里有 struct vc_data *vc = vc_cons[fg_console].d;赋值操作,那么往下找 vc_cons,其在 drivers/tty/vt/vt.c 文件中定义为struct vc vc_cons[MAX_NR_CONSOLES];其为全局量,至此整个关系就理清了,接下来是小细节问题:

fg_console 在 drivers/tty/vt/vt.c 文件中有如下注释:

fg_console is the current virtual console,

其在同文件中声明为 int fg_console;

至此,从 vc_cons 到 setledstate 的路及路障都清得差不多了,接下来得理理 vc_ioctl 了,其在 drivers/tty/vt/vt_ioctl.c 中的定义如下(只截函数头部分):

int vt_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg);

其中,tty 刚好对应上 struct vc 结构体里 struct vc_data *d 结构体成员的 struct tty_port port; 结构体成员的 struct tty_struct *tty; 成员;而 cmd 就是 vt_ioctl()里的 KDSETLED;而 arg 则是设置 LED 灯状态值,其对应 NumLock、CapsLock 和 ScrollLock 三个灯的状态,可以是任一灯、任两灯或全三灯亮灭的状态,在 include/linux/kbd_kern.h 文件中有如下内容:

#define VC_SCROLLOCK 0
#define VC_NUMLOCK 1
#define VC_CAPSLOCK 2
#define VC_KANALOCK 3

在 drivers/tty/vt/keyboard.c 文件中有 led 灯状态的默认值定义:

static unsigned char ledstate = 0xff; /* undefined */

而 setledstate 函数的定义如下:

void setledstate(struct kbd_struct *kbd, unsigned int led)
{
    unsigned long flags;
    spin_lock_irqsave(&led_lock, flags);

    if (!(led & ~7)) {
        ledioctl = led;
        kbd->ledmode = LED_SHOW_IOCTL;
    } else
        kbd->ledmode = LED_SHOW_FLAGS;

    set_leds();
    spin_unlock_irqrestore(&led_lock, flags);
}

由函数可知当传入的状态值为 7 时,使用 IOCTL 模式,其他则使用 FLAGS 模式;使用IOCTL 时 3 个灯全亮,当为 FLAGS 时则按键盘实际状态亮灭,而我们等会使用 0xff 恢复未定义状态。

好了,分析了这么多,跟踪了整个路线,我们还是上实例代码吧:

#include <linux/module.h>
#include <linux/timer.h>
#include <linux/tty.h>
#include <linux/kd.h>
#include <linux/console_struct.h>

#define BLINK_DELAY HZ/4
#define ALL_LEDS_ON 0x07
#define RESTORE_LEDS_STATUS 0xff

struct timer_list slam_timer;
char led_status = 0;

extern struct vc vc_cons [MAX_NR_CONSOLES];
extern int fg_console;

static struct tty_driver * slam_tty_driver;

void keyboard_led_timer_function(unsigned long data)
{
        int *pre_status = (int *)data;

        if (*pre_status == ALL_LEDS_ON)
                *pre_status = RESTORE_LEDS_STATUS;
        else
                *pre_status = ALL_LEDS_ON;

        (slam_tty_driver->ops->ioctl)(vc_cons[fg_console].d->port.tty, KDSETLED, *pre_status);
        mod_timer(&slam_timer, jiffies + msecs_to_jiffies(BLINK_DELAY));
}

static __init int timer_keyboard_led_init(void)
{
        slam_tty_driver = vc_cons[fg_console].d->port.tty->driver;
        setup_timer(&slam_timer, keyboard_led_timer_function, (unsigned long)&led_status);
        mod_timer(&slam_timer, jiffies + msecs_to_jiffies(BLINK_DELAY));

        return 0;
}

static __exit void timer_keyboard_led_exit(void)
{
        del_timer(&slam_timer);
        (slam_tty_driver->ops->ioctl)(vc_cons[fg_console].d->port.tty, KDSETLED, RESTORE_LEDS_STATUS);
}

module_init(timer_keyboard_led_init);
module_exit(timer_keyboard_led_exit);

相应的 Makefile 文件内容如下:

obj-m += timer_keyboard_led.o

CUR_PATH:=$(shell pwd)
LINUX_KERNEL_PATH:=/home/xinu/linux-3.13.6

all:
        make -C $(LINUX_KERNEL_PATH) M=$(CUR_PATH) modules

clean:
        make -C $(LINUX_KERNEL_PATH) M=$(CUR_PATH) clean

  相应的源码文件目录树如下:

/home/xinu/xinu/linux_kernel_driver_l1/timer_keyboard_led/
├── Makefile
└── timer_keyboard_led.c

好了,剩下的就是自己动手加载吧,注意观察 PC 键盘上右上角 3 个灯的闪烁吧,本文借助于现有的 keyboard 驱动来操作键盘上 LED 灯的状态,再结合定时器来实现 LED 灯闪烁,有没有其他办法呢?如直接操作键盘控制器。抽空多想想吧,多试试,总会有收获的。从这个例子,我们也渐渐往计算机接口技术迈进,不过这里传输的是使用思想,后期再说明控制思想。

参考网址:

http://ldp.icenet.is/LDP/lkmpg/2.6/html/lkmpg.html#AEN1194
http://wowocpp.blog.163.com/blog/static/1242627802009112851936332/
http://tuxthink.blogspot.com/2013/02/kbledsc-for-linux-kernel-version-375.html

相关文章

  • 定时中断键盘灯闪烁

    在学习完《趣味定时器》后,是不是只在 dmesg 里看到效果很没劲,这次来点更实际的,我们让键盘灯闪起来。 对于定...

  • C编程实现键盘LED灯闪烁方法2

    在《C编程实现键盘LED灯闪烁》一文中使用了定时器和ioctl的方式实现键盘LED灯周期性闪烁,而键盘本身作为一个...

  • 系统休眠,定时器唤醒。

    实验显现:灯闪烁3次,然后设置定时器5秒,进入休眠,5秒后唤醒,灯再次闪烁。

  • stm32使用TIM3产生定时器中断控制LED灯闪烁

    参考文章 STM32之定时器中断控制LED闪烁https://blog.csdn.net/qq_36554582/...

  • shell脚本实现键盘LED灯闪烁

    很幸运,在Ubuntu13.10的Terminal下发现了setleds命令,直接运行该命令后有如下输出: xin...

  • C编程实现键盘LED灯闪烁

    在《Shell脚本实现键盘LED灯闪烁》一文中,我们已感受到了控制的乐趣,一步步向硬件逼近,这次我们在Linux下...

  • 无线键盘鼠标接收器调试方法

    键盘对码:将键盘关机,接收器取掉,重新给键盘开机后尽快按下“esc+k”键,在“灯快速闪烁”的时候 ,立即插上接收...

  • 10.12学习总结

    今天我们学习了通用定时器,是以通用定时器timer3为例讲解的,仅是简单的用它触发中断,控制LED灯。基本的步骤是...

  • Linux C/C++定时器的实现原理和使用方法

    定时器的实现原理 定时器的实现依赖的是CPU时钟中断,时钟中断的精度就决定定时器精度的极限。一个时钟中断源如何实现...

  • 趣味定时器

    学习完中断后,接下来都是定时中断这一巧妙的中断模式。定时器是在设定时间并且时间一到后就会响应执行设定好的事件,但其...

网友评论

    本文标题:定时中断键盘灯闪烁

    本文链接:https://www.haomeiwen.com/subject/ttqryctx.html