如何在Linux内核中新增自己的驱动代码

  1. 开发环境

    1. Ubuntu版本:Ubuntu14.04 64位,使用内核 linux-headers-4.4.0-31-generic
    2. 开发板内核版本:linux-4.1.18
    3. 开发板:基于am3352某控制器
  2. 开发步骤

    1. 编写globalmem虚拟驱动程序,在Ubuntu环境中测试
      1. 新建globalmem文件夹,进入文件夹,新建globalmem.c和Makefile,代码如下:
        1. globalmem.c:
          #include 
          #include 
          #include 
          #include 
          #include 
          #include 
          #include 
          #include 
          #include 
          #include 
          #include 
          
          #define GLOBALMEM_SIZE 0x1000
          #define MEM_CLEAR 0X1
          #define GLOBALMEM_MAJOR 230
          
          static int globalmem_major = GLOBALMEM_MAJOR;
          module_param(globalmem_major,int,S_IRUGO);
          /* globalmem设备结构体 */
          struct globalmem_dev {
          	struct cdev cdev;
          	unsigned char mem[GLOBALMEM_SIZE];
          };
          
          struct globalmem_dev *globalmem_devp;
          /* 文件打开函数 */
          int globalmem_open(struct inode *inode, struct file *filp)
          {
          	/*  */
          	filp->private_data = globalmem_devp;
          	return 0;
          }
          /* 文件释放函数 */
          static int globalmem_release(struct inode *inode, struct file *filp)
          {
          	return 0;
          }
          
          /* ioctl 设备控制函数 */
          #if 1
          static long globalmem_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd, unsigned long arg)
          {
          //
          	struct globalmem_dev *dev = filp->private_data;/* 获得设备结构体指针 */ 
          
          	switch (cmd){
          	case MEM_CLEAR:
          		memset(dev->mem, 0, GLOBALMEM_SIZE);
          		printk(KERN_INFO "globalmem is set to zero\n");
          		break;
          
          	default:
          		return - EINVAL;
          	}
          
          	return 0;
          }
          #endif
          /* 读函数 */
          static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
          {
          
          	unsigned long p = *ppos;
          	unsigned int count = size;
          	int ret = 0;
          	struct globalmem_dev *dev = filp->private_data;/* 获得设备结构体指针 */ 
          
          
          	if (p >= GLOBALMEM_SIZE)
          		return 0;
          	if (count > GLOBALMEM_SIZE - p)
          		count = GLOBALMEM_SIZE - p;
          
          	/* 内核空间--> 用户空间 */
          	if (copy_to_user(buf, (void *)(dev->mem + p), count)) {
          		ret = EFAULT;
          	}else {
          		*ppos += count;
          		ret = count;
          
          		printk(KERN_INFO "read %u bytes(s) from %lu\n", count, p);
          	}
          
          	return ret;
          }
          /*写函数*/
          static ssize_t  globalmem_write(struct file *filp, const char __user *buf,size_t size, loff_t *ppos)
          {
            unsigned long p =  *ppos;
            unsigned int count = size;
            int ret = 0;
            struct globalmem_dev *dev =filp->private_data; /*获得设备结构体指针*/
           
            /*分析和获取有效的写长度*/
            if (p >= GLOBALMEM_SIZE)
              return count ?  - ENXIO: 0;
            if (count > GLOBALMEM_SIZE - p)
              count = GLOBALMEM_SIZE - p;
             
            /*用户空间->内核空间*/
            if (copy_from_user(dev->mem + p, buf,count))
              ret = - EFAULT;
            else
            {
              *ppos += count;
              ret = count;
             
              printk(KERN_INFO "written %u bytes(s) from %lu\n", count, p);
            }
           
            return ret;
          }
           
          /* seek文件定位函数 */
          static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)
          {
            loff_t ret = 0;
            switch (orig)
            {
              case 0:  /*相对文件开始位置偏移*/
                if (offset < 0)
                {
                  ret = - EINVAL;
                  break;
                }
                if ((unsigned int)offset >GLOBALMEM_SIZE)
                {
                  ret = - EINVAL;
                  break;
                }
                filp->f_pos = (unsigned int)offset;
                ret = filp->f_pos;
                break;
              case 1:  /*相对文件当前位置偏移*/
                if ((filp->f_pos + offset) >GLOBALMEM_SIZE)
                {
                  ret = - EINVAL;
                  break;
                }
                if ((filp->f_pos + offset) < 0)
                {
                  ret = - EINVAL;
                  break;
                }
                filp->f_pos += offset;
                ret = filp->f_pos;
                break;
              default:
                ret = - EINVAL;
                break;
            }
            return ret;
          }
          
          /* 文件操作结构体 */
          static const struct file_operations globalmem_fops = {
          	.owner = THIS_MODULE,
            .llseek = globalmem_llseek,
          	.read = globalmem_read,
            .write = globalmem_write,
          	.unlocked_ioctl = globalmem_ioctl,
          	.open = globalmem_open,
          	.release = globalmem_release,
          };
          
          /* 初始化并注册cdev */
          static void globalmem_setup_cdev(struct globalmem_dev *dev, int index)
          {
          	int err, devno = MKDEV(globalmem_major, index);
          
          	cdev_init(&dev->cdev, &globalmem_fops);
          	dev->cdev.owner = THIS_MODULE;
          	err = cdev_add(&dev->cdev, devno, 1);
          	if (err)
          		printk(KERN_NOTICE "Error %d adding globalmem %d", err, index);
          }
          
          /* 设备驱动模块加载函数 */
          int globalmem_init(void)
          {
          	int result;
          	dev_t devno = MKDEV(globalmem_major, 0);   /*   devno = (250<<20) | 0   */
          
          	/* 申请设备号 */
          	if (globalmem_major)
          		result = register_chrdev_region(devno, 1, "globalmem");
          	else { /* 动态申请设备号 */
          		result = alloc_chrdev_region(&devno, 0, 1, "globalmem");
          		globalmem_major = MAJOR(devno);
          	}
          	if (result < 0)
          		return result;
          
          	/* 动态申请设备结构体的内存 */
          	globalmem_devp = kmalloc(sizeof(struct globalmem_dev), GFP_KERNEL);
          	if (!globalmem_devp) { /* 申请失败 */
          		result = - ENOMEM;
          		goto fail_malloc;
          	}
          	
          	memset (globalmem_devp, 0, sizeof(struct globalmem_dev));
          
          	globalmem_setup_cdev(globalmem_devp, 0);
          	return 0;
          
          fail_malloc:
          	unregister_chrdev_region(devno, 1);
          	return result;
          }
          
          /* 模块卸载函数 */
          void globalmem_exit(void)
          {
          	cdev_del(&globalmem_devp->cdev);	/* 注销CDEV */
          	kfree(globalmem_devp); /* 释放设备结构体内存 */
          	unregister_chrdev_region(MKDEV(globalmem_major, 0), 1); /* 释放设备号 */
          }
          
          MODULE_AUTHOR("Fourier");
          MODULE_LICENSE("Dual BSD/GPL");
          
          //module_param(globalmem_major, int, S_IRUGO);
          
          module_init(globalmem_init);
          module_exit(globalmem_exit);
          
          

           

        2. Makefile:
          obj-m := globalmem.o
          
          KERNEL_DIR := /lib/modules/$(shell uname -r)/build   
          PWD := $(shell pwd)  
          
          all:
          	make -C $(KERNEL_DIR) M=$(PWD) modules
          clean:
          	make -C $(KERNEL_DIR) M=$(PWD) clean

           

      2. make,如下:
      3. 运行insmod globalmem.ko,globalmem模块已被加载。通过cat  /proc/devices,可以看到多出了主设备号为230的“globalmem”字符设备驱动。运行mknod  /dev/globalmem c 230 0创建设备节点,运行echo “hello globalmem” >  /dev/globalmem,cat /dev/globalmem,可以看到字符串被正确写入字符设备。
    2. Makefile、Kconfig配置
      1.  进入内核drivers目录,新建globalmem文件夹,进入文件夹,新建globalmem.c、Makefile、Kconfig三个文件,代码如下
        1. globalmem.c:同上
        2. Makefile:
          obj-$(CONFIG_GLOBALMEM) += globalmem.o

           

        3. Kconfig:
          menu "GLOBALMEM TEST Driver"
          comment "GLOBALMEM TEST Driver Config"
          config GLOBALMEM
          	tristate "globalmem module test"
          	default m
          	help
          	  This is the globalmem test driver --by zheng.
          endmenu

           

      2. 修改内核drivers目录的Makefile、Kconfig,如下:
        1. Makefile:添加obj-$(CONFIG_GLOBALMEM) += globalmem/
        2. Kconfig:添加source "drivers/globalmem/Kconfig"
      3. 修改arch/arm/Kconfig:添加source "drivers/globalmem/Kconfig"
    3. menuconfig配置:运行make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig,可以配置驱动为编译进内核(Y)、编译成内核模块(M)、不编译(N),这里我选择M
    4. 保存后退出,运行make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules,可以看到在drivers/globalmem文件夹下生成了globalmem.ko
    5. 使用tftp将globalmem.ko传至开发板,按照在ubuntu环境中的方法测试:

你可能感兴趣的:(drivers)