Linux驱动编写(块设备驱动代码)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
按照ldd的说法,linux的设备驱动包括了char,block,net三种设备。char设备是比较简单的,只要分配了major、minor号,就可以进行读写处理了。相对而言,block和net要稍微复杂些。net设备姑且按下不谈,我们在以后的博文中会有涉及。今天,我们可以看看一个简单的block是怎么设计的。
为了将block和fs分开,kernel的设计者定义了request queue这一种形式。换一句话说,所有fs对block设备的请求,最终都会转变为request的形式。所以,对于block设备驱动开发的朋友来说,处理好了request queue就掌握了block设备的一半。当然,block设备很多,hd、floppy、ram都可以这么来定义,有兴趣的朋友可以在drivers/block寻找相关的代码来阅读。兴趣没有那么强的同学,可以看看我们这篇博文,基本上也能学个大概。有个基本的概念,再加上一个简单浅显的范例,对于一般的朋友来说,已经足够了。
闲话不多说,我们看看一个ramdisk代码驱动是怎么写的,代码来自《深入linux 设备驱动程序内核机制》,
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h> #include <linux/fs.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/vmalloc.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h> #define RAMHD_NAME "ramhd"
#define RAMHD_MAX_DEVICE 2
#define RAMHD_MAX_PARTITIONS 4 #define RAMHD_SECTOR_SIZE 512
#define RAMHD_SECTORS 16
#define RAMHD_HEADS 4
#define RAMHD_CYLINDERS 256 #define RAMHD_SECTOR_TOTAL (RAMHD_SECTORS * RAMHD_HEADS *RAMHD_CYLINDERS)
#define RAMHD_SIZE (RAMHD_SECTOR_SIZE * RAMHD_SECTOR_TOTAL) //8mb typedef struct {
unsigned char* data;
struct request_queue* queue;
struct gendisk* gd;
}RAMHD_DEV; static char* sdisk[RAMHD_MAX_DEVICE] = {NULL};
static RAMHD_DEV* rdev[RAMHD_MAX_DEVICE] = {NULL}; static dev_t ramhd_major; static int ramhd_space_init(void)
{
int i;
int err = 0;
for(i = 0; i < RAMHD_MAX_DEVICE; i++){
sdisk[i] = vmalloc(RAMHD_SIZE);
if(!sdisk[i]){
err = -ENOMEM;
return err;
} memset(sdisk[i], 0, RAMHD_SIZE);
} return err;
} static void ramhd_space_clean(void)
{
int i;
for(i = 0; i < RAMHD_MAX_DEVICE; i++){
vfree(sdisk[i]);
}
} static int ramhd_open(struct block_device* bdev, fmode_t mode)
{
return 0;
} static int ramhd_release(struct gendisk*gd, fmode_t mode)
{
return 0;
} static int ramhd_ioctl(struct block_device* bdev, fmode_t mode, unsigned int cmd, unsigned long arg)
{
int err;
struct hd_geometry geo; switch(cmd)
{
case HDIO_GETGEO:
err = !access_ok(VERIFY_WRITE, arg, sizeof(geo));
if(err)
return -EFAULT; geo.cylinders = RAMHD_CYLINDERS;
geo.heads = RAMHD_HEADS;
geo.sectors = RAMHD_SECTORS;
geo.start = get_start_sect(bdev); if(copy_to_user((void*)arg, &geo, sizeof(geo)))
return -EFAULT; return 0;
} return -ENOTTY;
} static struct block_device_operations ramhd_fops = {
.owner = THIS_MODULE,
.open = ramhd_open,
.release = ramhd_release,
.ioctl = ramhd_ioctl,
}; static int ramhd_make_request(struct request_queue* q, struct bio* bio)
{
char* pRHdata;
char* pBuffer;
struct bio_vec* bvec;
int i;
int err = 0; struct block_device* bdev = bio->bi_bdev;
RAMHD_DEV* pdev = bdev->bd_disk->private_data; if(((bio->bi_sector * RAMHD_SECTOR_SIZE) + bio->bi_size) > RAMHD_SIZE){
err = -EIO;
return err;
} pRHdata = pdev->data + (bio->bi_sector * RAMHD_SECTOR_SIZE);
bio_for_each_segment(bvec, bio, i){
pBuffer = kmap(bvec->bv_page) + bvec->bv_offset;
switch(bio_data_dir(bio)){
case READ:
memcpy(pBuffer, pRHdata, bvec->bv_len);
flush_dcache_page(bvec->bv_page);
break; case WRITE:
flush_dcache_page(bvec->bv_page);
memcpy(pRHdata, pBuffer, bvec->bv_len);
break; default:
kunmap(bvec->bv_page);
goto out;
} kunmap(bvec->bv_page);
pRHdata += bvec->bv_len;
} out:
bio_endio(bio, err);
return 0;
} static int alloc_ramdev(void)
{
int i;
for(i = 0; i < RAMHD_MAX_DEVICE; i++){
rdev[i] = kzalloc(sizeof(RAMHD_DEV), GFP_KERNEL);
if(!rdev[i]){
return -ENOMEM;
}
} return 0;
} static void clean_ramdev(void)
{
int i; for(i = 0; i < RAMHD_MAX_DEVICE; i++){
if(rdev[i])
kfree(rdev[i]);
}
} static int __init ramhd_init(void)
{
int i; ramhd_space_init();
alloc_ramdev(); ramhd_major = register_blkdev(0, RAMHD_NAME); for(i = 0; i < RAMHD_MAX_DEVICE; i++){
rdev[i]->data = sdisk[i];
rdev[i]->queue = blk_alloc_queue(GFP_KERNEL);
blk_queue_make_request(rdev[i]->queue, ramhd_make_request); rdev[i]->gd = alloc_disk(RAMHD_MAX_PARTITIONS);
rdev[i]->gd->major = ramhd_major;
rdev[i]->gd->first_minor = i * RAMHD_MAX_PARTITIONS;
rdev[i]->gd->fops = &ramhd_fops;
rdev[i]->gd->queue = rdev[i]->queue;
rdev[i]->gd->private_data = rdev[i];
sprintf(rdev[i]->gd->disk_name, "ramhd%c", 'a' +i);
rdev[i]->gd->flags |= GENHD_FL_SUPPRESS_PARTITION_INFO;
set_capacity(rdev[i]->gd, RAMHD_SECTOR_TOTAL);
add_disk(rdev[i]->gd);
} return 0;
} static void __exit ramhd_exit(void)
{
int i;
for(i = 0; i < RAMHD_MAX_DEVICE; i++){
del_gendisk(rdev[i]->gd);
put_disk(rdev[i]->gd);
blk_cleanup_queue(rdev[i]->queue);
} clean_ramdev();
ramhd_space_clean();
unregister_blkdev(ramhd_major, RAMHD_NAME);
} module_init(ramhd_init);
module_exit(ramhd_exit); MODULE_AUTHOR("dennis__chen@ AMDLinuxFGL");
MODULE_DESCRIPTION("The ramdisk implementation with request function");
MODULE_LICENSE("GPL");
为了大家方便,顺便也把Makefile放出来,看过前面blog的朋友都知道,这其实很简单,
ifneq ($(KERNELRELEASE),)
obj-m := ramdisk.o else
PWD := $(shell pwd)
KVER := $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions modules.* Module.*
endif
这段代码究竟有没有用呢?可以按照下面的步骤来做,
a)make 一下,生成ramdisk.ko;
b)编译好了之后,就可以安装驱动了,在linux下是这么做的,sudo insmod ramdisk.ko;
c)安装好了,利用ls /dev/ramhd*, 就会发现在/dev下新增两个结点,即/dev/ramhda和/dev/ramhdb;
d)不妨选择其中一个节点进行分区处理, sudo fdisk /dev/ramhda,简单处理的话就建立一个分区, 生成/dev/ramhda1;
e)创建文件系统,sudo mkfs.ext3 /dev/ramhda1;
f)有了上面的文件系统,就可以进行mount处理,不妨sudo mount /dev/ramhda1 /mnt;
g)上面都弄好了,大家就可以copy、delete文件试试了,是不是很简单。
Linux驱动编写(块设备驱动代码)的更多相关文章
- 乾坤合一~Linux设备驱动之块设备驱动
1. 题外话 在蜕变成蝶的一系列学习当中,我们已经掌握了大部分Linux驱动的知识,在乾坤合一的分享当中,以综合实例为主要讲解,在一个月的蜕茧成蝶的学习探索当中,觉得数据结构,指针,链表等等占据了代码 ...
- linux下的块设备驱动(二)
上一章主要讲了请求队列的一系列问题.下面主要说一下请求函数.首先来说一下硬盘类块设备的请求函数. 请求函数可以在没有完成请求队列的中的所有请求的情况下就返回,也可以在一个请求都不完成的情况下就返回. ...
- FLASH驱动之-块设备驱动系统构架
一. 块设备是只能以块为单位进行访问的设备,块的大小一般是512个字节的整数倍,常见的块设备包括硬件,SD卡,光盘,flash等.驱动程序是块的整数倍从设备读写得到数据.块设备的最小访单位为块,不同 ...
- linux下的块设备驱动(一)
块设备的驱动比字符设备的难,这是因为块设备的驱动和内核的联系进一步增大,但是同时块设备的访问的几个基本结构和字符还是有相似之处的. 有一句话必须记住:对于存储设备(硬盘~~带有机械的操作)而言,调整读 ...
- 字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联
转载自:http://www.kancloud.cn/yueqian_scut/emlinux/106829 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sy ...
- [kernel]字符设备驱动、平台设备驱动、设备驱动模型、sysfs几者之间的比较和关联
转自:http://www.2cto.com/kf/201510/444943.html Linux驱动开发经验总结,绝对干货! 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动 ...
- Linux块设备驱动(一) _驱动模型
块设备是Linux三大设备之一,其驱动模型主要针对磁盘,Flash等存储类设备,本文以3.14为蓝本,探讨内核中的块设备驱动模型 框架 下图是Linux中的块设备模型示意图,应用层程序有两种方式访问一 ...
- linux 块设备驱动 (三)块设备驱动开发
一: 块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:int register_blkdev(unsigne ...
- linux块设备驱动---程序设计(转)
块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:int register_blkdev(unsigned i ...
- Linux 块设备驱动 (一)
1.块设备的I/O操作特点 字符设备与块设备的区别: 块设备只能以块为单位接受输入和返回输出,而字符设备则以字符为单位. 块设备对于I/O请求有对应的缓冲区,因此它们可以选择以什么顺序进行响应,字符设 ...
随机推荐
- php 登录实例演示
<pre name="code" class="python">一.模板的使用 (重点) a.规则 模板文件夹下[TPL]/[分组文件夹/][模板主 ...
- AlertDialog弹出时背景明暗程度调整
今天有个需求是把弹出AlertDialog时的变暗的背景调整得不要那么暗. 一开始懒惰就直接百度中文搜索,结果找到的代码试了几次都不行. 后来老老实实开google.stackoverflow搜索,搜 ...
- 把一个数组向右循环移动k位要求时间复杂度为O(n)
今晚做了下某公司的网络笔试题,好久没刷题了,现在渣得要死,里面有道程序设计题是 把一个数组向右循环移动k位要求时间复杂度为O(n) 给的方法定义为 public void solution(int a ...
- hive 0.10 0.11新增特性综述
我们的hive版本升迁经历了0.7.1 -> 0.8.1 -> 0.9.0,并且线上shark所依赖的hive版本也停留在0.9.0上,在这些版本上有我们自己的bug fix patch和 ...
- php:根据中文裁减字符串函数方法
define(CHARSET, 'UTF-8'); // 系统默认编码 /** * 根据中文裁减字符串 * @param $string - 字符串 * @param $length - 长度 * @ ...
- mysql待整理
1. MYSQL SQL_NO_CACHE的真正含义 http://www.dewen.org/q/5149/Mysql 是 结果不缓存,但查询还是缓存了. 如果要重新测试,就在查询前先执行一下&qu ...
- (" use strict")Javascript 严格模式详解
Javascript 严格模式详解 转载别人的博客内容,浏览了一遍,没有全部吸收,先保存一下链接 http://www.ruanyifeng.com/blog/2013/01/javascript_s ...
- memcache学习使用
一.安装 二.telnet测试 启动:memcached -d -m 120.24.242.100 -p 11211 -u root 测试:telnet localhost 11211 >> ...
- 调用 sphinx-build生成HTML文件
安装 Sphinx $ easy_install sphinx Searching for sphinx Reading http://pypi.python.org/simple/sphinx/ R ...
- r语言之给定的概率密度函数生成随机数
假设概率密度函数为: 思路: 首先产生-1到1之间的均匀分布随机数x,和0到1之间的均匀分布随机数y. 如果y<f(x),则x是符合该概率密度的随机数,否则,重复上述操作. 用r语言生成100个 ...