目的:

通过I/O端口方式访问RTC的秒寄存器;

由于本人从来没看过linux方面的书籍,也只是会在终端用些常用的命令而已,这次老大叫我学着通过I/O端口方式直接去读写寄存器。于是我在google中搜索,得到了一些答案,比如要先申请内存空间,再用ioremap映射到虚拟空间啊之类的。我学着网上的例子,写好了我的第一份代码。编译时竟然找不到头文件,非常头疼,头文件明明在那儿,怎么就找不到呢?在这里非常感谢,linux内核涉及与实现QQ群里面各位师哥师姐的鼎力相助,尽管说什么我都不懂,他们还是非常耐心。在群里大哥的指引下,我有了思路。要么添加系统调用,要么写个驱动。这里我选择了第二种方法。

方案:

引用群里大哥给我画的图片,感谢!

过程:

1. 编写驱动

  1. #include <linux/ioport.h>
  2. #include <linux/module.h>
  3. #include <linux/init.h>
  4. #include <linux/kernel.h>
  5. #include <linux/cdev.h>
  6. #include <linux/moduleparam.h>
  7. #include <linux/slab.h> /* kmalloc() */
  8. #include <linux/fs.h> /* everything... */
  9. #include <linux/errno.h> /* error codes */
  10. #include <linux/types.h> /* size_t */
  11. #include <linux/proc_fs.h>
  12. #include <linux/fcntl.h> /* O_ACCMODE */
  13. #include <linux/seq_file.h>
  14. #include <linux/cdev.h>
  15.  
  16. #include <asm/uaccess.h> /* copy_*_user */
  17. #include <asm/io.h>
  18.  
  19. #define DEVICE_NAME "rtcport"
  20. #define DEVICE_MAJOR 250
  21.  
  22. #ifndef BCD_TO_BIN
  23. #define BCD_TO_BIN(val) ((val)=((val)&15) + ((val4)*10)
  24. #endif
  25.  
  26. dev_t dev = 0;
  27. static struct resource *rtc_resource;
  28. static struct cdev my_dev;
  29. static int RTC_open(struct inode *inode,struct file *filp)
  30. {
  31. printk("open device\n");
  32. return 0;
  33. }
  34.  
  35. static int RTC_close(struct inode *inode,struct file *filp)
  36. {
  37. printk("close device\n");
  38. return 0;
  39. }
  40. static int RTC_read(struct file *filp, char __user *buf, loff_t *f_pos)
  41. {
  42. outb(0,0x70);
  43. int test=inb(0x71);
  44. printk(KERN_DEBUG "second is %02X\n",test);
  45. return 0;
  46. }
  47.  
  48. static int RTC_write(void)
  49. {
  50. return 0;
  51. }
  52.  
  53. static struct file_operations fops={
  54. .owner=THIS_MODULE,
  55. .open=RTC_open,
  56. .release=RTC_close,
  57. .read=RTC_read,
  58. .write=RTC_write,
  59. };
  60. int RTC_init(void)
  61. {
  62. int ret;
  63. //ret=register_chrdev(DEVICE_MAJOR,DEVICE_NAME,&fops);
  64. ret=alloc_chrdev_region(&dev,0,1,DEVICE_NAME);
  65. if (ret < 0) {
  66. printk("RTC: can't get major %d\n", MAJOR(dev));
  67. return ret;
  68. }
  69. printk("Register device successfully!\n");
  70. release_region(0x70, 0x02);
  71. rtc_resource = request_region(0x70,0x02,DEVICE_NAME);
  72. if(rtc_resource == NULL)
  73. {
  74. printk("Unable to register RTC I/O addresses\n");
  75. return -1;
  76. }
  77. cdev_init(&my_dev,&fops);
  78. my_dev.owner=THIS_MODULE;
  79. my_dev.ops=&fops;
  80. ret=cdev_add(&my_dev,MKDEV(MAJOR(dev),0),1);
  81. if(ret<0)
  82. {
  83. printk("RTC: can't add device");
  84. }
  85.  
  86. return 0;
  87. }
  88.  
  89. void RTC_exit(void)
  90. {
  91. // unregister_chrdev(DEVICE_MAJOR,DEVICE_NAME);
  92. // devfs_remove(DEVICE_NAME);
  93. release_region(0x70,0x02);
  94. cdev_del(&my_dev);
  95. unregister_chrdev_region(dev,1);
  96. printk("Device has been unregistered!\n");
  97. }
  98.  
  99. MODULE_LICENSE("GPL");
  100. MODULE_AUTHOR("HJW");
  101. module_init(RTC_init);
  102. module_exit(RTC_exit);

2. Makefile

  1. obj-m += rtc_port.o
  2. ccflags-y=-I/root/testdxx
  3. all:
  4. make $(ccflags-y) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
  5.  
  6. clean:
  7. 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

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. #include <sys/ioctl.h>
  6. #include <unistd.h>
  7.  
  8. int main(void)
  9. {
  10. int fd;
  11. char buf[20];
  12. fd=open("/dev/rtcport",O_RDWR);
  13. if (fd<0)
  14. {
  15. perror("open");
  16. return -1;
  17. }
  18. for(int i=0;i<60;i++)
  19. {
  20. read(fd,buf,20);
  21. sleep(1);
  22. }
  23. close(fd);
  24. return 0;
  25. }

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寄存器。的更多相关文章

  1. linux驱动开发:用户空间操作LCD显示简单的图片【转】

    转自:http://blog.csdn.net/changliang7731/article/details/53074616 上一章我们简单介绍了LCD的一些基本原理.当然更深奥的还有,比如gamm ...

  2. Android系统移植与驱动开发——第六章——使用实例来理解Linux驱动开发及心得

    Linux驱动的工作方式就是交互.例如向Linux打印机驱动发送一个打印命令,可以直接使用C语言函数open打开设备文件,在使用C语言函数ioctl向该驱动的设备文件发送打印命令.编写Linux驱动最 ...

  3. linux驱动开发重点关注内容--摘自《嵌入式Linux驱动模板精讲与项目实践》

    本文摘自本人拙著 <嵌入式Linux驱动模板精讲与项目实践> 初步看起来Linux设备驱动开发涉及内容非常多,而须要实现驱动的设备千差万别.事实上做一段时间驱动之后回首看来主要就是下面几点 ...

  4. 第一个Linux驱动-流水灯【转】

    转自:http://www.xuebuyuan.com/1856562.html 水平有限,描述不当之处请指出,转载请注明出处http://blog.csdn.net/vanbreaker/artic ...

  5. 一个linux 驱动升级的小问题记录

    重复踩了两次坑,所以简单记录下. 内核 3.10. 在修改了驱动的gro实现之后,进行驱动版本的升级,make && make install 之后,发现tg3的驱动,没有生效. 相同 ...

  6. 用Visual Studio 2015 编写第一个UMDF驱动遇到的问题!!

    前提:Visual Studio 2015已经成功安装了驱动环境,WDK都已经完全正常安装了,在Visual Studio 2015的菜单可以看到"Driver"菜单项了.这说明已 ...

  7. Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动【转】

    本文转载自:https://blog.csdn.net/zqixiao_09/article/details/50858776 版权声明:本文为博主原创文章,未经博主允许不得转载.    https: ...

  8. 用户空间与内核驱动的交互过程 — ioctl

    在Linux内核模块的开发过程中,经常涉及到运行在用户空间上的应用程序与内核模块进行交互,ioctl系统调用是常用的一种方式.本文并不涉及vlan的具体原理,仅通过vconfig与vlan内核模块进行 ...

  9. linux 驱动学习笔记03--Linux 内核的引导

    如图所示为 X86 PC 上从上电/复位到运行 Linux 用户空间初始进程的流程.在进入与 Linux相关代码之间,会经历如下阶段. ( 1 ) 当系统上电或复位时, CPU 会将 PC 指针赋值为 ...

随机推荐

  1. 转:不会定义jQuery插件,不要说会jQuery

    一:导言 有些WEB开发者,会引用一个JQuery类库,然后在网页上写一写$("#"),$("."),写了几年就对别人说非常熟悉JQuery.我曾经也是这样的人 ...

  2. 生成唯一32位ID编码代码Java(GUID)

    源码下载链接:http://pan.baidu.com/s/1jGCEWlC 扫扫关注"茶爸爸"微信公众号 坚持最初的执着,从不曾有半点懈怠,为优秀而努力,为证明自己而活. /* ...

  3. kinect for windows - 环境搭建

    我是在虚拟机上搭建的开发环境,需要准备如下软件: 1)vmware workstation 10.0.2 (可以去官网下载,key就自己百度吧) 2)win7 32位(一定是32位的) 3)vs201 ...

  4. Eclipse 取消import自动补全具体的类名

    有时候,在代码里写了一个JFrame,然后Eclipse就自动添加了import javax.swing.JFrame; 但有时候希望只要import javax.swing.*;就可以了,不希望具体 ...

  5. BZOJ 1708: [Usaco2007 Oct]Money奶牛的硬币

    1708: [Usaco2007 Oct]Money奶牛的硬币 Description 在创立了她们自己的政权之后,奶牛们决定推广新的货币系统.在强烈的叛逆心理的驱使下,她们准备使用奇怪的面值.在传统 ...

  6. 【C语言学习】存储类型

    C语言中的存储类型主要有四种:auto.static.extern.register ★auto存储类型 默认的存储类型.在C语言中,假设忽略了变量的存储类型,那么编译器就会自己主动默认为auto型 ...

  7. Oracle执行计划——处理一种并行hint不生效的情况

    刚刚在itpub上看到有人在问并行hint不生效的一个问题.我做了实验也出现一样的问题,如下: 原因在这是小表,在联合时走索引了,加上full的hint,就可以启动并行的执行计划. 当然也可以采用pa ...

  8. 【数据库摘要】5_Sql_IN

    IN 操作符 IN 操作符同意您在 WHERE 子句中查找多个值. SQL IN 语法 SELECT column_name(s) FROM table_name WHERE column_name ...

  9. (Problem 9)Special Pythagorean triplet

    A Pythagorean triplet is a set of three natural numbers, a  b  c, for which, a2 + b2 = c2 For exampl ...

  10. 如何去掉IE控件的垂直滚动条(使用QAxWidget加载IE控件)

    如果使用MFC的CHtmlView或Qt的QAxWidget加载IE控件,载入html文件后都会自动带一个垂直滚动条,我们不想要这个滚动条,改怎么办呢?搜索了一下“隐藏IE控件滚动条”,发现在 htt ...