BSP开发学习1通用字符设备开发
Posted 与光同程
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BSP开发学习1通用字符设备开发相关的知识,希望对你有一定的参考价值。
Linux字符驱动设备API
设备号申请API
动态申请:
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
静态申请:
int register_chrdev_region(dev_t from, unsigned count, const char *name)
参数 from 是要申请的起始设备号,也就是给定的设备号;参数 count 是要申请的数量,一般都是一个;参数 name 是设备名字。
释放:
void unregister_chrdev_region(dev_t from, unsigned count)
设备号申请示例:
1 int major; /* 主设备号 */
2 int minor; /* 次设备号 */
3 dev_t devid; /* 设备号 */
4
5 if (major) /* 定义了主设备号 */
6 devid = MKDEV(major, 0); /* 大部分驱动次设备号都选择 0 */
7 register_chrdev_region(devid, 1, "test");
8 else /* 没有定义设备号 */
9 alloc_chrdev_region(&devid, 0, 1, "test"); /* 申请设备号 */
10 major = MAJOR(devid); /* 获取分配号的主设备号 */
11 minor = MINOR(devid); /* 获取分配号的次设备号 */
12
用户程序和内核数据交换
copy_to_user和copy_from_user就是在进行驱动相关程序设计的时候,要经常遇到的两个函数。由于内核空间与用户空间的内存不能直接互访,因此借助函数copy_to_user()完成用户空间到内核空间的复制
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
cdev API
cdev 定义
1 struct cdev
2 struct kobject kobj;
3 struct module *owner;
4 const struct file_operations *ops;
5 struct list_head list;
6 dev_t dev;
7 unsigned int count;
8 ;
在 cdev 中有两个重要的成员变量:ops 和 dev,这两个就是字符设备文件操作函数集合file_operations 以及设备号 dev_t。编写字符设备驱动之前需要定义一个 cdev 结构体变量,这个变量就表示一个字符设备
cdev_init 函数
定义好 cdev 变量以后就要使用 cdev_init 函数对其进行初始化
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
参数 cdev 就是要初始化的 cdev 结构体变量,参数 fops 就是字符设备文件操作函数集合
cdev_add 函数
cdev_add 函数用于向 Linux 系统添加字符设备(cdev 结构体变量)
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
参数 p 指向要添加的字符设备(cdev 结构体变量),参数 dev 就是设备所使用的设备号,参数 count 是要添加的设备数量。
cdev_del 函数
卸载驱动的时候一定要使用 cdev_del 函数从 Linux 内核中删除相应的字符设备
void cdev_del(struct cdev *p)
参数 p 就是要删除的字符设备。
自动创建设备节点
经过测试发现,在没有添加自动创建设备节点的代码之前,insmod 驱动之后并不会直接在/dev文件夹下创建设备节点,需要使用mknod 手动创建一个设备文件并且将这个文件和设备号关联
mkdnod /dev/global_mem c 250 0
这样有麻烦 busybox 提供类似于udev的简化版本 mdev 这个程序开机是不会自己启动的
所以需要在启动脚本中添加:
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
在完成上面的工作之后,我们还需要进行类和设备的创建
创建删除类
将宏 class_create 展开以后内容如下:
struct class *class_create (struct module *owner, const char *name)
class_create 一共有两个参数,参数 owner 一般为 THIS_MODULE,参数 name 是类名字。
返回值是个指向结构体 class 的指针,也就是创建的类。
卸载驱动程序的时候需要删除掉类,类删除函数为 class_destroy,函数原型如下:
void class_destroy(struct class *cls);
参数 cls 就是要删除的类。
创建删除设备
上一小节创建好类以后还不能实现自动创建设备节点,我们还需要在这个类下创建一个设
备。使用 device_create 函数在类下面创建设备,device_create 函数原型如下:
struct device *device_create(struct class *class,
struct device *parent,
dev_t devt,
void *drvdata,
const char *fmt, …)
device_create 是个可变参数函数,参数 class 就是设备要创建哪个类下面;参数 parent 是父
设备,一般为 NULL,也就是没有父设备;参数 devt 是设备号;参数 drvdata 是设备可能会使用
的一些数据,一般为 NULL;参数 fmt 是设备名字,如果设置 fmt=xxx 的话,就会生成/dev/xxx
这个设备文件。返回值就是创建好的设备。同样的,卸载驱动的时候需要删除掉创建的设备,设备删除函数为 device_destroy,函数原型如下:
void device_destroy(struct class *class, dev_t devt)
参数 class 是要删除的设备所处的类,参数 devt 是要删除的设备号。
文件操作
cdev 中的file_operation变量是驱动中必须进行设置的
需要对其中几个函数指针进行赋值,常见函数原型如下:
static int global_mem_open(struct inode *inode ,struct file *flip)
static int global_mem_release(struct inode *inode ,struct file *flip)
static ssize_t global_mem_read(struct file* flip, char __user *buf,size_t size ,loff_t *ppos)
static ssize_t global_mem_write(struct file* flip, const char __user *buf,size_t size ,loff_t *ppos)
static loff_t global_mem_llseek(struct file* flip ,loff_t offset,int orig)
static long global_mem_ioctl(struct file* flip,unsigned int cmd,unsigned long arg)
static const struct file_operations global_mem_fops=
.owner=THIS_MODULE,
.open=global_mem_open,
.release=global_mem_release,
.read=global_mem_read,
.write=global_mem_write,
.llseek=global_mem_llseek,
.unlocked_ioctl=global_mem_ioctl
;
字符设备驱动整体结构
#include <linux/module.h>//MOUDLE_XXX moudle_xxx
#include <linux/fs.h> //file_operations register_chrdev_region alloc_chrdev_region
#include <linux/init.h> //__init __exit
#include <linux/cdev.h> //cdev
#include <linux/uaccess.h>//copy_to_user copy_from_user
#include <linux/device.h> // device class
typedef struct
//设备驱动变量
struct cdev cdev;
//设备号变量
dev_t devid;
int major;
int minor;
//设备节点相关变量
struct class *class;
struct device *device;
//用户变量
unsigned char mem[GLOBAL_MEM_SIZE];
xxx_t;
xxx_t xxx;
//操作实现
xxx_open
xxx_release
···
static const struct file_operations xxx_fops=
.owner=THIS_MODULE,
.open=xxx_open,
.release=xxx_release,
.read=xxx_read,
.write=xxx_write,
.llseek=xxx_llseek,
.unlocked_ioctl=xxx_ioctl
;
/* 驱动入口函数 */
static int __init xxx_init(void)
/*申请设备号*/
xxx.devid=MKDEV(xxx.major,xxx.minor);
if(global_mem.major!=0)//从devno 静态申请 一个设备号
ret=register_chrdev_region(xxx.devid,1,"globalmem");
else//从devno 动态申请一个设备号
ret=alloc_chrdev_region(&xxx.devid,0,1,"globalmem");
xxx.major=MAJOR(xxx.devid);
xxx.minor=MINOR(xxx.devid);
//初始化并且添加cdev
int devno=MKDEV(xxx.major,minor_index);
cdev_init(&xxx.cdev,&xxx_fops);
xxx.cdev.owner=THIS_MODULE;
ret=cdev_add(&xxx.cdev,devno,1);
if(ret)printk("CDEV ADD ERROR:%d\\n",minor_index);
/* 创建类 */
class = class_create(THIS_MODULE, "xxx");
/* 创建设备 */
device = device_create(class, NULL, devid, NULL, "xxx");
return 0;
/* 驱动出口函数 */
static void __exit xxx_exit(void)
//删除驱动和设备号
cdev_del(&xxx.cdev);
unregister_chrdev_region(xxx.devid,1);
/* 删除设备 */
device_destroy(xxx.class, xxx.devid);
/* 删除类 */
class_destroy(xxx.class);
//申明模块出口和入口
module_init(xxx_init);
module_exit(xxx_exit);
//申明作者 和 开源协议
MODULE_AUTHOR("YURI");
MODULE_LICENSE("GPL");
Linux 字符设备驱动编写
/*
* @Copyright:
* @FileNames:
* @Description: 申请全局内存4096 并使用该内存进行 用户和内核之间的数据交换
* @Author:
* @Date: 2022-07-29 09:03:02
* @Version: V1.0
* @LastEditTime: 2022-07-29 16:28:25
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#define GLOBAL_MEM_SIZE 0X1000
#define GLOBAL_MEM_DEBUG 1
#define GLOBAL_MEM_CLASS_NAME "global_mem" //创建的类名称
#define GLOBAL_MEM_NODE_NAME "global_mem_0" //创建的节点名称
#define GLOBAL_MEM_MAJOR 250
//CMD
#define GLOBAL_MEM_CLEAR 0X01 //清理内存 命令
typedef struct
//设备驱动变量
struct cdev cdev;
//设备号变量
dev_t devid;
int major;
int minor;
//设备节点相关变量
struct class *class;
struct device *device;
//用户变量
unsigned char mem[GLOBAL_MEM_SIZE];
global_mem_t;
global_mem_t global_mem;
/**
* @function: global_mem_open
* @description: 打开设备 并将内存清零
* @input:
* @output:
* @return *
* @param inode *inode
* @param file *flip
*/
static int global_mem_open(struct inode *inode ,struct file *flip)
printk("OPEN GLOBAL MEM\\r\\n");
flip->private_data=(void *)(&global_mem);
return 0;
/**
* @function: global_mem_release
* @description: 释放设备节点
* @input:
* @output:
* @return *
* @param inode *inode
* @param file *flip
*/
static int global_mem_release(struct inode *inode ,struct file *flip)
printk("RELEASE GLOBAL MEM\\r\\n");
return 0;
/**
* @function: global_mem_read
* @description: 拷贝数据到用户区
* @input:
* @output:
* @return *
* @param file* flip 文件结构体
* @param char __user *buf 从用户区拷贝出来的数据
* @param size_t size 传入数据大小
* @param loff_t *ppos 当前数据位置
*/
static ssize_t global_mem_read(struct file* flip, char __user *buf,size_t size ,loff_t *ppos)
int ret=0;
printk("READ GLOBAL MEM\\r\\n");
global_mem_t* dev=(global_mem_t*) flip->private_data;
//当前文件指针所处于的位置
unsigned long p=*ppos;
//需要读出的个数
unsigned int count=(unsigned int)size;
//检查读取位置合法性
if(p>=GLOBAL_MEM_SIZE)return 0;
if(count+p>GLOBAL_MEM_SIZE)count=GLOBAL_MEM_SIZE-p;
//拷贝数据到用户区
ret=copy_to_user(buf,dev->mem+p,count);
if (ret<0)
ret=-EFAULT;
else
*ppos+=count;
ret=count;
if(GLOBAL_MEM_DEBUG)printk("READ %d BYTES FROM KERNEL At %d\\n",count,p);
return ret;
/**
* @function: global_mem_write
* @description:
* @input:
* @output:
* @return *
* @param file* flip
* @param char __user *buf
* @param size_t size
* @param loff_t *ppos
*/
static ssize_t global_mem_write(struct file* flip, const char __user *buf,size_t size ,loff_t *ppos)
int ret=0;
printk("WRITE GLOBAL MEM\\r\\n");
global_mem_t* dev=(global_mem_t*) flip->private_data;
//当前文件指针所处于的位置
unsigned long p=*ppos;
//需要读出的个数
unsigned int count=(unsigned int)size;
//检查读取位置合法性
if(p>=GLOBAL_MEM_SIZE)return 0;
if(count+p>GLOBAL_MEM_SIZE)count=GLOBAL_MEM_SIZE-p;
ret=copy_from_user(dev->mem+p,buf,count);
if(ret<0)ret=-EFAULT;
else
*ppos+=count;
ret=count;
if(GLOBAL_MEM_DEBUG)printk("WRITE %d BYTES TO KERNEL AT %d\\n",(int)count,(int)p);
return ret;
static loff_t global_mem_llseek(struct file* flip ,loff_t offset,int orig)
loff_t ret=0;
if(orig==0)//从头开始偏移
if(offset<0)
ret=-EINVAL;
goto end;
if((unsigned int) offset>GLOBAL_MEM_SIZE)
ret=-EINVAL;
goto end;
flip->f_pos=(unsigned int) offset;
ret=flip->f_pos;
else if(orig==1)//从当前位置开始偏移
if(flip->f_pos+offset>GLOBAL_MEM_SIZE)
ret=-EINVAL;
goto end;
flip->f_pos+=offset;
ret=flip->f_pos;
else
ret=-EINVAL;
end:
return ret;
/**
* @function: global_mem_ioctl
* @description:
* @input:
* @output:
* @return *
* @param file* flip
* @param unsigned int cmd
* @param unsigned long arg
*/
static long global_mem_ioctl(struct file* flip,unsigned int cmd,unsigned long arg)
global_mem_t *dev =(global_mem_t *) flip->private_data;
switch(cmd)
case GLOBAL_MEM_CLEAR:
memset(dev->mem,0,sizeof(dev->mem));
break;
default:
return -EINVAL;
return 0;
static const struct file_operations global_mem_fops=
.owner=THIS_MODULE,
.open=global_mem_open,
.release=global_mem_release,
.read=global_mem_read,
.write=global_mem_write,
.llseek=global_mem_llseek,
.unlocked_ioctl=global_mem_ioctl
;
/**
* @function: static void global_mem_setup_cdev(int minor_index)
* @description: 根据一个从设备号生成cdev
* @input:
* @output:
* @return *
*/
static void global_mem_setup_cdev(int minor_index)
int ret;
//得到设备号
int devno=MKDEV(global_mem.major,minor_index);
cdev_init(&global_mem.cdev,&global_mem_fops);
global_mem.cdev.owner=THIS_MODULE;
ret=cdev_add(&global_mem.cdev,devno,1);
if(ret)printk("CDEV ADD ERROR:%d\\n",minor_index);
/**
* @function: global_mem_init
* @description: 申请设备号 并且注册cdev
* @input: void
* @output:
* @return *
*/
static int __init global_mem_init(void)
int ret;
//如果定义了静态主设备号 就采用静态申请的方式
global_mem.major=GLOBAL_MEM_MAJOR;
//得到需要注册的设备号
global_mem.devid=MKDEV(global_mem.major,global_mem.minor);
if(global_mem.major!=0)//从devno 静态申请 一个设备号
ret=register_chrdev_region(global_mem.devid,1,"globalmem");
else//从devno 动态申请一个设备号
ret=alloc_chrdev_region(&global_mem.devid,0,1,"globalmem");
global_mem.major=MAJOR(global_mem.devid);
global_mem.minor=MINOR(global_mem.devid);
memset(global_mem.mem,0,sizeof(global_mem.mem));
if(GLOBAL_MEM_DEBUG)
printk("主设备号:%d\\n",global_mem.major);
printk("从设备号:%d\\n",global_mem.minor);
global_mem_setup_cdev(global_mem.minor);
//创建设备 创建类
global_mem.class = class_create(THIS_MODULE, GLOBAL_MEM_CLASS_NAME);
global_mem.device = device_create(global_mem.class, NULL, global_mem.devid, NULL, GLOBAL_MEM_NODE_NAME);
return 0;
module_init(global_mem_init);
static BSP开发学习2平台设备驱动