三.平台设备驱动测试
这里我们采用Mini2440开发板,编写基于平台设备的按键驱动,要求按键驱动触发方式为单边沿触发,同时要求添加设备属性项。因为这个驱动比较简单,我就不去细致分析了,如果对硬件不理解可以参考mini2440开发板数据手册,如果对软件不理解,可以参考上文平台设备的讲解。在此,我提供platform设备模块代码,platform驱动模块代码,应用层测试代码,需要注意的是在动态加载测试时需要先加载设备模块,再加载驱动模块。
1. platform设备模块代码
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/sysfs.h>
#include <linux/stat.h>
#define GPGCON 0x56000060 //控制端口地址
#define GPGDAT 0x56000064 //数据端口地址
ssize_t test_show(struct device *dev, struct attribute *attr, char *buf);
ssize_t test_store(struct device *dev, struct attribute *attr, char *buf,size_t count);
static DEVICE_ATTR(buttons, S_IRWXUGO, test_show, test_store); //设备属性
ssize_t test_show(struct device *dev, struct attribute *attr, char *buf) //读设备属性
{
printk("call : test_show . \n");
printk("attrname:%s . \n",attr->name);
sprintf(buf,"%s\n",attr->name);
return strlen(attr->name)+2;
}
ssize_t test_store(struct device *dev, struct attribute *attr, char *buf,size_t count) //写设备属性
{
printk("call : test_store . \n");
printk("write : %s . \n",buf);
strcpy(attr->name,buf);
return count;
}
static struct resource s3c_buttons_resource[]=
{
[0]={ //内存资源
.start = GPGCON,
.end = GPGDAT,
.flags=IORESOURCE_MEM,
},
[1]={ //中断号
//KEY1
.start = IRQ_EINT8,
.end = IRQ_EINT8,
.flags=IORESOURCE_IRQ,
},
[2]={
//KEY2
.start = IRQ_EINT11,
.end = IRQ_EINT11,
.flags=IORESOURCE_IRQ,
},
};
MODULE_AUTHOR("WJB");
MODULE_LICENSE("Dual BSD/GPL");
static struct platform_device *my_device = NULL;
static int __init my_device_init(void)
{
int ret = 0;
my_device = platform_device_alloc("s3c2410-buttons", -1); //申请平台设备
platform_device_add_resources(my_device, s3c_buttons_resource, 3); //添加资源
ret = platform_device_add(my_device); //注册平台设备
device_create_file(&my_device->dev,&dev_attr_buttons); //添加设备属性
if(ret)
platform_device_put(my_device);
return ret;
}
static void my_device_exit(void)
{
platform_device_unregister(my_device);
device_remove_file(&my_device->dev,&dev_attr_buttons);
}
module_init(my_device_init);
module_exit(my_device_exit);
2. platform驱动模块代码
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
MODULE_AUTHOR("WJB");
MODULE_LICENSE("Dual BSD/GPL");
#define BUTTONS_12INPUT_MASK 0x41
struct button_irq_desc { //私有数据结构体
int number;
char *name;
};
static struct button_irq_desc buttons_irqs [] = { //私有数据
{ 0, "KEY1"},
{ 1, "KEY2"},
};
static volatile char key_values [] = {'0', '0'};
static DECLARE_WAIT_QUEUE_HEAD(button_waitq); //定义等待队列
static volatile int ev_press = 0;
static struct resource *buttons_irq1;
static struct resource *buttons_irq2;
static struct resource *buttons_mem;
static void __iomem *buttons_base;
static irqreturn_t s3c2410buttons_irq(int irq, void *dev_id)
{
struct button_irq_desc *buttons_irqs = (struct button_irq_desc *)dev_id;
unsigned int tmp;
void __iomem *base = buttons_base;
tmp=readb(base+0x04);
if(buttons_irqs->number==0)
{
tmp &=0x01;
}else{
tmp &=0x08;
}
// process data
if (tmp == (key_values[buttons_irqs->number] & 1)) { // Changed
key_values[buttons_irqs->number] = '1' ;
}
ev_press = 1;
wake_up_interruptible(&button_waitq);
return IRQ_RETVAL(IRQ_HANDLED);
}
static int s3c24xx_buttons_open(struct inode *inode, struct file *file)
{ int ret;
unsigned int tmp;
void __iomem *base = buttons_base;
// set key1 and key2 input
tmp=readb(base);
writeb(tmp|BUTTONS_12INPUT_MASK ,base);
ret = request_irq(buttons_irq1->start, s3c2410buttons_irq,IRQ_TYPE_EDGE_FALLING, "KET1", (void *)&buttons_irqs[0]);
if (ret != 0) {
printk( "failed to install irq (%d)\n", ret);
goto err1;
}
ret = request_irq(buttons_irq2->start, s3c2410buttons_irq, IRQ_TYPE_EDGE_FALLING, "KET2", (void *)&buttons_irqs[1]);
if (ret != 0) {
printk( "failed to install irq (%d)\n", ret);
goto err2;
}
ev_press = 1;
return 0;
err2: disable_irq(buttons_irq2->start);
free_irq(buttons_irq2->start, (void *)&buttons_irqs[1]);
err1: disable_irq(buttons_irq1->start);
free_irq(buttons_irq1->start, (void *)&buttons_irqs[0]);
return -EBUSY;
}
static int s3c24xx_buttons_close(struct inode *inode, struct file *file)
{
disable_irq(buttons_irq2->start);
free_irq(buttons_irq2->start, (void *)&buttons_irqs[1]);
disable_irq(buttons_irq1->start);
free_irq(buttons_irq1->start, (void *)&buttons_irqs[0]);
return 0;
}
static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
unsigned long err;
int i;
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(buff, (const void *)key_values, min(sizeof(key_values), count));
for (i=0;i<2;i++)
{
key_values[i]='0';
}
return err ? -EFAULT : min(sizeof(key_values), count);
}
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.open = s3c24xx_buttons_open,
.release = s3c24xx_buttons_close,
.read = s3c24xx_buttons_read,
};
static struct miscdevice s3c2410buttons_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "s3c2410-buttons",
.fops = &dev_fops,
};
static int my_probe(struct platform_device* pdev)
{
int ret;
struct resource *res;
struct device *dev;
dev = &pdev->dev;
// get resource
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "failed to get memory region resource\n");
return -ENOENT;
}
buttons_mem = request_mem_region(res->start,
res->end-res->start+1,
pdev->name);
if (buttons_mem == NULL) {
dev_err(&pdev->dev, "failed to reserve memory region\n");
ret = -ENOENT;
goto err_nores;
}
buttons_base = ioremap(res->start, res->end - res->start + 1);
if (buttons_base == NULL) {
dev_err(&pdev->dev, "failed ioremap()\n");
ret = -EINVAL;
goto err_nores;
}
//get key1 interrupt
buttons_irq1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (buttons_irq1 == NULL) {
dev_err(dev, "no irq resource specified\n");
ret = -ENOENT;
goto err_map;
}
//get key2 interrupt
buttons_irq2 = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
if (buttons_irq2 == NULL) {
dev_err(dev, "no irq resource specified\n");
ret = -ENOENT;
goto err_map;
}
// register misc device
ret = misc_register(&s3c2410buttons_miscdev);
if (ret) {
dev_err(dev, "cannot register miscdev on minor=%d (%d)\n",
WATCHDOG_MINOR, ret);
goto err_map;
}
printk("driver found device which my driver can handle!\n");
err_map:
iounmap(buttons_base);
err_nores:
release_resource(buttons_mem);
kfree(buttons_mem);
return ret;
}
static int my_remove(struct platform_device* pdev)
{
release_resource(buttons_mem);
kfree(buttons_mem);
buttons_mem = NULL;
free_irq(buttons_irq1->start, (void *)&buttons_irqs[0]);
buttons_irq1 = NULL;
free_irq(buttons_irq2->start, (void *)&buttons_irqs[1]);
buttons_irq2 = NULL;
iounmap(buttons_base);
misc_deregister(&s3c2410buttons_miscdev);
printk("drvier found device unpluged!/n");
return 0;
}
static struct platform_driver my_driver = {
.probe = my_probe,
.remove = my_remove,
.driver = {
.owner = THIS_MODULE,
.name = "s3c2410-buttons",
},
};
static int __init my_driver_init(void)
{
return platform_driver_register(&my_driver);
}
static void my_driver_exit(void)
{
platform_driver_unregister(&my_driver);
}
module_init(my_driver_init);
module_exit(my_driver_exit);
3.应用层测试代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
int main(void)
{
int buttons_fd;
char buttons[2] = {'0', '0'};
buttons_fd = open("/dev/s3c2410-buttons", 0);
if (buttons_fd < 0) {
perror("open device buttons");
exit(1);
}
for (;;) {
char current_buttons[2];
int count_of_changed_key;
int i;
if (read(buttons_fd, current_buttons, sizeof current_buttons) != sizeof current_buttons) {
perror("read buttons:");
exit(1);
}
for (i = 0, count_of_changed_key = 0; i < sizeof buttons / sizeof buttons[0]; i++) {
if (buttons[i] != current_buttons[i]) {
printf("%skey %d is %s", count_of_changed_key? ", ": "", i+1, buttons[i] == '0' ? "up" : "down");
count_of_changed_key++;
}
}
if (count_of_changed_key) {
printf("\n");
}
}
close(buttons_fd);
return 0;
}
平台设备测试:
在超级终端下:
cd /home/platform/device
insmod device.ko
cd ../driver
insmod driver.ko
cd ../
./buttons
然后通过Mini2440开发板的按键,观察到超级终端的按键信息。
设备属性项测试:
在超级终端下:
cd /sys/platform/ s3c2410-buttons
ls后,会显示buttons这一目录
读取设备属性:cat buttons
修改设备属性:echo modify>buttons