-
#include<linux/module.h>
-
#include<linux/init.h>
-
#include<linux/fs.h>
-
#include<linux/kernel.h>
-
#include<linux/slab.h>
-
#include<linux/types.h>
-
#include<linux/errno.h>
-
#include<linux/cdev.h>
-
#include<asm/uaccess.h>
-
-
#define TEST_DEVICE_NAME "test_dev"
-
#define BUFF_SZ 1024
-
-
static struct cdev test_dev;
-
static char *data = NULL;
-
int major =0;
-
-
//读函数
-
static ssize_t test_read(struct file *file,char *buf,size_t count,loff_t *f_pos)
-
{
-
int len;
-
if(count<0)
-
{
-
return -EINVAL;
-
}
-
len=strlen(data);
-
count=(len>count)?count:len; //取len与count的小值
-
if(copy_to_user(buf,data,count)) //把内核缓冲数据复制到用户空间
-
{
-
return -EFAULT;
-
}
-
-
return count;
-
-
}
-
-
static int test_write(struct file *file ,const char *buffer,unsigned int count,loff_t *f_pos)
-
{
-
if(count <0)
-
{
-
return -EINVAL;
-
}
-
-
memset(data,0 ,BUFF_SZ);
-
count=(BUFF_SZ >count)?count:BUFF_SZ;
-
if(copy_from_user(data,buffer,count)) //用户空间数据复制到内核空间
-
{
-
return - EFAULT;
-
}
-
return count;
-
-
}
-
-
-
static int test_open(struct inode *inode,struct file *file)
-
{
-
printk("This is open operation\n");
-
//分配并初始化缓存区
-
data = (char *)kmalloc(sizeof(char) *BUFF_SZ,GFP_KERNEL);
-
if(!data)
-
{
-
return -ENOMEM;
-
}
-
memset(data,0,BUFF_SZ);
-
return 0;
-
-
}
-
-
//关闭函数
-
static int test_release(struct inode *inode,struct file *file)
-
{
-
printk("this is release operation\n");
-
if(data)
-
{
-
kfree(data); //释放缓存区
-
data = NULL; //防止野指针
-
}
-
return 0;
-
}
-
-
-
//创建,初始化字符设备,并注册到系统
-
static int test_setup_cdev(struct cdev *dev,int minor,struct file_operations *fops)
-
{
-
int err,devno =MKDEV(major,minor);
-
cdev_init(dev,fops);;//初始化cdev结构
-
dev->owner =THIS_MODULE;
-
dev->ops = fops;
-
err = cdev_add(dev,devno,1);//注册字符设备
-
if(err)
-
{
-
printk(KERN_NOTICE " %d add test %d failed",err,minor);
-
}
-
-
return 0;
-
}
-
-
-
//虚拟设备的file operation 结构
-
static struct file_operations test_fops=
-
{
-
.owner = THIS_MODULE,
-
.read = test_read,
-
.write =test_write,
-
.open = test_open,
-
.release =test_release,
-
-
};
-
-
//设备驱动模块加载函数
-
int init_module(void)
-
{
-
int result;
-
dev_t dev =MKDEV(major,0); //dev_t
-
if(major)
-
{//静态注册设备,设备号先前制定好,并设定设备名
-
result =register_chrdev_region(dev,1,TEST_DEVICE_NAME);
-
}
-
else
-
{ //动态注册。指定设备的次设备号(即需要的设备个数)
-
result = alloc_chrdev_region(&dev,0,1,TEST_DEVICE_NAME);
-
major = MAJOR(dev); //获得主设备号
-
}
-
-
if(result<0)
-
{
-
printk(KERN_WARNING"Test device:fail to get major %d\n",major);
-
return result;
-
}
-
test_setup_cdev(&test_dev,0,&test_fops);
-
printk("this major of test device is %d\n",major);
-
return 0;
-
}
-
-
//卸载模块
-
void cleanup_module()
-
{
-
cdev_del(&test_dev);
-
unregister_chrdev_region(MKDEV(major,0),1);
-
printk("test device uninstalled\n");
-
-
-
}
在linux2.6的基础上,编译成内核代码,Makefile如下:
-
ifeq ($(KERNELRELEASE),)
-
-
KDIR ?= /lib/modules/$(shell uname -r)/build
-
PWD :=$(shell pwd)
-
modules:
-
$(MAKE) -C $(KDIR) M=$(PWD) modules
-
modules_install:
-
$(MAKE) -C $(KDIR) M=$(PWD) modules_install
-
-
clean:
-
rm -rf *.o *.ko *.mod.c .*.cmd *.markers *.order *.symvers .tmp_versions
-
.PHONY: modules modules_install clean
-
else
-
obj-m:= test_drv.o
-
-
endif
通过脚本文件实现驱动程序的加载与卸载
模块加载脚本
test_drv_load
-
#驱动模块名称
-
module="test_drv"
-
#设备名称
-
device="test_dev"
-
-
echo /dev/${device}
-
-
mode="664"
-
#删除已经存在的设备节点
-
rm -f /dev/${device}
-
#加载驱动模块
-
echo $module.ko $*
-
/sbin/insmod -f ./$module.ko $*
-
-
#查到创建设备的的主设备号
-
-
major=`cat /proc/devices | awk "\\$2==\"$device\" {print \\$1}"`
-
echo major=$major
-
#创建设备文件节点
-
mknod /dev/${device} c $major 0
-
-
#设备文件属性
-
chmod $mode /dev/${device}
模块卸载脚本
test_drv_unload
-
#卸载驱动模块
-
-
#删除设备文件
-
module="test_drv"
-
device="test_dev"
-
-
# invoke rmmod with all arguments we got
-
/sbin/rmmod $module $* || exit 1
-
-
# remove nodes
-
rm -f /dev/${device}
-
-
exit 0
对驱动程序测试的应用程序:(通过打开设备文件,进行字符的写入和读出)
test.c
-
/* test.c */
-
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <string.h>
-
#include <sys/stat.h>
-
#include <sys/types.h>
-
#include <unistd.h>
-
#include <fcntl.h>
-
#define TEST_DEVICE_FILENAME "/dev/test_dev"
-
#define BUFF_SZ 1024
-
-
int main()
-
{
-
int fd, nwrite, nread;
-
char buff[BUFF_SZ];
-
-
fd = open(TEST_DEVICE_FILENAME , O_RDWR);
-
if (fd < 0)
-
{
-
perror("open");
-
exit(1);
-
}
-
-
do
-
{
-
printf("Input some words to kernel(enter 'quit' to exit):");
-
memset(buff, 0, BUFF_SZ);
-
if (fgets(buff, BUFF_SZ, stdin) == NULL)
-
{
-
perror("fgets");
-
break;
-
}
-
buff[strlen(buff) - 1] = '\0';
-
-
if (write(fd, buff, strlen(buff)) < 0)
-
{
-
perror("write");
-
break;
-
}
-
-
if (read(fd, buff, BUFF_SZ) < 0)
-
{
-
perror("read");
-
break;
-
}
-
else
-
{
-
printf("The read string is from kernel:%s\n", buff);
-
}
-
-
} while(strncmp(buff, "quit", 4));
-
-
close(fd);
-
exit(0);
-
}
先执行make,运行加载脚本./test_drv_load. 再运行测试程序test.对驱动程序进行测试。最后卸载驱动程序 ./test_drv_unload.