Linux下的platform总线驱动(二)

 

版权所有,转载请说明转自 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

 

 

你可能感兴趣的:(Linux下的platform总线驱动(二))