字符設備驅動(點燈)
驅動程序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應用程序放到板端執行。