字符设备驱动(三)

dev_fifo.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/export.h>
#include <asm/setup.h>
#include <asm/uaccess.h>
#include <asm/current.h>

MODULE_LICENSE("Dual BSD/GPL");

extern char *saved_command_line;
//指定的主设备号
#define MAJOR_NUM 232

//自己的字符设备
struct mycdev 
{
    int len;
    unsigned char buffer[256];  //全局内存
    struct cdev cdev;       //记录描述字符设备的结构体
};

//设备号 dev_t记录设备类型,字符,块,网络
static dev_t dev_num = {0};

//全局gcd
struct mycdev *gcd;

//设备类
struct class  *cls;

void dev_fifo_info(void)
{       
        printk(KERN_INFO "The process is \"%s\" (pid %i) (state %ld)\n", current->comm,current->pid,current->state);    
        //printk(KERN_INFO "Kernel command line: %s\n", saved_command_line);
}

//打开设备
static int dev_fifo_open(struct inode *inode, struct file *file)
{   
    printk("dev_fifo_open success!\n");
    dev_fifo_info();
    return 0;
}

//读设备
static ssize_t dev_fifo_read(struct file *file, char __user *ubuf, size_t size, loff_t *ppos)
{
    int n;
    int ret;
    char *kbuf;

    printk("read *ppos : %lld\n",*ppos);

    if(*ppos == gcd->len) //都为0返回0
        return 0;

    //请求大大小 > buffer剩余的字节数 :读取实际记得字节数
    if(size > gcd->len - *ppos) 
        n = gcd->len  - *ppos;  //写进去的长度
    else
        n = size;                               

    printk("n = %d\n",n);
    //从上一次文件位置指针的位置开始读取数据
    kbuf = gcd->buffer + *ppos;

    //拷贝数据到用户空间
    ret = copy_to_user(ubuf,kbuf, n);
    if(ret != 0)
        return -EFAULT;

    //更新文件位置指针的值
    *ppos += n;

    printk("dev_fifo_read success!\n");
    dev_fifo_info();
    return n;
}

//写设备
static ssize_t dev_fifo_write(struct file *file, const char __user *ubuf, size_t size, loff_t *ppos)
{
    int n;
    int ret;
    char *kbuf;

    printk("write *ppos : %lld\n",*ppos);
    //已经到达buffer尾部了
    if(*ppos == sizeof(gcd->buffer))
        return -1;

    //请求大大小 > buffer剩余的字节数(有多少空间就写多少数据)
    if(size > sizeof(gcd->buffer) - *ppos)
        n = sizeof(gcd->buffer) - *ppos;
    else 
        n = size;

    //从上一次文件位置指针的位置开始写入数据
    kbuf = gcd->buffer + *ppos;

    //拷贝数据到内核空间
    ret = copy_from_user(kbuf, ubuf, n);
    if(ret != 0)
        return -EFAULT;

    //更新文件位置指针的值
    *ppos += n;

    //更新dev_fifo.len 
    gcd->len += n;
    printk("dev_fifo_write success!\n");
    dev_fifo_info();
    return n;
}

//设备操作函数接口
static const struct file_operations fifo_operations = {
    .owner = THIS_MODULE,
    .open  = dev_fifo_open,
    .read  = dev_fifo_read,
    .write = dev_fifo_write,
};

//模块入口
int __init dev_fifo_init(void)
{
    int ret;
    struct device *device;

    gcd = kzalloc(sizeof(struct mycdev), GFP_KERNEL);
    if(!gcd){
        return -ENOMEM;
    }

    //设备号 : 主设备号(12bit) | 次设备号(20bit)
    dev_num = MKDEV(MAJOR_NUM, 0);

    //静态注册设备号
    ret = register_chrdev_region(dev_num,1,"dev_fifo");
    if(ret < 0){

        //静态注册失败,进行动态注册设备号
        ret = alloc_chrdev_region(&dev_num,0,1,"dev_fifo");
        if(ret < 0){
            printk("Fail to register_chrdev_region\n");
            goto err_register_chrdev_region;
        }
    }

    //创建设备类
    cls = class_create(THIS_MODULE, "dev_fifo");
    if(IS_ERR(cls)){
        ret = PTR_ERR(cls);
        goto err_class_create;
    }


    //初始化字符设备
    cdev_init(&gcd->cdev,&fifo_operations);

    //添加设备到操作系统
    ret = cdev_add(&gcd->cdev,dev_num,1);
    if (ret < 0)
    {
        goto  err_cdev_add;
    }
    //导出设备信息到用户空间(/sys/class/类名/设备名)
    device = device_create(cls,NULL,dev_num,NULL,"dev_fifo%d",0);
    if(IS_ERR(device)){
        ret = PTR_ERR(device);
        printk("Fail to device_create\n");
        goto err_device_create; 
    }
    printk("Register dev_fifo to system,ok!\n");
    dev_fifo_info();
    return 0;

err_device_create:
    cdev_del(&gcd->cdev);

err_cdev_add:
    class_destroy(cls);

err_class_create:
    unregister_chrdev_region(dev_num, 1);

err_register_chrdev_region:
    return ret;
}

void __exit dev_fifo_exit(void)
{
    //删除sysfs文件系统中的设备
    device_destroy(cls,dev_num );   

    //删除系统中的设备类
    class_destroy(cls);

    //从系统中删除添加的字符设备
    cdev_del(&gcd->cdev);

    //释放申请的设备号
    unregister_chrdev_region(dev_num, 1);
    dev_fifo_info();
    return;
}

module_init(dev_fifo_init);
module_exit(dev_fifo_exit);

Makefile

ifeq ($(KERNELRELEASE),)

    KERNEL_DIR ?=/lib/modules/$(shell uname -r)/build
    PWD :=$(shell pwd)
modules:

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

.PHONY:modules clean 
clean:
    $(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean 
else
obj-m := -DEXPORT_SYMTAB
obj-m := dev_fifo.o
endif

instatll.sh

sudo rmmod dev_fifo 
sudo insmod dev_fifo.ko
sudo chmod 666 /dev/dev_fifo0
sudo dmesg -c

test.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, const char *argv[])
{
    int fd ;
    int n;
    char buf[256];

    fd = open("/dev/dev_fifo0",O_RDWR);
    if(fd < 0){
        perror("Fail to open");
        return -1;
    }
    else
    {
        printf("open successful ,fd = %d\n",fd);
        read(fd, buf, 256);
        printf("读取内部值:%s\n",buf);
        printf("输入一个值: ");
        scanf("%s",buf);
        n = write(fd,buf,strlen(buf));
        read(fd, buf, strlen(buf));
        printf("读取当前值:%s\n",buf);
        printf("write %d bytes!\n",n);

        if(n < 0){
            perror("Fail to write");
            return -1;
        }
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值