当前位置: 首页 > news >正文

《嵌入式驱动(三):字符设备驱动开发》

一、字符设备驱动开发框架

1.驱动根据设备功能分为不同类型的设备

查看设备类型        cat /proc/devices

2.Linux系统下的设备号都有一个设备号

设备号 = 主设备号(设备类型) + 次设备号(该种类第几个设备)

3.添加设备

每添加一个设备就需要在文件系统中添加一个该设备的设备结点

4.应用层通过文件IO操作设备节点

假设设备路径 /dev/led0

open read write loctl close -> /dev/led0

二、字符设备驱动编写流程

字符设备操作接口

函数名功能特点
register_chrdev_region静态注册已知设备号范围需提前确定主设备号,适合已知空闲设备号的情况
alloc_chrdev_region动态分配设备号内核自动分配主设备号,避免冲突
unregister_chrdev_region释放注册的设备号配合上述两个函数使用
register_chrdev注册设备号并关联file_operations一次性完成注册和操作关联,但灵活性差
unregister_chrdev注销设备号与register_chrdev配套使用

1. 用 alloc_chrdev_region让系统自动分配主设备号,并占用该主设备号开头的若干设备编号(相当于申请设备号资源)。

2. 用 cdev_alloc 申请cdev 结构(cdev 是内核里描述字符设备的核心结构体,用来管理字符设备)。

3. 用 cdev_add 把申请到的 cdev 结构,加入内核的字符设备管理列表(让内核能识别、管理这个字符设备)。

4. 自己创建设备对应的设备节点(比如在 `/dev` 目录下生成 `/dev/xxx` 这样的文件,应用程序后续通过这个节点访问设备)。

应用层创建mknod  /dev/led0/  c 248 0

5.初始化结构体file_operations(read,write...)。

6.class_creat    sys文件系统中创建设备类型,device_creat    sys文件系统中创建设备(mdev -s检测内核插拔事件,发送uevet,mdev会创建)

7. 编写应用层代码,通过 open、read、write 等操作,和字符设备交互。

三、Linux中查看内核驱动信息的常用命令

        1.cat /proc/devices        查看设备类型  

        2.ls /sys/class         查看sys文件系统中的设备类型

           ls /sys/class/myled/led0        查看myled设备类型下的led0

        3.cat /sys/firmware/devicetree/base/节点名        查看当前系统中所有注册的中断号信息

        4.cat /proc/interrupts        查看当前系统中所有注册的中断号信息       

        5. insmod module_drv.ko,加载驱动

            rmmod moudule_drv.ko,删除

        6.make -C 是 GNU Make 工具中的一个命令行选项,用于在执行 make 命令时指定工作目录。-C 是 --directory 的简写形式,后跟一个目录路径,表示在该目录下执行 make 操作。

        7.devm_ioremap建立映射后,会自动解除映射

        device_create_file创建调试节点

        device_remove_file卸载调试节点

四、代码

led

1.led_app.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>int main(void)
{int fd = 0;int status = 0;int curstat = 0;fd = open("/dev/led0", O_RDWR);if (-1 == fd){perror("fail to open");return -1;}while (1){status = 1;write(fd, &status, sizeof(status));read(fd, &curstat, sizeof(curstat));printf("curstat = %d\n", curstat);sleep(1);status = 0;write(fd, &status, sizeof(status));read(fd, &curstat, sizeof(curstat));printf("curstat = %d\n", curstat);sleep(1);}/*status = 1;write(fd, &status, sizeof(status));*/close(fd);return 0;
}
2.Makefile
#模块名
modulename := led_app#工具链
CC := arm-linux-gnueabihf-gcc all:$(CC) $(modulename).c -o $(modulename)cp $(modulename) ~/nfs/rootfs .PHONY:
distclean:rm $(modulename)rm ~/nfs/rootfs/$(modulename)
clean:rm $(modulename)
3.led_drv.c
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/mutex.h>#define IMX6ULL_SW_MUX_CTL      0x20E0068
#define IMX6ULL_SW_PAD_CTL      0x20E02F4
#define IMX6ULL_GPIO_DIR        0x209C004
#define IMX6ULL_GPIO_DAT        0x209C000static int led_open(struct inode *node, struct file *fp);
static ssize_t led_write(struct file *fp, const char __user *puser, size_t n, loff_t *off);
static int led_release(struct inode *node, struct file *fp);
static ssize_t led_read(struct file *fp, char __user *puser, size_t n, loff_t *off);static dev_t devno = 0;
static int status = 0;
static struct cdev *pcdev = NULL;
static void __iomem *pmuxreg = NULL;
static void __iomem *ppadreg = NULL;
static void __iomem *pgpiodir = NULL;
static void __iomem *pgpiodat = NULL;
static struct class *pclass = NULL;
static struct device *pdevice = NULL;
static struct mutex lock;static ssize_t led_show(struct device *dev, struct device_attribute *attr, char *buf)
{int nret = 0;mutex_lock(&lock);nret = sprintf(buf, "%s\n", status ? "LED_ON" : "LED_OFF");mutex_unlock(&lock);return nret;
}static ssize_t led_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{char tmpbuff[32] = {0};sscanf(buf, "%s", tmpbuff);mutex_lock(&lock);if (!strcmp(tmpbuff, "LED_ON")) {writel((readl(pgpiodat) & (~(0x1 << 3))), pgpiodat);status = 1;} else if (!strcmp(tmpbuff, "LED_OFF")) {writel((readl(pgpiodat) | (0x1 << 3)), pgpiodat);status = 0;}mutex_unlock(&lock);return count;
}static struct device_attribute led_attr = __ATTR(attr, 0664, led_show, led_store);static struct file_operations fops = {.owner = THIS_MODULE,.open = led_open,.read = led_read,.write = led_write,.release = led_release,
};static int led_open(struct inode *node, struct file *fp)
{pr_info("drv:led_open\n");return 0;
}static ssize_t led_write(struct file *fp, const char __user *puser, size_t n, loff_t *off)
{unsigned long nret = 0;int status = 0;mutex_lock(&lock);nret = copy_from_user(&status, puser, n);if (nret) {pr_info("copy_from_user failed\n");return -1;}if (status) {writel((readl(pgpiodat) & (~(0x1 << 3))), pgpiodat);} else {writel((readl(pgpiodat) | (0x1 << 3)), pgpiodat);}mutex_unlock(&lock);pr_info("drv:led_write\n");return 0;
}static int led_release(struct inode *node, struct file *fp)
{pr_info("drv:led_release\n");return 0;
}static ssize_t led_read(struct file *fp, char __user *puser, size_t n, loff_t *off)
{unsigned long nret = 0;mutex_lock(&lock);nret = copy_to_user(puser, &status, sizeof(status));if (nret) {pr_info("copy_to_user failed\n");return -1;}mutex_unlock(&lock);pr_info("drv:led_read\n");return 0;
}static __init int led_drv_init(void)
{int ret = 0;mutex_init(&lock);//申请主设备号,及此设备号ret = alloc_chrdev_region(&devno, 0, 1, "myled");if (ret) {pr_info("alloc_chrdev_region failed\n");return -1;}pr_info("major:%d\n", MAJOR(devno));//构建一个新的设备类型pcdev = cdev_alloc();if (!pcdev) {pr_info("cdev_alloc failed\n");return -1;}pcdev->ops = &fops;//将新的cdev加入到字符设备类型列表中ret = cdev_add(pcdev, devno, 1);if (ret) {pr_info("cdev_add failed\n");return -1;}//sys文件系统中创建设备类型pclass = class_create(THIS_MODULE, "myled");if (NULL == pclass) {pr_info("class_create failed\n");return -1;}//sys文件系统中创建设备pdevice = device_create(pclass, NULL, devno, NULL, "led0");if (NULL == pdevice) {pr_info("device_create failed\n");return -1;}//映射寄存器地址pmuxreg = ioremap(IMX6ULL_SW_MUX_CTL, 4);if (NULL == pmuxreg) {pr_info("ioremap failed\n");return -1;}ppadreg = ioremap(IMX6ULL_SW_PAD_CTL, 4);if (NULL == ppadreg) {pr_info("ioremap failed\n");return -1;}pgpiodir = ioremap(IMX6ULL_GPIO_DIR, 4);if (NULL == pgpiodir) {pr_info("ioremap failed\n");return -1;}pgpiodat = ioremap(IMX6ULL_GPIO_DAT, 4);if (NULL == pgpiodat) {pr_info("ioremap failed\n");return -1;}//对寄存器赋值writel(((readl(pmuxreg) & ~0xf) | 0x5), pmuxreg);writel(0x10B0, ppadreg);writel((readl(pgpiodir) | (0x1 << 3)), pgpiodir);writel((readl(pgpiodat) | (0x1 << 3)), pgpiodat);pr_info("led drv init success\n");return 0;
}static __exit void led_drv_exit(void)
{iounmap(pmuxreg);iounmap(ppadreg);iounmap(pgpiodir);iounmap(pgpiodat);device_remove_file(pdevice, &led_attr);device_destroy(pclass, devno);cdev_del(pcdev);unregister_chrdev_region(devno, 1);mutex_destroy(&lock);pr_info("led drv exit success\n");return;
}module_init(led_drv_init);
module_exit(led_drv_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("pute");
4.Makefile
#模块名
modulename := led_drv#内核路径
kerdir := /home/linux/imx6ull/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek#当前目录路径
pwd := $(shell pwd)#加入模块                                                                                                                       
obj-m += $(modulename).oall:make -C $(kerdir) modules M=$(pwd)cp $(modulename).ko ~/nfs/rootfs .PHONY:
distclean:make -C $(kerdir) modules M=$(pwd) cleanrm ~/nfs/rootfs/$(modulename).ko
clean:make -C $(kerdir) modules M=$(pwd) clean
5.Makefile
modulename := led_all:make -C $(modulename)appmake -C $(modulename)drv.PHONY:
distclean:make -C $(modulename)app distcleanmake -C $(modulename)drv distclean
clean:make -C $(modulename)app cleanmake -C $(modulename)drv clean

beep

1.beep_app.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>int main(void)
{int fd = 0;int status = 0;fd = open("/dev/beep0", O_RDWR);if (-1 == fd){perror("fail to open");return -1;}while (1){status = 1;write(fd, &status, sizeof(status));sleep(1);status = 0;write(fd, &status, sizeof(status));sleep(1);}/*status = 1;write(fd, &status, sizeof(status));*/close(fd);return 0;
}
2.Makefile
#模块名
modulename := beep_app#工具链
CC := arm-linux-gnueabihf-gcc all:$(CC) $(modulename).c -o $(modulename)cp $(modulename) ~/nfs/rootfs .PHONY:
distclean:rm $(modulename)rm ~/nfs/rootfs/$(modulename)
clean:rm $(modulename)
3.beep_drv.c
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/uaccess.h>#define SW_MUX_CTL          0x229000C 
#define SW_PAD_CTL          0x2290050
#define GPIO_DIR            0x20AC004
#define GPIO_DAT            0x20AC000static int beep_open(struct inode *node, struct file *fp);
static ssize_t beep_write(struct file *fp, const char __user *puser, size_t n, loff_t *off);
static int beep_release(struct inode *node, struct file *fp);
static ssize_t beep_read(struct file *fp, char __user *puser, size_t n, loff_t *off);static dev_t devno = 0;
static struct cdev *pcdev = NULL;
static void __iomem *pmuxreg = NULL;
static void __iomem *ppadreg = NULL;
static void __iomem *pgpiodir = NULL;
static void __iomem *pgpiodat = NULL;
static struct class *pclass = NULL;
static struct device *pdevice = NULL;extern void __iomem *devm_ioremap(struct device *dev, resource_size_t offset, resource_size_t size);static struct file_operations fops = {.owner = THIS_MODULE,.open = beep_open,.read = beep_read,.write = beep_write,.release = beep_release,
};static int beep_open(struct inode *node, struct file *fp)
{pr_info("drv:beep_open\n");return 0;
}static ssize_t beep_write(struct file *fp, const char __user *puser, size_t n, loff_t *off)
{unsigned long nret = 0;int status = 0;nret = copy_from_user(&status, puser, n);if (nret) {pr_info("copy_from_user failed\n");return -1;}if (status) {writel((readl(pgpiodat) & (~(0x1 << 1))), pgpiodat);} else {writel((readl(pgpiodat) | (0x1 << 1)), pgpiodat);}pr_info("drv:beep_write\n");return 0;
}static int beep_release(struct inode *node, struct file *fp)
{pr_info("drv:beep_release\n");return 0;
}static ssize_t beep_read(struct file *fp, char __user *puser, size_t n, loff_t *off)
{pr_info("drv:beep_read\n");return 0;
}static __init int beep_drv_init(void)
{int ret = 0;//申请主设备号,及此设备号ret = alloc_chrdev_region(&devno, 0, 1, "mybeep");if (ret) {pr_info("alloc_chrdev_region failed\n");return -1;}pr_info("major:%d\n", MAJOR(devno));//构建一个新的设备类型pcdev = cdev_alloc();if (!pcdev) {pr_info("cdev_alloc failed\n");return -1;}pcdev->ops = &fops;//将新的cdev加入到字符设备类型列表中ret = cdev_add(pcdev, devno, 1);if (ret) {pr_info("cdev_add failed\n");return -1;}//sys文件系统中创建设备类型pclass = class_create(THIS_MODULE, "mybeep");if (NULL == pclass) {pr_info("class_create failed\n");return -1;}//sys文件系统中创建设备pdevice = device_create(pclass, NULL, devno, NULL, "beep0");if (NULL == pdevice) {pr_info("device_create failed\n");return -1;}//映射寄存器地址// pmuxreg = ioremap(IMX6ULL_SW_MUX_CTL, 4);// if (NULL == pmuxreg) {//     pr_info("ioremap failed\n");//     return -1;// }// ppadreg = ioremap(IMX6ULL_SW_PAD_CTL, 4);// if (NULL == ppadreg) {//     pr_info("ioremap failed\n");//     return -1;// }// pgpiodir = ioremap(IMX6ULL_GPIO_DIR, 4);// if (NULL == pgpiodir) {//     pr_info("ioremap failed\n");//     return -1;// }// pgpiodat = ioremap(IMX6ULL_GPIO_DAT, 4);// if (NULL == pgpiodat) {//     pr_info("ioremap failed\n");//     return -1;// }/*devm_ioremap自动解除映射*/pmuxreg = devm_ioremap(pdevice, SW_MUX_CTL, 4);if (NULL == pmuxreg) {pr_info("devm_ioremap failed\n");return -1;}ppadreg = devm_ioremap(pdevice, SW_PAD_CTL, 4);if (NULL == ppadreg) {pr_info("devm_ioremap failed\n");return -1;}pgpiodir = devm_ioremap(pdevice, GPIO_DIR, 4);if (NULL == pgpiodir) {pr_info("devm_ioremap failed\n");return -1;}pgpiodat = devm_ioremap(pdevice, GPIO_DAT, 4);if (NULL == pgpiodat) {pr_info("devm_ioremap failed\n");return -1;}//对寄存器赋值writel(((readl(pmuxreg) & ~0xf) | 0x5), pmuxreg);writel(0x10B0, ppadreg);writel((readl(pgpiodir) | (0x1 << 1)), pgpiodir);writel((readl(pgpiodat) | (0x1 << 1)), pgpiodat);pr_info("beep drv init success\n");return 0;
}static __exit void beep_drv_exit(void)
{// iounmap(pmuxreg);// iounmap(ppadreg);// iounmap(pgpiodir);// iounmap(pgpiodat);device_destroy(pclass, devno);class_destroy(pclass);/*cdev_del(pcdev);unregister_chrdev_region(devno, 1);*/pr_info("beep drv exit success\n");return;
}module_init(beep_drv_init);
module_exit(beep_drv_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("pute");
4.Makefile
#模块名
modulename := beep_drv#内核路径
kerdir := /home/linux/imx6ull/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek#当前目录路径
pwd := $(shell pwd)#加入模块                                                                                                                       
obj-m += $(modulename).oall:make -C $(kerdir) modules M=$(pwd)cp $(modulename).ko ~/nfs/rootfs .PHONY:
distclean:make -C $(kerdir) modules M=$(pwd) cleanrm ~/nfs/rootfs/$(modulename).ko
clean:make -C $(kerdir) modules M=$(pwd) clean
5.Makefile
modulename := beep_all:make -C $(modulename)appmake -C $(modulename)drv.PHONY:
distclean:make -C $(modulename)app distcleanmake -C $(modulename)drv distclean
clean:make -C $(modulename)app cleanmake -C $(modulename)drv clean
http://www.dtcms.com/a/445873.html

相关文章:

  • 做的网站提示磁盘空间不足投票活动网站怎么做
  • 项目1:FFMPEG推流器讲解(二):FFMPEG输出模块初始化
  • 中级前端进阶方向 框架篇 三十四) 前端自动化测试 + 【步骤落地 + 了解】
  • 【开题答辩全过程】以 python杭州亚运会数据分析与可视化开题为例,包含答辩的问题和答案
  • 中国做外贸网站有哪些网站下雪的效果怎么做的
  • XSLT `<choose>` 元素详解
  • 汽车零部件英语词汇 | 3000 最常用单词系列
  • 深圳优秀网站建设价格网站视频开发平台
  • 菏泽最好的网站建设公司安徽建设工程信息网查
  • Video-of-Thought论文阅读
  • 做下载类型网站怎样划算做网站常用的技术有哪些
  • stp instance 0 cost 5000 概念及题目
  • KVM创建的虚拟机,虚拟机的网卡是如何生成的
  • 网站开发人员结构清新太和做网站
  • 【开题答辩全过程】以 SportsGo健身网站为例,包含答辩的问题和答案
  • Cobalt Strike
  • Java Servlet(三)--- 写一个简单的网站,表白墙程序,登录功能的实现
  • 达梦数据库(DM8)物理备份与还原
  • 【AI论文】OpenGPT-4o-Image:面向高级图像生成与编辑的综合性数据集
  • Pyenv 使用教程:安装与卸载
  • 告别PECL,拥抱PIE:像Composer一样管理PHP扩展
  • weex做网站python做网页
  • 【超详细】使用conda配置python的开发环境
  • 深圳沙井公司网站建设网上银行登录
  • 高端公司网站建设自适应网站cms
  • isis整体知识梳理
  • 水管 / 污水管道巡检机器人(研究思路_1)
  • 操作系统命令:Linux与Shell(Operating System Command Line, OS/CLI)目录导航、文件操作与日志查看命令实践
  • 北海建设工程信息网站自助提卡网站怎么做
  • 【QT】概述补充——对象树