Linux(2) 字符设备驱动


字符设备驱动是Linux驱动中最基本的一类设备驱动,能够像字节流一样被访问的设备,可按照字节进行读写操作设备,读写数据是分先后顺序的,字符设备驱动程序一般可以实现open、close、read、write等系统调用,应用层可以通过设备节点来访问。

逻辑图

在这里插入图片描述

字符设备驱动

在这里插入图片描述

驱动源码
#include <linux/init.h>				//宏定义声明__init和__exit
#include <linux/module.h>			//声明module_init和module_exit
#include <linux/fs.h>				//声明file_operations结构体表示虚拟文件系统文件操作API
#include <linux/cdev.h>				//声明字符设备类型cdev
#include <linux/device.h>			//声明设备节点类型class和设备类型device
#include <linux/types.h>     		//声明设备号类型dev_t
#include <linux/uaccess.h>			//声明函数copy_from_user和copy_to_user
#include <linux/io.h>				//声明函数ioremap和iounmap


#define DEMO_NAME "c_dev"                  //设备名
#define DEMO_CONUT 1                       //次设备号数量

struct mkdev_t {
	struct cdev *c_dev;                 //字符设备cdev
	struct class *class;                //设备节点类
	struct device *device;              //设备实例
	dev_t dev;                          //设备号
	int major;		                    //主设备号
	int minor;                          //次设备号   
};
static struct mkdev_t mkdev;            //自定义的字符设备结构体

static char readbuf[100];                  //读缓冲区
static char writebuf[100];                 //写缓冲区
static char kerneldata[] = {"kernel data!"};

static int c_drv_open(struct inode *inode, struct file *filp)
{
	struct mkdev_t *dev = (struct mkdev_t *)filp->private_data;       //获取设备私有数据
    int major = MAJOR(inode->i_rdev);        //从设备号提取主设备号
    int minor = MINOR(inode->i_rdev);        //从设备号提取次设备号
    printk("%s: major=%d, minor=%d\n", __func__, major, minor);
    return 0;
}

static int c_drv_release(struct inode *inode, struct file *filp)
{
	printk("%s: enter\n", __func__ );
    return 0;
}

static ssize_t c_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{  
    //向用户空间发送数据
    memcpy(readbuf, kerneldata, sizeof(kerneldata));
    int ret = copy_to_user(buf, readbuf, count);
    if(!ret) {
    	printk("kernel senddata ok!\n");
    } else {
    	printk("kernel senddata failed!\n");
    }
    printk("%s: enter\n", __func__ );
    return 0;
}

static ssize_t c_drv_write(struct file *filp, char __user *buf, size_t count,loff_t *ppos)
{   
    //接收用户空间的数据并打印
    int ret = copy_from_user(writebuf, buf, count);
    if(!ret) {
    	printk("kernel getdata ok: %s\n", writebuf);
    } else {
    	printk("kernel getdata failed!\n");
    }
    printk("%s: enter\n", __func__ );
    return 0;
}

static const struct file_operations c_drv_fops = {
    .owner = THIS_MODULE,
    .open = c_drv_open,
    .release = c_drv_release,
    .read = c_drv_read,
    .write = c_drv_write,
};

static int __init c_drivers_init(void)
{
    int ret;

	/*1、申请设备号*/
    if(mkdev.major) {                                                  		//若指定主设备后则静态分配
    	mkdev.dev = MKDEV(mkdev.major, 0);                                  //将主次设备号拼凑成设备号
    	ret = register_chrdev_region(mkdev.dev, DEMO_CONUT, DEMO_NAME);     //静态申请设备号
    } else { 	
    	ret = alloc_chrdev_region(&mkdev.dev, 0, DEMO_CONUT, DEMO_NAME);    //动态申请设备号 (0表示从0开始动态申请)
        mkdev.major = MAJOR(mkdev.dev);        //从设备号提取主设备号
	    mkdev.minor = MINOR(mkdev.dev);        //从设备号提取次设备号
	}
    if(ret) {
        printk("failed to allocate char device region\n"); 
        return ret;
    }
    printk("successed register char device: %S\n", DEMO_NAME);
    printk("Major number = %d, minor number = %d\n", mkdev.major, mkdev.minor);
    
    /*2、创建并注册字符设备cdev*/
    mkdev.c_cdev = cdev_alloc();                     //动态申请内存cdev对象
    if(!mkdev.c_cdev) {
        printk("cdev alloc failed\n"); 
        goto unregister_chrdev;
    }
    cdev_init(mkdev.c_cdev, &c_drv_fops);                        //初始化cdev成员,并建立与file_operations的联系
    ret = cdev_add(mkdev.c_cdev, mkdev.dev, DEMO_CONUT);        //注册cdev设备对象,添加到系统字符设备列表
    if(ret) {
        printk("cdev add failed\n");
        goto cdev_fail;
    }

	/*3、创建设备节点类*/
	mkdev.class = class_create(THIS_MODULE, DEMO_NAME);
	if(IS_ERR(mkdev.class)) {
		return PTR_ERR(mkdev.class);
	}
	
	/*4、创建设备实例*/
	mkdev.device = device_create(mkdev.class, NULL, mkdev.dev, NULL, DEMO_NAME);
	if(IS_ERR(mkdev.device)) {
		return PTR_ERR(mkdev.device);
	}

	/*5、错误处理*/
    cdev_fail:
        cdev_del(mkdev.c_cdev);                                 //注销cdev设备对象
    unregister_chrdev:
        unregister_chrdev_region(mkdev.dev, DEMO_CONUT);       //释放设备号
        
    ratura ret;
}


static void __exit c_drivers_exit(void)
{
    printk("removing device\n"); 

	device_destory(mkdev.class, mkdev.device);               //注销设备实例
	class_destory(mkdev.class);                              //注销设备节点类
	
    cdev_del(mkdev.c_cdev);                                  //注销cdev设备对象  
    unregister_chrdev_region(mkdev.dev, DEMO_CONUT);        //释放设备号
}

module_init(c_drivers_init);
module_exit(c_drivers_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxxx");
MODULE_DESCRIPTION("cdev test driver"); 
MODULE_VERSION("1.0.0");
驱动Makefile
SRC_PATH := $(shell pwd)           
KERNEL := $(shell uname -r)
BUILD_PATH := /usr/src/linux-headers-$(KERNEL)

obj-m := c_driver.o

build: kernel_modules

kernel_modules:
	make -C $(BUILD_PATH) M=$(SRC_PATH) modules
clean:
	make -C $(BUILD_PATH) M=$(SRC_PATH) clean

demo

demo源码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h">
#include <fcntl.h>
#include <string.h>


static char usrdata[] = {"usr data!"};

/*
 * @description		: main主程序
 * @param - argc 	: argv数组元素个数
 * @param - argv 	: 具体参数
 * @return 			: 0 成功;其他 失败
 */
int main(int argc, char *argv[])
{
	int fd, ret;
	char *filename;
	char readbuf[100], writebuf[100];

	if(argc != 3){
		printf("Error Usage!\r\n");
		return -1;
	}

	filename = argv[1];

	/* 打开驱动文件 */
	fd  = open(filename, O_RDWR);
	if(fd < 0){
		printf("Can't open file %s\r\n", filename);
		return -1;
	}

	if(atoi(argv[2]) == 1){  
		/* 从驱动文件读取数据 */
		ret = read(fd, readbuf, 50);
		if(ret < 0){
			printf("read file %s failed!\r\n", filename);
		}else{
			/*  读取成功,打印出读取成功的数据 */
			printf("read data:%s\r\n",readbuf);
		}
	}

	if(atoi(argv[2]) == 2){
 		/* 向设备驱动写数据 */
		memcpy(writebuf, usrdata, sizeof(usrdata));
		ret = write(fd, writebuf, 50);
		if(ret < 0){
			printf("write file %s failed!\r\n", filename);
		}
	}

	/* 关闭设备 */
	ret = close(fd);
	if(ret < 0){
		printf("Can't close file %s\r\n", filename);
		return -1;
	}
	return 0;
}
demo的Makefile
CFLAG=-I/usr/include/sys

LDFLAG=-ldrm

SRC=demo.c

TARGET=demo

all:
	gcc -g -O2 $(SRC) -o $(TARGET) $(CTLAG) $(LDFLAG)

clean:
	rm -rf  $(TARGET) *.o
测试
test@ubuntu:~$ cat /dev |grep cdev
100 0 cdev
test@ubuntu:~$ ./demo /dev/cdev 1            //读操作
test@ubuntu:~$ ./demo /dev/cdev 2            //写操作

参考资料:https://gitee.com/xiebaoyou/dim-sum

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值