代码:
led.h
#ifndef __LED_H__
#define __LED_H__
#define PHY_RCC 0x50000a28
#define PHY_MODER_E 0x50006000
#define PHY_MODER_F 0x50007000
#define PHY_ODR_E 0x50006014
#define PHY_ODR_F 0x50007014
#endif
led.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/printk.h>
#include <linux/io.h>
#include "led.h"
unsigned int *vir_rcc;
unsigned int *vir_moder_e;
unsigned int *vir_odr_e;
unsigned int *vir_moder_f;
unsigned int *vir_odr_f;
char kbuf[128] = {0};
ssize_t mycdev_read(struct file *file, char __user *ubuf, size_t size, loff_t *off)
{
int ret = -1;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
size = (size > sizeof(kbuf)) ? sizeof(kbuf) : size;
ret = copy_to_user(ubuf, kbuf, size);
if(ret)
{
printk("copy_to_user::error...\n");
return -1;
}
return 0;
}
ssize_t mycdev_write(struct file *file, const char __user *ubuf, size_t size, loff_t *off)
{
int ret = -1;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
size = (size > sizeof(kbuf)) ? sizeof(kbuf) : size;
ret = copy_from_user(kbuf, ubuf, size);
if(ret)
{
printk("copy_from_user::error...\n");
return -1;
}
switch(kbuf[0])
{
case '0':
printk("kbuf[0] = 0...\n");
(*vir_odr_e) &= (~(0x1 << 10));
break;
case '1':
printk("kbuf[0] = 1...\n");
(*vir_odr_e) |= (0x1 << 10);
break;
case '2':
printk("kbuf[0] = 2...\n");
(*vir_odr_f) &= (~(0x1 << 10));
break;
case '3':
printk("kbuf[0] = 3...\n");
(*vir_odr_f) |= (0x1 << 10);
break;
case '4':
printk("kbuf[0] = 4...\n");
(*vir_odr_e) &= (~(0x1 << 8));
break;
case '5':
printk("kbuf[0] = 5...\n");
(*vir_odr_e) |= (0x1 << 8);
break;
default:
printk("kbuf[0] = %d...\n", kbuf[0]);
break;
}
return 0;
}
int mycdev_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
const struct file_operations fop = {
.read = mycdev_read,
.write = mycdev_write,
.open = mycdev_open,
.release = mycdev_close,
};
int major = -1;
int leds_init(void)
{
RCC
// RCC_AHB4ENSETR[4]->1 GPIOE使能 GPIOF使能
vir_rcc = ioremap(PHY_RCC, 4);
if(NULL == vir_rcc)
{
printk("vir_rcc map failed...\n");
return -1;
}
(*vir_rcc) |= (0x1 << 4);
(*vir_rcc) |= (0x1 << 5);
/// LED1 LED3
// GPIOE_MODER[21:20]->01 PE10输出模式
// GPIOE_MODER[17:16]->01 PE8 输出模式
vir_moder_e = ioremap(PHY_MODER_E, 4);
if(NULL == vir_moder_e)
{
printk("vir_moder_e map failed...\n");
return -1;
}
(*vir_moder_e) &= (~(0x3 << 20));
(*vir_moder_e) |= (0x1 << 20);
(*vir_moder_e) &= (~(0x3 << 16));
(*vir_moder_e) |= (0x1 << 16);
// GPIOE_ODR[10]->0 / 1 输出电平
// GPIOE_ODR[ 8]->0 / 1 输出电平
vir_odr_e = ioremap(PHY_ODR_E, 4);
if(NULL == vir_odr_e)
{
printk("vir_odr_e map failed...\n");
return -1;
}
(*vir_odr_e) |= (0x1 << 10);
(*vir_odr_e) |= (0x1 << 8);
printk("all registers for led1 init success...\n");
printk("all registers for led3 init success...\n");
/// LED2
// GPIOF_MODER[21:20]->01 PF10输出模式
vir_moder_f = ioremap(PHY_MODER_F, 4);
if(NULL == vir_moder_f)
{
printk("vir_moder_f map failed...\n");
return -1;
}
(*vir_moder_f) &= (~(0x3 << 20));
(*vir_moder_f) |= (0x1 << 20);
// GPIOF_ODR[10]->0 / 1 输出电平
vir_odr_f = ioremap(PHY_ODR_F, 4);
if(NULL == vir_odr_f)
{
printk("vir_odr_f map failed...\n");
return -1;
}
(*vir_odr_f) |= (0x1 << 10);
printk("all registers for led2 init success...\n");
return 0;
}
static int __init mode_init(void)
{
int ret = -1;
/*注册字符设备驱动*/
major = register_chrdev(0, "mycdev", &fop);
printk("mode_init::major = %d\n", major);
/*初始化LED{PE10 PF10 PE8}*/
ret = leds_init();
if(ret < 0)
{
return ret;
}
return 0;
}
static void __exit mode_exit(void)
{
iounmap(vir_rcc);
iounmap(vir_moder_e);
iounmap(vir_odr_e);
iounmap(vir_moder_f);
iounmap(vir_odr_f);
printk("mode_exit::major = %d\n", major);
return ;
}
module_init(mode_init);
module_exit(mode_exit);
MODULE_LICENSE("GPL");
test.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, const char *argv[])
{
char ubuf[128] = {0};
char kbuf[128] = {0};
int fd = open("/dev/mycdev", O_RDWR);
if(fd < 0)
{
printf("打开设备文件失败...\n");
exit(-1);
}
else
{
printf("打开设备文件成功...\n");
while(1)
{
printf("请输入控制码{<LED1>0/1 <LED2>2/3 <LED3>4/5}::");
fgets(ubuf, sizeof(ubuf), stdin);
ubuf[strlen(ubuf)-1] = '\0';// 替换换行符
if(0 == strcasecmp(ubuf, "quit"))
break;// 出口
write(fd, ubuf, sizeof(ubuf));// mycdev_read
memset(ubuf, 0, sizeof(ubuf));
}
}
return 0;
}
Makefile:
ARCH?=arm
ifeq ($(ARCH),x86)
KERNDIR:=/lib/modules/$(shell uname -r)/build
endif
ifeq ($(ARCH),arm)
KERNDIR:=/home/hades/linux-5.10.61
endif
modname?=led
PWD:=$(shell pwd)
all:
@make -C $(KERNDIR) M=$(PWD) modules
clean distclean:
@make -C $(KERNDIR) M=$(PWD) clean
obj-m:=$(modname).o
运行截图:
开始 insmod mknod
测试 运行交叉编译生成的a.out
结束 rm rmmod