/***************************************************************************************************
设备驱动程序
* gvar.c
* 动态分配设备号
* 自动加入节点
* 注意:user.c 要找 /dev/gvar66
* 张永辉 2012年9月4日10:49
***************************************************************************************************/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>
MODULE_LICENSE("GPL");
static int gvar = 0;
dev_t gvar_num; // 设备号
static int gvar_Major = 0; //主设备号
static int gvar_Minor = 0; //次设备号
static struct cdev *gvar_cdev;
static struct class *gvar_class;
/**************************************读写操作****************************************************/
static ssize_t gvar_read (struct file *, char *, size_t, loff_t*);
static ssize_t gvar_write(struct file *, const char *, size_t, loff_t*);
static ssize_t gvar_read(struct file *filp, char *buf, size_t len, loff_t *off)
{
//将global_var从内核空间复制到用户空间
if ( copy_to_user(buf, &gvar, sizeof(int)))
{
return - EFAULT;
}
printk(KERN_ALERT "gvar_read enter ,the data is %d\n",gvar);
return sizeof(int);
}
static ssize_t gvar_write(struct file *filp, const char *buf, size_t len, loff_t *off)
{
//将用户空间的数据复制到内核空间的global_var
if (copy_from_user(&gvar, buf, sizeof(int)))
{
return - EFAULT;
}
printk(KERN_ALERT "gvar_write enter ,the data is %d\n",gvar);
return sizeof(int);
}
/*****************************************创建和注销************************************************/
//file_operations 主要提供的入口有:open ()、release ()、read ()、write ()、ioctl ()、llseek()、poll()等。
//初始化字符设备驱动的file_operations结构体 read: gvar_read, write: gvar_write,
//此写法是非标准C的写法,linux独家提供。
struct file_operations gvar_fops =
{
.write = gvar_write,
.read = gvar_read,
.owner = THIS_MODULE,
};
static int __init gvar_init(void)
{
int ret;
//初始化cdev
gvar_cdev = cdev_alloc();
cdev_init(gvar_cdev, &gvar_fops);
gvar_cdev->owner = THIS_MODULE;
//动态获取主设备号(dev_t devid中包含"主设备号"和"次设备号"信息)
alloc_chrdev_region(&gvar_num, 66, 1, "gvar"); //从66开始分配1个设备号
gvar_Major = MAJOR(gvar_num);
gvar_Minor = MINOR(gvar_num);
printk(KERN_INFO "gvar major number %d \n", gvar_Major);
printk(KERN_INFO "gvar minor number %d \n", gvar_Minor);
//注册字符设备
ret = cdev_add(gvar_cdev, gvar_num, 1);
if (ret)
{
printk(KERN_NOTICE "gvar adding device error %d \n", ret);
return -1;
}
gvar_class = class_create(THIS_MODULE, "gvar_class1");//在/sys目录下建立了一个gvar_class1的目录
if (IS_ERR(gvar_class))
{
printk(KERN_INFO "gvar_class create error\n");
return -1;
}
device_create(gvar_class,NULL,gvar_num,NULL,"gvar" "%d", MINOR(gvar_num));
printk(KERN_ALERT "gvar create seccse , gvar_num = %d \n",gvar_num);
return 0;
}
static void __exit gvar_exit(void)
{
printk(KERN_ALERT "gvar exit success\n");
unregister_chrdev_region(gvar_num, 1);
cdev_del(gvar_cdev);
device_destroy(gvar_class, gvar_num);
class_destroy(gvar_class);
}
module_init(gvar_init);
module_exit(gvar_exit);
/***************************************************************************************************
自动创建设备节点
原理:
内核提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面。
创建好了这个类后调用device_create(…)函数,会在/dev目录下创建相应的设备节点。
加载模块时,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。
关于类 struct class:
定义处: include/linux/device.h
实 现: /drivers/base/class.c
创建:struct class *class_create(struct module *owner, const char *name);
第一个参数指定类的所有者是哪个模块,第二个参数指定类名。
用于动态创建设备的逻辑类,并完成部分字段的初始化,然后将其添加进linux内核系统中。
释放:class_destroy(…)
用于在模块卸载时删除类。
关于设备:
定义:#include <linux/device.h>
实现 /drivers/base/core.c中实现
创建:struct device *device_create(
struct class *class, 要创建的设备所从属的类,
struct device *parent, void类型的指针,代表回调函数的输入参数
dev_t devt, 设备号, 这个设备的父设备,如果没有就指定为NULL,
const char *fmt, 逻辑设备的设备名
...)
释放:void device_destroy(struct class *dev, dev_t devt);
从linux内核系统设备驱动程序模型中移除一个设备,
会删除/sys/devices/virtual目录下对应的设备目录及/dev/目录下对应的设备文件
***************************************************************************************************/
/***************************************************************************************************
使用方法
共三个文件: gvar.c 驱动代码
Makefile 用于编译驱动
user.c 用户程序,会使用gvar驱动。
放到同一目录,做如下操作
#make 编译驱动
#insmod gvar.ko 安装驱动
#lsmod 查看都有哪些驱动,是否成功安装
#gcc user.c 编译用户程序
#./a.out 执行用户程序,处会执行成功
...
#rmmod gvar.ko 卸载驱动
#./a.out 执行用户程序,处会执行失败,以为没有gvar模块了。
...
张永辉 2012年8月28日 suseLinux验证通过。
***************************************************************************************************/
#include <stdio.h>
#include <sys/stat.h> //获得文件的属性,它可以返回一个结构,里面包含文件全部属性
#include <fcntl.h> //设备驱动程序接口是由结构说明,它定义在fcntl.h中
#include <sys/types.h> //类型 clock_t,dev_t,off_t,ptrdiff,size_t,ssize_t,time_t
int main(void)
{
int fd, num;
fd = open("/dev/gvar66", O_RDWR, S_IRUSR | S_IWUSR);
printf("fd = %d \n",fd);
if (fd != -1 )
{
read(fd, &num, sizeof(int));
printf("The gvar is %d\n", num);
printf("Please input the num written to gvar\n");
scanf("%d", &num);
write(fd, &num, sizeof(int));
read(fd, &num, sizeof(int));
printf("The gvar is %d\n", num);
close(fd);
}else
{
printf("Device open failure\n");
}
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
obj-m := gvar.o #模块编译的目标必须以obj-m 这样的形式指出
KERNELDIR ?= /lib/modules/$(shell uname -r)/build #模块的编译必须指定内核源代码的路径
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules #本行开头必须使用 Tab 键
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clearn:
rm -rf *.o *.ko *mod.o [mM]odule* .*.cmd .tmp_versions