Linux字符驱动

学习完了字符驱动,是按照宋宝华的Linux设备驱动开发讲解学习的,代码练习敲了一遍,自己也理解了。
字符驱动主要的就是一些open,close,read,write等操作
通过上层调用到自己写的底层函数

这里写代码片
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>

#define GLOBALMEM_SIZE  0x1000  /*全局内存4kb*/
#define MEM_CLEAR       0x1     /*清除全局内存*/
#define GLOBALMEM_MAJOR 250     /*主设备号*/

static int globalmem_major = GLOBALMEM_MAJOR;

struct globalmem_dev
{
    struct 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)
{
    struct globalmem_dev* dev = filp->private_data;
    return 0;

}

static int globalmem_ioctl(struct inode* inodep, struct file* filp,
        unsigned int cmd,unsigned long arg)
{
    struct globalmem_dev* dev = filp->private_data;

    return 0;

}

static ssize_t globalmem_read(struct file* filp, char __user *buf,
        size_t count, loff_t *ppos)
{
    struct globalmem_dev* dev = filp->private_data;
    unsigned long p = *ppos;
    int ret = 0;
    if (p >= GLOBALMEM_SIZE ) //偏移量超过数组范围
        return 0;
    if (count > GLOBALMEM_SIZE - p) //读的个数太大
        count = GLOBALMEM_SIZE - p;
    //从内核读到用户
    if(copy_to_user(buf,dev->mem + p,count))
        ret = - EFAULT; //EFAULT 参数buf指向无效内存地址
    else {
            *ppos += count;
            ret = count;
            printk(KERN_INFO "read %d bytes from %x\n",count,p);
    }

    return ret;

}

static ssize_t globalmem_write(struct file* filp, char __user *buf,
        size_t count, loff_t *ppos)
{
    struct globalmem_dev* dev = filp->private_data;
    unsigned long p = *ppos;
    int ret = 0;
    if(p >= GLOBALMEM_SIZE) //偏移量超过内核空间大小
        return 0;
    if(count > GLOBALMEM_SIZE - p) //写的个数超过现有的
        count = GLOBALMEM_SIZE - p;
    //从用户写到内核
    if(copy_from_user(dev->mem + p,(void*)buf,count))
        ret = - EFAULT;
    else
    {
        *ppos += count;
        ret = count;
        printk(KERN_INFO "write %d bytes from %x\n", count,dev->mem + p);
    }
    return ret;

}

//filp是内核文件结构体
static loff_t globalmem_llseek(struct file* filp, loff_t offset, int orig)
{
    struct globalmem_dev* dev = filp->private_data;
    int ret;
    //SEEK_SET 0 文件开头
    //SEEK_CUR 1 当前位置
    //SEEK_END 2 文件尾
    switch(orig){
        case 0:
            if(offset < 0){
                ret = - EINVAL;
                break;
            }
            if(offset > GLOBALMEM_SIZE){
                ret = - EINVAL;
                break;
            }
            filp->f_pos = (unsigned int)offset;
            ret = filp->f_pos;
            break;
        case 1:
            if(offset > GLOBALMEM_SIZE){
                ret = - EINVAL;
                break;
            }
            if(filp->f_pos + offset > GLOBALMEM_SIZE){
                ret = - EINVAL;
                break;
            }
            filp->f_pos += offset;
            ret = filp->f_pos;
            break;
        case 2:
            if(offset > 0){ 
                ret = - EINVAL; //在文件末尾,只能偏移赋值
                break;
            }
            if(offset + GLOBALMEM_SIZE < 0){
                ret = - EINVAL;
                break;
            }
            filp->f_pos += offset;
            ret = filp->f_pos;
            break;
        default:
            ;
    }
    return 0;

}
static const struct file_operations globalmem_fops = {
    .owner = THIS_MODULE,
    .open = globalmem_open,
    .read = globalmem_read,
    .write = globalmem_write,
    .release = globalmem_release,
    .llseek = globalmem_llseek,
    .compat_ioctl = globalmem_ioctl
};

static void globalmem_setup_dev(struct globalmem_dev* globalmem_dev,int index)
{
    int err,devno;
    devno = MKDEV(globalmem_major,0);
    cdev_init(&globalmem_dev->cdev,&globalmem_fops);
    err = cdev_add(&globalmem_dev->cdev,devno,1);
    if(err)
    {
        printk(KERN_NOTICE "ERROR %d adding globalmem %d",err,index);
    }
}

static int __init globalmem_init(void)
{
    printk(KERN_INFO "globalmem_init");
    int result;
    dev_t devno = MKDEV(globalmem_major,0);//根据主设备号,或得设备号,一般次设备号为0
    if(globalmem_major != 0)
        register_chrdev_region(devno,1,"globalmem_dev");
    else
    {
        result = alloc_chrdev_region(&devno,0,1,"globalmem_dev");//自动分配一个设备号
        globalmem_major = MAJOR(devno);
    }
    if(result < 0)
        return result;

    //动态申请结构体内存
    globalmem_devp = kmalloc(sizeof(struct globalmem_dev),GFP_KERNEL);
    if(!globalmem_devp)
        goto fail_malloc;
    memset(globalmem_devp,0,sizeof(struct globalmem_dev));
    globalmem_setup_dev(globalmem_devp,0);
    return 0;
fail_malloc:
    unregister_chrdev_region(devno,1);
    return result;
}

static int __exit  globalmem_exit(void)
{
    printk(KERN_INFO "globalmem_exit...");
    dev_t devno;
    devno = MKDEV(globalmem_major,0);
    cdev_del(&globalmem_devp->cdev); // 删除cdev结构体
    unregister_chrdev_region(devno,1);//注销设备
    kfree(globalmem_devp);
    return 0;
}

module_init(globalmem_init);
module_exit(globalmem_exit);

Makefile

KVERS = $(shell uname -r)
obj-m += globalmem_private.o

build:kernel_modules

kernel_modules:
    make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules

insmod:
    insmod globalmem.ko

clean:
    make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules clean

测试程序

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define path "/dev/globalmem_dev"

int main()
{
    char buf[] = "Hello word!\n";
    int fd = open(path,O_RDWR);
    if(fd == -1){
        perror("open");
        exit(1);
    }

    write(fd,buf,sizeof(buf));
    close(fd);
}

执行结果
技术分享

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。