tiny6410中的按键驱动没有提供按键的platform驱动,于是闲来无事把/drivers/char/mini6410_buttons.c给移植成platform驱动,下面是整个的移植过程:
1.首先在 /arch/arm/mach-s3c64xx 目录下添加我们的按键device,文件名定为dev-buttons.c,其源码为:
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <plat/devs.h>//这个是必须添加的,否则会产生错误
/* 平台资源的定义 */
static struct resource tiny6410_buttons_resource[] = {
[0] = {
.start = IRQ_EINT(0),
.end = IRQ_EINT(0),
.flags = IORESOURCE_IRQ,
},
[1] = {
.start = IRQ_EINT(1),
.end = IRQ_EINT(1),
.flags = IORESOURCE_IRQ,
},
[2] = {
.start = IRQ_EINT(2),
.end = IRQ_EINT(2),
.flags = IORESOURCE_IRQ,
},
[3] = {
.start = IRQ_EINT(3),
.end = IRQ_EINT(3),
.flags = IORESOURCE_IRQ,
},
[4] = {
.start = IRQ_EINT(4),
.end = IRQ_EINT(4),
.flags = IORESOURCE_IRQ,
},
[5] = {
.start = IRQ_EINT(5),
.end = IRQ_EINT(5),
.flags = IORESOURCE_IRQ,
},
[6] = {
.start = IRQ_EINT(19),
.end = IRQ_EINT(19),
.flags = IORESOURCE_IRQ,
},
[7] = {
.start = IRQ_EINT(20),
.end = IRQ_EINT(20),
.flags = IORESOURCE_IRQ,
} /* 这里不需要加逗号 */
};
struct platform_device tiny6410_buttons_device = //此处不要定义为static的,否则会有错误
{
.name = "tiny6410_buttons",
.id = -1,
.num_resources = ARRAY_SIZE(tiny6410_buttons_resource),
.resource = tiny6410_buttons_resource,
};
2.需要对/arch/arm/mach-s3c64xx 目录下的Makefile和Kconfig进行修改:
makefile的最后添加这样一行代码:
obj-y += dev-buttons.o
这里-y表示不管在make menuconfig中有没有选择都会强制编译
Kconfig的最后添加:
config BUTTONS_TINY6410
bool "Buttons_tiny6410"
help
Machine support for the Buttons_tiny6410
3.修改/arch/arm/mach-s3c64xx 目录下的 mach-mini6410.c的代码:
在此结构体中 static struct platform_device *mini6410_devices[] __initdata = { 最后一行添加按键设备:
&s3c_device_ts
此结构会被添加到static void __init mini6410_machine_init(platform_add_devices(mini6410_devices, ARRAY_SIZE(mini6410_devices));)中,此函数会在内核启动初始化时被调用,从而将按键device挂载到内核,等待驱动去匹配。(注意:驱动的name要和device的name一样,这样才能进行匹配)
4.我们还有在/arch/arm/plat-samsung/include/plat/文件中声明我们的platform设备:
在其中添加 extern struct platform_device tiny6410_buttons_device;
至此,平台设备这一边已经告一段落了!下面就是按键驱动。
驱动以模块方式添加到内核,下面是驱动的代码,文件名为:plat_btn_drv.c,
#include <linux/module.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <mach/map.h>
#include <mach/regs-gpio.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/unistd.h>
#include <linux/device.h>
#include <mach/gpio-bank-n.h>
#include <mach/gpio-bank-l.h>
#define DRIVER_NAME "tiny6410_buttons"
#define DEVICE_NAME "tiny6410_buttons"
/* 定义并初始化等待队列 */
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
static volatile int ev_press = 0;
/* 记录键值 */
static int key_value;
/* 记录按键中断号 */
typedef struct
{
int irq; /* 中断号,初始化为 0,将在
* probe 获取资源 */
int num; /* 对应的按键编号 */
char *name; /* 中断所属的名字 */
} button_irq_t;
button_irq_t button_irqs[] =
{
{0, 0, "KEY0"},
{0, 1, "KEY1"},
{0, 2, "KEY2"},
{0, 3, "KEY3"},
{0, 4, "KEY4"},
{0, 5, "KEY5"},
{0, 6, "KEY6"},
{0, 7, "KEY7"},
};
/*
* buttons_interrupt
* 使用一个整形变量 key_value 的 0~7 位来记录键值,0~7
* 位分别对应 KEY0~KEY7 的抬起或者按下情况( 1 表示按下)。
*/
static irqreturn_t buttons_interrupt(int irq,void *dev_id)
{
/* 定义一个指针,指向所对应的中断号 */
button_irq_t *button_irqs = (button_irq_t *)dev_id;
int down;
int num;
unsigned tmp;
num = button_irqs->num;
switch(num)
{
case 0: case 1: case 2: case 3: case 4: case 5:
tmp = readl(S3C64XX_GPNDAT);
down = !(tmp & (1 << num));
break;
case 6: case 7:
tmp = readl(S3C64XX_GPLDAT);
down = !(tmp & (1 << (num + 5)));
break;
default:
down = 0;
}
if(down == !(key_value & (1 << num)))
{
key_value = down ? key_value | (1<<num) : key_value & ~(1 << num);
ev_press = 1;
wake_up_interruptible(&button_waitq);
}
return IRQ_RETVAL(IRQ_HANDLED);
}
/*
* tiny6410_buttons_open
*/
static int tiny6410_buttons_open(struct inode *inode,struct file *file)
{
int i;
int err = 0;
for(i = 0;i < sizeof(button_irqs) / sizeof(button_irqs[0]);i ++ )
{
if (button_irqs[i].irq < 0)
continue;
/* 申请中断 */
err = request_irq(button_irqs[i].irq,buttons_interrupt,
IRQ_TYPE_EDGE_BOTH,
button_irqs[i].name,
(void *)&button_irqs[i]);
if (err)
break;
}
if(err)
{
i--;
for(;i >= 0;i -- )
{
if(button_irqs[i].irq < 0)
continue;
disable_irq(button_irqs[i].irq);
free_irq(button_irqs[i].irq,(void *)&button_irqs[i]);
}
return -EBUSY;
}
ev_press = 1;
return 0;
}
/*
* tiny6410_buttons_close
*/
static int tiny6410_buttons_close(struct inode *inode,struct file *file)
{
int i;
for(i = 0;i < sizeof(button_irqs)/sizeof(button_irqs[0]);i ++ )
{
if(button_irqs[i].irq < 0)
continue;
free_irq(button_irqs[i].irq,(void *)&button_irqs[i]);
}
return 0;
}
/*
* tiny6410_buttons_read
*/
static int tiny6410_buttons_read(struct file *filp,char __user *buff,
size_t count,loff_t *offp)
{
unsigned long err;
if(!ev_press)
{
if(filp->f_flags & O_NONBLOCK)
return -EAGAIN;
else
wait_event_interruptible(button_waitq,ev_press);
}
ev_press = 0;
err = copy_to_user((void *)buff,(const void *)(&key_value),
min(sizeof(key_value),count));
return err ? -EFAULT : min(sizeof(key_value),count);
}
/*
* poll 实现函数
*/
static unsigned int tiny6410_buttons_poll(struct file *file,
struct poll_table_struct *wait)
{
unsigned int mask = 0;
poll_wait(file,&button_waitq,wait);
if(ev_press)
mask |= POLLIN | POLLRDNORM;
return mask;
}
/*
* file_operation
*/
static struct file_operations dev_fops =
{
.owner = THIS_MODULE,
.open = tiny6410_buttons_open,
.release = tiny6410_buttons_close,
.read = tiny6410_buttons_read,
.poll = tiny6410_buttons_poll,
};
/*
* 混合设备结构体
*/
static struct miscdevice tiny6410_buttons_misc =
{
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
/*
* probe 实现函数
*/
static int tiny6410_buttons_probe(struct platform_device *pdev)
{
int ret;
int i;
struct device *dev;
static struct resource *buttons_irq;
printk("[call %s]\n", __func__);
/* 获取设备资源 */
for(i = 0;i < 8;i ++ )
{
buttons_irq = platform_get_resource(pdev,IORESOURCE_IRQ,i);
button_irqs[i].irq = buttons_irq->start;
}
ret = misc_register(&tiny6410_buttons_misc);
return 0;
}
/*
* remove 实现函数
*/
static int tiny6410_buttons_remove(struct platform_device *dev)
{
misc_deregister(&tiny6410_buttons_misc);
return 0;
}
/* 平台设备驱动结构 */
static struct platform_driver tiny6410_buttons_driver = {
.probe = tiny6410_buttons_probe,
.remove = tiny6410_buttons_remove,
.driver =
{
.owner = THIS_MODULE,
.name = DRIVER_NAME,
},
};
static int __init buttons_init(void)
{
printk("[Call buttons_init!]\n");
/* 注册驱动 */
platform_driver_register(&tiny6410_buttons_driver);
return 0;
}
static void __exit buttons_exit(void)
{
printk("[Call buttons_exit!]\n");
platform_driver_unregister(&tiny6410_buttons_driver);
}
module_init(buttons_init);
module_exit(buttons_exit);
MODULE_AUTHOR("_Justin");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Tiny6410 Buttons Driver");
最后我们需要重新编译内核:make zImage
然后将生成的zImage拷贝到开发板上,运行,编写按键驱动的makefile:
ifneq ($(KERNELRELEASE),)
obj-m := plat_btn_drv.o
else
KDIR := /opt/FriendlyARM/mini6410/linux/linux-2.6.38
all:
make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order
endif
最后把驱动的plat_btn_drv.ko文件通过拷贝到开发板,通过命令insmod plat_btn_drv.ko插入内核:
此时会在超级终端中打印出下面的话:
[Call buttons_init!]
[call tiny6410_buttons_probe]
表明platform驱动成功加载,
这时候,在/sys/bus/platform/devices下,我们发现我们添加的设备tiny6410_buttons已经存在了。
另外,在/sys/bus/platform/drivers下,我们发现我们添加的驱动tiny6410_buttons也已经存在了。
在/sys/bus/platform/devices/tiny6410_buttons/driver下,有设备tiny6410_buttons,这说明tiny6410_buttons设备和tiny6410_buttons驱动已经联系起来了。
另外,在/sys/bus/platform/drivers/tiny6410_buttons/device下,有驱动tiny6410_buttons,这也说明tiny6410_buttons设备和tiny6410_buttons驱动已经联系起来了。
我们可以写一个测试程序测试一下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <time.h>
#define DEVICE_NAME "/dev/tiny6410_buttons"
static int key_value = 0;
int main(void)
{
int fd, ret;
fd_set rfds;
int last_kval = key_value;
if(-1 == (fd = open(DEVICE_NAME,O_RDONLY)))
{
printf("open %s error\n",DEVICE_NAME);
_exit(EXIT_FAILURE);
}
/* 先清空集合 */
FD_ZERO(&rfds);
/* 设置要监控的文件描述符 */
FD_SET(fd,&rfds);
printf("Test for tiny6410_buttons: ...\n");
while(1)
{
if(-1 == (ret = select(fd + 1,&rfds,NULL,NULL,NULL)))
{
printf("select error\n");
_exit(EXIT_FAILURE);
}
if(FD_ISSET(fd,&rfds))
{
read(fd, &key_value, sizeof(key_value));
int i;
for(i = 0;i < 8;i ++ )
{
if((key_value & (1 << i)) != (last_kval & (1 << i)))
{
printf("KEY%d: %s (key_value=0x%x)\n", i+1,
(key_value& (1<<i))? "DOWN": "UP", key_value);
}
}
last_kval = key_value;
}
}
_exit(EXIT_SUCCESS);
}
运行测试程序会打印出:
KEY4: DOWN (key_value=0x8)