linux 字符驱动完整框架(poll,async,waitqueue,nonblock等)

2023-05-16

一个linux内核驱动的完整框架,包含了能遇到的大部分内容,例如timer, poll, async, waitqueue, nonblock等等,不过基本上没啥大用,就是用来熟悉基础的,正常的驱动不太会这么简单。

后面还包含了makefile,直接在ubuntu上都可以编译运行。

另外还有应用程序在加上了,直接在ubuntu上gcc编译即可。


#include "linux/init.h"
#include "linux/kernel.h"
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kfifo.h>
#include <asm/io.h>
#include <asm/uaccess.h>
//#include <asm/mach/map.h>
#include "linux/poll.h"
#include "linux/timer.h"
#include "linux/fcntl.h"
#include "linux/fs.h"
#include "linux/syscalls.h"

#define VSER_DEV_NAME "vser"
//#define VSER_MAJOR 255
#define VSER_MAJOR 0
#define VSER_MINOR 0
#define VSER_DEV_CNT 1

struct vser_t
{
    struct cdev vsdev;
    dev_t dev;
    int major;
    int minor;
    struct device *device;
    struct class  *class;
    wait_queue_head_t rwqh;
    wait_queue_head_t wwqh;
    struct timer_list timer;
    struct fasync_struct *fasync;
}
g_vser_dev;

DEFINE_KFIFO(vsfifo,char,32);


static int vser_fasync(int fd, struct file *filp, int on)
{
    struct vser_t *pvser_dev=(struct vser_t*)filp->private_data;

    return fasync_helper(fd, filp, on, &pvser_dev->fasync);
}


static int vser_open(struct inode *inode,struct file *filp)
{
    printk("vser_open\n");

    filp->private_data=&g_vser_dev;

    return 0;
}


static int vser_release(struct inode *inode,struct file *filp)
{
    int ret;
   
    ret=vser_fasync(-1, filp, 0);
    printk("vser_release %d\n", ret);

    return ret;
}

static ssize_t vser_read(struct file *filp,char __user *buf,size_t count, loff_t *pos)
{
    unsigned int copied = 0;
    int ret;
    struct vser_t *pvser_dev=(struct vser_t*)filp->private_data;

    //non-blocking read process
    if(kfifo_is_empty(&vsfifo))
    {
        printk("kfifo is empty\n");
        if(filp->f_flags & O_NONBLOCK)
        {
            printk("nonblock return\n");
            return -EAGAIN;
        }  
        printk("blocking read\n");
    }

    if(wait_event_interruptible(pvser_dev->rwqh, !kfifo_is_empty(&vsfifo)))
    {
        printk("kfifo is empty, wait_queue interrupted by signal\n");
        return -ERESTARTSYS;
    }
    
    ret=kfifo_to_user(&vsfifo,buf,count,&copied);
    printk("real read %d %d\n", ret, copied);

    if(!kfifo_is_full(&vsfifo)) {
        printk("kfifo is write available, wakeup wwqh\n");
        wake_up_interruptible(&pvser_dev->wwqh);
    }

    return ret==0 ? copied: ret;
}

static ssize_t vser_write(struct file *filp,const char __user *buf,size_t count,loff_t *pos)
{
    unsigned int copied = 0;
    int ret;
    struct vser_t *pvser_dev=(struct vser_t*)filp->private_data;

    //nonblocking write process
    if(kfifo_is_full(&vsfifo))
    {
        printk("kfifo is full\n");
        if(filp->f_flags & O_NONBLOCK)
        {
            printk("nonblock write return\n");
            return -EAGAIN;
        }
        printk("blocking write\n");
    }

    if(wait_event_interruptible(pvser_dev->wwqh, !kfifo_is_full(&vsfifo)))
    {
        printk("kfifo wwqh interrupted by signal\n");
        return -ERESTARTSYS;
    }

    ret=kfifo_from_user(&vsfifo,buf,count,&copied);
    printk("real write %d %d\n", ret, copied);

    if(!kfifo_is_empty(&vsfifo))
    {
        printk("wakeup rwqh in write\n");
        wake_up_interruptible(&pvser_dev->rwqh);
    }

    if(!kfifo_is_empty(&vsfifo))
    {
        kill_fasync(&g_vser_dev.fasync, SIGIO, POLL_IN);
        printk("kill_fasync for read\n");
    }

    return ret==0?copied:ret;
}


static unsigned int vser_poll(struct file *filp, struct poll_table_struct *p)
{
    int mask=0;
    struct vser_t *pvser_dev=(struct vser_t*)filp->private_data;

    poll_wait(filp, &pvser_dev->rwqh, p);
    poll_wait(filp, &pvser_dev->wwqh, p);

    if(!kfifo_is_empty(&vsfifo))
    {
        mask|=POLLIN|POLLRDNORM;
    }
    if(!kfifo_is_full(&vsfifo))
    {
        mask|=POLLOUT|POLLWRNORM;
    }

    return mask;
}

static struct file_operations vser_ops = {
    .owner = THIS_MODULE,
    .open = vser_open,
    .release = vser_release,
    .read = vser_read,
    .write = vser_write,
    .poll = vser_poll,
    .fasync = vser_fasync,
};


static void timer_callback(struct timer_list *timer)
{
    /*
    struct vser_t *pvser=(struct vser_t*)arg;
    static int i=0;
    printk("timer:%d\n", i++);
    mod_timer(&pvser->timer, jiffies + msecs_to_jiffies(5000));*/

    static int i=0;
    printk("timer:%d\n", i++);
    mod_timer(timer, jiffies + msecs_to_jiffies(5000));
}

extern long sys_chmod(const char __user *filename, umode_t mode);
#define filename "/dev/"VSER_DEV_NAME

static int __init vser_init(void)
{
    int ret;
    //int major,minor;

    if(VSER_MAJOR == 0)
    {
        ret=alloc_chrdev_region(&g_vser_dev.dev, VSER_MINOR, 1, VSER_DEV_NAME);
        if(ret)
        goto reg_err;
    }
    else
    {
        g_vser_dev.dev = MKDEV(VSER_MAJOR,VSER_MINOR);
        ret = register_chrdev_region(g_vser_dev.dev,VSER_DEV_CNT,VSER_DEV_NAME);
        if (ret)
        goto reg_err;
    }

    //dev_t
    g_vser_dev.major=MAJOR(g_vser_dev.dev);
    g_vser_dev.minor=MINOR(g_vser_dev.dev);
    printk("major: %d minor: %d\n",g_vser_dev.major,g_vser_dev.minor);

    //cdev
    cdev_init(&g_vser_dev.vsdev,&vser_ops);
    g_vser_dev.vsdev.owner = THIS_MODULE;
    ret = cdev_add(&g_vser_dev.vsdev,g_vser_dev.dev,VSER_DEV_CNT);
    if (ret)
    goto add_err;

    //class
    g_vser_dev.class = class_create(THIS_MODULE, VSER_DEV_NAME); 
    if (IS_ERR(g_vser_dev.class)) { 
        ret=PTR_ERR(g_vser_dev.class); 
        goto cls_err;
    }

    //device
    g_vser_dev.device = device_create(g_vser_dev.class, NULL, g_vser_dev.dev, NULL, VSER_DEV_NAME); 
    if (IS_ERR(g_vser_dev.device)) { 
        ret=PTR_ERR(g_vser_dev.device);
        goto dev_err;
    }

    //ret=sys_chmod(filename, 0777);    
    //printk("chmod %s ret=%d\n", filename, ret);

    //wait_queue
    init_waitqueue_head(&g_vser_dev.rwqh);
    init_waitqueue_head(&g_vser_dev.wwqh);

    //timer
    /*
    init_timer(&g_vser_dev.timer);
    g_vser_dev.timer.function=timer_callback;
    g_vser_dev.timer.data=(unsigned long)&g_vser_dev;*/
    timer_setup(&g_vser_dev.timer, timer_callback, 0);
    mod_timer(&g_vser_dev.timer, jiffies + msecs_to_jiffies(5000));
    printk("start timer\n");

    printk("vser_init\n");
    return 0;

dev_err:
    class_destroy(g_vser_dev.class);
cls_err:
    cdev_del(&g_vser_dev.vsdev);
add_err:
    unregister_chrdev_region(g_vser_dev.dev,VSER_DEV_CNT);
reg_err:
    printk("vser_init failed\n");
    return ret;
}


static void __exit vser_exit(void)
{
    //dev_t dev;
    //dev = MKDEV(VSER_MAJOR,VSER_MINOR);
    int major,minor;
    major=MAJOR(g_vser_dev.dev);
    minor=MINOR(g_vser_dev.dev);
    printk("major: %d minor: %d\n",major,minor);

    cdev_del(&g_vser_dev.vsdev);
    unregister_chrdev_region(g_vser_dev.dev,VSER_DEV_CNT);

    device_destroy(g_vser_dev.class, g_vser_dev.dev); 
    class_destroy(g_vser_dev.class);

    del_timer_sync(&g_vser_dev.timer);

    printk("vser exit\n");
}


module_init(vser_init);
module_exit(vser_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("oushaojun");
MODULE_DESCRIPTION("A simple character device driver");
MODULE_ALIAS("virtual-serial");







makefile内容:


ifeq ($(KERNELRELEASE),)
	ifeq ($(ARCH),arm)
		KERNELDIR ?= /home/farsight/fs4412/linux-3.14.25-fs4412
		ROOTFS ?= /nfs/rootfs
	else
		KERNELDIR ?= /lib/modules/$(shell uname -r)/build
	endif
	PWD := $(shell pwd)

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) INSTALL_MOD_PATH=$(ROOTFS) modules_install

clean:
	rm -rf *.o *.ko.*.cmd *.mod.* modules.order Module.symvers.tmp_versi ons
else
	obj-m := vser.o
endif

应用程序,用来测试驱动是否工作正常:


#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "sys/mman.h"
#include "fcntl.h"
#include "errno.h"
#include "unistd.h"
#include "poll.h"
#include "sys/select.h"
#include "sys/time.h"
#include "linux/ioctl.h"
#include "signal.h"

static int fd=0;


static void sigio_signal_handler(int signum)
{
    int err=0;
    __uint8_t buffer[100]={0};

    err=read(fd, buffer, sizeof(buffer));
    if(err<0){
        perror("async read");
    }
    else{
        printf("async read success %d %s\n", err, (char*)buffer);
    }
}

int main(int argc, char **argv)
{
    int ret;
    int flags;
    char buffer[1024];

    fd=open("/dev/vser", O_RDWR/*|O_NONBLOCK*/);
    if(fd==-1)
    {
        perror("open");
        return -1;
    }
    else
    {
        printf("open ok\n");
    }

    //sigio callback
    signal(SIGIO, sigio_signal_handler);
    //set own
    fcntl(fd, F_SETOWN, getpid());
    //enable async
    flags=fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, flags|FASYNC);

    while(1) {
        sleep(1);
    }

    close(fd);
    printf("close\n");

    exit(0);
}











本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux 字符驱动完整框架(poll,async,waitqueue,nonblock等) 的相关文章

  • Visual Studio 2022 搭建GLFW OpenGL开发环境

    最近工作需要 需要写一个全景的视频播放器 网上搜了下大概解决方案是 ffmpeg 43 opengl b站有很多视频 按照视频 搭建了OpenGL的开发环境 先去GLFW的网站下载 windows平台的库文件 为什么使用GLFW 因为GLF
  • Pixhawk原生固件PX4之自定义MAVLink消息

    欢迎交流 个人 Gitter 交流平台 xff0c 点击直达 xff1a 本着想在PX4基础上加点什么东西的我又开始折腾了 xff0c 先尝试用串口加传感器通过QGC查看 xff0c 要是能在原固件上加点内容就棒哉了 先立Flag 自定义u
  • Pixhawk原生固件PX4之MAVLink协议解析

    欢迎交流 个人 Gitter 交流平台 xff0c 点击直达 xff1a PX4 对Mavlink 协议提供了良好的原生支持 该协议既可以用于地面站 Ground ControlStation GCS 对无人机 UAV 的控制 xff0c
  • GPS和RTK的基本知识

    RTK的基本原理介绍 xff0c RTK一般由基站 移动站以及数据链路组成 下文摘自天宝 Trimble 官网 原文链接 xff1a http www trimble com OEM ReceiverHelp V4 44 en What i
  • freeRTOS系统栈与任务栈

    中断过来之后 xff0c 由任务栈切换到main stack xff08 系统栈 xff09 任务栈保存 系统栈的地址范围为0xfede8000 4K xff0c 向下生长 xff0c 所以按照ld的定义 xff0c 0xfede9000
  • ROS下上位机和stm32单片机通信

    1 需要实例化串口节点建立监听者listener和发布之publisher 2 上位机通过游戏手柄发布自定义消息类型control int64 mode 手柄模式切换 int64 lidar 雷达数据 int64 gamePad x 控制前
  • 奇偶校验码

    偶校验为例 xff1a 例图中 xff0c 下划线为校验位 xff0c 其余为信息位 检错步骤如下 xff1a 1 根据信息位算出校验位 xff08 通过异或运算 xff1a 相同为0 xff0c 不同为1 xff09 xff1a 得出校验
  • C++中#define和const的区别

    一 define是预处理指令 xff08 pre processor directive xff09 而const是关键字 define用于给一些值定义名称 字符串 xff0c 这样定义的字符串在C C 43 43 中称为宏定义 xff0c
  • select函数实现tcp服务器与客户端随时收发

    服务器 include lt stdio h gt include lt sys types h gt include lt sys socket h gt include lt arpa inet h gt include lt neti
  • STM32F10X库函数逻辑

    define PERIPH BASE unsigned int 0x40000000 定义外围总线基地址 define APB1PERIPH BASE PERIPH BASE xff09 APB1总线开始与外围总线基地址 define AP
  • STM32F10x外部中断EXTI

    目录 一 EXTI是什么 xff1f 二 使用方法 1 功能框图及寄存器 2 库函数编程 总结 提示 xff1a 以下是本篇文章正文内容 xff0c 下面案例可供参考 一 EXTI是什么 xff1f EXTI External interr
  • QT + OpenGL + FFmpeg写的一个全景视频播放器

    临时被分配了一个任务 写一个C 43 43 版本的全景视频播放器 网上搜了搜 基于前辈的基础上 写的差不多了 测试视频源是用ffmpeg拉RTSP的流 最终是要嵌入到别的一个视频播放器模块 所以解码这块我不用太关注 只要实现渲染就可以了 效
  • 15 侥幸:在随机性面前处变不惊

    引言 之前讲过的量化思维 xff0c 已经有概率思维的影子了 xff0c 开始学着用数字提高认识世界的分辨率 现在将继续加深对概率的理解 xff0c 来探讨随机性 它解决的人生难题是 xff1a 我们时常因为心存侥幸而失败 在随机的世界里
  • 【无人驾驶】自动驾驶领域有哪些岗位可选?

    导读 想要进入自动驾驶这个领域 xff0c 便首先去调查了下这个领域的岗位 xff0c 希冀能从中找出自己最感兴趣且匹配度也比较高的方向 废话不多说 xff0c 见下 下图为自动驾驶方向的所有岗位 xff0c 总量的来说 xff0c 方向可
  • 01_搭建百度apollo环境实操可用

    搭建百度apollo环境 0 前言1 目标2 方法3 Apollo环境搭建3 1 CPU版3 1 1前置依赖硬件条件3 2 GPU版前置依赖软件1 安装 Ubuntu 18 042 安装 GIT3 安装 Docker 引擎 3 1 3克隆
  • 02_两小时了解自动驾驶

    02 两小时了解自动驾驶 目标你将学到什么什么是无人驾驶无人驾驶的运作方式参考车辆与硬件平台开源软件架构仿真环境地图简介高精地图与传统地图地图与定位 感知 规划的关系高精地图定位简介GNSS RTK惯性导航激光雷达定位视觉定位融合定位感知简
  • PCA9555的使用个人总结

    1 德州仪器PCA9555 简化PCB布线 xff0c 对处理器有限的IO口进行补充 2 带中断 xff0c 不带重启 3 24 引脚的CMOS器件 xff0c 提供I2C SMBus16位通用并行 xff08 GPIO xff09 的扩展
  • stm32掉电前的数据存储到flash

    对FLASH 的操作 FLASh 必须是先擦 后 写 下面的函数是分析案例 void FLASH WriteByte u32 addr u16 flashdata1 FLASH Status FLASHstatus 61 FLASH COM
  • SPI对外部w25Q64的读写

    SPI 1 SPI是串行外围设备接口 SPI的接口主要应用在EEPROM xff0c FLASH xff0c 实时时钟 xff0c AD 转换器 xff0c 还有数字信号处理器和数字信号 解码器之间 2 SPI xff0c 是一种高速的 x
  • Qt实战开发-仪表盘制作

    仪表盘制作 相关的基础知识 QPainter用来执行具体的绘图相关的操作 xff0c 用来画点 xff0c 线 xff0c 填充 xff0c 变换 xff0c alpha 阿尔法通道 xff08 透明度 xff09 Appha的值越大 xf

随机推荐

  • Qt实战开发-数字软键盘

    开发的思路 布局键盘界面每一个button对应一个槽函数把输入的字符返回到点击处的文本编辑框 开发过程首先定义功能button xff0c 在头文件中定义 QString getText QPushButton span class hlj
  • Qt图形视图框架封装-拿来就用

    简述 图形视图 xff08 Graphics View xff09 提供了一个平台 xff0c 用于大量自定义2D图元的管理与交互 xff0c 并提供了一个视图部件 xff08 view widget xff09 来显示可以缩放和旋转的图元
  • 带参数的宏定义

    带参数的宏定义的一般形式如下 xff1a define lt 宏名 gt xff08 lt 参数表 gt xff09 lt 宏体 gt 其中 xff0c lt 宏名 gt 是一个标识符 xff0c lt 参数表 gt 中的参数可以是一个 x
  • QT 视图(View)/委托(Delegate)/ 模型(Model)/项(Item) 之间的关系

    引言 在学习Qt 中此部分的使用是界面自定义出美观的重要部分 xff0c 熟练的使用 xff0c 可以制作出很好的效果 xff0c 在此梳理一下他们的关系 Model View xff08 模型 视图 xff09 视图 xff08 View
  • char(数字) 转换 int

    刷题遇到一个考点是 char型数字 转 int 进行计算的问题 一看就会 xff0c 一做就错 xff0c 显然是在这里的认识薄弱了 将一番搜索的结果记录下来 xff0c 以备再忘来打脸 char的定义参考 xff1a Java基本数据类型
  • ROS的ros_canopen调试(1)

    Ros canopen是ros支持can通信的package 链接 xff1a http wiki ros org ros canopen distro 61 indigo Ros canopen包结构如下 SocketCAN 是一组开源的
  • linux gcc编译错误:undefined reference to `aio_error‘解决方法

    include lt aio h gt void aiow completion handler sigval t sigval int ret struct aiocb req req 61 struct aiocb sigval siv
  • wait_event_interruptible_locked的使用方法

    wait event interruptible locked interface New wait event interruptible exclusive locked irq macros added They work just
  • printk在应用层的设置方式及读取内核打印信息的方法

    如果 printk 中没有加调试级别 xff0c 则使用默认的调试级别 注意 xff0c 调试级别和格式化字符串之间没有逗号 当前控制台的各打印级别可以通过下面的命令来查看 cat proc sys kernel printk 4 4 1
  • qt编译Qxlsx模块及安装

    主要参考如下地址 xff1a https www icode9 com content 4 715555 html 注意的点 xff1a 1 把下载的代码复制到根目录下 xff0c 路径不要有什么空格啥的 xff0c 否则你会发现perl老
  • GitLab 使用Tortoisegit询问“git@192.168.1.18‘s password“问题解决

    现象如下 xff1a 使用TortoiseGit去拉本地GitLab上建立的项目时 xff0c 一直提示输入密码 xff08 如下图 xff09 xff0c 这个密码又没有指定用户名 xff0c 就算你输入你用户名的密码也是失败 但是很诡异
  • 二代身份证读写器原理及开发

    身份证读写器的作用就是从身份证中读取身份信息 xff08 例如姓名 民族 身份证号等 xff09 xff0c 然后显示或者传输给其他模块使用 功能框架如下 xff1a 功能框图说明 xff1a 1 业务模块 负责向安全模块发送命令 xff0
  • JLINK V10 V11固件修复

    先去我的资源里面下载bootloader和app固件文件 步骤 xff1a 1 PC上安装JLINK V4 9工具 xff08 貌似不能使用太高版本的工具 xff0c 否则有问题 xff09 2 打开j flash v4 9 xff0c 新
  • ROS | 话题通信的编程实现

    ROS 话题通信的编程实现 1 创建功能包2 节点编程与消息定义2 1 案例说明2 2 话题消息的定义2 3 创建 cpp文件2 4 话题发布者编程2 5 话题订阅者编程 3 配置与编译3 1 在CMaKeLists txt中添加编译选项3
  • Cocos2dx 3.0配置环境

    3 15 cocos2dx 3 0rc0 终于放出来了 在这里不得不吐槽一件事 xff0c 3 0版本从Alpha xff0c 到beta xff0c 再到rc xff0c 三个版本竟然都有各自创建项目的方式 xff0c 这样真的不会被人打
  • linux 开机运行应用程序

    把运行应用程序的脚本放在 etc rc local里面 xff0c 如果没有 etc rc local xff0c 需要执行前面的3条指令新建这个文件 注意执行应用最好要在后台执行 xff08 后面加个 amp xff09 xff0c 否则
  • arm linux游戏手柄(joystick)驱动移植

    在arm linux中移植usb joystick驱动 xff0c 参考了如下经验 xff1a Linux系统中使用Xbox360手柄 知 行 博客园 cnblogs com 使用BlueZ连接蓝牙手柄 Dokin丶的博客 CSDN博客 蓝
  • linux ubuntu下网络调试助手(GUI)工具

    mNetAssist这个工具在ubuntu下可以运行 xff0c 是个带界面的tcp调试工具 1 UDP通讯 xff1b 2 可做 TCP客户端 xff1b 3 可做 TCP服务器 xff1b 4 可以 十六进制 传送接收数据 5 可以传送
  • fft的通俗解释

    FFT是离散傅立叶变换的快速算法 xff0c 可以将一个信号变换 到频域 有些信号在时域上是很难看出什么特征的 xff0c 但是如 果变换到频域之后 xff0c 就很容易看出特征了 这就是很多信号 分析采用FFT变换的原因 另外 xff0c
  • linux 字符驱动完整框架(poll,async,waitqueue,nonblock等)

    一个linux内核驱动的完整框架 xff0c 包含了能遇到的大部分内容 xff0c 例如timer poll async waitqueue nonblock等等 xff0c 不过基本上没啥大用 xff0c 就是用来熟悉基础的 xff0c