linux下的字符设备驱动

Linux字符设备驱动程序的一个简单示例

一.开发环境:

  机:VMWare--Fedora 9

开发板:友善之臂mini2440--256MB Nandflash 

编译器:arm-linux-gcc-4.3.2

 

二.驱动源码:

该源码很浅显易懂,非常适合初学者。

memdev.h#ifndef _MEMDEV_H_
#define _MEMDEV_H_#ifndef MEMDEV_MAJOR
#define MEMDEV_MAJOR 254   /*预设的mem的主设备号*/
#endif#ifndef MEMDEV_NR_DEVS
#define MEMDEV_NR_DEVS 2    /*设备数*/
#endif#ifndef MEMDEV_SIZE
#define MEMDEV_SIZE 4096
#endif/*mem设备描述结构体*/
struct mem_dev                                    
{                                                       char *data;                     unsignedlong size;      
};#endif /* _MEMDEV_H_ */
memdev.c#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>#include "memdev.h"static mem_major = MEMDEV_MAJOR;module_param(mem_major,int, S_IRUGO);struct mem_dev *mem_devp; /*设备结构体指针*/struct cdev cdev; /*文件打开函数*/
int mem_open(struct inode *inode, struct file *filp)
{struct mem_dev *dev;/*获取次设备号*/int num = MINOR(inode->i_rdev);if (num >= MEMDEV_NR_DEVS) return -ENODEV;dev = &mem_devp[num];/*将设备描述结构指针赋值给文件私有数据指针*/filp->private_data = dev; //方便以后对该指针的使用return 0;
}/*文件释放函数*/
int mem_release(struct inode *inode, struct file *filp)
{return 0;
}/*读函数*/
static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{unsignedlong p =  *ppos;unsignedint count = size;int ret = 0;struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*//*判断读位置是否有效*/if (p >= MEMDEV_SIZE) //超出读取范围,返回0表示读取不到数据return 0;if (count > MEMDEV_SIZE - p)count = MEMDEV_SIZE - p;/*读数据到用户空间*/if (copy_to_user(buf, (void*)(dev->data + p), count)){ret =  - EFAULT;}else{*ppos += count;ret = count;printk(KERN_INFO"read %d bytes(s) from %d\n", count, p);}return ret;
}/*写函数*/
static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{unsignedlong p =  *ppos;unsignedint count = size;int ret = 0;struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*//*分析和获取有效的写长度*/if (p >= MEMDEV_SIZE)return 0;if (count > MEMDEV_SIZE - p)count = MEMDEV_SIZE - p;/*从用户空间写入数据*/if (copy_from_user(dev->data + p, buf, count))ret =  - EFAULT;else{*ppos += count;ret = count;printk(KERN_INFO"written %d bytes(s) from %d\n", count, p);}return ret;
}/* seek文件定位函数 */
static loff_t mem_llseek(struct file *filp, loff_t offset, int whence)
{loff_t newpos;switch(whence) {case 0:/* SEEK_SET */newpos = offset;break;case 1:/* SEEK_CUR */newpos = filp->f_pos + offset;break;case 2:/* SEEK_END */newpos = MEMDEV_SIZE -1 + offset;break;default:/* can't happen */return -EINVAL;}if ((newpos<0) || (newpos>MEMDEV_SIZE))return -EINVAL;filp->f_pos = newpos;return newpos;}/*文件操作结构体*/
static const struct file_operations mem_fops =
{.owner = THIS_MODULE,.llseek = mem_llseek,.read = mem_read,.write = mem_write,.open = mem_open,.release = mem_release,
};/*设备驱动模块加载函数*/
static int memdev_init(void)
{int result;int i;dev_t devno = MKDEV(mem_major, 0);/* 静态申请设备号*/if (mem_major)result = register_chrdev_region(devno, 2, "memdev");else  /* 动态分配设备号 */{result = alloc_chrdev_region(&devno, 0, 2, "memdev");mem_major = MAJOR(devno);} if (result < 0)return result;/*初始化cdev结构*/cdev_init(&cdev, &mem_fops);//使cdev与mem_fops联系起来cdev.owner = THIS_MODULE;//owner成员表示谁拥有这个驱动程序,使“内核引用模块计数”加1;THIS_MODULE表示现在这个模块被内核使用,这是内核定义的一个宏cdev.ops = &mem_fops;/* 注册字符设备 */cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);/* 为设备描述结构分配内存*/mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL);//目前为止我们始终用GFP_KERNELif (!mem_devp)   /*申请失败*/{result =  - ENOMEM;goto fail_malloc;}memset(mem_devp, 0, sizeof(struct mem_dev));/*为设备分配内存*/for (i=0; i < MEMDEV_NR_DEVS; i++) {mem_devp[i].size = MEMDEV_SIZE;mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);//分配出来的地址存在此memset(mem_devp[i].data, 0, MEMDEV_SIZE);}return 0;fail_malloc:unregister_chrdev_region(devno, 1);return result;
}/*模块卸载函数*/
static void memdev_exit(void)
{cdev_del(&cdev);  /*注销设备*/kfree(mem_devp);    /*释放设备结构体内存*/unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*释放设备号*/
}MODULE_LICENSE("GPL");module_init(memdev_init);
module_exit(memdev_exit);


三.编译源码

  1.把这两个驱动源文件复制进内核linux-2.6.32.2/drivers/char目录下

  2.修改该目录下的Kconfig文件添加

?
1
2
config MEMDEV_DRIVER
        tristate"memdev driver"

  3.修改该目录下的Makefile文件,依葫芦画瓢,添加

    obj-$(CONFIG_HELLO_DRIVER)   +=  memdev.o

    至此,文件以添加进内核。

  4.到Linux-2.6.32.2源代码根目录下执行

    make menuconfig

    在字符设备中找到菜单项“memdev driver“,就是我们刚才添加的驱动模块,选为M

  5.在Linux-2.6.32.2源代码根目录下执行

    make modules

    就能生成内核模块文件memdev.ko

  至此,我们已经完成驱动模块的编译。

四.把驱动下载到开发版并安装

  1.把memdev.ko 下载到开发板,并移到/lib/modules/2.6.29.4-FriendlyARM目录下,然后在开发板中执行 

  #modprobe memdev

  (注意用modprobe命令不需要.ko后缀,rmmod也是如此,这个经常会忘记)

  当然你也可以用insmod命令:insmod memdev.ko

  2.创建设备文件节的

    #mknod  /dev/memdev0  c  254  0

五.测试

测试代码如下:

app_mem.c#include <stdio.h>int main()
{FILE *fp0 = NULL;char Buf[4096];/*初始化Buf*/strcpy(Buf,"Mem is char dev!");printf("BUF: %s\n",Buf);/*打开设备文件*/fp0 = fopen("/dev/memdev0","r+");if (fp0 == NULL){printf("Open Memdev0 Error!\n");return -1;}/*写入设备*/fwrite(Buf,sizeof(Buf), 1, fp0);/*重新定位文件位置(思考没有该指令,会有何后果)*/fseek(fp0,0,SEEK_SET);/*清除Buf*/strcpy(Buf,"Buf is NULL!");printf("BUF: %s\n",Buf);/*读出设备*/fread(Buf,sizeof(Buf), 1, fp0);/*检测结果*/printf("BUF: %s\n",Buf);return 0;  }


把程序交叉编译后传到板子上执行

程序输出结果如下:

  

结果和预想的一样。

 

 

六.总结

  我原本并没打算把他下载到开发板上运行,以为在虚拟机上就可以运行了,但是insmod的时候老是出现一个错误

insmod error inserting 'memdev.ko': -1 Invalid module format

  后来在网上查了一些资料,这个错误可能是因为内核代码树与内核不一样,我用uname -a 查看了一下,发现我的内核版本是2.6.25-14。另外我用file命令查看了一下生成的memdev.ko 文件,发现是编译成了ARM平台上的文件。我的开发板的内核是和内核代码树一样的,于是还是下载到了板子上实验,在板子上没有出现刚才的错误。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/469848.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

logistic模型原理与推导过程分析(3)

附录&#xff1a;迭代公式向量化 θ相关的迭代公式为&#xff1a; ​ 如果按照此公式操作的话&#xff0c;每计算一个θ需要循环m次。为此&#xff0c;我们需要将迭代公式进行向量化。 首先我们将样本矩阵表示如下&#xff1a; 将要求的θ也表示成矩阵的形式&#xff1a; 将x…

计算机表示法是知识 表示法么,计算机三级考试关于IP地址知识点

计算机三级考试关于IP地址知识点IP地址是IP协议提供的一种统一的地址格式&#xff0c;它为互联网上的每一个网络和每一台主机分配一个逻辑地址&#xff0c;以此来屏蔽物理地址的差异&#xff0c;同时也是计算机三级考试的重要内容&#xff0c;小编整理了相关知识点&#xff0c;…

VMware 下Linux无法上网 新增支持WIFI方式 无线连接

试过很管用、如果宿主机可以上网、不管是有线还是无线、只在在连接网络那里是.net的方式就可以正常上网的 ADSL-VMware 共享上网 单机环境&#xff0c;ADSL拨号上网&#xff0c;安装VMware后&#xff0c;客户机如何与宿主机共享上网&#xff1f;网友经常问这个问题&#xff0…

Http学习笔记

在 MIME 扩展中会使用一种称为多部分对象集合&#xff08;Multipart&#xff09;的方法&#xff0c;来容纳多份不同类型的数据。包含的对象如下&#xff1a; form-data在 Web 表单文件上传时使用。byteranges状态码 206&#xff08;Partial Content&#xff0c;部分内容&#x…

监督学习与无监督学习

监督学习 用一个例子介绍什么是监督学习把正式的定义放在后面介绍。 假如说你想预测房价。前阵子&#xff0c;一个学生从波特兰俄勒冈州的研究所收集了一些房价的数据。你把这些数据画出来&#xff0c;看起来是这个样子&#xff1a; 横轴表示房子的面积&#xff0c;单位是平…

html css配色方案,链接css不同的配色方案问题

为什么导航链接采用正常链接的风格&#xff1f;这是一个基本的导航菜单&#xff1a;Home |Autobelettering |Reclame |Prints |Textiel |Ontwerpen |Aanleveren |Contact这是CSS/* Normal links */a {font-size: 12px;color: #DC342F;}a:link {text-decoration: none;color: #D…

嵌入式linux文件系统

嵌入式linux文件系统 操作系统中最重要的莫过于“进程管理”和“文件系统”两大部分,而linux从一开始就把外部设备当成是“文件”处理。从某种意义来讲,凡是能够产生或接受消息的都是“文件” 以下是几种文件的含义 1、一种特定的文件格式,如FAE32,EXT2,EXT3等; 2、按特定的…

【CodeVS】1023 GPA计算

1023 GPA计算 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 青铜 Bronze 题目描述 Description 小松终于步入了大学的殿堂&#xff0c;带着兴奋和憧憬&#xff0c;他参加了信息科学技术学院的新生大会。会上&#xff0c;院长梅教授给大家介绍了在大学中的成绩计算方式&…

单变量与多变量线性回归(Linear Regression with One Variable)

2.1 线性回归算法模型表示 让我们通过一个例子来开始&#xff1a;这个例子是预测住房价格的&#xff0c;我们要使用一个数据集&#xff0c;数据集包含俄勒冈州波特兰市的住房价格。在这里&#xff0c;我要根据不同房屋尺寸所售出的价格&#xff0c;画出我的数据集。比方说&…

SPI、I2C、UART三种串行总线的原理、区别及应用

SPI协议解析&#xff0c;链接如下 https://blog.csdn.net/weiqifa0/article/details/82765892 I2C协议解析&#xff0c;链接如下 https://blog.csdn.net/weiqifa0/article/details/82765349 串口UART串行总线协议 https://blog.csdn.net/weiqifa0/article/details/8269988…

计算机网络课制作双绞线实验,《计算机网络》课程实验——网线制作.pdf

第1 章 网线的制作和应用第1 章 网线的制作和应用实验1 直连双绞线的制作和应用双绞线一般分为屏蔽双绞线(STP)和非屏蔽双绞线(UTP )两类。如果没有特殊要求&#xff0c;在计算机网络中一般使用非屏蔽双绞线&#xff0c;所以本节主要以非屏蔽双绞线为例进行介绍。1.1.1 实验概述…

Python网络数据采集系列-------概述

这是一个正在准备中的系列文章&#xff0c;主要参考的是《Web Scraping with Python_Collecting Data from the Modern Web-OReilly(2015)》。这是一本关于网络数据采集&#xff08;俗称的爬虫&#xff09;的书&#xff0c;由浅入深&#xff0c;比较适合入门。在学习过程中也肯…

AttributeError: ‘list‘ object has no attribute ‘ndim‘

错误&#xff1a; AttributeError Traceback (most recent call last) <ipython-input-6-9b77ac20aa23> in <module>()1 # Print the images dimensions ----> 2 print(images.ndim)3 4 # Print the number of imagess elements5 p…

驱动程序操作IO口API函数

驱动程序总会操作一些CPU的IO口下面列一些常用函数 int check_region(unsigned int from,unsigned int extent); 这个函数查看系统的I/O表,看是否有别的驱动程序占用某一段的I/o口 参数1:端口基地址 参数2:端口占用范围 返回:0为没有占用 非0 为已经有驱动占用 void re…

网络教育计算机 判断,北京师范大学网络教育计算机作业1、4、8

计算机11.自计算机问世至今已经经历了4个时代&#xff0c;划分时代的主要依据是计算机的__D__。A规模 B功能 C性能 D构成元件2.世界上第一台电子数字计算机采用的逻辑元件是__D__。A大规模集成电路 B集成电路 C晶体管 D电子管3.早期的计算机体积大、耗能高、速度慢&#xff0c;…

ImportError: DLL load failed while importing _ssl: 找不到指定的模块。

解决方案一&#xff08;自己的方法&#xff09;&#xff1a; 找到Anaconda3\pkgs\python-3.8.12-h900ac77_2_cpython\DLLs下的_ssl.pyd文件&#xff0c;查阅在该环境上安装的python版本号&#xff0c;下载python寻找对应的_ssl.pyd覆盖到上述目录中&#xff0c;即可解决问题。解…

Python核心编程-细节

直接从六张开始看看书里有什么。 cmp()  len()  max() and min()  sorted() and reversed()  enumerate() and zip()  sum()  list() and tuple()  dir()方法来得到它所有的方法和属性    extend() and append() extend()接受一个列表的内容&#xff0c;然…

查看linux IO地址范围

端口统一编址原理是把IO控制器中的端口地址归入存储器寻址空间范围内&#xff0c;因此这种编址方式也成为存储器映像编址。CPU访问一个端口的操作与访问内存的操作一样&#xff0c;也使用访问内存的指令。 在linux下可以查看设置的IO地址范围

命令行import torch正常,但pycharm中显示“No module named torch”解决方法

pytorch第一步配置环境测试运行就遇到了如题目所述问题&#xff0c;问题如图&#xff1a; 步骤一 首先检查环境导入是否有问题&#xff0c;工具栏file–settings–project–project interpreter&#xff0c;在这里添加配置好的conda环境&#xff0c;注意添加时要选中对应的con…

机械厂html5手机模板,营销型机械消费设备企业通用织梦模板(带html5手机端) v1.0...

营销型机械消费设备企业通用织梦模板简介营销型机械消费设备企业通用织梦模板(带html5手机端)v1.0是一个自适应手机端和PC端的最新版织梦V5.7为内核的网站模板&#xff0c;软件兼容主流浏览器&#xff0c;模板包含安装说明&#xff0c;并包含测试数据。dedecms最新版内核开发&a…