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