将linux混杂驱动改为平台驱动案例

把/drivers/char/mini6410_buttons.c给移植成platform驱动,下面是整个的移植过程:
1.首先在/opt/FriendlyARM/mini6410/linux/linux-2.6.38/drivers/char 目录下添加我们的按键device,文件名定为dev_buttons.c,其源码为:
#include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.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,
};

static int __init dev_init(void)
{
   platform_device_register(&tiny6410_buttons_device);
   return 0;
}

static void __exit dev_exit(void)
{
   platform_device_unregister(&tiny6410_buttons_device);
}

module_init(dev_init);
module_exit(dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");

2.需要对/opt/FriendlyARM/mini6410/linux/linux-2.6.38/drivers/char 目录下的Makefile和Kconfig进行修改:
makefile的最后添加这样一行代码:
obj-$(CONFIG_DEV_BUTTONS) +=      dev_buttons.o 
Kconfig的最后添加:
config DEV_BUTTONS
         tristate "Buttons_tiny6410"
         depends on CPU_S3C6410
         default y
         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一样,这样才能进行匹配)
第3条好像可以跳过!!!!这第三步应该是用于静态编译到内核的,我们这里是动态编译,用不到,但是,即使是静态编译,也应该是&tiny6410_buttons_device 而不是&s3c_device_ts!!!!

4.我们还有在/arch/arm/plat-samsung/include/plat/devs.h文件中声明我们的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");
在/opt/FriendlyARM/mini6410/linux/linux-2.6.38/drivers/char 目录下Kconfig文件中添加:
  config PLAT_BTN_DRV
         tristate "PLAT_BTN_DRV"
         depends on CPU_S3C6410
         default y
         help
             Machine support for the Buttons_tiny6410

在/opt/FriendlyARM/mini6410/linux/linux-2.6.38/drivers/char 目录下Makefile文件中添加:
   obj-$(CONFIG_PLAT_BTN_DRV) +=      plat_btn_drv.o
5.以上两个驱动程序,各自生成ko文件,复制到开发板后,进行加载
我们可以写一个测试程序测试一下:
#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);
}


注意:在平台驱动和设备驱动程序里,务必注意头文件,特别是关于平台设备的头文件,一定要加上去,不然就不能编译通过,如:#include <linux/platform_device.h>、#include <linux/device.h>、 #include <plat/devs.h>等等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值