1. 实验目的:
实现一个名字为 blackhole 的设备:从该设备读不到任何数据,却可以写入任意多的数据。
2. 实验环境
系统:Ubuntu 14.04
内核版本:3.13.0-24
3. 实验代码
//blackhole.c
//驱动代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
int scull_major = 0;
int scull_minor = 0;
struct cdev cdev;
#define MAX_SIZE 10
size_t size = 0;
char store[MAX_SIZE];
int scull_open(struct inode *inode, struct file *filp) {
/* trim to 0 the length of the device if open was write-only */
if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) {
size = 0;
}
return 0; /* success */
}
int scull_release(struct inode *inode, struct file *filp) {
return 0;
}
ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos){
//读函数照常即可。
ssize_t retval = 0;
if (*f_pos >= size)
goto out;
if (*f_pos + count > size)
count = size - *f_pos;
if (copy_to_user(buf, store + *f_pos, count)) {
retval = -EFAULT;
goto out;
}
*f_pos += count;
retval = count;
out:
return retval;
}
ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos){
return count;//返回输入的字符的size,假装已经写入了该写入的字符。
}
loff_t scull_llseek(struct file *filp, loff_t off, int whence){
loff_t newpos;
switch(whence) {
case 0: /* SEEK_SET */
newpos = off;
break;
case 1: /* SEEK_CUR */
newpos = filp->f_pos + off;
break;
case 2: /* SEEK_END */
newpos = size + off;
break;
default: /* can't happen */
return -EINVAL;
}
if (newpos < 0)
return -EINVAL;
filp->f_pos = newpos;
return newpos;
}
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.llseek = scull_llseek,
.read = scull_read,
.write = scull_write,
.release = scull_release,
};
int scull_init_module(void){
int result; dev_t dev = 0;
/*
* Get a range of minor numbers to work with, asking for a dynamic major
*/
result = alloc_chrdev_region(&dev, scull_minor, 1, "scull");
scull_major = MAJOR(dev);
if (result < 0) {
printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
return result;
}
/* register our device */
cdev_init(&cdev, &scull_fops);
cdev.owner = THIS_MODULE;
cdev.ops = &scull_fops;
result = cdev_add (&cdev, dev, 1);
if (result) {
printk(KERN_WARNING "Error %d adding scull", result);
unregister_chrdev_region(dev, 1);
return result;
}
return 0; /* succeed */
}
void scull_cleanup_module(void){
/* cleanup_module is never called if registering failed */
dev_t dev;
cdev_del(&cdev);
dev = MKDEV(scull_major, scull_minor);
unregister_chrdev_region(dev, 1);
}
module_init(scull_init_module);
module_exit(scull_cleanup_module);
//Makefile
//驱动的Makefile文件
obj-m := blackhole.o
//testDev.c
//测试设备情况代码
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/rtc.h>
#include <linux/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int fd;
int i;
char data[256] = {0};
int retval;
fd = open("/dev/scull", O_RDWR);
if (fd == 1) {
perror("open error\n");
exit(-1);
}
printf("open /dev/scull successfully\n");
retval = write(fd, "1234567", 7);//你要写入的东西。
if (retval == -1) {
perror("write error\n");
exit(-1);
}
retval = lseek(fd, 0, 0);
if (retval == -1) {
perror("lseek error\n");
exit(-1);
}
retval = read(fd, data, 10);
if (retval == -1) {
perror("read error\n");
exit(-1);
}
printf("read successfully: %s\n", data);
close(fd);
return 0;
}
4. 实验过程
编译驱动程序。
· 把Makefile文件和驱动代码blackhole.c放在一个目录下,在这个目录下执行命令:make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
安装驱动模块。
· 执行命令:sudo insmod blackhole.ko
检查设备安装情况,记录设备号。
· 执行命令,并记录设备号:cat /proc/devices | grep scull
在文件系统里创建设备对应的条目
· 执行命令,请注意:命令中的第一个数字 250 应替换为你的系统中设备分配到的 号码,不同系统中分配到的号码可能不一样。:sudo mknod /dev/scull c 250 0
5.测试设备
· 编译运行testDev.c:sudo gcc testDev.c -o testDev.o sudo ./testDev.o
参考文档
- linux驱动学习3:实现一简单完整驱动(包括open,read,write,ioctl)
http://blog.csdn.net/yangjin_unique/article/details/8222465
- 北京交通大学杨武杰老师课程课件