嵌入式 LINUX 驱动开发 day02 字符设备驱动 字符设备驱动 虚拟串口, 一个驱动支持多个设备

1. 驱动开发 字符设备驱动

代码: 

vser.c

#include 
#include 
#include 

#include    
/***** 设备相关信息 ******/
static unsigned int VSER_MAJOR = 256;      //主设备号
static unsigned int VSER_MINOR = 0;        //次设备号
#define VSER_DEV_CNT   1        //个数为1个
#define VSER_DEV_NAME  "vser"   //设备名称

/***** 字符设备注册步骤 ***************************************************
 *        初始化函数
 * 1.将主设备和次设备组合成设备号   MKDEV(主设备号,次设备号)
 * 2.将该设备号注册到内核         register_chrdev_region(设备号,设备个数,设备名称) 或 
 *        注销函数
 * 1.将申请的设备号注销           unregister_chrdev_region(设备号,设备个数) 
 * 
 * 跟文件系统下创建 设备文件 cat /proc/devices  
 * 查看到设备信息  
            Character devices:
            1 mem
            256 vser   我们自己申请的设备信息
            4 /dev/vc/0
            4 tty
            4 ttyS
            5 /dev/tty
            5 /dev/console  
 * ***********************************************************************/

static int __init vser_init(void)
{
    int ret;    //用于获取函数返回值,并该函数是否执行成功

    dev_t dev;  //用于存储设备号
    dev = MKDEV(VSER_MAJOR,VSER_MINOR); //组合成设备号 (VSER_MAJOR << 20) | VSER_MINOR

    ret = register_chrdev_region(dev,VSER_DEV_CNT,VSER_DEV_NAME);
    if(ret != 0)
    {
        printk("静态申请设备号失败\n");

        ret = alloc_chrdev_region(&dev,VSER_MINOR,VSER_DEV_CNT,VSER_DEV_NAME);
        if(ret != 0)
        {
            printk("动态申请设备号失败\n");
            goto register_err;
        }
        VSER_MAJOR = MAJOR(dev);    //从设备号中提取主设备号
        VSER_MINOR = MINOR(dev);    //从设备号中提取次设备号
    }

    return 0;   //表示成功,直接结束函数并返回0

register_err:   /* 设备号申请失败 */
    return ret;
    
}

static void __exit vser_exit(void)
{
    dev_t dev;  //用于存储设备号
    dev = MKDEV(VSER_MAJOR,VSER_MINOR); //组合成设备号 (VSER_MAJOR << 20) | VSER_MINOR

    unregister_chrdev_region(dev,VSER_DEV_CNT);
}

/****** 加入到内核 *******/
module_init(vser_init);
module_exit(vser_exit);
/****** 模块说明 ********/
MODULE_LICENSE("GPL");  /*开元许可协议:GPL协议*/

Makefile

#动态编译内核驱动生成.ko文件的Makeifle

#自己的模块代码名
obj-m = vser.o	#就会生成一个 vser.ko 文件

#内核源代码路径
ifeq ($(ARCH),arm)
	KERNELDIR ?= /home/student/linux-5.4.31
else
	KERNELDIR ?= /lib/modules/${shell uname -r}/build
endif

#当前模块路径
PWD ?= $(shell pwd)

#编译源码生成 .ko 文件 make all
all:
	${MAKE} -C ${KERNELDIR} M=${PWD} modules
#伪代码之清除垃圾
clean:
	rm Module.* modules.* *.mod *.ko


1.  命令:  make    (编译形成vser .ko 文件)

嵌入式 LINUX 驱动开发 day02 字符设备驱动 字符设备驱动 虚拟串口, 一个驱动支持多个设备_第1张图片


2.  命令 :  mknod  /dev/vser0  c  256 0        (创建一个字符设备)

解释
mknod  == make node  //创建一个节点
/dev/vser0  设备名称
c   字符设备
256  主设备号
0    次设备号

 


3. 命令  :  ls -l  /dev/vser0(设备查看存在)


4.命令:  sudo rmmod vser  (卸载模块   防止之前的模块没有卸载)

 


5. 命令 :  sudo  insmod vser.ko   (加载  模块)


 6. 命令 :  lsmod   ( 查看模块是否加载)

  嵌入式 LINUX 驱动开发 day02 字符设备驱动 字符设备驱动 虚拟串口, 一个驱动支持多个设备_第2张图片


7. 命令:  dmesg  (查看  内核模块的输出   比如说  printk)(这个没有内核信息打印)


 8. 过程多差不多!!!!    (下面的 我将不写过程了,,除了一个驱动 多个设备)


2. 字符设备注册

代码: 

vser.c

#include 
#include 
#include 

#include    
#include 
/***** 设备相关信息 ******/
static unsigned int VSER_MAJOR = 256;      //主设备号
static unsigned int VSER_MINOR = 0;        //次设备号
#define VSER_DEV_CNT   1        //个数为1个
#define VSER_DEV_NAME  "vser"   //设备名称

/**** 字符设备结构体 ******/
static struct cdev vser_cdev;   //字符设备结构体

/**** 设备操作相关函数 到时候就可以使用 open 打开该设备文件 *****/
int vser_open(struct inode *p_inode, struct file *p_file)
{
    printk("vser设备打开了\n");
    return 0;
}

/**** 设备操作集 *****/
static struct file_operations vser_ops = {  //设备操作集结构体
    .owner = THIS_MODULE,
    .open = vser_open,
};


/***** 字符设备注册步骤 ***************************************************
 *        初始化函数
 * 1.将主设备和次设备组合成设备号     MKDEV(主设备号,次设备号)
 * 2.将该设备号注册到内核           register_chrdev_region(设备号,设备个数,设备名称) 或 
 * 3.初始化字符设备                cdev_init(字符设备结构体地址,操作集结构体地址)
 * 4.将字符设备添加到内核散列表map中  cdev_add(字符设备结构体地址,设备号,设备个数)
 *        注销函数
 * 1.从内核散链表map中删除字符设备    cdev_del(字符设备结构体地址)
 * 2.将申请的设备号注销              unregister_chrdev_region(设备号,设备个数) 
 * 
 * 跟文件系统下创建 设备文件 cat /proc/devices  
 * 查看到设备信息  
            Character devices:
            1 mem
            256 vser   我们自己申请的设备信息
            4 /dev/vc/0
            4 tty
            4 ttyS
            5 /dev/tty
            5 /dev/console  
 * ***********************************************************************/

static int __init vser_init(void)
{
    int ret;    //用于获取函数返回值,并该函数是否执行成功

    /*********  申请设备号 **************/
    dev_t dev;  //用于存储设备号
    dev = MKDEV(VSER_MAJOR,VSER_MINOR); //组合成设备号 (VSER_MAJOR << 20) | VSER_MINOR

    ret = register_chrdev_region(dev,VSER_DEV_CNT,VSER_DEV_NAME);
    if(ret != 0)
    {
        printk("静态申请设备号失败\n");

        ret = alloc_chrdev_region(&dev,VSER_MINOR,VSER_DEV_CNT,VSER_DEV_NAME);
        if(ret != 0)
        {
            printk("动态申请设备号失败\n");
            goto register_err;
        }
        VSER_MAJOR = MAJOR(dev);    //从设备号中提取主设备号
        VSER_MINOR = MINOR(dev);    //从设备号中提取次设备号
    }

    /******** 申请字符设备 *********/
    cdev_init(&vser_cdev,&vser_ops);
    vser_cdev.owner = THIS_MODULE;
    ret = cdev_add(&vser_cdev,dev,VSER_DEV_CNT);
    if(ret != 0)
    {
        printk("申请字符设备失败\n");
        goto cdev_err;
    }


    return 0;   //表示成功,直接结束函数并返回0

cdev_err:       /* 申请字符设备失败 */
    unregister_chrdev_region(dev,VSER_DEV_CNT); //释放设备号

register_err:   /* 设备号申请失败 */
    return ret;
    
}

static void __exit vser_exit(void)
{
    dev_t dev;  //用于存储设备号
    dev = MKDEV(VSER_MAJOR,VSER_MINOR); //组合成设备号 (VSER_MAJOR << 20) | VSER_MINOR

    cdev_del(&vser_cdev);                       //1.从内核散列表中删除字符设备
    unregister_chrdev_region(dev,VSER_DEV_CNT); //2.释放设备号
}

/****** 加入到内核 *******/
module_init(vser_init);
module_exit(vser_exit);
/****** 模块说明 ********/
MODULE_LICENSE("GPL");  /*开元许可协议:GPL协议*/

main.c     (记得使用  gcc   编译   )

#include 
#include 
#include 
#include 
#include 
int main(int argc,char *argv[])
{
    if(argc != 2)
    {
        return -1;
    }

    int fd = open(argv[1],O_RDWR); //会调用内核的 struct file_operations 结构体中的 open 指针,指针指向 vser_open 函数
    if(fd < 0)
    {
        perror("打开设备失败:");
        return -2;
    }
    
    close(fd);

    return 0;
}

Makefile 

#动态编译内核驱动生成.ko文件的Makeifle

#自己的模块代码名
obj-m = vser.o	#就会生成一个 vser.ko 文件

#内核源代码路径
ifeq ($(ARCH),arm)
	KERNELDIR ?= /home/student/linux-5.4.31
else
	KERNELDIR ?= /lib/modules/${shell uname -r}/build
endif

#当前模块路径
PWD ?= $(shell pwd)

#编译源码生成 .ko 文件 make all
all:
	${MAKE} -C ${KERNELDIR} M=${PWD} modules
#伪代码之清除垃圾
clean:
	rm Module.* modules.* *.mod *.ko


1. 过程:  gcc  mian.c    (把main.c  编译成  a.out)

 


2.  sudo  rmmod  vser  (卸载之前加载的模块)


3. make  (编译  makefile  文件)


4. sudo insmod vser.ko   (加载 模块)


5.命令: lsmod  (查看是否加载)

 



6. dmesg  (查看内核打印的信息, 这个没有内核信息打印)


3.虚拟串口 

代码: 

main_read.c

#include 
#include 
#include 
#include 
#include 
#include 
int main(int argc,char *argv[])
{
    if(argc != 2)
    {
        return -1;
    }

    int fd = open(argv[1],O_RDWR); //会调用内核的 struct file_operations 结构体中的 open 指针,指针指向 vser_open 函数
    if(fd < 0)
    {
        perror("打开设备失败:");
        return -2;
    }
    
    char buf[10];
    while(1)
    {
        memset(buf,0,sizeof(buf));
        read(fd,buf,10);
        printf("读取到:%s\n",buf);
        sleep(1);
    }

    close(fd);

    return 0;
}

 main_write.c

#include 
#include 
#include 
#include 
#include 
#include 
int main(int argc,char *argv[])
{
    if(argc != 2)
    {
        return -1;
    }

    int fd = open(argv[1],O_RDWR); //会调用内核的 struct file_operations 结构体中的 open 指针,指针指向 vser_open 函数
    if(fd < 0)
    {
        perror("打开设备失败:");
        return -2;
    }
    
    char buf[10];
    while(1)
    {
        printf("请输入要写入的数据:");
        scanf("%s",buf);
        write(fd,buf,strlen(buf));
    }

    close(fd);

    return 0;
}

vser.c

#include 
#include 
#include 

#include    
#include 
/***** 设备相关信息 ******/
static unsigned int VSER_MAJOR = 256;      //主设备号
static unsigned int VSER_MINOR = 0;        //次设备号
#define VSER_DEV_CNT   1        //个数为1个
#define VSER_DEV_NAME  "vser"   //设备名称

/**** 字符设备结构体 ******/
static struct cdev vser_cdev;   //字符设备结构体

/**** 虚拟串口 ******/
#include 
DEFINE_KFIFO(vser_fifo,char,32);    //创建一个名为 vser_fifo 的 char 类型 大小为 32 个的队列管道

/**** 设备操作相关函数 到时候就可以使用 open 打开该设备文件 *****/
int vser_open(struct inode *p_inode, struct file *p_file)
{
    printk("vser设备打开了\n");
    return 0;
}

int vser_close(struct inode *p_inode, struct file *p_file)
{
    printk("vser设备关闭了\n");
    return 0;
}

ssize_t vser_read(struct file *p_file, char __user *user_buf, size_t user_size, loff_t *ps)
{   
    int copied = 0;
    
    kfifo_to_user(&vser_fifo,user_buf,user_size,&copied); //将内核队列管道数据拷贝到用户空间

    return copied;
}

ssize_t vser_write(struct file *p_file, const char __user *user_buf, size_t user_size, loff_t *ps)
{
    int copied = 0;

    kfifo_from_user(&vser_fifo,user_buf,user_size,&copied);//将用户空间数据拷贝到内核队列管道中

    return copied;
}

/**** 设备操作集 *****/
static struct file_operations vser_ops = {  //设备操作集结构体
    .owner = THIS_MODULE,
    .release = vser_close,
    .open = vser_open,
    .read = vser_read,
    .write = vser_write,
};


/***** 字符设备注册步骤 ***************************************************
 *        初始化函数
 * 1.将主设备和次设备组合成设备号     MKDEV(主设备号,次设备号)
 * 2.将该设备号注册到内核           register_chrdev_region(设备号,设备个数,设备名称) 或 
 * 3.初始化字符设备                cdev_init(字符设备结构体地址,操作集结构体地址)
 * 4.将字符设备添加到内核散列表map中  cdev_add(字符设备结构体地址,设备号,设备个数)
 *        注销函数
 * 1.从内核散链表map中删除字符设备    cdev_del(字符设备结构体地址)
 * 2.将申请的设备号注销              unregister_chrdev_region(设备号,设备个数) 
 * 
 * 跟文件系统下创建 设备文件 cat /proc/devices  
 * 查看到设备信息  
            Character devices:
            1 mem
            256 vser   我们自己申请的设备信息
            4 /dev/vc/0
            4 tty
            4 ttyS
            5 /dev/tty
            5 /dev/console  
 * ***********************************************************************/

static int __init vser_init(void)
{
    int ret;    //用于获取函数返回值,并该函数是否执行成功

    /*********  申请设备号 **************/
    dev_t dev;  //用于存储设备号
    dev = MKDEV(VSER_MAJOR,VSER_MINOR); //组合成设备号 (VSER_MAJOR << 20) | VSER_MINOR

    ret = register_chrdev_region(dev,VSER_DEV_CNT,VSER_DEV_NAME);
    if(ret != 0)
    {
        printk("静态申请设备号失败\n");

        ret = alloc_chrdev_region(&dev,VSER_MINOR,VSER_DEV_CNT,VSER_DEV_NAME);
        if(ret != 0)
        {
            printk("动态申请设备号失败\n");
            goto register_err;
        }
        VSER_MAJOR = MAJOR(dev);    //从设备号中提取主设备号
        VSER_MINOR = MINOR(dev);    //从设备号中提取次设备号
    }

    /******** 申请字符设备 *********/
    cdev_init(&vser_cdev,&vser_ops);
    vser_cdev.owner = THIS_MODULE;
    ret = cdev_add(&vser_cdev,dev,VSER_DEV_CNT);
    if(ret != 0)
    {
        printk("申请字符设备失败\n");
        goto cdev_err;
    }


    return 0;   //表示成功,直接结束函数并返回0

cdev_err:       /* 申请字符设备失败 */
    unregister_chrdev_region(dev,VSER_DEV_CNT); //释放设备号

register_err:   /* 设备号申请失败 */
    return ret;
    
}

static void __exit vser_exit(void)
{
    dev_t dev;  //用于存储设备号
    dev = MKDEV(VSER_MAJOR,VSER_MINOR); //组合成设备号 (VSER_MAJOR << 20) | VSER_MINOR

    cdev_del(&vser_cdev);                       //1.从内核散列表中删除字符设备
    unregister_chrdev_region(dev,VSER_DEV_CNT); //2.释放设备号
}

/****** 加入到内核 *******/
module_init(vser_init);
module_exit(vser_exit);
/****** 模块说明 ********/
MODULE_LICENSE("GPL");  /*开元许可协议:GPL协议*/

Makefile

#动态编译内核驱动生成.ko文件的Makeifle

#自己的模块代码名
obj-m = vser.o	#就会生成一个 vser.ko 文件

#内核源代码路径
ifeq ($(ARCH),arm)
	KERNELDIR ?= /home/student/linux-5.4.31
else
	KERNELDIR ?= /lib/modules/${shell uname -r}/build
endif

#当前模块路径
PWD ?= $(shell pwd)

#编译源码生成 .ko 文件 make all
all:
	${MAKE} -C ${KERNELDIR} M=${PWD} modules
#伪代码之清除垃圾
clean:
	rm Module.* modules.* *.mod *.ko


1. 编译所有的  .c 文件

gcc main_write.c -o write    (写文件)

gcc main_read.c -o  read    (读文件)

make    (编译  vser.ko 文件)


2. 命令: sudo rmmod  vser   (卸载之前加载的模块)


3. 命令:  sudo vser.ko     (加载现在的模块)


4. 命令: lsmod   (查看模块是否加载)


5. 命令:  sudo ./read   /dev/vser0    (运行读文件     后面的/dev/vser0   是之前创建的字符设备)

              sudo ./write  /dev/vser0     (运行写文件    后面的/dev/vser0   是之前创建的字符设备)

嵌入式 LINUX 驱动开发 day02 字符设备驱动 字符设备驱动 虚拟串口, 一个驱动支持多个设备_第3张图片


6. 命令 : dmesg  (查看内核信息)

嵌入式 LINUX 驱动开发 day02 字符设备驱动 字符设备驱动 虚拟串口, 一个驱动支持多个设备_第4张图片




4. 一个驱动支持多个设备

代码: 

vser.c

#include 
#include 
#include 

#include    
#include 
/***** 设备相关信息 ******/
static unsigned int VSER_MAJOR = 256;      //主设备号
static unsigned int VSER_MINOR = 0;        //次设备号
#define VSER_DEV_CNT   2        //个数为1个
#define VSER_DEV_NAME  "vser"   //设备名称

// mknod /dev/vser0 c 256 0 
// mknod /dev/vser1 c 256 1

/**** 字符设备结构体 ******/
static struct cdev vser_cdev;   //字符设备结构体

/**** 虚拟串口 ******/
#include 
DEFINE_KFIFO(vser_fifo0,char,32);    //创建一个名为 vser_fifo0 的 char 类型 大小为 32 个的队列管道
DEFINE_KFIFO(vser_fifo1,char,32);    //创建一个名为 vser_fifo1 的 char 类型 大小为 32 个的队列管道
/**** 设备操作相关函数 到时候就可以使用 open 打开该设备文件 *****/
int vser_open(struct inode *p_inode, struct file *p_file)
{
    int minor = MINOR(p_inode->i_rdev); //获取设备的次设备号

    printk("vser设备打开了\n");
    printk("当前的次设备号;%d\n",minor);
    switch (minor)
    {
    case 0:
            p_file->private_data = &vser_fifo0;
            
        break;
    case 1:
            p_file->private_data = &vser_fifo1;
        break;
    }

    printk("操作的FIFO = %p\n",p_file->private_data);
    return 0;
}

int vser_close(struct inode *p_inode, struct file *p_file)
{
    printk("vser设备关闭了\n");
    return 0;
}

ssize_t vser_read(struct file *p_file, char __user *user_buf, size_t user_size, loff_t *ps)
{   
    int copied = 0;
    struct kfifo *fifo = p_file->private_data;

    kfifo_to_user(fifo,user_buf,user_size,&copied); //将内核队列管道数据拷贝到用户空间

    return copied;
}

ssize_t vser_write(struct file *p_file, const char __user *user_buf, size_t user_size, loff_t *ps)
{
    int copied = 0;
    struct kfifo *fifo = p_file->private_data;
    printk("操作的FIFO = %p\n",p_file->private_data);
    kfifo_from_user(fifo,user_buf,user_size,&copied);//将用户空间数据拷贝到内核队列管道中

    return copied;
}

/**** 设备操作集 *****/
static struct file_operations vser_ops = {  //设备操作集结构体
    .owner = THIS_MODULE,
    .release = vser_close,
    .open = vser_open,
    .read = vser_read,
    .write = vser_write,
};


/***** 字符设备注册步骤 ***************************************************
 *        初始化函数
 * 1.将主设备和次设备组合成设备号     MKDEV(主设备号,次设备号)
 * 2.将该设备号注册到内核           register_chrdev_region(设备号,设备个数,设备名称) 或 
 * 3.初始化字符设备                cdev_init(字符设备结构体地址,操作集结构体地址)
 * 4.将字符设备添加到内核散列表map中  cdev_add(字符设备结构体地址,设备号,设备个数)
 *        注销函数
 * 1.从内核散链表map中删除字符设备    cdev_del(字符设备结构体地址)
 * 2.将申请的设备号注销              unregister_chrdev_region(设备号,设备个数) 
 * 
 * 跟文件系统下创建 设备文件 cat /proc/devices  
 * 查看到设备信息  
            Character devices:
            1 mem
            256 vser   我们自己申请的设备信息
            4 /dev/vc/0
            4 tty
            4 ttyS
            5 /dev/tty
            5 /dev/console  
 * ***********************************************************************/

static int __init vser_init(void)
{
    int ret;    //用于获取函数返回值,并该函数是否执行成功

    /*********  申请设备号 **************/
    dev_t dev;  //用于存储设备号
    dev = MKDEV(VSER_MAJOR,VSER_MINOR); //组合成设备号 (VSER_MAJOR << 20) | VSER_MINOR

    ret = register_chrdev_region(dev,VSER_DEV_CNT,VSER_DEV_NAME);
    if(ret != 0)
    {
        printk("静态申请设备号失败\n");

        ret = alloc_chrdev_region(&dev,VSER_MINOR,VSER_DEV_CNT,VSER_DEV_NAME);
        if(ret != 0)
        {
            printk("动态申请设备号失败\n");
            goto register_err;
        }
        VSER_MAJOR = MAJOR(dev);    //从设备号中提取主设备号
        VSER_MINOR = MINOR(dev);    //从设备号中提取次设备号
    }

    /******** 申请字符设备 *********/
    cdev_init(&vser_cdev,&vser_ops);
    vser_cdev.owner = THIS_MODULE;
    ret = cdev_add(&vser_cdev,dev,VSER_DEV_CNT);
    if(ret != 0)
    {
        printk("申请字符设备失败\n");
        goto cdev_err;
    }


    return 0;   //表示成功,直接结束函数并返回0

cdev_err:       /* 申请字符设备失败 */
    unregister_chrdev_region(dev,VSER_DEV_CNT); //释放设备号

register_err:   /* 设备号申请失败 */
    return ret;
    
}

static void __exit vser_exit(void)
{
    dev_t dev;  //用于存储设备号
    dev = MKDEV(VSER_MAJOR,VSER_MINOR); //组合成设备号 (VSER_MAJOR << 20) | VSER_MINOR

    cdev_del(&vser_cdev);                       //1.从内核散列表中删除字符设备
    unregister_chrdev_region(dev,VSER_DEV_CNT); //2.释放设备号
}

/****** 加入到内核 *******/
module_init(vser_init);
module_exit(vser_exit);
/****** 模块说明 ********/
MODULE_LICENSE("GPL");  /*开元许可协议:GPL协议*/

main_write.c

#include 
#include 
#include 
#include 
#include 
#include 
int main(int argc,char *argv[])
{
    if(argc != 2)
    {
        return -1;
    }

    int fd = open(argv[1],O_RDWR); //会调用内核的 struct file_operations 结构体中的 open 指针,指针指向 vser_open 函数
    if(fd < 0)
    {
        perror("打开设备失败:");
        return -2;
    }
    
    char buf[10];
    while(1)
    {
        printf("请输入要写入的数据:");
        scanf("%s",buf);
        write(fd,buf,strlen(buf));
    }

    close(fd);

    return 0;
}

main_read.c

#include 
#include 
#include 
#include 
#include 
#include 
int main(int argc,char *argv[])
{
    if(argc != 2)
    {
        return -1;
    }

    int fd = open(argv[1],O_RDWR); //会调用内核的 struct file_operations 结构体中的 open 指针,指针指向 vser_open 函数
    if(fd < 0)
    {
        perror("打开设备失败:");
        return -2;
    }
    
    char buf[10];
    while(1)
    {
        memset(buf,0,sizeof(buf));
        read(fd,buf,10);
        printf("读取到:%s\n",buf);
        sleep(1);
    }

    close(fd);

    return 0;
}

Makefile

#动态编译内核驱动生成.ko文件的Makeifle

#自己的模块代码名
obj-m = vser.o	#就会生成一个 vser.ko 文件

#内核源代码路径
ifeq ($(ARCH),arm)
	KERNELDIR ?= /home/student/linux-5.4.31
else
	KERNELDIR ?= /lib/modules/${shell uname -r}/build
endif

#当前模块路径
PWD ?= $(shell pwd)

#编译源码生成 .ko 文件 make all
all:
	${MAKE} -C ${KERNELDIR} M=${PWD} modules
#伪代码之清除垃圾
clean:
	rm Module.* modules.* *.mod *.ko


1. 命令: sudo /dev/vser*     (卸载之前创建的字符设备模块  )


2.  sudo mknod  /dev/vser1  c  256 0   ( 创建字符设备)

    sudo  mknod  /dev/vser2  c  256 1    (设备名称不同,  次设备号不同 ,  自己看 vser.c  里面的  switch   的数字 决定次设备号)


3.命令:  su   (进入超级用户)

 嵌入式 LINUX 驱动开发 day02 字符设备驱动 字符设备驱动 虚拟串口, 一个驱动支持多个设备_第5张图片


4. 命令:sudo rmmod  vser


5.命令 : make


6. 命令: sudo insmod  vser.ko 


7. 命令: gcc main_read.c  -o read


8. 命令:  gcc main_write.c  -o  write


9. 命令:  echo "我是" >  /dev/vser1

嵌入式 LINUX 驱动开发 day02 字符设备驱动 字符设备驱动 虚拟串口, 一个驱动支持多个设备_第6张图片


10 . 命令: cat /dev/vser1

嵌入式 LINUX 驱动开发 day02 字符设备驱动 字符设备驱动 虚拟串口, 一个驱动支持多个设备_第7张图片

嵌入式 LINUX 驱动开发 day02 字符设备驱动 字符设备驱动 虚拟串口, 一个驱动支持多个设备_第8张图片

 

 嵌入式 LINUX 驱动开发 day02 字符设备驱动 字符设备驱动 虚拟串口, 一个驱动支持多个设备_第9张图片



作业: 

 嵌入式 LINUX 驱动开发 day02 字符设备驱动 字符设备驱动 虚拟串口, 一个驱动支持多个设备_第10张图片

 1.字符设备和块设备的区别不包括()
[A]字符设备按字节流进行访问,块设备按块大小进行访问

[B]字符设备只能处理可打印字符,块设备可以处理二进制数据

[C]多数字符设备不能随机访问,而块设备一定能随机访问

[D]字符设备通常没有页高速缓存,而块设备有


2.在3.14.25版本的内核中,主设备号占———位,次设备号占———位。

[A]8
[B] 16
[C] 12
[D]20


3.用于分配主次设备号的函数是()。
[A] register_chrdev_region
[B] MKDEV
[C]alloc_chrdev_region
[D] MAJOR


4、在字符设备驱动中,struct file_operations 结构中的函数指针成员不包含$,

[A] open
[B] close
[C] read
[D] show_fdinfo

 

你可能感兴趣的:(linux,驱动开发,驱动开发)