/dev下添加设备节点的方法步骤

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

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

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

demo_chardev.c

#include <linux/init.h> 
#include <linux/module.h>
#include <linux/kernel.h>
/*结构体file_operations定义的头文件*/
#include <linux/fs.h>
/*声明copy_to/from_user函数的头文件*/
#include <linux/uaccess.h>
/*声明class_create 和device_create相关信息*/
#include <linux/device.h>

#define DEMO_DEBUG
#ifdef  DEMO_DEBUG
#define dem_dbg(fmt, arg...)  printk(KERN_WARNING fmt, ##arg)
#else
#define dem_dbg(fmt, arg...)  printk(KERN_DEBUG fmt, ##arg)
#endif

#define DEVICE_COUNT   2


/*记录当前驱动所占用的主设备号*/
static int major = 0;

static int demo_open (struct inode *pnode, struct file *filp)
{
     dem_dbg("[kern func]: %s  major: %d  minor: %d\n",
     __FUNCTION__, imajor(pnode), iminor(pnode));
     return 0;
}
static ssize_t demo_read (struct file *filp, char __user *buf, size_t count, loff_t *offp)
{
    unsigned char ary[100] = "you are reading successfully!";
    unsigned long len = min(count, sizeof(ary)); //min是个宏,用来获取两个数中较小的值
     int retval;

    dem_dbg("[kern func]: %s  major: %d  minor: %d\n",
    __FUNCTION__, imajor(filp->f_dentry->d_inode), 
    iminor(filp->f_dentry->d_inode));
 
    //file结构体的f_flags成员可用来判断是否阻塞读取,然后进行相应处理

     if(copy_to_user(buf, ary, len) != 0){
           retval = -EFAULT;
           goto cp_err;
     }

     return len; //成功返回实际传输的字节数
      cp_err:
     return retval; 
}

static ssize_t demo_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp)
{
    unsigned char ary[100] = "";
    unsigned long len = min(count, sizeof(ary)); //min是个宏,用来获取两个数中较小的值
     int retval;

    dem_dbg("[kern func]: %s  major: %d  minor: %d\n",
    __FUNCTION__, imajor(filp->f_dentry->d_inode), 
    iminor(filp->f_dentry->d_inode));
 
    if(copy_from_user(ary, buf, len) != 0){
           retval = -EFAULT;
           goto cp_err;
    }
    printk("[msg]: writing context: %s\n",ary);
    return len; //成功返回实际传输的字节数
     cp_err:
    return retval; 
}
static int demo_release (struct inode *pnode, struct file *filp)
{
     dem_dbg("[kern func]: %s  major: %d  minor: %d\n",
     __FUNCTION__, imajor(pnode), iminor(pnode));
     return 0;
}


/*@定义file_operations结构体变量*/
static struct file_operations fops = {
      .owner = THIS_MODULE,
      .read = demo_read,
      .write = demo_write,
      .open = demo_open,
      .release = demo_release,
}; 

static struct class *demo_class;
static int __init drvdemo_init(void)
{
    struct device *demo_device;
    int i;
    int retval;
 
    dem_dbg("[msg]:this is a driver demo, in module initial function\n");
    /*注册字符驱动函数,成功 返回动态分配好的主设备号,失败
      *返回错误码(负值)*/
    major = register_chrdev(0, "demo_chrdev", &fops);
    if(major < 0){
            retval = major;
            goto chrdev_err;
    }

    /*创建设备类*/
    demo_class = class_create(THIS_MODULE,"demo_class");
    if(IS_ERR(demo_class)){
           retval =  PTR_ERR(demo_class);
           goto class_err;
    }

    /*创建设备文件,通知用户在“/dev/”目录下创件名字为demoX的设备文件*/
    for(i=0; i<DEVICE_COUNT; i++){ //最多可创建255个设备节点(register_chrdev函数会申请0-254范围的从设备号)
        demo_device = device_create(demo_class,NULL, MKDEV(major, i), NULL,"demo%d",i);
        if(IS_ERR(demo_device)){
              retval = PTR_ERR(demo_device);
              goto device_err;
        }
    }    
    return 0; 
device_err: 
    while(i--) //设备节点创建的回滚操作 device_destroy(demo_class,MKDEV(major, i)); 
         class_destroy(demo_class); //删除设备类 
class_err: 
    unregister_chrdev(major, "demo_chrdev");
chrdev_err: 
    return retval; 
}
static void __exit drvdemo_exit(void)
{
      int i;
 
      dem_dbg("[msg]:in module exit function\n");
      /*注销字符驱动函数,无返回值,major为已分配的主设备号*/
      unregister_chrdev(major, "demo_chrdev");
      /*删除设备节点和设备类*/
      for(i=0; i<DEVICE_COUNT; i++)
          device_destroy(demo_class,MKDEV(major, i));
      class_destroy(demo_class);
}

module_init(drvdemo_init);
module_exit(drvdemo_exit);

MODULE_LICENSE("Dual BSD/GPL"); //BSD/GPL双重许可证
MODULE_AUTHOR("hanbo");  //模块作者(可选)
MODULE_DESCRIPTION("used for studing linux drivers"); //模块儿简介(可选)

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

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

   config  DEMO_CHARDEV

                bool  "demo_chardev  driver  for  hanbo  chardev  boards"

                default  y

                 help 

                     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  

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <fcntl.h>

#include <string.h>

int main(int argc, char *argv[])
{
       int fd1 = 0, fd2 = 0;
       unsigned char buf1[100] = "I am a test program!";
       unsigned char buf2[100] = {0};
       int retval;

       //以读写、不阻塞方式打开设备文件
        fd1 = open("/dev/demo0", O_RDWR | O_NONBLOCK);
        if(fd1 < 0){
               perror("open /dev/demo1");
                goto out;
         }
        //以只读、阻塞方式打开设备文件
       fd2 = open("/dev/demo1", O_RDONLY);
       if(fd2 < 0){
               perror("open /dev/demo2");
               goto out;
         }

        //成功返回实际写入字节数,失败返回负值
        retval = write(fd1, buf1, strlen(buf1)+1);
        if(retval < 0){
               perror("writing fd1 failed!");
               goto out;
         }
        printf("<user space>: write bytes: %d   write content: %s\n", retval, buf1);

        //成功返回实际读取字节数,失败返回负值
        retval = read(fd2, buf2, sizeof(buf2));
        if(retval < 0){
                perror("reading fd2 failed!");
                goto out;
         }
         printf("<user space>: read bytes: %d   read content: %s\n", retval, buf2);

         return 0;
   out:
         if(fd1 > 0)
               close(fd1);
         if(fd2 > 0)
               close(fd2);
         return -1;
  }

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

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

            Makefile    

#如果已定义KERNELRELEASE,说明是由内核构造系统调用的
#可以利用内建语句
ifneq ($(KERNELRELEASE),) 
    obj-m +=demo_chrdev.o
    #此时由内核构造系统调用
else 
    #定义并记录内核源码路径
    KERNELDIR = /home/hanbo/linux-2.6.35.7(自己源码路径,2.6.35.7指当前内核版本)
    #记录当前工程目录
    PWD := $(shell pwd)

default: 
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 
    @rm -rf *.o .t* .m* .*.cmd *.mod.c *.order *.symvers
 
endif
 
clean:
    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(跟当前内核版本同名)

 

 

         

你可能感兴趣的:(makefile,驱动,ARM,linux内核,交叉编译)