第一个驱动程序

 2023-09-05 阅读 69 评论 0

摘要:文章目录功能源码Makefile测试挂载驱动创建设备结点测试卸载驱动程序 功能 通过在内存中开辟一块内存作为设备,编写一套针对这篇内存空间的驱动程序。实现对该片内存空间的系统调用(read,write,iotcl,open,lseek) 基于宋宝

文章目录

  • 功能
  • 源码
  • Makefile
  • 测试
    • 挂载驱动
    • 创建设备结点
    • 测试
    • 卸载驱动程序


功能

通过在内存中开辟一块内存作为设备,编写一套针对这篇内存空间的驱动程序。实现对该片内存空间的系统调用(read,write,iotcl,open,lseek)

基于宋宝华版 Linux设备驱动开发详解

源码

#include<linux/module.h>
#include<linux/fs.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<linux/slab.h>
#include <linux/uaccess.h>#define GLOBALMEM_SIZE 0x1000 //设置设备内存大小
#define MEM_ClEAR 0x1   //设置inctol命令
#define GLOBALEMEM_MAJOR 230  //设置主设备号//使用一个静态变量来存储主设备号吗,用于后面传参
static int  globalmem_major=GLOBALEMEM_MAJOR;
module_param(globalmem_major,int,S_IRUGO);
/*驱动传参的宏,即类似与main函数中的argv,可以传参给内核
*globalmem_major:表示设备在sysf文件系统中显示的名子,int是参数globalmem_major的类型
*S_IRUGO:表示的是该参数在文件系统中的访问权限 S_IRUGO:表示用户,组和其他人都可读。
*意思就是这个设备的名字,在文件系统上是不可以被修改的且类型为int
*//*定义设备结构*/
struct globalmem_dev{struct cdev cdev;  //设备结构体unsigned char mem[GLOBALMEM_SIZE];//该设备占有的大小
};//定义设备结构体的全局变量
struct globalmem_dev *globalmem_devp;//开始实现文件操作结构体中的函数,将文件操作结构体实例化
static int globalmem_open(struct inode *inode,struct file *filp)
{/*struct inode 结构体表示打开文件的inode索引结点,每个文件的索引结点都是唯一的*struct file 结构体表示一个打开的文件在内核中所对应的struct file结构体。包含了*文件偏移指针,文件访问权限等信息。*/filp->private_data=globalmem_devp;/*private_data的作用是用来保存设备结构体的地址,方便在read,write等驱动函数中*被传递和调用结构体的成员*/return 0;
}static int globalmem_release(struct inode *inode,struct file *filp)
{return 0;
}static long globalmem_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
{struct globalmem_dev *devp=filp->private_data;//指向定义的全局设备结构体变量switch(cmd){case MEM_ClEAR:memset(devp->mem,0,GLOBALMEM_SIZE);printk(KERN_INFO "globalmem is set to zero\n");break;default:return -EINVAL;//无效参数}return 0;
}static ssize_t globalmem_read(struct file *filp,char __user* buf,size_t size,loff_t *ppos)
{/*filep:打开文件在内核对应的文件结构体指针,*buf:表示用户空间的内存地址*size:表示要读取的空间大小*opps:表示文件偏移量*/unsigned long p=*ppos;unsigned int count=size;int ret=0;//返回值struct globalmem_dev *dp=filp->private_data;if(p>=GLOBALMEM_SIZE){return 0;}if(count>GLOBALMEM_SIZE-p){count=GLOBALMEM_SIZE-p;}if(copy_to_user(buf,dp->mem+p, count)){return -EINVAL;}else{*ppos+=count;ret=count;printk(KERN_INFO "read %u bytes(s) from %lu\n",count,p);}return ret;
}static ssize_t globalmem_write(struct file* filp,const char __user *buf,size_t size,loff_t *ppos)
{unsigned long p=*ppos;unsigned int count=size;int ret=0;struct globalmem_dev* dp=filp->private_data;if(p>=GLOBALMEM_SIZE){return 0;}if(count>GLOBALMEM_SIZE-p){count=GLOBALMEM_SIZE-p;}if(copy_from_user(dp->mem+p, buf, count)){return -EINVAL;}else{*ppos+=count;ret=count;printk(KERN_INFO"written %u bytes(s) from %lu\n",count,p);}return ret;
}static loff_t globalmem_llseek(struct file *filp,loff_t offset,int orig)
{loff_t ret=0;switch(orig){case 0:if(offset<0){return -EINVAL;break;}if((unsigned int)offset>GLOBALMEM_SIZE){return -EINVAL;break;}filp->f_pos=(unsigned int)offset; //设置文件结构体的文件指针偏移ret=filp->f_pos;break;case 1:if((filp->f_pos + offset)>GLOBALMEM_SIZE){return -EINVAL;break;}if(filp->f_pos+offset<0){return -EINVAL;break;}filp->f_pos+=(unsigned int)offset;ret=filp->f_pos;break;default:ret=-EINVAL;break;}return ret;
}static const struct file_operations globalmem_fops={.owner = THIS_MODULE,.llseek = globalmem_llseek,.read = globalmem_read,.release = globalmem_release,.write = globalmem_write,.open = globalmem_open,.unlocked_ioctl = globalmem_ioctl,
};//绑定文件操作结构体和设备,设置设备结构体
static void globalmem_setup_cdev(struct globalmem_dev *dev,int index)
{/*index:表示次设备号*/int err,devno=MKDEV(globalmem_major, index);//获取设备号cdev_init(&dev->cdev,&globalmem_fops);//将文件操作结构体地址赋值给cedv->opsdev->cdev.owner=THIS_MODULE;err=cdev_add(&dev->cdev,devno,1);/*用于向linux内核系统中添加一个新的cdev结构体所描述的字符设备,并且使立即生效* devno表示设备号,1表示想要注册的设备号范围,为cdev->count赋值,*其实就是该类设备的数量*/if(err){printk(KERN_NOTICE "Error %d adding globalmem %d",err,index);}
}//向Linux内核注册设备,获取设备号
static int __init globalmem_init(void)
{int ret;dev_t devno=MKDEV(globalmem_major, 0);if(globalmem_major){ret=register_chrdev_region(devno, 1, "globalmem");}else{ret=alloc_chrdev_region(&devno, 0, 1, "globalmem");globalmem_major=MAJOR(devno);//获取主设备号}if(ret<0){return ret;}globalmem_devp = kzalloc(sizeof(struct globalmem_dev), GFP_KERNEL);if(!globalmem_devp){ret=-EINVAL;goto fail_malloc;}globalmem_setup_cdev(globalmem_devp,0);return 0;fail_malloc:unregister_chrdev_region(devno, 1);return ret;
}module_init(globalmem_init);//使系统动态加载该模块static void __exit globalmem_exit(void)
{cdev_del(&globalmem_devp->cdev);//卸载设备kfree(globalmem_devp);unregister_chrdev_region(MKDEV(globalmem_major, 0), 1);
}module_exit(globalmem_exit);MODULE_AUTHOR("jacky");  //作者
MODULE_LICENSE("GPL v2"); //遵守GPL v2协议共享开源

注意:所有的详细思路都在注释里面,对于不清楚的点欢迎评论留言或者网上搜索

Makefile

KVERS = $(shell uname -r)
# Kernel modules
obj-m += globalmem_jacky.o
# Specify flags for the module compilation.
#EXTRA_CFLAGS=-g -O0build: kernel_moduleskernel_modules:make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modulesclean:make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean

测试

挂载驱动

sudo insmod globalmem_jacky.ko

查看挂载情况:
cat /proc/devices

在这里插入图片描述

创建设备结点

mknod /dev/globalmem c 230 0

查看
在这里插入图片描述

测试

echo "hello jacky" > /dev/globalmem

cat globalmem

在这里插入图片描述

卸载驱动程序

rmmod globalmem_jacky

在这里插入图片描述

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://808629.com/555.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 86后生记录生活 Inc. 保留所有权利。

底部版权信息