分类: 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下调试通过。