字符设备驱动


在Linux内核中使用cdev结构体来描述字符设备,通过其成员dev_t来定义设备号(分为主、次设备号)以确定字符设备的唯一性。通过其成员file_operations来定义字符设备驱动提供给VFS的接口函数,如常见的open()、read()、write()等。

在Linux字符设备驱动中,模块加载函数通过register_chrdev_region( ) 或alloc_chrdev_region( )来静态或者动态获取设备号,通过cdev_init( )建立cdev与file_operations之间的连接,通过cdev_add( )向系统添加一个cdev以完成注册。模块卸载函数通过cdev_del( )来注销cdev,通过unregister_chrdev_region( )来释放设备号。

用户空间访问该设备的程序通过Linux系统调用,如open( )、read( )、write( ),来“调用”file_operations来定义字符设备驱动提供给VFS的接口函数。

详尽实现代码如下:


/*
 * Copyright (C) Continential- [email protected]
 *
 *The purpose of the following program is to create char devive drivern for realizing
 * the read,write,control and orientation funtion by using Linux system call
 */

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>

#define GLOBALMEM_SIZE    0x1000
#define MEM_CLEAR 0x1
#define CONTI_globalmem_major 230

static int CONTI_globalmem_major = CONTI_globalmem_major;

/*module_param is used to define the parameters for the specified module, three
 * parameters are name, data type and permission respectively*/
module_param(CONTI_globalmem_major, int, S_IRUGO);

/*For the purpose of packaging, cdev and global memory for the specified module is
 *integreted into the CONTI_globalmem_dev object*/
struct CONTI_globalmem_dev {
    struct cdev cdev;
    unsigned char mem[GLOBALMEM_SIZE];
};

struct CONTI_globalmem_dev *CONTI_globalmem_devp;

static int CONTI_globalmem_open(struct inode *inode, struct file *filp)
{
    filp->private_data = CONTI_globalmem_devp;
    return 0;
}

static int CONTI_globalmem_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static long CONTI_globalmem_ioctl(struct file *filp, unsigned int cmd,
                unsigned long arg)
{
    struct CONTI_globalmem_dev *dev = filp->private_data;

    switch (cmd) {
    case MEM_CLEAR:
        memset(dev->mem, 0, GLOBALMEM_SIZE);
        printk(KERN_INFO "globalmem is set to zero\n");
        break;

    default:
        return -EINVAL;
    }

    return 0;
}

static ssize_t CONTI_globalmem_read(struct file *filp, char __user * buf, size_t size,
                  loff_t * ppos)
{
    unsigned long p = *ppos;
    unsigned int count = size;
    int ret = 0;
    struct CONTI_globalmem_dev *dev = filp->private_data;

    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;
    } else {
        *ppos += count;
        ret = count;

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

    return ret;
}

static ssize_t CONTI_globalmem_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 CONTI_globalmem_dev *dev = filp->private_data;

    if (p >= GLOBALMEM_SIZE)
        return 0;
    if (count > GLOBALMEM_SIZE - p)
        count = GLOBALMEM_SIZE - p;

    if (copy_from_user(dev->mem + p, buf, count))
        ret = -EFAULT;
    else {
        *ppos += count;
        ret = count;

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

    return ret;
}

static loff_t CONTI_globalmem_llseek(struct file *filp, loff_t offset, int orig)
{
    loff_t ret = 0;
    switch (orig) {
    case 0:
        if (offset < 0) {
            ret = -EINVAL;
            break;
        }
        if ((unsigned int)offset > GLOBALMEM_SIZE) {
            ret = -EINVAL;
            break;
        }
        filp->f_pos = (unsigned int)offset;
        ret = filp->f_pos;
        break;
    case 1:
        if ((filp->f_pos + offset) > GLOBALMEM_SIZE) {
            ret = -EINVAL;
            break;
        }
        if ((filp->f_pos + offset) < 0) {
            ret = -EINVAL;
            break;
        }
        filp->f_pos += offset;
        ret = filp->f_pos;
        break;
    default:
        ret = -EINVAL;
        break;
    }
    return ret;
}

static const struct file_operations CONTI_globalmem_fops = {
    .owner = THIS_MODULE,
    .llseek = CONTI_globalmem_llseek,
    .read = CONTI_globalmem_read,
    .write = CONTI_globalmem_write,
    .unlocked_ioctl = CONTI_globalmem_ioctl,
    .open = CONTI_globalmem_open,
    .release = CONTI_globalmem_release,
};

static void globalmem_setup_cdev(struct CONTI_globalmem_dev *dev, int index)
{
    int err, devno = MKDEV(CONTI_globalmem_major, index);

    cdev_init(&dev->cdev, &CONTI_globalmem_fops);
    dev->cdev.owner = THIS_MODULE;
    err = cdev_add(&dev->cdev, devno, 1);
    if (err)
        printk(KERN_NOTICE "Error %d adding globalmem%d", err, index);
}

static int __init globalmem_init(void)
{
    int ret;
    dev_t devno = MKDEV(CONTI_globalmem_major, 0);

    if (CONTI_globalmem_major)
        ret = register_chrdev_region(devno, 1, "globalmem");
    else {
        ret = alloc_chrdev_region(&devno, 0, 1, "globalmem");
        CONTI_globalmem_major = MAJOR(devno);
    }
    if (ret < 0)
        return ret;

    CONTI_globalmem_devp = kzalloc(sizeof(struct CONTI_globalmem_dev), GFP_KERNEL);
    if (!CONTI_globalmem_devp) {
        ret = -ENOMEM;
        goto fail_malloc;
    }

    globalmem_setup_cdev(CONTI_globalmem_devp, 0);
    return 0;

 fail_malloc:
    unregister_chrdev_region(devno, 1);
    return ret;
}
module_init(globalmem_init);

static void __exit globalmem_exit(void)
{
    cdev_del(&CONTI_globalmem_devp->cdev);
    kfree(CONTI_globalmem_devp);
    unregister_chrdev_region(MKDEV(CONTI_globalmem_major, 0), 1);
}
module_exit(globalmem_exit);

MODULE_AUTHOR("Hemingway <[email protected]>");
MODULE_LICENSE("GPL v2");

				
时间: 2024-12-13 14:08:07

字符设备驱动的相关文章

register_chrdev_region/alloc_chrdev_region和cdev注册字符设备驱动

内核提供了三个函数来注册一组字符设备编号,这三个函数分别是 register_chrdev_region().alloc_chrdev_region() 和 register_chrdev(). (1)register_chrdev  比较老的内核注册的形式   早期的驱动(2)register_chrdev_region/alloc_chrdev_region + cdev  新的驱动形式 (3)register_chrdev()函数是老版本里面的设备号注册函数,可以实现静态和动态注册两种方法

linux字符设备驱动

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

linux 字符设备驱动开发详解

一.设备的分类及特点 1.字符设备 字符设备是面向数据流的设备,没有请求缓冲区,对设备的存取只能按顺序按字节的存取而不能随机访问.    Linux下的大多设备都是字符设备.应用程序是通过字符设备节点来访问字符设备的.通常至少需要实现 open, close, read, 和 write 等系统调用.    设备节点一般都由mknod命令都创建在/dev目录下,包含了设备的类型.主/次设备号以及设备的访问权限控制等,如:crw-rw----  1 root  root 4, 64 Feb 18

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

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

13、字符设备驱动的使用

编译和安装驱动 下面是通过一个例子来学会使用驱动程序: 1---驱动程序: Memdev.c #include <linux/module.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/cdev.h> #include <asm/uaccess.h> int dev1_registers[5]; int dev2_registers[5]; struct cdev

linux设备驱动之字符设备驱动模型(2)

在上一篇中我们已经了解了字符设备驱动的原理,也了解了应用层调用内核函数的机制,但是我们每次操作设备,都必须首先通过mknod命令创建一个设备文件名,比如说我们要打开u盘,硬盘等这些设备,难道我们还要自己创建,就如同刘老师常说的一句话,这也太山寨了吧,所以我们今天我们来点比较专业的,让函数帮我们自动创建: 在Linux 下,设备和驱动通常都需要挂接在一种总线上,总线有PCI.USB.I2C.SPI 等等,总线是处理器和设备之间的通道,在设备模型中,所有的设备都通过总线相连,一总线来管理设备和驱动函

字符设备驱动模型

1.设备描述结构cdev驱动模型种类繁多,这就需要我从众多的模型中提取出他们的一些共性:a.驱动初始化a.1 分配设备描述结构a.2 初始化设备描述结构a.3 注册设备描述结构a.4 硬件初始化b.实现设备操作c.驱动注销 ------------------------------------------------------------------ 设备描述结构:在任何一种驱动模型中,设备都会用的内核中的一种结构来描述,我们的字符设备在内核中使用struct cdev 来来描述.struc

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是根文件系统下

字符设备驱动框架

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 <