【从零开始,从内核驱动驱动到用户空间调用】编写第一个linux驱动,通过端口访问I/O寄存器。
目的:
通过I/O端口方式访问RTC的秒寄存器;
由于本人从来没看过linux方面的书籍,也只是会在终端用些常用的命令而已,这次老大叫我学着通过I/O端口方式直接去读写寄存器。于是我在google中搜索,得到了一些答案,比如要先申请内存空间,再用ioremap映射到虚拟空间啊之类的。我学着网上的例子,写好了我的第一份代码。编译时竟然找不到头文件,非常头疼,头文件明明在那儿,怎么就找不到呢?在这里非常感谢,linux内核涉及与实现QQ群里面各位师哥师姐的鼎力相助,尽管说什么我都不懂,他们还是非常耐心。在群里大哥的指引下,我有了思路。要么添加系统调用,要么写个驱动。这里我选择了第二种方法。
方案:
引用群里大哥给我画的图片,感谢!
过程:
1. 编写驱动
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/moduleparam.h>
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/seq_file.h>
#include <linux/cdev.h> #include <asm/uaccess.h> /* copy_*_user */
#include <asm/io.h> #define DEVICE_NAME "rtcport"
#define DEVICE_MAJOR 250 #ifndef BCD_TO_BIN
#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)»4)*10)
#endif dev_t dev = 0;
static struct resource *rtc_resource;
static struct cdev my_dev;
static int RTC_open(struct inode *inode,struct file *filp)
{
printk("open device\n");
return 0;
} static int RTC_close(struct inode *inode,struct file *filp)
{
printk("close device\n");
return 0;
}
static int RTC_read(struct file *filp, char __user *buf, loff_t *f_pos)
{
outb(0,0x70);
int test=inb(0x71);
printk(KERN_DEBUG "second is %02X\n",test);
return 0;
} static int RTC_write(void)
{
return 0;
} static struct file_operations fops={
.owner=THIS_MODULE,
.open=RTC_open,
.release=RTC_close,
.read=RTC_read,
.write=RTC_write,
};
int RTC_init(void)
{
int ret;
//ret=register_chrdev(DEVICE_MAJOR,DEVICE_NAME,&fops);
ret=alloc_chrdev_region(&dev,0,1,DEVICE_NAME);
if (ret < 0) {
printk("RTC: can't get major %d\n", MAJOR(dev));
return ret;
}
printk("Register device successfully!\n");
release_region(0x70, 0x02);
rtc_resource = request_region(0x70,0x02,DEVICE_NAME);
if(rtc_resource == NULL)
{
printk("Unable to register RTC I/O addresses\n");
return -1;
}
cdev_init(&my_dev,&fops);
my_dev.owner=THIS_MODULE;
my_dev.ops=&fops;
ret=cdev_add(&my_dev,MKDEV(MAJOR(dev),0),1);
if(ret<0)
{
printk("RTC: can't add device");
} return 0;
} void RTC_exit(void)
{
// unregister_chrdev(DEVICE_MAJOR,DEVICE_NAME);
// devfs_remove(DEVICE_NAME);
release_region(0x70,0x02);
cdev_del(&my_dev);
unregister_chrdev_region(dev,1);
printk("Device has been unregistered!\n");
} MODULE_LICENSE("GPL");
MODULE_AUTHOR("HJW");
module_init(RTC_init);
module_exit(RTC_exit);
2. Makefile
obj-m += rtc_port.o
ccflags-y=-I/root/testdxx
all:
make $(ccflags-y) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
说明:ccflags-y是需要包括的头文件的路径,如果不需要依赖自定义的头文件,可去除。
3.make clean(注:若第一次编译,此步骤可略过)
4.make(ls可以看到在路径下多了一些文件)
5. 将模块Insmod进内核
insmod rtc_port.ko
注释:有同学说,诶怎么一点打印信息都没有,原因是printk本身就不会把信息打印到屏幕上,如果有需要的话,大家可以自己去搜索搜索。
6.执行cat /proc/devices可以看到我们新添加的字符型设备rtcport
7.将rtcport创建dev节点
mknod /dev/rtcport c 237 0
8.应用程序app.cpp
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h> int main(void)
{
int fd;
char buf[20];
fd=open("/dev/rtcport",O_RDWR);
if (fd<0)
{
perror("open");
return -1;
}
for(int i=0;i<60;i++)
{
read(fd,buf,20);
sleep(1);
}
close(fd);
return 0;
}
9. 输出结果dmesg
10 总结
本文只是介绍了完整的步骤,很多小的知识点都没有提及。下面我将进行概括:
1) 模块的格式
关键函数:Init exit之类的;
2)字符型设备的动态注册(动态注册可以减少设备冲突的概率)
关键函数:
alloc_chrdev_region(&dev,0,1,DEVICE_NAME);
cdev_init;
cdev_add
等等
注:释放的关键函数请参见代码;
3) i/o端口的映射
关键函数:
request_region;
release_region;
小的知识点大家可以边google边对照我的代码,希望这篇文章可以帮助大家少走弯路!
11.展望
下一步是研究IPMI source code,非常渴望能找到志同道合的朋友,大家有过这方面的研究可以给我留言,非常感谢!
【从零开始,从内核驱动驱动到用户空间调用】编写第一个linux驱动,通过端口访问I/O寄存器。的更多相关文章
- linux驱动开发:用户空间操作LCD显示简单的图片【转】
转自:http://blog.csdn.net/changliang7731/article/details/53074616 上一章我们简单介绍了LCD的一些基本原理.当然更深奥的还有,比如gamm ...
- Android系统移植与驱动开发——第六章——使用实例来理解Linux驱动开发及心得
Linux驱动的工作方式就是交互.例如向Linux打印机驱动发送一个打印命令,可以直接使用C语言函数open打开设备文件,在使用C语言函数ioctl向该驱动的设备文件发送打印命令.编写Linux驱动最 ...
- linux驱动开发重点关注内容--摘自《嵌入式Linux驱动模板精讲与项目实践》
本文摘自本人拙著 <嵌入式Linux驱动模板精讲与项目实践> 初步看起来Linux设备驱动开发涉及内容非常多,而须要实现驱动的设备千差万别.事实上做一段时间驱动之后回首看来主要就是下面几点 ...
- 第一个Linux驱动-流水灯【转】
转自:http://www.xuebuyuan.com/1856562.html 水平有限,描述不当之处请指出,转载请注明出处http://blog.csdn.net/vanbreaker/artic ...
- 一个linux 驱动升级的小问题记录
重复踩了两次坑,所以简单记录下. 内核 3.10. 在修改了驱动的gro实现之后,进行驱动版本的升级,make && make install 之后,发现tg3的驱动,没有生效. 相同 ...
- 用Visual Studio 2015 编写第一个UMDF驱动遇到的问题!!
前提:Visual Studio 2015已经成功安装了驱动环境,WDK都已经完全正常安装了,在Visual Studio 2015的菜单可以看到"Driver"菜单项了.这说明已 ...
- Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动【转】
本文转载自:https://blog.csdn.net/zqixiao_09/article/details/50858776 版权声明:本文为博主原创文章,未经博主允许不得转载. https: ...
- 用户空间与内核驱动的交互过程 — ioctl
在Linux内核模块的开发过程中,经常涉及到运行在用户空间上的应用程序与内核模块进行交互,ioctl系统调用是常用的一种方式.本文并不涉及vlan的具体原理,仅通过vconfig与vlan内核模块进行 ...
- linux 驱动学习笔记03--Linux 内核的引导
如图所示为 X86 PC 上从上电/复位到运行 Linux 用户空间初始进程的流程.在进入与 Linux相关代码之间,会经历如下阶段. ( 1 ) 当系统上电或复位时, CPU 会将 PC 指针赋值为 ...
随机推荐
- glib 文档 代码 索引 编译
./configure --prefix=/opt/glib-2.28.8 --enable-staticmakemake install linux下载 WIN32下载 代码索引 文档索引 GLib ...
- qt 操作word
//修改doc QString filepath="e:\\aa.doc"; QAxWidget *word = new QAxWidget("Word.Applicat ...
- Windows Phone 8初学者开发—第14部分:在运行时绑定到真实的数据
原文 Windows Phone 8初学者开发—第14部分:在运行时绑定到真实的数据 第14部分:在运行时绑定到真实的数据 原文地址: http://channel9.msdn.com/Series/ ...
- 链接分析算法之:HillTop算法
链接分析算法之:HillTop算法 Hilltop算法是由Krishna Baharat 在2000年左右研究的,于2001年申请专利,但是有很多人以为Hilltop算法是由谷歌研究的.只 ...
- Java程序如何自动在线升级
有时候我们的程序需要连接服务器检测新版本,如果发现新版本则需要自动下载升级.这种需求在Linux下还好说,但在windows下如何替换正在运行的程序文件呢? 当然有办法,步骤如下: 1. 将我们的程序 ...
- Jquery获对HTML控件的控制
Jquery获对HTML控件的控制 1.获取控件的值 1.1.radio 1.1.1 获取一组radio被选中项的值 var item = $('input[name=items][checked] ...
- QQ邮箱添加公司邮箱步骤
经领导提示,发现QQ邮箱可以添加公司邮箱.这样,在有同事出差在外的时候,可以通过QQWEB邮箱,即可收发公司邮箱,不必安装邮箱客户端软件. 仅供各位同事参考使用. 1.打开QQ邮箱 2.点击其他邮箱, ...
- DOM操作HTML文档
概述 之前写过一些关于DOM方法的知识,理论方法的偏多,所以,这篇博客主要是实践方面的Demo,如果,大家觉得理论方面有所欠缺,大家可以看看这几篇博客:JavaScript总结(一.基本概念)和Jav ...
- javascript 数组部分
<html> <body> <script type="text/javascript"> var arr = new Array(6) arr ...
- 面向对象程序设计-C++ Inheritance & Multiple inheritance & RTTI【第十三次上课笔记】
Sadly, 这节课带过去的笔记本没电了 T^T 导致没有一行 Code, Sorry 笔记如下: Shape * p1; //使用指针创建对象的方法 p = new Circle (2.0); Sh ...