字符设备驱动(点灯)
驱动程序leddrv.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#define S3C2440_GPA(n) (0<<16 | n)
#define S3C2440_GPB(n) (1<<16 | n)
#define S3C2440_GPC(n) (2<<16 | n)
#define S3C2440_GPD(n) (3<<16 | n)
#define S3C2440_GPE(n) (4<<16 | n)
#define S3C2440_GPF(n) (5<<16 | n)
#define S3C2440_GPG(n) (6<<16 | n)
#define S3C2440_GPH(n) (7<<16 | n)
#define S3C2440_GPI(n) (8<<16 | n)
#define S3C2440_GPJ(n) (9<<16 | n)
static int led_pin = S3C2440_GPF(5);
static int major;
static struct class *led_class;
static volatile unsigned int *gpio_con;
static volatile unsigned int *gpio_dat;
static unsigned int gpio_base[] = {
0x56000000, // GPACON寄存处地址
0x56000010, // GPBCON寄存处地址
0x56000020, // GPCCON寄存处地址
0x56000030, // GPDCON寄存处地址
0x56000040, // GPECON寄存处地址
0x56000050, // GPFCON寄存处地址
0x56000060, // GPGCON寄存处地址
0x56000070, // GPHCON寄存处地址
0x56000080, // GPICON寄存处地址
0x56000090, // GPJCON寄存处地址
};
//1.分配
static struct file_operations myled_oprs = {
.owner = THIS_MODULE, //模块本身
.open = led_open,
.write = led_write,
.release = led_close,
}
//2.设置
static int led_open(struct inode * node, struct file * filp){
//不能直接操作物理地址,需要映射。举例GPF5
int bank = led_pin >> 16;
int base = gpio_base(bank);
int pin = led_pin & 0xffff;
gpio_con = ioremap(base, 8);//8为映射的大小,一般为一页。
gpio_dat = gpio_con + 1;//指针加4
*gpio_con &= ~(3<<(pin * 2));//bit10、11清零操作
*gpio_con |= ~(1<<(pin * 2));//bit10、11置1,设置为输出。
return 0;
}
static ssize_t led_write(struct file *filp, const char __user *buf, size_t size, loff_t *off){
//根据应用传入的值来设置LED引脚
unsigned char val;
int pin = led_pin & 0xffff;
copy_from_user(&val, buf, 1);
if(val){
//点灯
*gpio_dat &= ~(1<<pin);
}else{
//灭队
*gpio_dat |= ~(1<<pin);
}
return 1;//已写入一个数据。
}
static int led_close(struct inode *node, struct file * filp){
iounmap(gpio_con);//取消之前的映射
return 0;
}
//3.注册file_operations
//4.入口
static int myled_init(void)
{
major = register_chrdev(0, "myled", &myled_oprs);//主设备号0代表系统自动分配给major
led_class = class_create(THIS_MODULE,"myled");
device_class(led_class, NULL, MKDEV(major, 0), NULL, "led");//在用户空间通过led访问灯
return 0;
}
//5.出口
static int myled_exit(void)
{
unregister_chrdev(major, "myled“);
device_destory(led_class, MKDE(major, 0));
class_destroy(led_class);
return 0;
}
module_init(myled_init);//module宏定义实际上给myled_init起别名,以后可以直接通过module_init找到myled_init。
module_exit(myled_exit);//同理
MODULE_LICENSE("GPL");//加上协议避免警告
测试程序ledtest.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char **argv){
int fd;
int val = 1;
fd = open("dev/led", O_RDWR);
if(fd < 0){
printf("Can't open!\n");
}
if(argc != 2){
printf("Usage : \n");
printf(" %s <on|off>\n", argv[0]);
return 0;
}
if(strcmp(argv[1], "on") == 0){
val = 1;
}else{
val = 0;
}
}
使用方法:
将leddrv.c放入内核目录,然后编译内核。
交叉编译ledtest.c,将编译好的ledtest应用程序放到板端执行。