手头有本《操作系统 原理技术与编程》(蒋静,徐志伟著),做里面的驱动程序编写的例子,原书基于kernel 2.4,和现在相去甚远,所以又参考了《Linux设备驱动程序》(Jonathan Cprbet等著),加上网上的若干资料,以及师兄的帮助,终于算是搞定了,写程序记录一下。

目的:编写驱动程序,可以观察其有效性

由于linux本身的改动,和自己对linux不熟悉的原因,编写的过程绕了不少弯路,还是直接写结果会清晰一些。 由于是实验学习用途,所以代码可能在安全性检查上有所问题大致分为如下步骤:

0. 软件基础

从kernel.org上下载3.6.0源代码并编译

1. 硬件设施

由于要求可观察,就用一个电阻(10K欧)和一个发光二极管拧在一起,插进机箱上的25针并口中,如果电平不一致(输入端为高电平,输出端为低电平),则二极管可以发光。就不上图了,有材料拧一下就好。 查阅文章【3】并测试,将输入口插到数据口,输出口插到接地口,使用【3】中的测试代码运行成功。

2. 驱动编写

light.h代码如下:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>static ssize_t light_read(struct file *, char __user *, size_t, loff_t *);
static ssize_t light_write(struct file *, const char __user *, size_t,loff_t *);static int light_open(struct inode *, struct file *);
static int light_release(struct inode *, struct file *);static long light_unlocked_ioctl(struct file *, unsigned int, unsigned long);struct file_operations light_fops = {.owner = THIS_MODULE,.read = light_read,.write = light_write,.open = light_open,.release = light_release,.unlocked_ioctl = light_unlocked_ioctl,// all others are NULL
};#define BUFFER_SIZE 64
#define SUCCESS 0
light.c代码如下:       其中定时器参考[7]
#include <asm-generic/uaccess.h>
#include <linux/timer.h>
#include "light.h"/********* light status *******/
enum light_enum {ALL_ZERO = 1, // every time got zeroALL_ONE = 3, // every time got ONEALTERNATIVE = 5, // got 1,0,1,0...
};static enum light_enum light_flag = ALTERNATIVE;
static int lastword = 0;
static int Device_Open = 0;
static int BASE_ADDR = 0x378;extern struct file_operations light_fops;/******************************/
static struct timer_list light_timer;
static int SLEEP_INTERVAL = 500;int rounds = 1;void light_timer_callback(unsigned long data) {char output;
//  printk("my_timer_callback called (%ld).\n", jiffies);switch (light_flag) {case ALL_ONE:output = 0xff;break;case ALL_ZERO:output = 0;break;case ALTERNATIVE:output = lastword == 0 ? 0 : 0xff;lastword ^= 1;break;default:printk("see error flag.\n");return;}outb(output, BASE_ADDR);// need to send a 0x00 signal to stop thatmod_timer(&light_timer,jiffies + msecs_to_jiffies(SLEEP_INTERVAL * rounds));
}
/******************************//******* light functions *******/
// read the round number, nothing to do with count
static ssize_t light_read(struct file * file, char __user * buf, size_t count,loff_t * pos) {int len;char kbuf[BUFFER_SIZE];printk("read inside the kernel\n");if (!Device_Open) {printk("device not opened\n");return -1;}if (rounds > 100) {printk("rounds too big\n");return -1;}kbuf[0] = (char) rounds;kbuf[1] = 0;len = 1;copy_to_user(buf, kbuf, len);printk("read %d chars from kernel\n", len);return len;
}// write the type, not implemented yet
static ssize_t light_write(struct file * file, const char __user * buf,size_t count, loff_t * pos) {char kbuf[BUFFER_SIZE];int tmp;printk("write inside the kernel\n");if (!access_ok(VERIFY_WRITE, buf, count))return -EFAULT;if (!Device_Open) {printk("device not opened\n");return -1;}if (count == 0) {return -EFAULT;}if (copy_from_user(kbuf, buf, count))return -EFAULT;tmp = kbuf[0];if (0 < tmp && tmp < 100) {printk("modify interval rounds from %d to %d\n", rounds, tmp);rounds = tmp;}return 1;
}static int light_open(struct inode * inode, struct file * file) {if (Device_Open)return -EBUSY;Device_Open++;try_module_get(THIS_MODULE);return SUCCESS;
}
static int light_release(struct inode * inode, struct file * file) {Device_Open--;module_put(THIS_MODULE);return 0;
}// set flat to ALL_ONE, ALL_ZERO or ALTERNATIVE
static long light_unlocked_ioctl(struct file * file, unsigned int cmd,unsigned long arg) {switch (cmd) {case ALL_ONE:case ALL_ZERO:case ALTERNATIVE:printk("change status from %d to %d\n", light_flag, cmd);light_flag = cmd;break;default:printk("meet error %d, %ld\n", cmd, arg);return -1;}return SUCCESS;
}/********* light end *******//********* dev control *******/
static int Major;
#define DEVICE_NAME "light_test"int light_init(void) {Major = register_chrdev(0, DEVICE_NAME, &light_fops);if (Major < 0) {printk("Registering the character device failed with %d/n", Major);return Major;}printk("create a dev file with mknod /dev/? c %d 0.\n", Major);printk("ZERO %d, ONE %d, ALTER %d\n", ALL_ZERO, ALL_ONE, ALTERNATIVE);setup_timer( &light_timer, light_timer_callback, 0);mod_timer(&light_timer,jiffies + msecs_to_jiffies(SLEEP_INTERVAL * rounds));return 0;
}void light_exit(void) {int ret = 1;while (ret) {ret = del_timer(&light_timer);printk("The timer is still in use...\n");}printk("unregister_chrdev: %d\n", Major);unregister_chrdev(Major, DEVICE_NAME);
}
/********* dev control end*******/module_init(light_init);
module_exit(light_exit);
Makefile代码如下【2】:       
ifneq ($(KERNELRELEASE),)
# kbuild part of makefile
obj-m  := light.oelse
# normal makefileKDIR ?= /lib/modules/`uname -r`/builddefault:$(MAKE) -C $(KDIR) M=$$PWD modules
endif

3. 测试程序编写

test.c代码如下:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>void main() {char buf[10];FILE* fp;int fd;int round;while (1) {scanf("%d", &round);if (round == 0) {fp = fopen("/dev/light", "r");if (fp == NULL)return;fread(buf, sizeof(char), 1, fp);printf("read round: %d\n", buf[0]);fclose(fp);} else if (round > 0) {buf[0] = (char) round;fp = fopen("/dev/light", "w");if (fp == NULL)return;round = fwrite(buf, sizeof(char), 1, fp);printf("write count %d\n", round);fclose(fp);} else {if (round >= -100) {printf("trying to call the control function, %d\n", round);if ((fd = open("/dev/light", O_SYNC)) < 0) {printf("error in opening the device\n");return;}ioctl(fd, -round, 0);close(fd);} else {break;}}}
}

输入0,则查看当前的sleep round

输入>0的数字,则更改sleep round

输入-1,-3,-5则调整显示的测路[不亮、全亮、交替]

4. 测试

make
mknod /dev/light c 248 0
insmod ./light.ko
gcc -o test test.c
./test
// 下面是自己输入的测试rmmod light

5. 测试结果

本机【fedora16】查看printk的输出结果,使用tail -f /var/log/messages观察

同时观察插在并口上的发光二极管,可以发现其工作状态正常,如:

./test之后,输入

0,可以得到round值

10,可以设置round为10(默认为1),可以发现灯光亮和暗的持续时间变长

-1,发现灯长灭

-3,灯长亮

-5,灯交替亮灭

其他信息可以在/var/log/messages中观察到

5. 注意事项

ioctl函数,参见文章[4]: “应该用unlocked_ioctl代替ioctl,不能被compat_ioctl字面意思所迷惑,用户调用时候还是用ioctl不用变。”

ioctl调用,本来不是1,3,5的结构,而是1,2,3。但是我在使用ioctl传(fd, 2, 0)的时候,light_unlocked_ioctl不会被调用,而(fd, 1/3/4/5..., 0)都可以,不知为什么

-->> 原因见[5][6],符号冲突,使用生僻符号应该可以解决此问题

参考:

1. 《操作系统 原理技术与编程》(蒋静,徐志伟著)

2. 《Linux设备驱动程序》(Jonathan Cprbet等著,魏永明等译)

3.   并行端口操作http://www.igefo.com/post-686.html

4.   ioctl,unlocked_ioctl 的问题 http://zhidao.baidu.com/question/420928958.html

5.   ioctl is not called if cmd = 2   http://stackoverflow.com/questions/10071296/ioctl-is-not-called-if-cmd-2

6.   linux kernel code  http://lxr.linux.no/linux+v3.3.1/include/asm-generic/ioctl.h#L83    http://lxr.linux.no/linux+v3.3.1/+code=FIONCLEX

7. 2.6 内核中的计时器和列表 http://www.ibm.com/developerworks/cn/linux/l-timers-list/index.html

linux 3.6 内核驱动程序编写相关推荐

  1. 在Ubuntu上为Android系统编写Linux内核驱动程序

    在智能手机时代,每个品牌的手机都有自己的个性特点.正是依靠这种与众不同的个性来吸引用户,营造品牌凝聚力和用户忠城度,典型的代表非iphone莫属了. 据统计,截止2011年5月,AppStore的应用 ...

  2. Android驱动(1)---Ubuntu中为Android系统上编写Linux内核驱动程序实现方法

    Ubuntu中为Android系统上编写Linux内核驱动程序实现方法 本文主要介绍在Ubuntu 上为Android系统编写Linux内核驱动程序, 这里对编写驱动程序做了详细的说明,对研究Andr ...

  3. 在Ubuntu上为Android系统编写Linux内核驱动程序(学习老罗的)

    首先提出2个问题 1. 驱动程序的作用是什么? 答:驱动程序的作用主要是向上层提供访问设备寄存器的一个接口,包括读和写. 2. 访问设备驱动程序的方法? 答:a. 通过proc文件系统来访问:b. 通 ...

  4. 在Ubuntu上为Android系统编写Linux内核驱动程序 2

    如下书本参考:嵌入式Linux应用开发和Linux设备驱动程序. 在智能手机时代,每个品牌的手机都有自己的个性特点.正是依靠这种与众不同的个性来吸引用户,营造品牌凝聚力和用户忠城度,典型的代表非iph ...

  5. android驱动之旅-在Ubuntu上为Android系统编写Linux内核驱动程序(3)

    Android的硬件抽象层,简单来说,就是对Linux内核驱动程序的封装,向上提供接口,屏蔽低层的实现细节.也就是说,把对硬件的支持分成了两层,一层放在用户空间(User Space),一层放在内核空 ...

  6. 【1】在Ubuntu上为Android系统编写Linux内核驱动程序

    在智能手机时代,每个品牌的手机都有自己的个性特点.正是依靠这种与众不同的个性来吸引用户,营造品牌凝聚力和用户忠城度,典型的代表非iphone莫属了.据统计,截止2011年5月,AppStore的应用软 ...

  7. 为Android系统编写Linux内核驱动程序

    在智能手机时代,每个品牌的手机都有自己的个性特点.正是依靠这种与众不同的个性来吸引用户,营造品牌凝聚力和用户忠城度,典型的代表非iphone莫属了.据统计,截止2011年5月,AppStore的应用软 ...

  8. Linux驱动程序编写

    工作需要写了我们公司一块网卡的Linux驱动程序.经历一个从无到有的过程,深感技术交流的重要.Linux作为挑战微 软垄断的强有力武器,日益受到大家的喜爱.真希望她能在中国迅速成长.把程序文档贴出来, ...

  9. Linux网卡驱动程序编写

    Linux网卡驱动程序编写 [摘自 LinuxAID] 工作需要写了我们公司一块网卡的Linux驱动程序.经历一个从无到有的过程,深感技术交流的重要.Linux作为挑战微软垄断的强有力武器,日益受到大 ...

最新文章

  1. swift 方法的局部和外部参数名
  2. 计算机软件uml,计算机软件——UML旅游管理系统
  3. spring学习12 -Spring 框架模块以及面试常见问题注解等
  4. php和python哪个做第二语言-php之后如何选择第二语言?
  5. shell实现矩阵转置
  6. eclipse 在 Linux中常用命令,持续更新....
  7. 高级Python:定义类时要应用的9种最佳做法
  8. java对象转JSON JS取JSON数据
  9. linux diff 补丁,Linux中diff、补丁的用法及介绍
  10. Silverlight获取子控件和父控件方法
  11. 小技巧收集(10)-JS操作Cookie
  12. 宝塔面板SSpanel-v3-mod安装教程 搭建sspanel v3魔改前端
  13. Threshold函数详解
  14. 彻底阻止、禁用google chrome浏览器自动更新、升级
  15. 超过ChatGPT3达到ChatGPT4%90性能的小羊驼来了-Vicuna(校招社招必备,chatgpt风口来了赶紧学起来吧)
  16. 小学数学解题思维窍门
  17. greendao出现Failed to change locale for db ‘/data/data/xxx/databases/xxx.db‘ to ‘zh_CN‘.
  18. (收藏)让你平步青云的十个谈话技巧
  19. Java练习——牛客网天才问题
  20. 如何自学大数据开发?

热门文章

  1. 工业相机影响曝光的因素
  2. 一个oracle定时任务作业(生产实际里用),查询医院药房药库账目不相等的库存项目...
  3. 抖音数据统计_2019年抖音数据报告的分析探究
  4. 电子计算机制作探测,超声波探测系统设计及实物制作.pdf
  5. cesium实现信息提示tooltip(cesium篇.46)
  6. 关于电流互感器电流以及采集电路理解
  7. 2019年北京画室排名前十位(考央美比较好的画)
  8. RS485多机通信程序(终结版AVR)
  9. 从游戏设计角度——探究桌上足球玩法
  10. 【智能金融】信贷工厂:未来银行风控体系体系搭建