Code learning materials come from:
Only for personal study/review, intrusion deleted
1. Add the following LED node under the root node
alphaled {
#address-cells = <1>;
#size-cells = <1>;
compatible = "aplhaled,led";
status = "okay";
reg = < 0X020C406C 0x4 /* CCM_CCGR1_BASE */
0X020E0068 0x4 /* SW_MUX_GPIO1_IO03_BAS */
0X020E02F4 0x4 /* SW_PAD_GPIO1_IO03_BASE */
0X0209C000 0x4 /* GPIO1_DR_BASE */
0X0209C004 0x4 >; /* GPIO1_GDIR_BASE */
};
After creating the node, you can first check whether there is an alphaled node under /proc/device-tree
2. Write the driver
dtsled.c
This driver does not write code related to address mapping (only for personal study/review). The register information of the LED node is saved through an array.
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched/signal.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/parport.h>
#include <linux/ctype.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/major.h>
#include <linux/ppdev.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <linux/compat.h>
#define DTSLED_COUNT 1
#define DTSLED_NAME "dtsled"
struct dtsled_device
{
dev_t devid; /* 设备号 */
struct cdev cdev;
int major; /* 主设备号 */
int minor;
struct class *class; /* 类 */
struct device *device; /* 设备 */
struct device_node *nd; /* 设备节点 */
};
struct dtsled_device dtsled;
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;
/* 申请设备号 */
dtsled.major = 0;
if (dtsled.major) {
dtsled.devid = MKDEV(dtsled.major, 0); // 将主设备号和次设备号转换成设备号
ret = register_chrdev_region(dtsled.devid, DTSLED_COUNT, DTSLED_NAME);
} else {
alloc_chrdev_region(&dtsled.devid, 0, DTSLED_COUNT, DTSLED_NAME);
dtsled.major = MAJOR(dtsled.devid);
dtsled.minor = MINOR(dtsled.devid);
}
if (ret < 0) {
return -1;
}
/* 添加字符设备 */
dtsled.cdev.owner = THIS_MODULE;
cdev_init(&dtsled.cdev, &dtsled_fops);
ret = cdev_add(dtsled.cdev, dtsled.devid, DTSLED_COUNT);
if (ret < 0) {
unregister_chrdev_region(&dtsled.devid, DTSLED_COUNT);
return -1;
}
/* 创建设备节点 */
dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);
if (IS_ERR(dtsled.class)) {
ret = PTR_ERR(dtsled.class);
cdev_del(&dtsled.cdev);
unregister_chrdev_region(dtsled.devid, DTSLED_COUNT);
return ret;
}
dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME);
if (IS_ERR(dtsled.device)) {
ret = PTR_ERR(dtsled.device);
cdev_del(&dtsled.cdev);
unregister_chrdev_region(dtsled.devid, DTSLED_COUNT);
class_destroy(dtsled.class);
return ret;
}
dtsled.nd = of_find_node_by_path("/alphaled");
if (dtsled.nd == NULL) {
return -EINVAL;
cdev_del(&dtsled.cdev);
unregister_chrdev_region(dtsled.devid, DTSLED_COUNT);
device_destroy(dtsled.class, dtsled.devid);
class_destroy(dtsled.class);
}
u32 reg_data[10];
ret = of_property_read_u32_array(nd, "reg", reg_data, 10);
if (ret < 0) {
printk("reg property failed \n");
kfree(reg_data);
return -1;
} else {
for (int i = 0; i < 10; i++) {
printk("reg[%d] = %d \n", i, reg_data[i]);
}
}
return 0;
}
static void __exit dtsled_exit(void)
{
/* 删除字符设备 */
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);