泰山派学习11--字符设备驱动

news/2024/10/5 5:23:43

1、字符设备定义

  应用程序按字节/字符来读写数据的设备,不支持随机存取数据,系统直接从设备读取/写入每一个字符。

2、字符设备抽象

  Linux内核中将字符设备抽象成一个具体的数据结构(struct cdev),理解为字符设备对象。 字符设备的打开、读写、关闭等操作接口(file_operations)。 创建一个文件(设备节点)绑定对象的cdev

3、字符设备相关概念

3.1、设备号

主设备号区分设备类别,次设备号标识具体的设备。cdev结构体被内核用来记录设备号,而在使用设备时,我们通常会打开设备节点,通过设备节点的inode结构体、 file结构体最终找到file_operations结构体,并从file_operations结构体中得到操作设备的具体方法。 dev_t用来表示设备编号,dev_t是一个32位的数,其中,高12位表示主设备号,低20位表示次设备号。

3.2、设备节点

Linux中设备节点是通过“mknod”命令来创建的。一个设备节点其实就是一个文件, Linux中称为设备文件。设备节点被创建在/dev下,是连接内核与用户层的枢纽。

sudo mknod /dev/chardev c 236 0

4、数据结构

这三个数据结构体都是存在(内核源码/include/linux/fs.h)

4.1、文件操作结构体file_operations

系统调用和驱动程序关联起来的关键数据结构

struct file_operations {

struct module *owner;

ssize_t (*read)(struct file *, char __user *, size_t loff_t *);

ssize_t (*write)(struct file const char __user *, size_t, loff_t *);

int (*open)(struct inode *, struct file *);

int (* release)(struct inode *, struct file *);

......

};

4.2 文件描述结构体

内核中用file结构体来表示每个打开的文件,每打开一个文件,内核会创建一个结构体

struct file {

{......};

const struct file_operations *f_op;

void *private_data;

{......};

};

4.3、节点inode结构体

内核使用inode结构体在内核内部表示一个文件,它是Linux 管理文件系统的最基本单位,也是文件系统连接任何子目录、文件的桥梁.

struct inode{

dev_t i_rdev;

{......};

union{

struct pipe_inode_info *i_pipe;

struct block_device *i_bdev;

struct cdev *i_cdev;

char *i_link;

unsigned i_dir_seq;

};

{......};

};

5、设备号的注册申请及注销归还

5.1、宏定义常量进行申请

int register_chrdev_region(dev_t from, unsigned count, const char *name)

5.2、内核自动分配,成功会返回一个指针

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

5.3、注销删除设备

void unregister_chrdev_region(dev_t from, unsigned count)

6、字符设备驱动模块(chrdevbase.c)

//头文件

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

#define MY_NAME "chardev"

int major = 0;
char kbuf[128] = {0};

int my_open(struct inode *inode, struct file *file)
{
  printk("chardev open!\n");
  return 0;
}
ssize_t my_read(struct file *file, char __user *ubuf, size_t size, loff_t *offset)
{
  if(size > sizeof(kbuf)){
  size = sizeof(kbuf);
}
  if(copy_to_user(ubuf, kbuf, size)){
    printk("copy data to user failed.\n");
    return -EIO;
  }
  printk("user read data ok!\n");
  return size;
}
ssize_t my_write(struct file *file, const char __user *ubuf, size_t size, loff_t *offset)
{
  if(size > sizeof(kbuf)){
  size = sizeof(kbuf);
  }
  if(copy_from_user(kbuf, ubuf, size)){
    printk("copy data to kernel failed.\n");
    return -EIO;
  }
  printk("user write data ok!\n");
  return size;
}
int my_close(struct inode *inode, struct file *file)
{
  printk("chardev close!\n");
  return 0;
}

struct file_operations fops = {
  .owner = THIS_MODULE,
  .open = my_open,
  .read = my_read,
  .write = my_write,
  .release = my_close
};

static int __init mycdev_init(void)
{
  major = register_chrdev(0, MY_NAME, &fops);
  if(major < 0)
  {
    printk("chardev reg failed!\n");
    return -1;
  }
  printk("chardev reg successed\n");
  return 0;
}

static void __exit mycdev_exit(void)
{
  printk("hello linux %s\n"," chardev exit");
  unregister_chrdev(major, MY_NAME);
}

module_init(mycdev_init);

module_exit(mycdev_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("zbl");

7、用户应用程序(chrdevbaseapp.c)

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>


int main()
{

char buf[32] = "zbl tspi.";
char ubuf[64] = {0};
int fd = open("/dev/chardev",O_RDWR);
if(fd == -1)
{
  printf("open failed.\n");
  return -1;
}

memcpy(ubuf,buf,sizeof(buf));
printf("write ubuf = %s\n", ubuf);
write(fd,ubuf,sizeof(ubuf));

memset(ubuf, 0, sizeof(ubuf));
read(fd,ubuf,1024);
printf("read ubuf = %s\n",ubuf);

close(fd);
return 0;

}

**8、交叉编译makefile**

PWD ?= $(shell pwd)

KERNELDIR := /home/zbl/tspi-rk3566/sdk/linux/kernel

CROSS_COMPILE ?= /home/zbl/tspi-rk3566/sdk/linux/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-

CC := $(CROSS_COMPILE)gcc

obj-m += chrdevbase.o

module:

make -C $(KERNELDIR) M=$(PWD) ARCH=arm64 modules

@# -C 表示从当前目录切换到内核源码目录下,借助内核源码makefile进行make编译

@# M=$(PWD) 表示只编译当前目录下的驱动

@# ARCH=arm64 指定编译架构

$(CC) chrdevbaseapp.c -o app

@# 交叉编译应用程序

.PHONE:clean

clean:

make -C $(KERNELDIR) M=$(PWD) ARCH=arm64 clean

rm app

**9、修改终端printk显示等级**

////终端消息在串口才可以查看,ssh无法打印终端消息,并且需要修改printk的等级为8////

root@localhost:/home/lckfb/zbl-kernel-modules# ./app

root@localhost:/home/lckfb/zbl-kernel-modules# cat /proc/sys/kernel/printk

4       4       1       7

root@localhost:/home/lckfb/zbl-kernel-modules# echo 8 > /proc/sys/kernel/printk

root@localhost:/home/lckfb/zbl-kernel-modules# cat /proc/sys/kernel/printk

8       4       1       7

       

10、执行流程

1、编写内核驱动chrdevbase.c

2、编写应用程序chrdevbaseapp.c

3、编写makefile,添加交叉编译工具及编译应用执行文件

4、.ko及app应用执行文件导入开发板 cmd 使用adb命令或者局域网内登录ssh 传输 : adb push  xxx xxx

       

5、修改执行权限 chmod 777 app chrdevbase.ko

6、加载内核启动 sudo insmod chrdevbase.ko

lckfb@linux:~/zbl-kernel-modules$ sudo insmod chrdevbase.ko
[ 4343.651750] reg successed

       

7、查看驱动lsmod

lckfb@linux:~/zbl-kernel-modules$ lsmod
Module Size Used by
chrdevbase 16384 0
bcmdhd 1048576 0

       

8、查看驱动主设备号

cat /proc/devices

226 drm

236 chardev

237 hidraw

238 rpmb

239 ttyGS

      

9、创建设备节点文件 sudo mknod /dev/chardev c(字符设备) 236(主设备号) 0(从设备号)

lckfb@linux:~/zbl-kernel-modules$ sudo mknod /dev/chardev c 236 0

       

10、查看节点是否加载成功 ls -l /dev/chardev

lckfb@linux:~/zbl-kernel-modules$ ll /dev/chardev
crw-r--r-- 1 root root 236, 0 Jun 4 13:59 /dev/chardev

11、修改节点权限 sudo chmod 777 /dev/chardev

lckfb@linux:~/zbl-kernel-modules$ ll /dev/chardev
crwxrwxrwx 1 root root 236, 0 Jun 4 13:59 /dev/chardev

12、执行应用程序 ./app

root@localhost:/home/lckfb/zbl-kernel-modules# ./app
[ 4208.722682] open!
[ 4208.722768] read!
[ 4208.722785] write!
[ 4208.722801] close!
root@localhost:/home/lckfb/zbl-kernel-modules/04# ./app
[ 1090.308736] chardev open!
write ubuf = zbl tspi.
read ubuf = zbl tspi.
[ 1090.309171] user write data ok!
[ 1090.309209] user read data ok!
[ 1090.309500] chardev close!

      

13、卸载内核驱动 sudo rmmod chrdevbase.ko

root@localhost:/home/lckfb/zbl-kernel-modules# sudo rmmod chrdevbase.ko

[ 4236.212310] hello world mydev exit

       

14、删除设备节点文件 sudo rm /dev/chardev

root@localhost:/home/lckfb/zbl-kernel-modules# sudo rm /dev/chardev

       

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/44107.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

存入数据库的时间不对

存入数据库的时间和当前时间相差了8个小时?解决更改项目数据库连接串 GMT 为 Asia/Shanghai。如下图:GMT:为格林尼治天文台时间。与东八区,也就是北京时间相差8个小时。这里应该设置为东八区时间,即是Asia/Shanghai。

最低50元的京东云轻量服务器体验

‍ 最低50元的京东云轻量服务器体验:最低的价格,最差的体验。 ​​ 体验​​ 看路人评论 ​​ ‍ 处处是bug ‍ 从自己的机器上ssh到服务器,刚连接的时候正常 ​​ 用了一会,终端没反应了(非禁止画面)这是不是断连? ​​ ‍ 然后是网页的控制台 ​​vim 搜索 alias ,开…

UE Puerts 在 Android 的调试方法

配置流程 在 JsEnv.Build.cs 添加 WITH_INSPECTOR 编译添加这个宏就开启了全平台调试功能看到这里就知道 V8Inspector 之前仅在三个平台会编译,现在会在任意平台编译了 打包的时候会发现存在编译错误(如果没有就是后续 Puerts 版本修复了这个问题),接下来修复这个错误(没有…

django中orm的使用

Orm是django连接mysql数据库的工具,用户可以通过orm实现对数据库的增删改查。 1.启动mysql 创建数据库 在cmd中输入mysql -uroot -p 后再输入密码运行mysql 然后创建数据库:create database 数据库名 DEFAULT CHARSET utf8 COLLATE utf8_general_ci; 2 让django链接数据库 在…

linux内存管理(八)- 反向映射RMAP

这里有一篇博客讲的不错。linux内存管理笔记(三十八)----反向映射_linux 反向映射-CSDN博客 页表是把虚拟地址映射到物理页面,但是如何根据一个物理页找到所有映射它的pte呢?答案是用反向映射Reverse Mapping(RMAP)。这在页面回收中很有用。回收页面需要将到物理页的映射断…

一文了解Spark引擎的优势及应用场景

Spark引擎诞生的背景 Spark的发展历程可以追溯到2009年,由加州大学伯克利分校的AMPLab研究团队发起。成为Apache软件基金会的孵化项目后,于2012年发布了第一个稳定版本。以下是Spark的主要发展里程碑:初始版本发布:2010年开发的Matei Zaharia的研究项目成为Spark的前身。在…

Charles抓包工具

一.Charles介绍、安装、汉化 跨平台的抓包工具:Windows、linux、mac 它是基于http、https协议的道代理服务器 官网:https://www.charlesproxy.com/latest-release/download.do 特点:半免费 可以破解和汉化,仅供学习 优点: 1.支持按照域名查看、按接口列表的方式 2.弱网测试…

如何快速批量申请SSL证书

假如您手头拥有1千个域名,并且打算为每一个域名搭建网站,那么在当前的网络环境下,您必须确保这些网站通过https的方式提供服务。这意味着,您将为每一个域名申请SSL证书,以确保网站数据传输的安全性和可信度。那么,您该如何着手申请这些SSL证书呢? 一、一般方案 1、国内云…