输入图片中的命令进行编译操作!
如果想学编写流程的话就是看我之前的博客!!!
我会把这部分比上一个博客多的·部分·标出来!
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#define DTSLED_COUNT 1 /*设备号个数*/
#define DTSLED_NAME "dtsled" /*名字*/
#define LEDOFF 0 /*关闭*/
#define LEDON 1 /*打开*/
/*地址映射后的虚拟地址指针*/
static void __iomem *MP157_MPU_AHB4_PERIPH;
static void __iomem *MP157_GPIOI_MODER;
static void __iomem *MP157_GPIOI_OTYPER;
static void __iomem *MP157_GPIOI_OSPEEDR;
static void __iomem *MP157_GPIOI_PUPDR;
static void __iomem *MP157_GPIOI_BSRR;
/*地址映射*/
static void led_ioremap(u32 *regdata)
{
MP157_MPU_AHB4_PERIPH = ioremap(regdata[0],regdata[1]);
MP157_GPIOI_MODER = ioremap(regdata[2],regdata[3]);
MP157_GPIOI_OTYPER = ioremap(regdata[4],regdata[5]);
MP157_GPIOI_OSPEEDR = ioremap(regdata[6],regdata[7]);
MP157_GPIOI_PUPDR = ioremap(regdata[8],regdata[9]);
MP157_GPIOI_BSRR = ioremap(regdata[10],regdata[11]);
}
/*取消地址映射*/
static void led_iounmap(void)
{
iounmap(MP157_MPU_AHB4_PERIPH);
iounmap(MP157_GPIOI_MODER);
iounmap(MP157_GPIOI_OTYPER);
iounmap(MP157_GPIOI_OSPEEDR);
iounmap(MP157_GPIOI_PUPDR);
iounmap(MP157_GPIOI_BSRR);
}
/*dtsled设备结构体*/
struct dtsled_dev {
dev_t devid; /*设备号*/
struct cdev cdev;/*字符设备*/
struct class *class;/*类*/
struct device *device;/*设备*/
int major; /*主设备号*/
int minor; /*次设备号*/
struct device_node *nd;/*设备节点*/
};
struct dtsled_dev dtsled; /*led设备*/
/*LED打开或关闭*/
void led_switch(u8 sta)
{
u32 val=0;
if (sta == LEDON)
{
val = readl(MP157_GPIOI_BSRR);
val &= ~(0x1 << 16);
val |= (0x1 << 16);/*Bits 31:16 BR[15:0]*/
writel(val,MP157_GPIOI_BSRR);
}
else if(sta == LEDOFF)
{
val = readl(MP157_GPIOI_BSRR);
val &= ~(0X1 << 0);
val |= (0x1 << 0);/*Bits 15:0 BS[15:0],BS位比BR位有更高的优先级*/
writel(val,MP157_GPIOI_BSRR);
}
}
static int dtsled_open(struct inode *inode, struct file *filp)
{
filp->private_data = &dtsled;
return 0;
}
static int dtsled_release(struct inode *inode, struct file *filp)
{
struct dtsled_dev *dev = (struct dtsled_dev*)filp->private_data;
return 0;
}
static ssize_t dtsled_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
struct dtsled_dev *dev = (struct dtsled_dev*)filp->private_data;
int retvalue;
unsigned char databuf[1];
retvalue = copy_from_user(databuf,buf,count);
if (retvalue < 0)
{
printk("kernel write failed!\r\n");
return -EFAULT;
}
/*判断开灯还是关灯*/
led_switch(databuf[0]);
return 0;
}
/*dts字符设备操作集*/
static const struct file_operations dtsled_fops ={
.owner = THIS_MODULE,
.write = dtsled_write,
.open = dtsled_open,
.release = dtsled_release,
};
/*入口*/
static int __init dtsled_init(void)
{
int ret = 0;
u8 i = 0;
const char *str;
u32 regdata[20];
u32 val;
//struct property *proper;
/*注册字符设备*/
/*1.申请设备号*/
dtsled.major = 0;/*设备号由内核分配*/
if(dtsled.major){/*定义了设备号*/
dtsled.devid = MKDEV(dtsled.major,0);
ret = register_chrdev_region(dtsled.devid,DTSLED_COUNT,DTSLED_NAME);
}else{/*没有给定设备号*/
ret = alloc_chrdev_region(&dtsled.devid,0,DTSLED_COUNT,DTSLED_NAME);
dtsled.major = MAJOR(dtsled.devid);
dtsled.minor = MAJOR(dtsled.devid);
}
if(ret < 0){
goto fail_devid;
}
/*2.添加字符设备*/
dtsled.cdev.owner = THIS_MODULE;
cdev_init(&dtsled.cdev,&dtsled_fops);
ret = cdev_add(&dtsled.cdev,dtsled.devid,DTSLED_COUNT);
if(ret < 0){
goto fail_cdev;
}
/*3.自动创建设备节点*/
dtsled.class = class_create(THIS_MODULE,"DTSLED_NAME");
if(IS_ERR(dtsled.class))
{
ret = PTR_ERR(dtsled.class);
goto fail_class;
}
dtsled.device = device_create(dtsled.class,NULL,
dtsled.devid,NULL,DTSLED_NAME);
if(IS_ERR(dtsled.device))
{
ret = PTR_ERR(dtsled.device);
goto fail_device;
}
/*获取设备树属性内容*/
dtsled.nd = of_find_node_by_path("/stm32mp1_led");
if(dtsled.nd == NULL) {/*失败*/
return -EINVAL;
goto fail_findnd;
}
/*获取compatible属性内容*/
ret = of_property_read_string(dtsled.nd, "compatible", &str);
if(ret < 0){
goto fail_rs;
}else{
printk(KERN_CONT"compatible = %s\r\n",str);
}
/*获取 status 属性内容*/
ret = of_property_read_string(dtsled.nd, "status", &str);
if(ret < 0){
goto fail_rs;
}else{
printk(KERN_CONT"status = %s\r\n",str);
}
/*获取reg属性内容*/
ret = of_property_read_u32_array(dtsled.nd,"reg",regdata,12);
if(ret < 0){
goto fail_rs;
} else {
printk("reg data:\r\n");
for(i = 0; i < 12; i++){
printk(KERN_CONT"%#X ",regdata[i]);
}
printk("\r\n");
}
/*LED灯初始化*/
led_ioremap(regdata);
/*时钟初始化*/
val = readl(MP157_MPU_AHB4_PERIPH);/*获取虚拟地址下的32位数据*/
val &= ~(0X1 << 8);/*清除以前的地址,Bit8置0,清除之前的配置*/
val |= (0x1 << 8);/*Bit8置1,开启RCC-GPIOI时钟使能*/
writel(val,MP157_MPU_AHB4_PERIPH);/*重新写入数据到虚拟地址*/
/*设置PIO口通用输出*/
val = readl(MP157_GPIOI_MODER);
val &= ~(0X3 << 0); /* bit0:1 清零 */
val |= (0X1 << 0); /* bit0:1 设置 01 */
writel(val, MP157_GPIOI_MODER);/*GPIOI_MODER设置为01,通用输出模式*/
/*设置PIO口为推挽模式*/
val = readl(MP157_GPIOI_OTYPER);
val &= ~(0X1 << 0); /* bit0 清零,设置为推挽*/
writel(val,MP157_GPIOI_OTYPER);
/*设置PIO的速度*/
val = readl(MP157_GPIOI_OSPEEDR);
val &= ~(0X3 << 0);/*Bit0:1清零*/
val |= (0X2 << 0); /*Bit0:1设置为10*/
writel(val,MP157_GPIOI_OSPEEDR);
/*设置PIO口为上拉模式*/
val = readl(MP157_GPIOI_PUPDR);
val &= ~(0X3 << 0);/*Bit0:1清零*/
val |= (0X1 << 0);/*Bit0:1设置为01*/
writel(val,MP157_GPIOI_PUPDR);
/*默认关闭 LED*/
val = readl(MP157_GPIOI_BSRR);
val &= ~(0X1 << 0);
val |= (0x1 << 0);/*相当于在PIO高电平*/
writel(val,MP157_GPIOI_BSRR);
return ret;
fail_rs:
fail_findnd:
device_destroy(dtsled.class,dtsled.devid);
/*摧毁类*/
fail_device:
class_destroy(dtsled.class);
fail_class:
cdev_del(&dtsled.cdev);
fail_cdev:
unregister_chrdev_region(dtsled.devid,DTSLED_COUNT);
fail_devid:
return ret;
}
/*出口*/
static void __exit dtsled_exit(void)
{
/*取消地址映射*/
led_iounmap();
/*删除字符设备*/
cdev_del(&dtsled.cdev);
/*注销设备号*/
unregister_chrdev_region(dtsled.devid,DTSLED_COUNT);
/*摧毁设备*/
device_destroy(dtsled.class,dtsled.devid);
/*摧毁类*/
class_destroy(dtsled.class);
}
/*注册驱动和卸载驱动*/
module_init(dtsled_init);
module_exit(dtsled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("chensir");
MODULE_INFO(intree,"Y");
ledAPP.c
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h" /*open执行所需头文件,根文件目录终端输入man 2 open*/
#include "sys/stat.h" /*open执行所需头文件,根文件目录终端输入man 2 open*/
#include "fcntl.h" /*open执行所需头文件,根文件目录终端输入man 2 open*/
#include "stdlib.h"
#include "string.h" /*memcpy执行所需头文件,根文件目录终端输入man 3 memcpy*/
/*
*argc:应用程序参数个数
*argv[]:具体的参数内容,字符串形式
*./ledAPP <fliename> <0:1> 0表示关灯,1表示开灯
*./ledAPP /dev/dtsled 0 关灯
*./ledAPP /dev/dtsled 1 开灯
*/
#define LEDOFF 0
#define LEDON 1
int main(int argc,char *argv[])
{
int fd,retvalue;/*fd:类似文件的ID号;retvalue:返回值。*/
unsigned char databuf[1];/*这种数组可以存储数字或字符*/
char *filename;
if(argc !=3)
{
printf("Error usage!\r\n");
return -1;
}
filename = argv[1];
/*打开文件*/
/* int open (const char *pathname,int flags)*/
fd = open(filename,O_RDWR);/*以读写模式打开文件,fd用于打开文件获取ID号*/
if(fd<0)
{
printf("file %s open failed!\r\n",filename);
return -1;
}
databuf[0] = atoi(argv[2]);/*将字符转换为数字*/
retvalue=write(fd,databuf,sizeof(databuf));
if(retvalue<0)
{
printf("LED Control Failed!\r\n");
close(fd);
}
close(fd);
return 0;
}
makefile
KERNELDIR := /home/chensir/linux/atk-mp1/linux/my_linux/linux-5.4.31
CURRENT_PATH := $(shell pwd)
obj-m := dtsled.o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
开灯!
关灯!
比上一个博客多的部分·:
1
/dtsled设备结构体/
struct dtsled_dev {
dev_t devid; /设备号/
struct cdev cdev;/字符设备/
struct class *class;/类/
struct device *device;/设备/
int major; /主设备号/
int minor; /次设备号/
struct device_node *nd;/设备节点/
};
2
/获取设备树属性内容/
dtsled.nd = of_find_node_by_path(“/stm32mp1_led”);
if(dtsled.nd == NULL) {/失败/
return -EINVAL;
goto fail_findnd;
}
/获取compatible属性内容/
ret = of_property_read_string(dtsled.nd, “compatible”, &str);
if(ret < 0){
goto fail_rs;
}else{
printk(KERN_CONT"compatible = %s\r\n",str);
}
/获取 status 属性内容/
ret = of_property_read_string(dtsled.nd, “status”, &str);
if(ret < 0){
goto fail_rs;
}else{
printk(KERN_CONT"status = %s\r\n",str);
}
/获取reg属性内容/
ret = of_property_read_u32_array(dtsled.nd,“reg”,regdata,12);
if(ret < 0){
goto fail_rs;
} else {
printk(“reg data:\r\n”);
for(i = 0; i < 12; i++){
printk(KERN_CONT"%#X ",regdata[i]);
}
printk(“\r\n”);
}
3
fail_rs:
fail_findnd:
device_destroy(dtsled.class,dtsled.devid);
/摧毁类/
fail_device:
class_destroy(dtsled.class);
fail_class:
cdev_del(&dtsled.cdev);
fail_cdev:
unregister_chrdev_region(dtsled.devid,DTSLED_COUNT);
fail_devid:
return ret;
dtsled.c
4
/出口/
static void __exit dtsled_exit(void)
{
/取消地址映射/
led_iounmap();
/删除字符设备/
cdev_del(&dtsled.cdev);
/注销设备号/
unregister_chrdev_region(dtsled.devid,DTSLED_COUNT);
/摧毁设备/
device_destroy(dtsled.class,dtsled.devid);
/摧毁类/
class_destroy(dtsled.class);
}
没了!