驱动程序led_test.c,可以使用miscdevice会更简单些
/*
* 审请设备号与cdev初始化和注册合并
* int register_chrdev(MAJOR major, char *name, file_operations *fops);
* 参数:
* major: 主设备号,为0自动分配,不为0静态分配
* name: 内核中的设备对应名称
* fops: 操作指令集
*
* 返回值:
* 小于0:出错
* major为0,自动分配主设备号
* major不为0,返回0表示成功,否则失败
*
* led 使用
* 内核和用户空间都能使用 __IO(type, id)建立命令
* type是一个字符
*
* 物理内存映射
* ioremap(phy, size)
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/io.h>
#define LED_ON _IO('I', 0)
#define LED_OFF _IO('I', 1)
dev_t dev_id;
int __init led_init(void);
void __exit led_exit(void);
// gpiol_base address
void __iomem *gpiol_base = NULL;
#define register(address) (*(volatile unsigned long *)address)
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("XXX");
MODULE_VERSION("0.0.1");
static int led_open(struct inode *node, struct file *file){
printk("KERN_INFO led module is opened\n");
return 0;
}
static int led_close(struct inode *node, struct file *file){
printk("KERN_INFO led module is closed\n");
return 0;
}
long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg){
switch(cmd){
case LED_ON:
*(unsigned long *)(gpiol_base + 0x10) |= (1 << 4);
*(unsigned long *)(gpiol_base + 0x10) &= ~(1 << 7);
printk("KERN_INFO The led is on\n");
break;
case LED_OFF:
printk("KERN_INFO The led is off\n");
*(unsigned long *)(gpiol_base + 0x10) |= (1 << 7);
*(unsigned long *)(gpiol_base + 0x10) &= ~(1 << 4);
break;
default:
printk("KERN_ ERROR Unknow Command\n");
return -EINVAL;
}
return 0;
}
struct file_operations fops = {
.owner = THIS_MODULE,
.open = led_open,
.release = led_close,
.unlocked_ioctl = led_ioctl,
};
// struct class
struct class *dev_class;
// struct device
struct device *dev_device;
// 自动在/dev/目录下创建设备文件
int dev_fs_auto_create(void){
int ret = 0;
// create class
dev_class = class_create(THIS_MODULE, "led_class");
if(IS_ERR(&dev_class)){
unregister_chrdev(dev_id, "led");
ret = PTR_ERR(dev_class);
printk("KERN_INFO class create failed\n");
return ret;
}
dev_device = device_create(dev_class, NULL, dev_id, NULL, "led");
if(IS_ERR(dev_device)){
//放class
class_destroy(dev_class);
unregister_chrdev(dev_id, "led");
ret = PTR_ERR(dev_device);
printk("KERN_INFO device create failed\n");
return ret;
}
return ret;
}
int __init led_init(void){
// 动态审请设备号
int ret = 0;
ret = register_chrdev(0, "led", &fops);
if(ret < 0){
printk("KERN_INFO register_chrdev error\n");
return ret;
}
dev_id = MKDEV(ret, 0);
dev_fs_auto_create();
// 映射GPIOL
gpiol_base = ioremap(0x07022000, 0x24);
printk("KERN_INFO module led %d is register and and add into the kernel\n", dev_id);
if(IS_ERR(gpiol_base)){
printk("KERN_INFO ioremap error\n");
device_destroy(dev_class, dev_id);
// 放class
class_destroy(dev_class);
unregister_chrdev(dev_id, "led");
return -1;
}
// 设置输出
*(unsigned long *)gpiol_base &= ~(0xf << (4 * 4));
*(unsigned long *)gpiol_base &= ~(0xf << (4 * 7));
*(unsigned long *)(gpiol_base +0x1c) |= (0x2 << (2 * 4));
*(unsigned long *)(gpiol_base +0x1c) |= (0x2 << (2 * 7));
return 0;
}
void __exit led_exit(void){
// 放device
device_destroy(dev_class, dev_id);
// 放class
class_destroy(dev_class);
unregister_chrdev(dev_id, "led");
printk("KERN_INFO module led %d is unregister and remove from the kernel\n", dev_id);
}
Makefile
CONFIG_MODULE_SIG=n
KERNDIR:=/lib/modules/5.10.75-sunxi64/build
PWD:= $(shell pwd)
obj-m:= led_test.o
all:
make -C $(KERNDIR) M=$(PWD) modules ; \
gcc -o main main.c
clean:
make -C $(KERNDIR) M=$(PWD) clean ; \
rm main
测试程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define LED_ON _IO('I', 0)
#define LED_OFF _IO('I', 1)
void delay(long sec){
int i = 0;
for(; i < sec; i++){
for(int j = 0; j < 100; j++)
;
}
}
int main(int argc, char **argv){
int fd = 0;
char c = 0;
if((fd = open("/dev/ioctl", O_RDWR)) < 0){
fprintf(stderr,"Open /dev/ioctl error\n");
return -1;
}
while(1){
delay(1000000);
ioctl(fd, LED_ON);
delay(1000000);
ioctl(fd, LED_OFF);
}
return 0;
}
使用的开发板上GPIOL4和GPIOL7,因为系统有使用这两个灯,因此设置上拉或下拉会出错,不能设置上拉和下拉。但可以直接使用。
# 编译
make
# 加入内核
insmod led_test.ko
# 用测试程序测试
./main
# 下面是输出结果
Led on
Led off
Led on
Led off
# 下面是dmesg输出
[ 1519.648891] KERN_INFO module led 251658240 is register and and add into the kernel
[ 1522.234352] KERN_INFO led module is opened
[ 1524.288121] KERN_INFO The led is on
[ 1526.296431] KERN_INFO The led is off
[ 1528.304713] KERN_INFO The led is on
[ 1530.313120] KERN_INFO The led is off
[ 1532.321388] KERN_INFO The led is on
[ 1534.329778] KERN_INFO The led is off
[ 1536.338042] KERN_INFO The led is on
[ 1538.346303] KERN_INFO The led is off
[ 1540.354723] KERN_INFO The led is on
[ 1542.362982] KERN_INFO The led is off
[ 1542.980757] KERN_INFO led module is closed
正常开发板上的红灯和绿灯会交替开关,至此linux驱动led完成。