您的位置:首页 > 运维架构 > Linux

Linux下的platform总线驱动(二)

2012-12-02 09:47 281 查看
版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127
三.平台设备驱动测试

这里我们采用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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: