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

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

       

posted @ 2024-06-15 21:37  zbl1118  阅读(281)  评论(0)    收藏  举报