设备驱动实现

1在www.kernel.org下载所需要版本的内核,我下载的是linux-4.1.19.tar.xz版本。

2解压到/home/kernel/目录下。

解压:

xz -d linux-4.1.19.tar.xz

tar -xf linux-4.1.19.tar

移动:

sudo mv linux-4.1.19 /home/kernel/

3编译内核

cd /home/kernel/

cp /boot/config-4.10.0-37-generic ./.config

make oldconfig

sudo make

编译完成之后:

sudo make modules_install

sudo make install

这里我的gcc和linux16.04内核版本是:

4编写globalmem驱动程序

1打开终端输入命令 mkdir /home/user/globalmem(创建 globalmem 文件夹)。

2然后再输入命令cd /home/user/globalmem(进入globalmem 文件夹)。

3接着输入命令gedit globalmem.c(编写globalmem.c程序)

在/home/user/globalmem新建文件globalmem.c如下:

#include  <linux/module.h>  
#include  <linux/types.h>  
#include  <linux/fs.h>  
#include  <linux/errno.h>  
#include  <linux/mm.h>  
#include  <linux/sched.h>  
#include  <linux/init.h>  
#include  <linux/cdev.h>  
#include  <asm/io.h>  
#include <linux/slab.h> 
#include  <linux/version.h>
#if  LINUX_VERSION_CODE  >  KERNEL_VERSION(3,  3,  0)
    #include  <asm/switch_to.h>
#else
    #include  <asm/system.h>
#endif
#include  <asm/uaccess.h>  
  
#define  GLOBALMEM_SIZE  0x1000    /*全局内存最大  4K  字节*/  
#define  MEM_CLEAR  0x1    /*清  0  全局内存*/  
#define  GLOBALMEM_MAJOR  150        /*预设的  globalmem  的主设备号*/  
  
static  int  globalmem_major  =  GLOBALMEM_MAJOR;  
/*globalmem  设备结构体*/  
struct  globalmem_dev  
{  
    struct  cdev  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;
}  
int  globalmem_release(struct  inode  *inode,  struct  file  *filp)  
{  
    return  0;  
}  
  long globalmem_ioctl(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; 
}
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  count  ?    -  ENXIO:  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  %d  bytes(s)  from  %d\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  %d  bytes(s)  from  %d\n",  count,  p);  
    }  
    return  ret;  
}  

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,  
};  
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;  
    dev->cdev.ops  =  &globalmem_fops;  
    err  =  cdev_add(&dev->cdev,  devno,  1);  
    if  (err)  
        printk(KERN_NOTICE  "Error  %d  adding  LED%d",  err,  index);  
}  
int  globalmem_init(void)  
{  
    int  result;  
    dev_t  devno  =  MKDEV(globalmem_major,  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("Song  Baohua");  
MODULE_LICENSE("Dual  BSD/GPL");  
module_param(globalmem_major,  int,  S_IRUGO);  
module_init(globalmem_init);  
module_exit(globalmem_exit);

在/home/user/globalmem新建 Makefile 文件如下;

ifneq ($(KERNELRELEASE),)
# kbuild syntax. dependency relationshsip of files and target modules are listed here.
obj-m := globalmem.o  
else    
    PWD := $(shell pwd)
    KERNEL_VER ?= $(shell uname -r)
    KERNEL_DIR := /lib/modules/$(KERNEL_VER)/build
all:    
    $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules
clean:    
    rm ‐rf *.o *~ core .depend .*.cmd *.ko *.mod.c
endif

编译globalmem.c

命令行输入:make

目录中得到这些文件:

我没有管这些警告,直接进行下一步。

 

运行insmod globalmem.ko命令加载模块,通过lsmod命令,发现globalmem模块已被加载。再通 过cat /proc/devices命令查看,发现多出了主设备号为150的myglobalmem字符设备驱动,如下所示:

 

查看/proc/device(这个文件列出字符和块设备的主设备号, 以及分配到这些设备号的设备名称)发现设备号为150的globalmem。

编写应用程序test.c用于测试:

#include <sys/types.h> 
#include <sys/stat.h> 
#include <stdio.h> 
#include <string.h> 
#include <fcntl.h> 
main()
{ 
    char str[1024], str2[1024]; 
    int fd, stop; 
    int ret; 
    fd = open("/dev/globalmem", O_RDWR, S_IRUSR | S_IWUSR); 
    if (fd != -1)
    { 
        printf("Please input the string written to globalmem\n"); 
        scanf("%s", str); 
        printf("The str is %s\n", str); 
        ioctl(fd, 1, NULL); 
        ret = write(fd, str, strlen(str)); 
        printf("The ret is %d\n", ret); 
        lseek(fd, 0, SEEK_SET); 
        //scanf("%d", &stop); 
        read(fd, str2, 100); 
        printf("The globalmem is %s\n", str2); 
        close(fd); 
    } 
    else 
    { 
        printf("Device open failure\n"); 
    } 
}

输出结果:

在编译内核时,ubuntu的分区一定要充足,第一次在自己笔记本上编译内核,回来之后发现编译到一般磁盘容量以满,重启后ubuntu进入不了图形界面,只能在shell中输入sudo startx进入。之后只好在实验室编译内核。

在内核编译完后,需要修改globalmem.c中的程序,需要预设的globalmem的主设备号,还将globalmem_ioctl函数修改。并且将Makefile文件改为正确路径。这样就可以正确编译了。

在test.c文件中需要将中文符号改为英文符号即可正确运行。

posted @ 2017-12-13 20:47  奇热行  阅读(223)  评论(0)    收藏  举报