基于mini2440的led驱动编写的总结

基于mini2440的led驱动编写的总结(可以成为模板)

该驱动用到I/O常见的端口操作函数:s3c2410_gpio_cfgpin和s3c2410_gpio_setpin。前一函数实现了设置端口的状态为输入/输出/其他,后一函数实现了端口输出高/低电平。主要谈一下前一个函数的一些重要语句。首先是原型:s3c2410_gpio_cfgpin(unsignedint pin,unsigned intfunction),那么我们在驱动中怎么运用呢?比如:s3c2410_gpio_cfgpin(S3C2410_GPB(5),S3C2410_GPB_OUTPUT),那么里面两个函数是怎么操作的?请看下面的这几个定义:

1、#defineS3C2410_GPB(_nr)   (S3C2410_GPIO_B_START+(_nr))  //定义在arch/arm/mach-s3c2410/include/mach/gpio-nrs.h

2、enum s3c_gpio_number{

           S3C2410_GPIO_A_START = 0,

           S3C2410_GPIO_B_START =S3C2410_GPIO_NEXT(S3C2410_GPIO_A),

......

}//定义同上

3、#define S3C2410_GPIO_NEXT(__gpio)\
 ((__gpio##_START) + (__gpio##_NR) +CONFIG_S3C_GPIO_SPACE + 0) //定义同上

4、CONFIG_S3C_GPIO_SPACE的定义在.config文件中,见下面:

......

#

#power managment

#

CONFIG_S3C_LOWLEVEL_UART_PORT=0

CONFIG_S3C_GPIO_SPACE=0

......

5、#defineS3C2410_GPIO_A_NR (32)
   #defineS3C2410_GPIO_B_NR (32)

  ......

由以上所有定义可以得知以下结论:S3C2410_GPB(5)-->(S3C2410_GPIO_B_START+5)-->S3C2410_GPIO_NEXT(S3C2410_GPIO_A)+5-->((S3C2410_GPIO_A_START)+ S3C2410_GPIO_A_NR) + CONFIG_S3C_GPIO_SPACE + 0)+5,

这就表示从GPA首地址+GPA个数+GPB的偏移个数是当前GPB的I/O的偏移量。即GPB(5)等于32+0+5=37,即pin=37。至于这怎么和GPB端口的寄存器扯上关系的有待进一步深入内核。

另外在/arch/arm/mach-s3c2410/include/mach/regs-gpio.h的第45行有如下定义:

#defineS3C2410_GPIO_LEAVE  (0xFFFFFFFF)

#defineS3C2410_GPIO_INPUT  (0xFFFFFFF0)  //最后两位01表示输出

#defineS3C2410_GPIO_OUTPUT  (0xFFFFFFF1)

这时我们开始讲编写整个驱动的流程,其中分为两种方法。

方法一、(将LED注册为混杂设备)

a:首先确定file_operation中要填写几个操作函数,这里因为注册为混杂设备所以只有ioctl操作。

b:其次编写ioctl的实现。

c:注册LED为misc设备:

static struct miscdevicemisc={

       .minor=MISC_DYNAMIC_MINOR,

        .name=DEVICE_NAME,

       .fops=&mini2440_leds_fops,

}

d:编写初始化函数与退出函数。

具体程序如下:

#include<linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>


#define DEVICE_NAME "leds"

static unsigned longled_table [] = {
 S3C2410_GPB(5),
 S3C2410_GPB(6),
 S3C2410_GPB(7),
 S3C2410_GPB(8),
};

static unsigned intled_cfg_table [] = {
 S3C2410_GPIO_OUTPUT,
 S3C2410_GPIO_OUTPUT,
 S3C2410_GPIO_OUTPUT,
 S3C2410_GPIO_OUTPUT,
};

static intsbc2440_leds_ioctl(
 struct inode *inode,
 struct file *file,
 unsigned int cmd,
 unsigned long arg)
{
 if (arg > 4)

      {
         return -EINVAL;
       }

switch(cmd)

 {

   case LED_ON:
               s3c2410_gpio_setpin(led_table[arg], 0);

               return 0;
    caseLED_OFF:

               s3c2410_gpio_setpin(led_table[arg],1);

               return 0;

   default:
               return -EINVAL;

 }

}

static structfile_operations dev_fops = {
 .owner = THIS_MODULE,
 .ioctl = mini2440_leds_ioctl,
};

static structmiscdevice misc = {
 .minor = MISC_DYNAMIC_MINOR,
 .name = DEVICE_NAME,
 .fops = &dev_fops,
};

static int __initmini2440_leds_init(void)
{
 int ret;

 inti;
 
 for (i = 0; i < 4; i++)

 {
  s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]);
 }

 ret= misc_register(&misc);

 printk(DEVICE_NAME"\tinitialized\n");

 return ret;
}

static void __exitmini2440_leds_exit(void)
{
 misc_deregister(&misc);
}

module_init(mini2440_leds_init);
module_exit(mini2440_leds_exit);

方法二:(将LED注册为普通字符设备)

步骤与上面类似除还得编写open函数,但不用加上注册那个misc设备。具体程序如下:
#include<linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>


#define DEVICE_NAME"leds"  //这一行在每一个设备驱动中都必须有的,体现在/dev目录下

//将端口做成数组,以便于编写

static unsigned longled_table [] = {
 S3C2410_GPB(5),
 S3C2410_GPB(6),
 S3C2410_GPB(7),
 S3C2410_GPB(8),
};

static unsigned intled_cfg_table [] = {
 S3C2410_GPIO_OUTPUT,
 S3C2410_GPIO_OUTPUT,
 S3C2410_GPIO_OUTPUT,
 S3C2410_GPIO_OUTPUT,
};

//open函数的实现,其形参不能改变,这是模板

stativ intmini2440_leds_open(struct inode *inode,struct file*file)

{

   inti;

 for (i = 0; i < 4; i++)

      {
         s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]);
       }

   return0;

}

//ioctl函数的实现,里面的形参是固定格式,不需更改

static intmini2440_leds_ioctl(
 struct inode *inode,
 struct file *file,
 unsigned int cmd,
 unsigned long arg)
{
  if (arg > 4){
   return-EINVAL;
  }

switch(cmd)

{

 case LED_ON:
             s3c2410_gpio_setpin(led_table[arg], 0);

             return 0;
 case LED_OFF:

             s3c2410_gpio_setpin(led_table[arg],1);

             return 0;

 default:
             return-EINVAL;
 }
}

//最重要的结构体之一

static structfile_operations mini2440_leds_fops = {
          .owner = THIS_MODULE,
          .ioctl = mini2440_leds_ioctl,
          .open  =mini2440_leds_open,
};

//初始化函数以及退出函数的写法,记住他们的格式

static int __initmini2440_leds__init(void)
{
 int ret; 
 

 ret=register_chrdev(0,DEVICE_NAME,&mini2440_leds_fops);

 printk(DEVICE_NAME"\tinitialized\n");

if(ret<0)

{

printk(DEVICE_NAME"can not registermajor\n");

returnret;

}

printk(DEVICE_NAME"initialized!\n");

 return 0;
}

static void __exitmini2440_leds_exit(void)
{
 unregister_chrdev(0,DEVICE_NAME);
}

module_init(mini2440_leds_init);
module_exit(mini2440_leds_exit);


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章