字符设备驱动框架

scull from 《Linux设备驱动程序》

memdev.c

/*
 * memdev.c
 * create at 2015/01/07
 * 字符设备驱动程序框架
 */
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>#include <linux/slab.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/uaccess.h>

#include "memdev.h"

static mem_major = MEMDEV_MAJOR;

module_param(mem_major, int, S_IRUGO);

/* 设备结构体指针 */
struct mem_dev *mem_devp;

struct cdev cdev;

/* 文件打开函数 */
int mem_open(struct inode *inode, struct file *filp)
{
    struct mem_dev *dev;

    /* 获取次设备号 */
    int num = MINOR(inode->i_rdev);

    if (num > MEMDEV_NR_DEVS)
    return -ENODEV;
    dev = &mem_devp[num];

    filp->private_data = dev;

    return 0;
}

/* 文件释放函数 */
int mem_release(struct inode *inode, struct file *filp)
{
    return 0;
}

/* 读函数 */
static ssize_t mem_read(struct file *filp, char __user *buf, size size, loff_t *ppos)
{
    unsigned long p = *ppos;
    unsigned int count = size;
    int ret = 0;
    struct mem_dev *dev = filp->private_data;

    /* 判断读位置是否有效 */
    if (p >= MEMDEV_SIZE)
    return 0;
    if (count > MEMDEV_SIZE - p)
    count = MEMDEV_SIZE - p;

    /*读数据到用户空间*/
    if (copy_to_user(buf, (void*)(dev->data + p), count))
    {
    ret = - EFAULT;
    }
    else
    {
    *ppos += count;
    ret = count;

    printk(KERN_INFO "read %u bytes(s) from %lu\n", count, p);
    }

    return ret;
}

/*写函数*/
static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
    unsigned long p = *ppos;
    unsigned int count = size;
    int ret = 0;
    struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/

    /*分析和获取有效的写长度*/
    if (p >= MEMDEV_SIZE)
    return 0;
    if (count > MEMDEV_SIZE - p)
    count = MEMDEV_SIZE - p;

    /*从用户空间写入数据*/
    if (copy_from_user(dev->data + p, buf, count))
    ret = - EFAULT;
    else
    {
    *ppos += count;
    ret = count;

    printk(KERN_INFO "written %u bytes(s) from %lu\n", count, p);
    }

    return ret;
}

/* seek文件定位函数 */
static loff_t mem_llseek(struct file *filp, loff_t offset, int whence)
{
    loff_t newpos;

    switch(whence) {
    case 0: /* SEEK_SET */
        newpos = offset;
        break;

    case 1: /* SEEK_CUR */
        newpos = filp->f_pos + offset;
        break;

    case 2: /* SEEK_END */
        newpos = MEMDEV_SIZE -1 + offset;
        break;

    default: /* can‘t happen */
        return -EINVAL;
    }
    if ((newpos<0) || (newpos>MEMDEV_SIZE))
    return -EINVAL;

    filp->f_pos = newpos;
    return newpos;
}

/*文件操作结构体*/
static const struct file_operations mem_fops =
{
    .owner   = THIS_MODULE,
    .llseek  = mem_llseek,
    .read    = mem_read,
    .write   = mem_write,
    .open    = mem_open,
    .release = mem_release,
};

/*设备驱动模块加载函数*/
static int memdev_init(void)
{
    int result;
    int i;

    dev_t devno = MKDEV(mem_major, 0);

    /* 静态申请设备号*/
    if (mem_major)
    result = register_chrdev_region(devno, 2, "memdev");
    else /* 动态分配设备号 */
    {
    result = alloc_chrdev_region(&devno, 0, 2, "memdev");
    mem_major = MAJOR(devno);
    }

    if (result < 0)
    return result;

    /*初始化cdev结构*/
    cdev_init(&cdev, &mem_fops);
    cdev.owner = THIS_MODULE;
    cdev.ops = &mem_fops;

    /* 注册字符设备 */
    cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);

    /* 为设备描述结构分配内存*/
    mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL);
    if (!mem_devp) /*申请失败*/
    {
    result = - ENOMEM;
    goto fail_malloc;
    }
    memset(mem_devp, 0, sizeof(struct mem_dev));

    /*为设备分配内存*/
    for (i=0; i < MEMDEV_NR_DEVS; i++)
    {
        mem_devp[i].size = MEMDEV_SIZE;
        mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);
        memset(mem_devp[i].data, 0, MEMDEV_SIZE);
    }

    return 0;

fail_malloc:
    unregister_chrdev_region(devno, 1);

    return result;
}

/*模块卸载函数*/
static void memdev_exit(void)
{
    cdev_del(&cdev); /*注销设备*/
    kfree(mem_devp); /*释放设备结构体内存*/
    unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*释放设备号*/
}

MODULE_AUTHOR("Elvalad");
MODULE_LICENSE("GPL");

module_init(memdev_init);
module_exit(memdev_exit);

memdev.h

/*
 * memdev.h
 * create at 2015/01/07
 */

#ifndef _MEMDEV_H_
#define _MEMDEV_H_

#ifndef MEMDEV_MAJOR
#define MEMDEV_MAJOR 260 /*预设的mem的主设备号*/
#endif

#ifndef MEMDEV_NR_DEVS
#define MEMDEV_NR_DEVS 2 /*设备数*/
#endif

#ifndef MEMDEV_SIZE
#define MEMDEV_SIZE 4096
#endif

/*mem设备描述结构体*/
struct mem_dev
{
  char *data;
  unsigned long size;
};

#endif /* _MEMDEV_H_ */

Makefile

ifneq ($(KERNELRELEASE),)
obj-m:=memdev.o
else
KERNELDIR:=/lib/modules/$(shell uname -r)/build
PWD:=$(shell pwd)

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

clean:
    rm -rf *.o *.mod.c *.mod.o *.ko
endif

memdevtest.c

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/fcntl.h>

int main()
{
    int fd;
    //写入memdev设备的内容
    char buf[]="it‘s funny!!!";
    //memdev设备的内容读入到该buf中
    char buf_read[4096]; 

    if((fd=open("/dev/memdev",O_RDWR))==-1) //打开memdev设备
    printf("open memdev ERROR\n");
    else
    printf("open memdev SUCCESS!\n");

    printf("buf is %s\n",buf);
    write(fd,buf,sizeof(buf)); //把buf中的内容写入memdev设备
    lseek(fd,0,SEEK_SET); //把文件指针重新定位到文件开始的位置
    read(fd,buf_read,sizeof(buf)); //把memdev设备中的内容读入到buf_read中
    printf("buf_read is %s\n",buf_read);

    return 0;
}

编译字符驱动之后会产生一个memdev.ko文件,然后执行

sudo insmod memdev.ko
lsmod 或者 cat /proc/devices 查看ko是否插入

创建设备节点

sudo mknod memdev c 260 0

编译memdevtest.c程序测试这个字符设备驱动

gcc -o memdevtest memdevtest.c

执行此测试程序,因为设备文件建立在/dev目录下,在执行此程序时因为要打开设备文件,所以需要root权限下执行。

时间: 2024-08-02 02:48:36

字符设备驱动框架的相关文章

SylixOS字符设备驱动框架

1.概述 本文档主要介绍SylixOS中字符设备驱动框架,适用于在SylixOS集成开发环境下进行字符设备驱动开发的学习. 注:文中xxx是指具体设备名,编写对应驱动时,自行命名(如RTC.COMPASS等). 2.SylixOS字符设备驱动简介 字符设备是指只能以字节为单位进行读写的设备,读取数据需按照先后顺序,不能随机读取设备内存中某一数据.常见的字符设备如:鼠标.键盘.串口等. 在SylixOS中,每个字符设备都会在/dev目录下对应一个设备文件,用户程序可通过设备文件(或设备节点)来使用

Linux字符设备驱动框架

字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,常见的字符设备包括鼠标.键盘.显示器.串口等等,当我们执行ls -l /dev的时候,就能看到大量的设备文件,c就是字符设备,b就是块设备,网络设备没有对应的设备文件.编写一个外部模块的字符设备驱动,除了要实现编写一个模块所需要的代码之外,还需要编写作为一个字符设备的代码. 驱动模型 Linux一切皆文件,那么作为一个设备文件,它的操作方法接口封装在struct fi

LCD驱动分析(一)字符设备驱动框架分析

LCD驱动也是字符设备驱动,也遵循字符设备驱动的流程: a. 分配主设备号 b. 构建file_operations结构体中的open,write,read...等函数 c. 调用register_chrdev()函数注册字符设备 d. 调用class_register()注册类 e. 调用device_create()创建设备,linux会在sysfs目录下自动创建字符设备. 以上的步骤同样适用于分析输入子系统,只不过上面的各个步骤可能分散在不同的文件与函数中完成. 1.linux/drive

linux驱动开发(三) 字符设备驱动框架

还是老规矩先上代码 demo.c #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/cdev.h> #include <linux/fs.h> int demo_major = 250; int demo_minor = 0; int demo_count = 1; struct cdev cdev; int de

linux驱动开发(三) 字符设备驱动框架(自动创建设备节点)

代码如下 #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/device.h> int demo_major = 250; int demo_minor = 0; int demo_count = 1; stru

字符设备驱动、平台设备驱动、设备驱动模型、sysfs的关系

Linux驱动开发的童鞋们来膜拜吧:-)  学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sysfs等相关概念和技术.对于初学者来说会非常困惑,甚至对Linux有一定基础的工程师而言,能够较好理解这些相关技术也相对不错了.要深刻理解其中的原理需要非常熟悉设备驱动相关的框架和模型代码.网络上有关这些技术的文章不少,但多是对其中的某一点进行阐述,很难找到对这些技术进行比较和关联的分析.对于开发者而言,能够熟悉某一点并分享出来已很难得,但对于专注传授技术和经验给

[kernel]字符设备驱动、平台设备驱动、设备驱动模型、sysfs几者之间的比较和关联

转自:http://www.2cto.com/kf/201510/444943.html Linux驱动开发经验总结,绝对干货! 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sysfs等相关概念和技术.对于初学者来说会非常困惑,甚至对Linux有一定基础的工程师而言,能够较好理解这些相关技术也相对不错了.要深刻理解其中的原理需要非常熟悉设备驱动相关的框架和模型代码.网络上有关这些技术的文章不少,但多是对其中的某一点进行阐述,很难找到对这些技术进行比较和关

Linux字符设备驱动剖析

一.先看看设备应用程序 1.很简单,open设备文件,read.write.ioctl,最后close退出.如下: intmain(int argc ,char *argv[]){ unsigned char val[1] = 1; int fd =open("/dev/LED",O_RDWR);//打开设备 write(fd,val,1);//写入设备,这里代表LED全亮 close(fd);//关闭设备 return 0; } 二./dev目录与文件系统 2./dev是根文件系统下

Linux实现字符设备驱动的基础步骤

Linux应用层想要操作kernel层的API,比方想操作相关GPIO或寄存器,能够通过写一个字符设备驱动来实现. 1.先在rootfs中的 /dev/ 下生成一个字符设备.注意主设备号 和 从设备号.可用例如以下shell脚本生成: if [ ! -e audioIN ];then sudo mknod audioIN c 240 0 fi 生成的设备为 /dev/audioIN ,主设备号240,从设备号0. 2.写audioINdriver.ko ,audioINdriver.c 基本代码