前面写了一个最简单的模块,现在继续写一个简单的linux下led的驱动。
先看一下简单的驱动代码,命名为led_driver.c:
#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 <asm/uaccess.h> #include <asm/atomic.h> #include <asm/unistd.h>
#define DEVICE_NAME "leds"
//the next two structs used to config the pins for leds static unsigned long led_table[] = { S3C2410_GPF4, S3C2410_GPF5, S3C2410_GPF6, S3C2410_GPF7, };
static unsigned int led_cfg_table[] = { S3C2410_GPF4_OUTP, S3C2410_GPF5_OUTP, S3C2410_GPF6_OUTP, S3C2410_GPF7_OUTP, };
//cmd: 0---off 1---on //arg: the led number(0----3) static int s3c2440_led_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { switch(cmd){ case 0: case 1: if(arg > 3) { return -EINVAL; }
s3c2410_gpio_setpin(led_table[arg],!cmd); return 0; default: return -EINVAL; }
}
static struct file_operations dev_ops = { .owner = THIS_MODULE, .ioctl = s3c2440_led_ioctl, };
static struct miscdevice misc = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &dev_ops, };
static int __init led_init() { int i; int ret; for(i = 0; i < 4; i++) { s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]); s3c2410_gpio_setpin(led_table[i],0); } ret = misc_register(&misc); printk(DEVICE_NAME "initialized\n"); return ret; }
static void __exit led_exit() { misc_deregister(&misc); }
module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR(""); |
linux对ARM支持很好,有关gpio的操作都有函数写好了,直接用就可以。不需要我们自己去操作IO控制寄存器等。
s3c2410_gpio_cfgpin和s3c2410_gpio_setpin定义在linux/arch/arm/plat-s3c24xx/gpio.c这样的板级适配文件中。
对于不同的板子定义的gpio的操作是不一样的,我们s3c2440为例,其对于的gpio的操作函数定义在linux/arch/arm/plat-s3c24xx/gpio.c。如果是at91,就定义在linux/arch/arm/plat-at91/gpio.c。
而S3C2410_GPF4和S3C2410_GPF4_OUTP这样的管脚定义在arch/arm/mach-s3c2410/include/mach/regs-gpio.h中定义好了。
对于不同的硬件连接驱动会使用不同的io口。所以在写驱动的时候你需要按照你板子的原理图编写程序。我的板子使用的是DPF4.5.6.7连接led。所以我使用S3C2410_GPF。像友善的mini2440就是使用的S3C2410_GPB5.6.7.8。
这里我们使用到了杂设备的注册和卸载函数
misc_register(miscdevice *); //注册一个misc设备
misc_deregister(miscdevice * ); //卸载一个misc设备
miscdevice结构体定义在linux/miscdevice.h里面。minor为次设备号。调用misc_register(&misc);就完成一个杂设备的注册。
驱动写好了,Makefile可以直接借用上一篇hello_module的,只需要把Makefile里面的hello_module改为我们的led_driver就可以了。
测试程序:
#include<stdio.h> int main(int argc, char **argv) |
使用交叉编译器编译后就可以放到板子上测试了。
点亮第0个灯#./led 0 1
点亮第1个灯#./led 1 1
关闭第0个灯#./led 0 0
你可以在你打板子上看到效果。led驱动完成。