Linux内核(2)——设备驱动实验

今天做了Linux课设关于设备驱动的实验。

实验采用模块的方法编写一个可以进行简单读写的字符设备驱动,该设备可以存储一定长的字符串,写入设备即可以将字符串存入设备,读出即可以获取该字符串,并编写了测试程序对其测试。

 

(1)首先新建并编写了字符驱动设备chardev.c文件,文件代码如下:

/*

* 创建一个字符设备(读写)

*/

/* 必要的头文件,内核模块标准头文件 */

#include<linux/init.h>

#include<linux/kernel.h> /*内核工作*/

#include<linux/slab.h>/**/

#include<linux/vmalloc.h>

#include<linux/module.h> /*明确指定是模块*/

#include<linux/moduleparam.h>

/*对于字符设备*/

#include<linux/fs.h> /*字符设备定义*/

#include<linux/cdev.h>

#include<asm/system.h>

#include<asm/uaccess.h>

 

MODULE_AUTHOR("author");

MODULE_LICENSE("GPL");

 

struct char_dev *char_device;

int dev_major=0;

int dev_minor=0;

 

module_param(dev_major,int,S_IRUGO);

module_param(dev_minor,int,S_IRUGO);

 

//设备存储区的指针

char *p_mem=NULL;

//设备存储区的大小

long len=1000;

 

 

//表示设备的数据结构

struct char_dev

{

         char * data; // 模块中的数据

         long len; // 数据长度

         struct cdev cdev; //Linux 字符设备结构,由系统定义 

};

 

 

int char_open(struct inode *inode,struct file *filp)

{

         struct char_dev *dev;

         //inode 获取设备结构体

         dev=container_of(inode->i_cdev,struct char_dev,cdev);

         //赋值给file 结构体

         filp->private_data=dev;

         return 0;

}

 

 

//读时将调用的函数

static ssize_t char_read(struct file * filp, char __user *buf, size_t count, loff_t *offset)

{

             char * buffer = (char *)filp->private_data;   

      if(copy_to_user(buf, buffer, count))

             {

                 printk("copy_to_user error/n");

                 return -EFAULT;

             }

         printk("You are using the read function!");

             return count ;

}

 

 

//参数定义和char_read 类似

static ssize_t char_write(struct file * filp, const char __user *buf, size_t count, loff_t *offset)

{

     char * buffer = (char *)filp->private_data;

             //printk("new  %p/n", buffer);

             if(copy_from_user(buffer, buf, count))

             {

                   printk("copy_from_user error/n");

                 return -EFAULT;

             }

             return count;

}

 

 

//读写完毕后调用的函数

int char_release(struct inode *inode,struct file *filp)

{

         return 0;

}

 

 

//定义设备节点文件的操作

static struct file_operations char_ops={

.owner = THIS_MODULE,

.open = char_open,

.read = char_read,

.write = char_write,

.release =char_release

};

 

 

//设备初始化时调用的函数,用于获取存储区内存空间

int memory_init(void)

{

         p_mem=vmalloc(len*sizeof(char));

         if(!p_mem)

         {

                   printk(KERN_ALERT "error-memory_init/n");

                   return -1;

         }

         return 1;

}

 

 

//设备初始化

static int dev_init(void)

{

         int result=0;

         dev_t dev=0;

         dev_t devno=0;

         int err=0;

        

         //获取存储区

         //如果定义了主设备号

         if(dev_major)

         {

                   //则按照定义的设备号注册设备

                   dev=MKDEV(dev_major,dev_minor);

                   result=register_chrdev_region(dev,1,"char_dev");

         }else{

                   //否则分配新的设备号

                   result=alloc_chrdev_region(&dev,dev_minor,1,"char_dev");

                   dev_major=MAJOR(dev);

         }

         if(result < 0)

         {

                   printk(KERN_ALERT "can't get major %d/n",dev_major);

                   return result;

         }

         //返回主设备号

         printk("the dev_major %d/n",dev_major);

         //获取全局char_dev 结构体

         char_device = kmalloc(sizeof(struct char_dev),GFP_KERNEL);

         if(!char_device)

         {

                   result=-ENOMEM;

                   return result;

         }

         memset(char_device,0,sizeof(struct char_dev));

         //使用定义了的文件操作char_ops 初始化cdev

         cdev_init(&char_device->cdev, &char_ops);

         char_device->cdev.owner = THIS_MODULE;

         char_device->cdev.ops = &char_ops;

         //使用后区的设备号注册设备

         devno=MKDEV(dev_major,dev_minor);

         //添加此字符设备到系统

         err=cdev_add(&char_device->cdev,devno,1);

         char_device->data=p_mem;

         char_device->len=len;

         return 0;

}

 

 

//设备被移出时调用

static void dev_exit(void)

{

         dev_t devno=0;

 

         devno = MKDEV(dev_major,dev_minor);

         cdev_del(&char_device->cdev);

         kfree(char_device);

         unregister_chrdev_region(devno,1);

}

 

 

//注册模块初始化和卸载函数

module_init(dev_init);

module_exit(dev_exit);

 

 

 

 

(2)然后编写相应的Makefile文件:

# Makefile

# If KERNELRELEASE is defined, we've been invoked from the

# kernel build system and can use its language.

ifneq ($(KERNELRELEASE),)

obj-m := chardev.o

# Otherwise we were called directly from the command

# line; invoke the kernel build system.

else

KERNELDIR ?= /lib/modules/`uname -r`/build

default:

         make -C $(KERNELDIR) M=`pwd` modules

endif

 

 

 

 

(3)使用make 命令,生成驱动程序chardev.ko

(4)root 挂载设备

    insmod chardev.ko

(5)在文件系统为其创建一个代表节点(建立设备文件)

创建节点命令格式如下:

mknod /dev/<dev_name><type><major_number><minor_number>

例如(若主设备号为249)

   mknod mychardev0 c 249 0

 

 

 

 

(6)修改属性:

   chmod 666 mychardev*

 

 

 

 

(7)设备挂载后,就能够使用系统命令写入数据和读取数据啦~

如读操作:more mychardev0

(友情提示:mychardev0会很大,若想体验计算机编码的奇妙可尝试cat命令)

 

 

 

 

(8)编写测试程序test.cpp:

//test.cpp

#include<stdio.h>

#include<fcntl.h>

#include<stdlib.h>

#include<iostream>

using namespace std;

 

int main()

{

         int fd;

         char buffer_write[20] = "Hello World!";

         char buffer_read[20] = "Hello China!";

         fd=open("/home/Guest/dev/mychardev0", O_RDWR);

 

         if(fd < 0)

         {

                   cout<<"open dev error!/n";

                   exit(fd);

         }

 

         //向指定设备写入用户输入文本

         cout<<"Please input the text:/n";

         cin>>buffer_write;

         write(fd, buffer_write, 20);

 

         //输出设备中的内容

         read(fd, buffer_read, 20);

         cout<<"指定设备中的内容为:/n"<<buffer_read<<endl;

 

         close(fd);

        

         return 0;

}

 

代码功能:实现了设备的读数据写数据操作,有以下几点值得注意:

fd=open("/home/Guest/dev/mychardev0", O_RDWR);不能只看对应文件夹下是否有mychardev0设备,若打开不成功可能需要重新mknod一下。

iostream不能直接写#include<iostream.h>,而要写:

#include<iostream>

using namespace std;

③关于readwrite函数的第三个参数n

其作用要追溯到copy_to_usercopy_from_user两个函数上,其值就表示要写入或读出的字符串大小(以字节为单位)

 

 

 

你可能感兴趣的:(struct,user,Module,File,buffer,linux内核)