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);
}

执行结果

时间: 2024-08-01 11:29:39

Linux字符驱动的相关文章

05 Linux字符驱动---静态注册

1. mycdev.c 1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/cdev.h> 4 #include <linux/fs.h> 5 6 #define MAJOR_NUM 250 7 8 struct mycdev 9 { 10 struct cdev cdev; 11 }; 12 13 int mycdev_open(struct inode *inod

Linux 字符驱动

char_driver.c 1 #include <linux/module.h> 2 #include <linux/slab.h> //kmalloc 3 #include <linux/cdev.h> //cdev 4 #include <linux/fs.h> //register_chrdev_region 5 #include <linux/device.h> //class_create 6 #include <asm/uac

08 Linux字符驱动---信号量

1. mycdev.c 1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/cdev.h> 4 #include <linux/fs.h> 5 #include <linux/device.h> 6 #include <linux/err.h> 7 #include <linux/uaccess.h> 8 #include <

07 Linux字符驱动---read/write/ioctl

1. mycdev.c 1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/cdev.h> 4 #include <linux/fs.h> 5 #include <linux/device.h> 6 #include <linux/err.h> 7 #include <linux/uaccess.h> 8 #include <

linux字符设备驱动

一.字符设备.字符设备驱动与用户空间访问该设备的程序三者之间的关系. 如图,在Linux内核中使用cdev结构体来描述字符设备,通过其成员dev_t来定义设备号(分为主.次设备号)以确定字符设备的唯一性.通过其成员file_operations来定义字符设备驱动提供给VFS的接口函数,如常见的open().read().write()等. 在Linux字符设备驱动中,模块加载函数通过register_chrdev_region( ) 或alloc_chrdev_region( )来静态或者动态获

linux设备驱动第三篇:写一个简单的字符设备驱动

在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存. 下面就开始学习如何写一个简单的字符设备驱动.首先我们来分解一下字符设备驱动都有那些结构或者方法组成,也就是说实现一个可以使用的字符设备驱动我们必须做些什么工作. 1.主设备号和次设备号 对于字符设备的访问是通过文件系统中的设备名称进行的.他们通常位于/dev目录下.如下: [plain] vie

Linux设备驱动基本框架(字符设备)

Linux设备驱动都是以内核模块的形式出现的,但模块不一定是驱动.驱动可以编译进内核,在配置内核时,如果把某个配置项设为m,那么它将会随着内核编译时被编译成一个模块,但是这样向内核添加驱动程序会使得内核变得很大,而且在增加.修改.删除驱动程序时需要再一次编译内核,这样做极为麻烦.所以一般的驱动程序都是采用模块化装载,在需要使用时候通过insmod装载进内核,不需要使用时用rmmod卸载驱动模块. 内核模块的主要相关命令: lsmod:查看当前内核装载有哪些模块 insmod:加载模块 rmmod

深入浅出~Linux设备驱动之字符设备驱动

一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面向流的设备,常见的字符设备有鼠标.键盘.串口.控制台和LED设备等. 块设备:是指可以从设备的任意位置读取一定长度数据的设备.块设备包括硬盘.磁盘.U盘和SD卡等. 每一个字符设备或块设备都在/dev目录下对应一个设备文件.linux用户程序通过设备文件(或称设备节点)来使用驱动程序操作字符设备和块设备

linux设备驱动第三篇:如何写一个简单的字符设备驱动?

在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存. 下面就开始学习如何写一个简单的字符设备驱动.首先我们来分解一下字符设备驱动都有那些结构或者方法组成,也就是说实现一个可以使用的字符设备驱动我们必须做些什么工作. 1.主设备号和次设备号 对于字符设备的访问是通过文件系统中的设备名称进行的.他们通常位于/dev目录下.如下: [email prot