您的位置:首页 > 其它

分享一个char驱动的实例

2016-06-03 09:43 351 查看
一、程序代码

基于缓冲区的char驱动例子

=============================

char_test01.c

该例子支持单缓冲区,支持ioctl
ioctl命令需要写用户态的测试程序buf01.c

=========================================================

/**************************

 * char驱动的测试例子1

 * 驱动采用内核的缓冲区作为设备,用户态可以用echo或cat读写设备文件,字符串存储在缓冲区中;

 * 本驱动支持一个缓冲区硬件

 * 用户态可以通过read/write/ioctl来访问设备

 * Author: yhd

 * Date: 2016-05-11

 **************************/

#include <linux/module.h>

#include <linux/fs.h>

#include <linux/uaccess.h> //copy_to_user

#include <linux/cdev.h> //cdev,用于注册

#include <linux/proc_fs.h>

//定义硬件需要的信息

//为了保证驱动选择的设备号唯一,驱动应选一个没人用的主设备号,可以让内核帮助选一个,还可以查看文件/proc/devices,找一个没人用的

#define DEF_MAJOR    50

#define DEF_SIZE    100

//定义ioctl命令号(32位)

//#define MEM_RESET    101    //清空缓冲区

//#define MEM_RESIZE    102    //设置缓冲区大小

#define MEM_TYPE    'M'

#define MEM_RESET    _IO(MEM_TYPE, 1)

#define MEM_RESIZE    _IOW(MEM_TYPE, 2, int)

//如果一个驱动支持多个硬件,每个硬件都会有自己的信息,包括各自的地址,设备号等;

//驱动应该定义一个私有结构体,用于存储每个硬件各自的数据

//如果驱动只支持一个硬件,则可以不必定义

struct mem_priv {

    char *start;  //缓冲区的基地址

    int buf_size; //缓冲区的大小

    int wp;          //缓冲区的写偏移

    dev_t dev_id; //设备号

    struct cdev mem_cdev; //用于向VFS注册

};

//定义私有结构体的全局变量

static struct mem_priv dev;

//定义char驱动的各个操作函数

//当用户态open文件时调用

//需要分析一下设备有哪些操作必须在open中完成,如果没有,直接返回0

static int

mem_open(struct inode *inode, struct file *filp)

{

    return 0;

}

//当用户态close文件时调用

//完成的工作和open相反

static int

mem_release(struct inode *inode, struct file *filp)

{

    return 0;

}

//当用户态read时调用,返回缓冲区的内容。如:

//ret = read(fd, buf, 10);

//内核的read函数增加了一个参数,用于获取或设置文件内部的偏移

static ssize_t

mem_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)

{

    size_t cnt = min(count, (size_t)(dev.wp-*f_pos));

    if (0==cnt)

        return 0;

    //copy_to_user(to,from,cnt)

    if (copy_to_user(buf,dev.start+*f_pos,cnt))

        return -EFAULT;

    //更新读指针f_pos

    *f_pos += cnt;

    return cnt;

}

//当用户态write时调用,将数据写入缓冲区。如:

//ret = write(fd, "hello", 5);

static ssize_t

mem_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)

{

    size_t cnt = min(count, (size_t)(dev.buf_size-dev.wp));

    if (0 == cnt)

        return 0;

    //copy_from_user(to,from,cnt)

    if (copy_from_user(dev.start+dev.wp,buf,cnt))

        return -EFAULT;

    //更新wp

    dev.wp += cnt;

    return cnt;

}

//当用户态用ioctl向设备发命令时调用

//ret = ioctl(fd, 命令号);

//ret = ioctl(fd, 命令号, 命令参数);

static long

mem_ioctl(struct file *filp, unsigned int req, unsigned long arg)

{

    char *tmp;

    switch (req) {

    case MEM_RESET:

        memset(dev.start, 0, dev.buf_size);

        dev.wp = 0;

        break;

    case MEM_RESIZE: //释放旧缓冲区,分配新的

        tmp = (char *)kzalloc(arg, GFP_KERNEL);

        if (!tmp)

            return -ENOMEM;

        dev.buf_size = arg;

        dev.wp = 0;

        kfree(dev.start);

        dev.start = tmp;

        break;

    default:

        printk("Cannot support ioctl %#x\n", req);

        return -1;

    }

    return 0;

}

//声明并初始化file_operations

static struct file_operations mem_fops = {

    .owner = THIS_MODULE,

    .open = mem_open,

    .release = mem_release,

    .read = mem_read,

    .write = mem_write,

    .unlocked_ioctl = mem_ioctl,

};

static int __init my_init(void)

{

    //1.分配并初始化缓冲区

    dev.buf_size = DEF_SIZE;

    dev.start = (char *)kzalloc(dev.buf_size, GFP_KERNEL);

    if (!dev.start)

        return -ENOMEM;

    dev.wp = 0;

    //2.分配设备号(MAJOR+MINOR)

    dev.dev_id = MKDEV(DEF_MAJOR, 0);

    //3.将选好的设备号和file_ops一起注册到VFS

    //将信息封装到cdev结构体中,然后注册

    cdev_init(&dev.mem_cdev, &mem_fops);

    cdev_add(&dev.mem_cdev, dev.dev_id, 1);

    return 0;

}

static void __exit my_exit(void)

{

    cdev_del(&dev.mem_cdev);

    kfree(dev.start);

}

module_init(my_init);

module_exit(my_exit);

MODULE_AUTHOR("YHD");

MODULE_LICENSE("GPL");

===========

makefile

obj-m:=char_test01.o

KERNELDIR:=/lib/modules/3.19.0-25-generic/build    

PWD:=$(shell pwd)

modules:                                                                                         

    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:

    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

===========

二、测试验证

============================================================

/***********************

 * 用户态的测试例子

 * $>./user01 /dev/abc0 reset|resize 1000

 * 可以对缓冲区执行reset或resize命令

 * Author: zht

 * Date: 2016-05-11

 ***********************/

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <fcntl.h>

#include <sys/ioctl.h>

//命令号应该来自于驱动

#define MEM_TYPE    'M'

#define MEM_RESET    _IO(MEM_TYPE, 1)

#define MEM_RESIZE    _IOW(MEM_TYPE, 2, int)

int main(void)

{

    int fd, value, ret;

    char buf[100];

    //2.打开设备文件

    fd = open("/dev/yhd", O_RDWR);

    if (fd<0) {

        printf("Open /dev/yhd error\n");

        exit(1);

    }

    ret = ioctl(fd, MEM_RESET);

    if(ret < 0 )

    {

        printf("itcol error\n");

        exit(1);

        

    }

    ret = write(fd,"hello",5);

    if(ret <0)

    {

        printf("write error\n");

        exit(1);

    }

    //sleep(3);

    ret = write(fd,"asdfg",5);

    if(ret <0)

    {

        printf("write error\n");

        exit(1);

    }

    //ret = ioctl(fd, MEM_RESET);

    ret = read(fd,buf,15);

    if(ret <0)

    {

        printf("write error\n");

        exit(1);

    }

    printf("buf=%s\n",buf);

   close(fd);

    return 0;

}

============================================

需要在dev下创建

mknod  /dev/yhd c 50 0 

生成设备节点,测试两次write 后 ,read buf,看看是否fops×是否可以自动记录

测试结果

buf=helloasdfg
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: