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