ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

新字符设备驱动原理和框架源码分析

2022-08-24 14:32:37  阅读:173  来源: 互联网

标签:字符 return 框架 param chr 源码 newchr include 设备


一、分配和释放设备号

动态申请设备号:

/*
 dev:设备号--dev_t devid;
 count:是要申请的数量,一般都是一个;
 name:是设备名字
*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

静态申请设备号:

/*
 from:是要申请的起始设备号,也就是给定的设备号;
 count:是要申请的数量,一般都是一个;
 name:是设备名字
*/
int register_chrdev_region(dev_t from, unsigned count, const char *name)

释放(删除)设备号:

/*
 from:是要申请的起始设备号,也就是给定的设备号;
 count:是要申请的数量,一般都是一个;
*/
void unregister_chrdev_region(dev_t from, unsigned count)

【文章福利】小编推荐自己的Linux内核源码交流群:【点击链接加入群聊869634926】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!

 

 

学习直通车:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈


二、驱动模块的加载和卸载

module_init(xxx_init); //注册模块加载函数
module_exit(xxx_exit); //注册模块卸载函数
当在终端使用“insmod”命令加载驱动的时候,xxx_init 这个函数就会被调用。
当在终端使用“rmmod”命令卸载具体驱动的时候 xxx_exit 函数就会被调用。

字符驱动模块:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>

#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>


#define NEWCHR_CNT            1              /* 设备号个数 */
#define NEWCHR_NAME            "newchr"    /* 名字 */



/* newchr设备结构体 */
struct newchr_dev
{
    dev_t  devid;            /* 设备号      */
    struct cdev cdev;        /* cdev      */
    struct class *class;    /* 类          */
    struct device *device;    /* 设备      */
    int    major;            /* 主设备号     */
    int    minor;            /* 次设备号  */
};

struct newchr_dev newchr;    /* 设备 */

/*
 * @description        : 打开设备
 * @param - inode     : 传递给驱动的inode
 * @param - filp     : 设备文件,file结构体有个叫做private_data的成员变量
 *                       一般在open的时候将private_data指向设备结构体。
 * @return             : 0 成功;其他 失败
 */
static int chr_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &newchr; /* 设置私有数据 */
    return 0;
}

/*
 * @description        : 从设备读取数据
 * @param - filp     : 要打开的设备文件(文件描述符)
 * @param - buf     : 返回给用户空间的数据缓冲区
 * @param - cnt     : 要读取的数据长度
 * @param - offt     : 相对于文件首地址的偏移
 * @return             : 读取的字节数,如果为负值,表示读取失败
 */
static ssize_t chr_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    return 0;
}

/*
 * @description        : 向设备写数据
 * @param - filp     : 设备文件,表示打开的文件描述符
 * @param - buf     : 要写给设备写入的数据
 * @param - cnt     : 要写入的数据长度
 * @param - offt     : 相对于文件首地址的偏移
 * @return             : 写入的字节数,如果为负值,表示写入失败
 */
static ssize_t chr_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    int retvalue;
    unsigned char databuf[1];
    unsigned char state;

    retvalue = copy_from_user(databuf, buf, cnt);//将用户传递的参数转移到内核中
    if(retvalue < 0)
    {
        printk("kernel write fail!\r\n");
        return -EFAULT;
    }

    state = databuf[0];        /* 获取状态值 */
    /*处理....*/
    return 0;
}

/*
 * @description        : 关闭/释放设备
 * @param - filp     : 要关闭的设备文件(文件描述符)
 * @return             : 0 成功;其他 失败
 */
static int chr_release(struct inode *inode, struct file *filp)
{
    return 0;
}

/* 设备操作函数 */
static struct file_operations newchr_fops =
{
    .owner   = THIS_MODULE,
    .open    = chr_open,
    .read    = chr_read,
    .write   = chr_write,
    .release = chr_release,
};

/*
 * @description    : 驱动出口函数
 * @param         : 无
 * @return         : 无
 */
static int __init chr_init(void)
{
    u32 val = 0;

    /* 注册字符设备驱动 */
    newchr.major = 0;  //将设备号定义为0,则每次都动态申请设备号
    /* 1、创建设备号 */
    if (newchr.major)          /*  定义了设备号 */
    {
        newchr.devid = MKDEV(newchr.major, 0);
        register_chrdev_region(newchr.devid, newchr_CNT, newchr_NAME);
    }
    else                      /* 没有定义设备号 */
    {
        alloc_chrdev_region(&newchr.devid, 0, newchr_CNT, newchr_NAME);    /* 申请设备号 */
        newchr.major = MAJOR(newchr.devid);    /* 获取分配号的主设备号 */
        newchr.minor = MINOR(newchr.devid);    /* 获取分配号的次设备号 */
    }
    printk("newche major=%d,minor=%d\r\n",newchr.major, newchr.minor);

    /* 2、初始化cdev */
    newchr.cdev.owner = THIS_MODULE;
    cdev_init(&newchr.cdev, &newchr_fops);

    /* 3、添加一个cdev */
    cdev_add(&newchr.cdev, newchr.devid, newchr_CNT);

    /* 4、创建类 */
    newchr.class = class_create(THIS_MODULE, newchr_NAME);
    if (IS_ERR(newchr.class))
    {
        return PTR_ERR(newchr.class);
    }

    /* 5、创建设备 */
    newchr.device = device_create(newchr.class, NULL, newchr.devid, NULL, newchr_NAME);
    if (IS_ERR(newchr.device))
    {
        return PTR_ERR(newchr.device);
    }

    return 0;
}

/*
 * @description    : 驱动出口函数
 * @param         : 无
 * @return         : 无
 */
static void __exit chr_exit(void)
{

    /* 注销字符设备驱动 */
    cdev_del(&newchr.cdev);/*  删除cdev */
    unregister_chrdev_region(newchr.devid, newchr_CNT); /* 注销设备号 */

    device_destroy(newchr.class, newchr.devid);
    class_destroy(newchr.class);
}

module_init(chr_init);
module_exit(chr_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xjq");

Makefil 文件 注意这里需要修改路径,请按照自身路径修改

gedit Makefil
KERNELDIR := /home/xue1995/linux/IMX6ULL/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
CURRENT_PATH := $(shell pwd)

obj-m := newchr.o

build: kernel_modules

kernel_modules:
    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

clean:
    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
终端输入:make -j32

编译成功以后就会生成一个名为“newchr.ko”的驱动模块文件

APP
终端输入:

gedit App.c

导入下面代码

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
/***************************************************************
使用方法     :./test /dev/chr    0 关闭
               ./test /dev/chr    1 打开    
***************************************************************/


/*
 * @description        : main主程序
 * @param - argc     : argv数组元素个数
 * @param - argv     : 具体参数
 * @return             : 0 成功;其他 失败
 */
int main(int argc, char *argv[])
{
    int fd, retvalue;
    char *filename;
    unsigned char databuf[1];
    
    if(argc != 3){
        printf("Error Usage!\r\n");
        return -1;
    }

    filename = argv[1];

    /* 打开驱动 */
    fd = open(filename, O_RDWR);
    if(fd < 0){
        printf("file %s open failed!\r\n", argv[1]);
        return -1;
    }

    databuf[0] = atoi(argv[2]);    /* 要执行的操作:打开或关闭 */

    /* 向/dev/chr文件写入数据 */
    retvalue = write(fd, databuf, sizeof(databuf));
    if(retvalue < 0){
        printf("Control Failed!\r\n");
        close(fd);
        return -1;
    }

    retvalue = close(fd); /* 关闭文件 */
    if(retvalue < 0){
        printf("file %s close failed!\r\n", argv[1]);
        return -1;
    }
    return 0;
}

终端输入,进行编译:arm-linux-gnueabihf-gcc App.c -o App

运行测试

将编译出来的 newchr.ko 和 App 这两个文件拷贝到 rootfs/lib/modules/4.1.15目录中,重启开发板,在串口工具中,进入到目录 lib/modules/4.1.15 中,输入如下命令加载 newchr.ko 驱动模块:

depmod //第一次加载驱动的时候需要运行此命令
./App /dev/newchr 1

卸载驱动输入如下命令即可:

rmmod newchrled.ko


标签:字符,return,框架,param,chr,源码,newchr,include,设备
来源: https://www.cnblogs.com/qqqq33/p/16619763.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有