实验平台:Tiny6410 + Linux Kernel 2.6.38
本问主要实践下总线设备驱动模型和输入子系统。功能上实现四个按键("l"、"s"、"Enter"、"Shift")控制下的LED等点亮和熄灭,并通过输入子系统向用户空间上报按键信息。用户空间程序打印按键信息。驱动测试可以参考USB那份总结。
-
//平台驱动:platform_driver
-
-
-
#include <linux/module.h>
-
#include <linux/kernel.h>
-
#include <linux/init.h>
-
#include <linux/fs.h>
-
#include <linux/errno.h>
-
#include <linux/cdev.h>
-
#include <linux/types.h>
-
#include <linux/device.h>
-
#include <linux/mm.h>
-
#include <linux/slab.h>
-
#include <linux/sched.h>
-
#include <linux/poll.h>
-
#include <linux/ioctl.h>
-
#include <linux/input.h>
-
#include <linux/semaphore.h>
-
#include <linux/platform_device.h>
-
#include <linux/interrupt.h>
-
#include <linux/irq.h>
-
#include <linux/compiler.h>
-
#include <asm/uaccess.h>
-
#include <asm/irq.h>
-
#include <asm/io.h>
-
#include <asm/system.h>
-
#include <mach/map.h>
-
#include <mach/regs-gpio.h>
-
#include <mach/gpio-bank-k.h>
-
#include <mach/gpio-bank-n.h>
-
#include <asm-generic/errno-base.h>
-
-
-
static struct timer_list button_control_leds_timer;
-
static struct input_dev *button_control_leds_dev;
-
static int led_tmp;
-
static int button_control_leds_open(struct input_dev *dev)
-
{
-
//all the leds went out
-
led_tmp = readl(S3C64XX_GPKDAT);
-
led_tmp |= 0x000000f0;
-
writel(led_tmp, S3C64XX_GPKDAT);
-
-
-
return 0;
-
}
-
static void button_control_leds_close(struct input_dev *dev)
-
{
-
//all the leds went out
-
led_tmp = readl(S3C64XX_GPKDAT);
-
led_tmp |= 0x000000f0;
-
writel(led_tmp, S3C64XX_GPKDAT);
-
}
-
-
-
-
//4. 定时器中断处理函数 static void button_control_leds_timer_function(unsigned long data)
-
{
-
int button_value, invalid_keys;
-
/* GPN[3:0] is connected to buttons */
-
//4.1 读取按键值并上报给用户空间
-
button_value = readl(S3C64XX_GPNDAT) & 0x0000000f;
-
switch(~button_value & 0x0000000f) {
-
case 0x00000001: //key "l"
-
input_event(button_control_leds_dev, EV_KEY, KEY_L, 1);
-
break;
-
case 0x00000002: //key "s"
-
input_event(button_control_leds_dev, EV_KEY, KEY_S, 1);
-
break;
-
case 0x00000004: //key "Enter"
-
input_event(button_control_leds_dev, EV_KEY, KEY_ENTER, 1);
-
break;
-
case 0x00000009: //key "SHIFT" + key "l"
-
input_event(button_control_leds_dev, EV_KEY, KEY_LEFTSHIFT, 1);
-
input_event(button_control_leds_dev, EV_KEY, KEY_L, 1);
-
break;
-
case 0x0000000a: //key "SHIFT" + key "s"
-
input_event(button_control_leds_dev, EV_KEY, KEY_LEFTSHIFT, 1);
-
input_event(button_control_leds_dev, EV_KEY, KEY_S, 1);
-
break;
-
case 0x00000000: //no key is pressed
-
input_event(button_control_leds_dev, EV_KEY, KEY_L, 0);
-
input_event(button_control_leds_dev, EV_KEY, KEY_S, 0);
-
input_event(button_control_leds_dev, EV_KEY, KEY_ENTER, 0);
-
input_event(button_control_leds_dev, EV_KEY, KEY_LEFTSHIFT, 0);
-
break;
-
default: //invalid keys
-
invalid_keys = 1;
-
break;
-
}
-
//如果按键有效,就上报给用户空间
-
if(!invalid_keys)
-
input_sync(button_control_leds_dev);
-
else
-
invalid_keys = 0;
-
-
-
-
/* GPK[7:4] is connected to LEDs */
-
//4.2 根据按键值,点亮相应的LED灯
-
led_tmp = readl(S3C64XX_GPKDAT);
-
switch(button_value) {
-
case 0x0000000e: //light led1
-
led_tmp |= 0x000000f0;
-
led_tmp &= 0xffffffef;
-
break;
-
case 0x0000000d: //light led2
-
led_tmp |= 0x000000f0;
-
led_tmp &= 0xffffffdf;
-
break;
-
case 0x0000000b: //light led3
-
led_tmp |= 0x000000f0;
-
led_tmp &= 0xffffffbf;
-
break;
-
case 0x00000007: //light led4
-
led_tmp |= 0x000000f0;
-
led_tmp &= 0xffffff7f;
-
break;
-
case 0x0000000f: //all the leds went out
-
led_tmp |= 0x000000f0;
-
break;
-
default: //light all the leds
-
led_tmp &= 0xffffff0f;
-
break;
-
}
-
writel(led_tmp, S3C64XX_GPKDAT);
-
}
-
-
//3. 按键中断处理函数
-
static irqreturn_t button_control_leds_irq(int irq, void *dev_id)
-
{
-
//3.1 修改定时器,10ms延时,防止按键抖动引起的多次处理
-
mod_timer(&button_control_leds_timer, jiffies + HZ/100);
-
-
-
return IRQ_RETVAL(IRQ_HANDLED);
-
}
-
-
-
//2. 当设备和驱动匹配时,probe函数会被调用 static int button_control_leds_probe(struct platform_device *pdev)
-
{
-
struct resource *res;
-
int ret, i, j;
-
-
//2.1 输入子系统input_dev结构体的分配、设置和注册
-
//2.1.1 分配一个input_dev
-
button_control_leds_dev = input_allocate_device();
-
-
//2.1.2 设置input_dev参数
-
set_bit(EV_KEY, button_control_leds_dev->evbit); //按键类型
-
set_bit(EV_REP, button_control_leds_dev->evbit); //支持重复按键
-
set_bit(KEY_L, button_control_leds_dev->keybit); //按键“l”
-
set_bit(KEY_S, button_control_leds_dev->keybit); //按键“s”
-
set_bit(KEY_ENTER, button_control_leds_dev->keybit); //按键“Enter”
-
set_bit(KEY_LEFTSHIFT, button_control_leds_dev->keybit); //按键“Shift”
-
button_control_leds_dev->open = button_control_leds_open; //open函数
-
button_control_leds_dev->close = button_control_leds_close; //close函数
-
//2.1.3 注册一个input_dev结构体
-
ret = input_register_device(button_control_leds_dev);
-
if(unlikely(ret))
-
goto fail1;
-
-
-
//2.2 定时器初始化、设置定时器中断处理函数、添加一个定时器
-
init_timer(&button_control_leds_timer);
-
button_control_leds_timer.function = button_control_leds_timer_function;
-
add_timer(&button_control_leds_timer);
-
-
//2.3 获取平台设备资源、注册按键中断
-
for(i = 0; i < pdev->num_resources; i++) {
-
//2.3.1 获取平台设备资源:中断获取
-
res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
-
if(unlikely(res == NULL)) {
-
printk("Button %d: get platform resource failed!\n", i);
-
goto fail2;
-
}
-
//2.3.2 注册按键中断
-
ret = request_irq(res->start, button_control_leds_irq, \
-
IRQ_TYPE_EDGE_BOTH, res->name, pdev);
-
if(unlikely(ret != 0)) {
-
printk("Button %d: request_irq failed\n", i);
-
goto fail3;
-
}
-
}
-
return 0;
-
fail1:
-
input_unregister_device(button_control_leds_dev);
-
input_free_device(button_control_leds_dev);
-
return ret;
-
-
-
fail2:
-
del_timer(&button_control_leds_timer);
-
input_unregister_device(button_control_leds_dev);
-
input_free_device(button_control_leds_dev);
-
return -ENXIO;
-
-
fail3:
-
for(j = 0; j <= i; j++) {
-
res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
-
free_irq(res->start, pdev);
-
}
-
del_timer(&button_control_leds_timer);
-
input_unregister_device(button_control_leds_dev);
-
input_free_device(button_control_leds_dev);
-
return ret;
-
}
-
-
static int button_control_leds_remove(struct platform_device *pdev)
-
{
-
struct resource *res;
-
int i;
-
-
-
for(i = 0; i < pdev->num_resources; i++) {
-
res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
-
free_irq(res->start, pdev);
-
}
-
del_timer(&button_control_leds_timer);
-
input_unregister_device(button_control_leds_dev);
-
input_free_device(button_control_leds_dev);
-
-
-
-
return 0;
-
}
-
-
-
-
//1. 平台总线platform_driver结构体及其注册
-
struct platform_driver button_control_leds_drv = {
-
.probe = button_control_leds_probe,
-
.remove = button_control_leds_remove,
-
.driver = {
-
.name = "button_control_leds",
-
.owner = THIS_MODULE,
-
}
-
};
-
-
static int __init button_control_leds_init(void)
-
{
-
platform_driver_register(&button_control_leds_drv);
-
return 0;
-
}
-
-
static void __exit button_control_leds_exit(void)
-
{
-
platform_driver_unregister(&button_control_leds_drv);
-
}
-
module_init(button_control_leds_init);
-
module_exit(button_control_leds_exit);
-
MODULE_AUTHOR("Jason Lu");
-
MODULE_VERSION("0.1.0");
-
MODULE_DESCRIPTION("Jason's blog: jason2012.blog.chinaunix.net");
-
MODULE_LICENSE("Dual MPL/GPL");
-
MODULE_ALIAS("Driver of button control leds");
-
//平台设备:platform_device
-
-
#include <linux/module.h>
-
#include <linux/version.h>
-
#include <linux/init.h>
-
#include <linux/kernel.h>
-
#include <linux/types.h>
-
#include <linux/interrupt.h>
-
#include <linux/list.h>
-
#include <linux/timer.h>
-
#include <linux/serial_core.h>
-
#include <linux/platform_device.h>
-
#include <mach/gpio.h>
-
#include <mach/irqs.h>
-
//1. 平台设备资源
-
static struct resource button_control_leds_resource[] = {
-
[0] = {
-
.name = "button 1",
-
.start = IRQ_EINT(0),
-
.end = IRQ_EINT(0),
-
.flags = IORESOURCE_IRQ,
-
},
-
[1] = {
-
.name = "button 2",
-
.start = IRQ_EINT(1),
-
.end = IRQ_EINT(1),
-
.flags = IORESOURCE_IRQ,
-
},
-
[2] = {
-
.name = "button 3",
-
.start = IRQ_EINT(2),
-
.end = IRQ_EINT(2),
-
.flags = IORESOURCE_IRQ,
-
},
-
[3] = {
-
.name = "button 4",
-
.start = IRQ_EINT(3),
-
.end = IRQ_EINT(3),
-
.flags = IORESOURCE_IRQ,
-
},
-
};
-
-
static void button_control_leds_release(struct device * dev)
-
{
-
}
//平台设备platform_device结构体
-
static struct platform_device button_control_leds_dev = {
-
.name = "button_control_leds",
-
.id = -1,
-
.num_resources = ARRAY_SIZE(button_control_leds_resource),
-
.resource = button_control_leds_resource,
-
.dev = {
-
.release = button_control_leds_release,
-
},
-
};
//3. 注册一个平台设备
-
static int button_control_leds_dev_init(void)
-
{
-
platform_device_register(&button_control_leds_dev);
-
return 0;
-
}
-
-
static void button_control_leds_dev_exit(void)
-
{
-
platform_device_unregister(&button_control_leds_dev);
-
}
-
-
module_init(button_control_leds_dev_init);
-
module_exit(button_control_leds_dev_exit);
-
-
MODULE_AUTHOR("Jason Lu");
-
MODULE_VERSION("0.1.0");
-
MODULE_DESCRIPTION("Jason's blog: jason2012.blog.chinaunix.net");
-
MODULE_LICENSE("Dual MPL/GPL");
-
MODULE_ALIAS("Device of button control leds");
阅读(3640) | 评论(0) | 转发(2) |