基於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);


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