上一篇文章学习了向设备树文件添加 Led设备节点信息。经过测试,开发板上电后系统上可以看到Led灯的设备节点。文章地址如下:
设备树下Led驱动实验-向设备树文件添加Led设备节点-CSDN博客
本文继续进行Led 驱动开发实验,具体学习 LED驱动代码框架的搭建。
这里我所存放的驱动实验代码在? ubuntu系统下的如下目录:
/home/wangtian/zhengdian_Linux/Linux_Drivers
打开 ubuntu系统,进入如上目录,创建 工程目录 5_dtsled:
mkdir 5_dtsled
进入 5_dtsled目录下,将前面实验 3_newchrled工程中 .vscode目录及其下文件拷贝到该工程下:
wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers$ cd 5_dtsled.c/
angtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/5_dtsled.c$ sudo cp ../3_newchrled/.vscode/ ./ -rf
注意: 拷贝.vscode.目录及其下文件的原因在于,在newchrled工程中的 .vscode目录下文件,设置了 驱动程序可能会调用到的 Linux内核源码的路径(NXP官方)。
拷贝 Makefile文件
将前面实验 3_newchrled工程中Makefile拷贝到该工程下,操作如下:
wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/5_dtsled.c$ sudo cp ../3_newchrled/Makefile ./ -f
更改 Makefile文件的目标文件名,更改为如下:
obj-m := dtsled.o
创建 dtsled.c文件:
wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/5_dtsled.c$ touch dtsled.c
将 3_newchrled工程中 newchrled.c文件中头文件拷贝到 dtsled.c中:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
接下来再重新编写一次,Led驱动框架代码(代码实现其实与 3_newchrled工程的驱动框架是一样的)。这里重新写一次以熟悉框架流程。
dtsled.c 文件中的驱动框架代码实现如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#define DEV_NAME "alpha_led"
#define DEV_CNT 1
//Led设备结构体
struct dtsled_dev {
dev_t devid; //设备号(主+次设备号)
int major; //主设备号
int minor; //次设备号
struct cdev cdev;
struct class* class;
struct device * dev;
};
const struct file_operations dtsled_fops = {
.owner = THIS_MODULE,
// .open = dtsled.open,
// .write = dtsled_write,
// .release = dtsled_release,
};
struct dtsled_dev dtsled;
/*驱动入口函数 */
static int __init dtsled_init(void)
{
int ret = 0;
dtsled.major = 0;
/*1. 申请设备号 */
if(dtsled.major) //给定设备号
{
dtsled.devid = MKDEV(dtsled.major, 0);
ret = register_chrdev_region(dtsled.devid, DEV_CNT, DEV_NAME);
}
else //向内核申请设备号
{
ret = alloc_chrdev_region(&dtsled.devid, 0, DEV_CNT, DEV_NAME);
dtsled.major = MAJOR(dtsled.devid);
dtsled.minor = MINOR(dtsled.devid);
}
if(ret < 0)
{
goto apply_devid_failed;
}
/*2. 添加字符设备 */
dtsled.cdev.owner = THIS_MODULE;
cdev_init(&dtsled.cdev, &dtsled_fops);
ret = cdev_add(&dtsled.cdev, dtsled.devid, DEV_CNT);
if(ret < 0)
{
goto cdev_add_failed;
}
return 0;
/*3. 自动创建设备节点 */
//创建类
dtsled.class = class_create(THIS_MODULE, DEV_NAME);
if (IS_ERR(dtsled.class)) {
ret = PTR_ERR(dtsled.class);
goto auto_class_create_failed;
}
//创建设备
dtsled.dev = device_create(dtsled.class, NULL, dtsled.devid, NULL, DEV_NAME);
if (IS_ERR(dtsled.dev)) {
ret = PTR_ERR(dtsled.dev);
goto auto_create_dev_failed;
}
auto_create_dev_failed:
class_destroy(dtsled.class);
auto_class_create_failed:
cdev_del(&dtsled.cdev);
cdev_add_failed:
unregister_chrdev_region(dtsled.devid, DEV_CNT);
apply_devid_failed:
return ret;
}
/*驱动出口函数 */
static void __exit dtsled_exit(void)
{
/*1.删除字符设备 */
cdev_del(&dtsled.cdev);
/*2.注销设备号 */
unregister_chrdev_region(dtsled.devid, DEV_CNT);
/*3. 摧毁设备 */
device_destroy(dtsled.class, dtsled.devid);
/*4. 摧毁类*/
class_destroy(dtsled.class);
}
/*模块入口与出口*/
module_init(dtsled_init);
module_exit(dtsled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("LingXiaoZhan");