将自己开发的内核代码加入到Linux内核中,需要3个步骤:

1、确定把自己开发代码放入到内核合适的位置

将demo_chardev.c文件拷贝到.../drivers/char/目录下。

demo_chardev.c

  1. #include <linux/init.h>
  2. #include <linux/module.h>
  3. #include <linux/kernel.h>
  4. /*结构体file_operations定义的头文件*/
  5. #include <linux/fs.h>
  6. /*声明copy_to/from_user函数的头文件*/
  7. #include <linux/uaccess.h>
  8. /*声明class_create 和device_create相关信息*/
  9. #include <linux/device.h>
  10. #define DEMO_DEBUG
  11. #ifdef  DEMO_DEBUG
  12. #define dem_dbg(fmt, arg...)  printk(KERN_WARNING fmt, ##arg)
  13. #else
  14. #define dem_dbg(fmt, arg...)  printk(KERN_DEBUG fmt, ##arg)
  15. #endif
  16. #define DEVICE_COUNT   2
  17. /*记录当前驱动所占用的主设备号*/
  18. static int major = 0;
  19. static int demo_open (struct inode *pnode, struct file *filp)
  20. {
  21. dem_dbg("[kern func]: %s  major: %d  minor: %d\n",
  22. __FUNCTION__, imajor(pnode), iminor(pnode));
  23. return 0;
  24. }
  25. static ssize_t demo_read (struct file *filp, char __user *buf, size_t count, loff_t *offp)
  26. {
  27. unsigned char ary[100] = "you are reading successfully!";
  28. unsigned long len = min(count, sizeof(ary)); //min是个宏,用来获取两个数中较小的值
  29. int retval;
  30. dem_dbg("[kern func]: %s  major: %d  minor: %d\n",
  31. __FUNCTION__, imajor(filp->f_dentry->d_inode),
  32. iminor(filp->f_dentry->d_inode));
  33. //file结构体的f_flags成员可用来判断是否阻塞读取,然后进行相应处理
  34. if(copy_to_user(buf, ary, len) != 0){
  35. retval = -EFAULT;
  36. goto cp_err;
  37. }
  38. return len; //成功返回实际传输的字节数
  39. cp_err:
  40. return retval;
  41. }
  42. static ssize_t demo_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp)
  43. {
  44. unsigned char ary[100] = "";
  45. unsigned long len = min(count, sizeof(ary)); //min是个宏,用来获取两个数中较小的值
  46. int retval;
  47. dem_dbg("[kern func]: %s  major: %d  minor: %d\n",
  48. __FUNCTION__, imajor(filp->f_dentry->d_inode),
  49. iminor(filp->f_dentry->d_inode));
  50. if(copy_from_user(ary, buf, len) != 0){
  51. retval = -EFAULT;
  52. goto cp_err;
  53. }
  54. printk("[msg]: writing context: %s\n",ary);
  55. return len; //成功返回实际传输的字节数
  56. cp_err:
  57. return retval;
  58. }
  59. static int demo_release (struct inode *pnode, struct file *filp)
  60. {
  61. dem_dbg("[kern func]: %s  major: %d  minor: %d\n",
  62. __FUNCTION__, imajor(pnode), iminor(pnode));
  63. return 0;
  64. }
  65. /*@定义file_operations结构体变量*/
  66. static struct file_operations fops = {
  67. .owner = THIS_MODULE,
  68. .read = demo_read,
  69. .write = demo_write,
  70. .open = demo_open,
  71. .release = demo_release,
  72. };
  73. static struct class *demo_class;
  74. static int __init drvdemo_init(void)
  75. {
  76. struct device *demo_device;
  77. int i;
  78. int retval;
  79. dem_dbg("[msg]:this is a driver demo, in module initial function\n");
  80. /*注册字符驱动函数,成功 返回动态分配好的主设备号,失败
  81. *返回错误码(负值)*/
  82. major = register_chrdev(0, "demo_chrdev", &fops);
  83. if(major < 0){
  84. retval = major;
  85. goto chrdev_err;
  86. }
  87. /*创建设备类*/
  88. demo_class = class_create(THIS_MODULE,"demo_class");
  89. if(IS_ERR(demo_class)){
  90. retval =  PTR_ERR(demo_class);
  91. goto class_err;
  92. }
  93. /*创建设备文件,通知用户在“/dev/”目录下创件名字为demoX的设备文件*/
  94. for(i=0; i<DEVICE_COUNT; i++){ //最多可创建255个设备节点(register_chrdev函数会申请0-254范围的从设备号)
  95. demo_device = device_create(demo_class,NULL, MKDEV(major, i), NULL,"demo%d",i);
  96. if(IS_ERR(demo_device)){
  97. retval = PTR_ERR(demo_device);
  98. goto device_err;
  99. }
  100. }
  101. return 0;
  102. device_err:
  103. while(i--) //设备节点创建的回滚操作 device_destroy(demo_class,MKDEV(major, i));
  104. class_destroy(demo_class); //删除设备类
  105. class_err:
  106. unregister_chrdev(major, "demo_chrdev");
  107. chrdev_err:
  108. return retval;
  109. }
  110. static void __exit drvdemo_exit(void)
  111. {
  112. int i;
  113. dem_dbg("[msg]:in module exit function\n");
  114. /*注销字符驱动函数,无返回值,major为已分配的主设备号*/
  115. unregister_chrdev(major, "demo_chrdev");
  116. /*删除设备节点和设备类*/
  117. for(i=0; i<DEVICE_COUNT; i++)
  118. device_destroy(demo_class,MKDEV(major, i));
  119. class_destroy(demo_class);
  120. }
  121. module_init(drvdemo_init);
  122. module_exit(drvdemo_exit);
  123. MODULE_LICENSE("Dual BSD/GPL"); //BSD/GPL双重许可证
  124. MODULE_AUTHOR("hanbo");  //模块作者(可选)
  125. MODULE_DESCRIPTION("used for studing linux drivers"); //模块儿简介(可选)

2、把自己开发的功能增加到Linux内核的配置选项中,使用户能够选择此功能

vi drivers/char/Konfig   在文件结尾,endmenu的前面加入一个config选项

  1. config  DEMO_CHARDEV
  2. bool  "demo_chardev  driver  for  hanbo  chardev  boards"
  3. default  y
  4. help
  5. this  is  CHARDEV  driver  for  hanbo  chardev  boards.

3、构建或修改Makefile,根据用户的选择,将相应的代码编译到最终生成的Linux内核中去

make  menuconfig(添加配置选项)(如果提示找不到“ncurses”库则执行命令: sudo apt-get install libncurses5-dev )

Device driver -->

character devices ->

[*] demo_chardev driver for hanbo chardev boards

4、vi  drivers/char/Makefile  添加内容如下:

..........

obj-$(CONFIG_DEMO_CHARDEV)        +=demo_chardev.o (添加)

obj-$(CONFIG_JS_RTC)                         +=js-rtc.o(自带)

js-rtc-y = rtc.o (自带)
 5、make  (更新内核镜像到开发板)

6、交叉编译测试程序,放到开发板运行

arm-linux-gcc-gcc  test.c  -o  demo

test.c

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <fcntl.h>
  5. #include <string.h>
  6. int main(int argc, char *argv[])
  7. {
  8. int fd1 = 0, fd2 = 0;
  9. unsigned char buf1[100] = "I am a test program!";
  10. unsigned char buf2[100] = {0};
  11. int retval;
  12. //以读写、不阻塞方式打开设备文件
  13. fd1 = open("/dev/demo0", O_RDWR | O_NONBLOCK);
  14. if(fd1 < 0){
  15. perror("open /dev/demo1");
  16. goto out;
  17. }
  18. //以只读、阻塞方式打开设备文件
  19. fd2 = open("/dev/demo1", O_RDONLY);
  20. if(fd2 < 0){
  21. perror("open /dev/demo2");
  22. goto out;
  23. }
  24. //成功返回实际写入字节数,失败返回负值
  25. retval = write(fd1, buf1, strlen(buf1)+1);
  26. if(retval < 0){
  27. perror("writing fd1 failed!");
  28. goto out;
  29. }
  30. printf("<user space>: write bytes: %d   write content: %s\n", retval, buf1);
  31. //成功返回实际读取字节数,失败返回负值
  32. retval = read(fd2, buf2, sizeof(buf2));
  33. if(retval < 0){
  34. perror("reading fd2 failed!");
  35. goto out;
  36. }
  37. printf("<user space>: read bytes: %d   read content: %s\n", retval, buf2);
  38. return 0;
  39. out:
  40. if(fd1 > 0)
  41. close(fd1);
  42. if(fd2 > 0)
  43. close(fd2);
  44. return -1;
  45. }

二、手动加载驱动 .ko文件

1、上面的demo_chardev.c文件放到内核下编译生成 .ko文件

Makefile

  1. #如果已定义KERNELRELEASE,说明是由内核构造系统调用的
  2. #可以利用内建语句
  3. ifneq ($(KERNELRELEASE),)
  4. obj-m +=demo_chrdev.o
  5. #此时由内核构造系统调用
  6. else
  7. #定义并记录内核源码路径
  8. KERNELDIR = /home/hanbo/linux-2.6.35.7(自己源码路径,2.6.35.7指当前内核版本)
  9. #记录当前工程目录
  10. PWD := $(shell pwd)
  11. default:
  12. $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
  13. @rm -rf *.o .t* .m* .*.cmd *.mod.c *.order *.symvers
  14. endif
  15. clean:
  16. rm -rf *.ko *.o .t* .m* .*.cmd *.mod.c *.order *.symvers

2、 然后用命令加载 .ko 驱动

lsmod          列举当前系统中的所有模块

lsmod          列举当前系统中的所有模块

rmmod  xxx      卸载指定模块(不需要.ko后缀)

3、如果自己编译的代码中没有用

/*创建设备类*/
            demo_class = class_create(THIS_MODULE,"demo_class");

/*创建设备文件,通知用户在“/dev/”目录下创件名字为demoX的设备文件*/

demo_device = device_create(demo_class,NULL, MKDEV(major, i), NULL,"demo%d",i);

则需要手动添加设备节点

mknod /dev/demo1 c 主设备号 0

mknod /dev/demo2 c 主设备号 1

注意:若卸载时出现提示 rmmod:chdir(2.6.35.7):No  such  file  or  directory

则在开发板根文件系统下创建目录:/lib/modules/2.6.35.7(跟当前内核版本同名)

/dev下添加设备节点的方法步骤(通过device_create)的更多相关文章

  1. Linux /dev 自动创建设备节点

    #include <linux/module.h> #include <linux/module.h> #include <linux/kernel.h> #inc ...

  2. Android 6.0中在/dev下添加新设备驱动下Selinux相关设置【转】

    本文转载自:https://blog.csdn.net/fantasy_wxe/article/details/52013922 错误1: 07-23 13:06:57.617   117   117 ...

  3. linux系统下添加新硬盘的方法详解

    对于linux新手来说,在linux上添加新硬盘,是很有挑战性的一项工作. 在Linux服务器上把硬盘接好,启动linux,以root登陆. fdisk -l ## 这里是查看目前系统上有几块硬盘 D ...

  4. android源码framework下添加新资源的方法

    编译带有资源的jar包,需要更改frameworks层,方法如下: 一.增加png类型的图片资源 1.将appupdate模块所有用到的png格式图片拷贝到framework/base/core/re ...

  5. WordPress 添加Meta Box的方法步骤

    需要使用到add meta boxes Action,该Action允许我们为任何文章类型注册Meta Box,在该Action中,我们需要使用add_meta_box()方法来添加Meta Box的 ...

  6. LINUX下添加磁盘空间的方法详解

    给Linux系统添加磁盘空间在工作会经常遇到. 在添加第二块磁盘一般系统默认为hdb(IDE硬盘)sdb(SCSI 硬盘),以hdb为例. linux-isep:~ # fdisk /dev/hdb ...

  7. eclipse下添加viplugin插件的方法

    http://www.viplugin.com/ 在eclipse根目录下建立文件:viplugin2.lic,然后在里面添加以下字符串: nd4UFjUMBADcUSeSW8ocLKoGP3lpbW ...

  8. JS添加父节点的方法。

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  9. 【总文档】rac增加新节点的方法步骤 How to Add Node/Instance or Remove Node/Instance in 10gR2, 11gR1, 11gR2 and 12c Oracle Clusterware and RAC

    [总文档]How to Add Node/Instance or Remove Node/Instance in 10gR2, 11gR1, 11gR2 and 12c Oracle Clusterw ...

随机推荐

  1. 欧拉函数(codevs 4939)

    题目描述 Description 输入一个数n,输出小于n且与n互素的整数个数 输入描述 Input Description 包含多组数据,n=0时结束 测试数据组数不会很多,不必先打表后输出 输出描 ...

  2. 关于事件委托和时间冒泡(以及apply和call的事项)

    搜索事件委托和事件冒泡,网上一大堆乱七八糟的解释,当然意思都对,没毛病. but,真的无聊. 事件冒泡:事件会从点击的元素开始依次向上流出,直到html,遇见事件监听则执行. 事件委托:原因——父元素 ...

  3. SQL 随机取出一条数据

    今天遇到一需求,需要随机取出一条数据.网上找了下,sqlserver自带的有newID()这个函数,可以随机出来一个guid,用来取随机数还是蛮不错的. 直接上SQL: select top 1 *, ...

  4. poj 3461 Oulipo,裸kmp

    传送门 Oulipo Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 32373   Accepted: 13093 Desc ...

  5. linux的at定时任务的使用

    linux的at定时任务的使用 使用at只能执行一次性任务:使用at命令需要开启atd进程. 以下情况需要安装at命令: 情况1.查看是否开启atd进程:ps -ef | grep atd.[test ...

  6. Loj #6307. 「雅礼国庆 2017 Day1」Clique

    link: https://loj.ac/problem/6307 最大团转补图的独立集,这样的话只有r[x]<l[y]或者r[y]<l[x],x和y才能连边,所以排序之后乱搞就行了. 需 ...

  7. 迈出从3K到1W的重要一步——掌握设计模式

    IT职场的小菜经常有这样的疑问: 为什么一个相似的功能,大牛一会儿就搞定,然后悠闲地品着下午茶逛淘宝:而自己加班加点搞到天亮还做不完. 为什么用户提出需求变更后,大牛只需潇洒地敲敲键盘,改改配置:而自 ...

  8. [WASM Rust] Use the js-sys Crate to Invoke Global APIs Available in Any JavaScript Environment

    js-sys offers bindings to all the global APIs available in every JavaScript environment as defined b ...

  9. 微信小程序 项目实战(一)生命周期 配置服务器信息 splash启动页

    步骤一:小程序 生命周期 //app.js App({ onLaunch: function () { //当小程序初始化完成时,会触发onLaunch(全局只触发一次) }, onShow: fun ...

  10. linux cat 文件操作

    简略版: cat主要有三大功能:1.一次显示整个文件.$ cat filename2.从键盘创建一个文件.$ cat > filename     只能创建新文件,不能编辑已有文件.3.将几个文 ...