Chinaunix首页 | 论坛 | 博客
  • 博客访问: 380775
  • 博文数量: 149
  • 博客积分: 25
  • 博客等级: 民兵
  • 技术积分: 220
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-27 14:53
文章分类

全部博文(149)

文章存档

2016年(3)

2015年(2)

2014年(144)

我的朋友

分类: LINUX

2014-09-05 07:49:27

Linux下的设备驱动程序被组织为一组完成不同任务的函数的集合,通过这些函数使得Windows的设备操作犹如文件一般。在应用程序看来,硬件 设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作,如open ()、close ()、read ()、write () 等。
  Linux主要将设备分为二类:字符设备和块设备。字符设备是指设备发送和接收数据以字符的形式进行;而块设备则以整个数据缓冲区的形式 进行。字符设备的驱动相对比较简单。下面假设一个非常简单的虚拟字符设备:这个设备中只有一个4个字节的全局变量int global_var,设备的名为"gobalvar"。对"gobalvar"设备的读写等操作即是对其中全局变量global_var的操作。

1、编写globalvar.c文件,代码如下:
#include
#include
#include
#include
#include
#include

MODULE_LICENSE("GPL");

int globalvar_open(struct inode *, struct file *);
int globalvar_release(struct inode *, struct file *);
ssize_t globalvar_read(struct file *, char *, size_t, loff_t *);
ssize_t globalvar_write(struct file *, const char *, size_t, loff_t *);

int dev_major=0;
int dev_minor=0;

struct file_operations globalvar_fos=
{
    .owner=THIS_MODULE,
    .open=globalvar_open,
    .release=globalvar_release,
    .read=globalvar_read,
    .write=globalvar_write,
};

struct globalvar_dev
{
    int global_var;                    //代表要操作的设备
    struct cdev cdev;                  //内核中表示字符设备的结构
};

struct globalvar_dev *my_dev;


static void __exit globalvar_exit(void)       //退出模块时的操作
{
    dev_t devno=MKDEV(dev_major, dev_minor);
    cdev_del(&my_dev->cdev);               //从系统中移除一个字符设备
    kfree(my_dev);                         //释放自定义的设备结构
    unregister_chrdev_region(devno, 1);    //注销已注册的驱动程序
    printk("globalvar unregister success!\n");   
}

static int __init globalvar_init(void)        //初始化模块的操作
{
    int ret, err;

    dev_t devno=MKDEV(dev_major, dev_minor);

    //注册设备号
    if(dev_major)
    {
        ret=register_chrdev_region(devno, 1, "globalvar");
    }
    else
    {
        ret=alloc_chrdev_region(&devno, dev_minor, 1, "globalvar");
        dev_major=MAJOR(devno);
    }
    if(ret<0)
    {
        printk("globalvar register failure!\n");
        globalvar_exit();               //如果注册设备号失败就退出。这个有问题?
        return ret;
    }
    else
    {
        printk("globalvar register success!\n");
    }

    //为设备分配内核空间
    my_dev=kmalloc(sizeof(struct globalvar_dev), GFP_KERNEL);
    if(!my_dev)
    {
        ret=-ENOMEM;
        printk("create device failed!\n");
    }
    else       //初始化设备,添加设备
    {
        my_dev->global_var=0;            //设备变量初始化为0
        cdev_init(&my_dev->cdev, &globalvar_fos);  //初始化设备中的cdev结构
        my_dev->cdev.owner=THIS_MODULE;     //初始化cdev中的所有者字段
        //向内核添加cdev结构,注意到此时用到了devno
        err=cdev_add(&my_dev->cdev, devno, 1);
        if(err<0)        //如果添加字符设备失败,打印错误信息
            printk("add charater device failure!\n");
        else
            printk("add charater device success!\n");
    }
    return ret;
}

//打开操作
int globalvar_open(struct inode *inode, struct file *filp)
{
    struct globalvar_dev *dev;
    //根据inode结构的cdev字段,获得整个设备结构的指针
    dev=container_of(inode->i_cdev, struct globalvar_dev, cdev);
    //分配并填写置于filp->private_data里的数据结构,private_data是跨系统调用时
    //保存状态信息的非常有用的资源   
    filp->private_data=dev;
    return 0;
}

//释放操作
int globalvar_release(struct inode *inode, struct file *filp)
{
    //为什么不释放filp->private_data???
    return 0;
}

//读操作
ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t *off)
{
    struct globalvar_dev *dev=filp->private_data;   //获取已指向分配数据的指针
    //将设备变量值复制到用户空间
    if(copy_to_user(buf, &dev->global_var, sizeof(int)))
        return -EFAULT;
    return sizeof(int);     //返回读取数据字节数
}

//写操作
ssize_t globalvar_write(struct file *filp, const char *buf, size_t len, loff_t *off)
{
    struct globalvar_dev *dev=filp->private_data;   //获取已指向分配数据的指针
    //将用户空间值复制到设备变量
    if(copy_from_user(&dev->global_var, buf, sizeof(int)))
        return -EFAULT;
    return sizeof(int);
}

module_init(globalvar_init);
module_exit(globalvar_exit);
2、编写 Makefile 文件,内容如下:
obj-m:=globalvar.o

KERNELDIR:=/usr/src/linux-headers-2.6.38-8-generic
PWD:=$(shell pwd)

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

modules_install:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
3、当前工作目录开始编译模块:

~/linux_study/CharacterDevice$ make

4、加载模块,并用 lsmod 或 dmesg 查看:

~/linux_study/CharacterDevice$ sudo insmod globalvar.ko
~/linux_study/CharacterDevice$ lsmod
Module Size  Used by globalvar 12630  0 binfmt_misc 13213 1

5、查看动态生成的设备号(主设备号):
~/linux_study/CharacterDevice$ cat /proc/devices
Character devices:
...
usb_device
drm
globalvar

6、使用 mknod 命令创建设备节点文件:

~/linux_study/CharacterDevice$ sudo mknod /dev/globalvar c 250 0

~/linux_study/CharacterDevice$ sudo chgrp staff /dev/globalvar

~/linux_study/CharacterDevice$ sudo chmod 664 /dev/globalvar

7、编写测试程序:
//test.c
#include
#include
#include
#include

main()
{
    int fd, num;

    //可读写方式打开设备
    fd=open("/dev/globalvar", O_RDWR, S_IRWXU|S_IRWXG);
    if(fd!=-1)
    {
        read(fd, &num, sizeof(int));        //读取设备变量
        printf("The globalvar is %d.\n", num);
        printf("Please input the num written to globalvar:\n");
        scanf("%d", &num);
        write(fd, &num, sizeof(int));     //写设备变量
        read(fd, &num, sizeof(int));        //再次读取设备变量
        printf("The globalvar is %d now.\n", num);
        close(fd);     //关闭设备文件
    }
    else
        printf("Device open failure!\n");
}
8、编译 test.c
~/linux_study/CharacterDevice$ gcc -o test test.c
~/linux_study/CharacterDevice$ sudo ./test
The globalvar is 0.
Please input the num written to globalvar:
The globalvar is 20 now.
如果可以出现上面的的提示则证明字符设备驱动编写成功。
以上流程在内核2.6.38.8下调试通过。

阅读(1264) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~