大雀软件园

首页 软件下载 安卓市场 苹果市场 电脑游戏 安卓游戏 文章资讯 驱动下载
技术开发 网页设计 图形图象 数据库 网络媒体 网络安全 站长CLUB 操作系统 媒体动画 安卓相关
当前位置: 首页 -> 操作系统 -> Linux -> 入门文章:教你学会编写Linux设备驱动

入门文章:教你学会编写Linux设备驱动

时间: 2021-07-31 作者:daque

内核本子: 2.4.22

观赏此文的手段: 学会编写linux摆设启动。

观赏此文的本领: 观赏以次2个文献: hello.c,asdf.c。

此文假如读者群:

仍旧能用c谈话编写linux运用步调,

领会"字符摆设文献, 块摆设文献, 主摆设号, 次摆设号",

会写大略的shell剧本和makefile。

1. "hello.c"

--------------------------------

/*

* 这是咱们的第一个源文献,

* 它是一个不妨加载的内核模块,

* 加载时表露"hello,world!",

* 卸载时表露"bye!"。

* 须要证明一点,写内核或内核模块不许用写运用步调时的体例挪用或因变量库,

* 由于咱们写的即是为运用步调供给体例挪用的代码。

* 内核有专用的因变量库,如, , 等, * 此刻还没需要领会得很精细,

* 这边用到的printk的功效一致于printf。

* "/usr/src/linux"是你本质的内核源码目次的一个标记链接,

* 即使没有此刻就创造一个,由于底下和此后城市用到。

* 编写翻译它用"gcc -c -i/usr/src/linux/include hello.c",

* 即使平常会天生文献hello.o,

* 加载它用"insmod hello.o",

* 惟有在文本结尾下本领看到输入。

* 卸载它用"rmmod hello"

*/

/*

* 小本领: 在用户目次的.bashrc里加上一条龙:

* alias mkmod='gcc -c -i/usr/src/linux/include'

* 而后从新登岸shell,

* 此后就不妨用"mkmod hello.c"的办法来编写翻译内核模块了。

*/

/* 发端官样文章 */

#ifndef __kernel__

# define __kernel__

#endif

#ifndef module

# define module

#endif

#include #include

module_license("gpl");

#ifdef config_smp

#define __smp__

#endif

/* 中断官样文章 */

#include /* printk()在这个文献里 */

static int

init_module

(){

printk("hello,world!\n");

return 0; /* 即使初始处事波折,就归来非0 */

}

static void

cleanup_module

(){

printk("bye!\n");

}

------------------------------------

2. "asdf.c"

------------------------------------

/*

* 这个文献是一个内核模块。

* 内核模块的编写翻译,加载和卸载在前方仍旧引见了。

* 这个模块的功效是,创造一个字符摆设。

* 这个摆设是一块4096字节的共享外存。

* 内核调配的主摆设号会在加载模块时表露。

*/

/* 发端官样文章 */

#ifndef __kernel__

# define __kernel__

#endif

#ifndef module

# define module

#endif

#include #include

#ifdef config_smp

#define __smp__

#endif

module_license("gpl");

/* 中断官样文章 */

#include /* copy_to_user(), copy_from_user */ #include /* struct file_operations, register_chrdev(), ... */ #include /* printk()在这个文献里 */ #include /* 和工作安排相关 */ #include /* u8, u16, u32 ... */

/*

* 对于内核功效库,不妨去网上探求精细材料,

*/

/* 文献被操纵时的回调功效 */

static int asdf_open (struct inode *inode, struct file *filp);

static int asdf_release (struct inode *inode, struct file *filp);

static ssize_t asdf_read (struct file *filp, char *buf, size_t count,loff_t *f_pos);

static ssize_t asdf_write (struct file *filp, const char *buf, size_t count,loff_t *f_pos);

static loff_t asdf_lseek (struct file * file, loff_t offset, int orig);

/* 请求主摆设号时用的构造, 在linux/fs.h里设置 */

struct file_operations asdf_fops = {

open: asdf_open,

release: asdf_release,

read: asdf_read,

write: asdf_write,

llseek: asdf_lseek,

};

static int asdf_major; /* 用来生存请求到的主摆设号 */

static u8 asdf_body[4096]="asdf_body\n"; /* 摆设 */

static int

init_module

(){

printk ("hi, this' a simple device file!\n");

asdf_major = register_chrdev (0, "a simple device file", &asdf_fops); /* 请求字符摆设的主摆设号 */

if (asdf_major < 0) return asdf_major; /* 请求波折就径直归来缺点编号 */

printk ("the major is:%d\n", asdf_major); /* 表露请求到的主摆设号 */

return 0; /* 模块平常初始化 */

}

static void

cleanup_module

(){

unregister_chrdev(asdf_major, "a simple device file"); /* 刊出此后,摆设就不生存了 */

printk("a simple device has been removed,bye!\n");

}

/*

* 编写翻译这个模块而后加载它,

* 即使平常,会表露你的摆设的主摆设号。

* 此刻你的摆设就创造好了,咱们不妨尝试一下。

* 假如你的模块请求到的主摆设号是254,

* 运转"mknod abc c 254 0",就创造了咱们的摆设文献abc。

* 不妨把它当成一个4096字节的外存块来尝试一下,

* 比方"cat abc", "cp abc image", "cp image abc",

* 或写几个运用步调用它来举行通信。

* 引见一下两个须要提防的事,

* 一是printk()的表露惟有在非图形形式的结尾下本领看到,

* 二是加载过的模块最佳在不必此后卸载掉。

* 即使对linux情况的体例挪用很生疏,倡导先看apue这该书。

*/

static int

asdf_open /* open回调 */

(

struct inode *inode,

struct file *filp

){

printk("^_^ : open %s\n ",\

current->comm);

/*

* 运用步调的运转情况由内核供给,内核的运转情况由硬件供给。

* 这边的current是一个指向当进步程的南针,

* 此刻没需要领会current的详细。

* 在这边,当进步程正翻开这个摆设,

* 归来0表白翻开胜利,内核会给它一个文献刻画符。

* 这边的comm是当进步程在shell下的command字符串。

*/

return 0;

}

static int

asdf_release /* close回调 */

(

struct inode *inode,

struct file *filp

){

printk("^_^ : close\n ");

return 0;

}

static ssize_t

asdf_read /* read回调 */

(

struct file *filp,

char *buf,

size_t count,

loff_t *f_pos

){

loff_t pos;

pos = *f_pos; /* 文献的读写场所 */

if ((pos==4096) || (count>4096)) return 0; /* 确定能否仍旧到摆设尾,或写的长度胜过摆设巨细 */

pos += count;

if (pos > 4096) {

count -= (pos - 4096);

pos = 4096;

}

if (copy_to_user(buf, asdf_body+*f_pos, count)) return -efault; /* 把数据写到运用步调空间 */

*f_pos = pos; /* 变换文献的读写场所 */

return count; /* 归来读到的字节数 */

}

static ssize_t

asdf_write /* write回调,和read逐一对应 */

(

struct file *filp,

const char *buf,

size_t count,

loff_t *f_pos

){

loff_t pos;

pos = *f_pos;

if ((pos==4096) || (count>4096)) return 0;

pos += count;

if (pos > 4096) {

count -= (pos - 4096);

pos = 4096;

}

if (copy_from_user(asdf_body+*f_pos, buf, count)) return -efault;

*f_pos = pos;

return count;

}

static loff_t

asdf_lseek /* lseek回调 */

(

struct file * file,

loff_t offset,

int orig

){

loff_t pos;

pos = file->f_pos;

switch (orig) {

case 0:

pos = offset;

break;

case 1:

pos += offset;

break;

case 2:

pos = 4096+offset;

break;

default:

return -einval;

}

if ((pos>4096) || (pos<0)) {

printk("^_^ : lseek error %d\n",pos);

return -einval;

}

return file->f_pos = pos;

}

热门阅览

最新排行

Copyright © 2019-2021 大雀软件园(www.daque.cn) All Rights Reserved.