linux 3.6 内核驱动程序编写

Linux_编译内核并编写一个最基础的字符设备驱动程序 linux内核编译以及字符设备驱动程序的编写 关键词: 内核编译; globalmem字符设备驱动编写; linux; linux设备驱动编写; 字符设备驱动程序主要内容 虚拟内存设备globalmem驱动实现,编写程序,然后将生成的驱动模块插入到驱动之中,接着编写测试程序,对设备globalmem进行测试。(Copyright © http://blog.csdn.net/s_... 阅读详情

手头有本《操作系统 原理技术与编程》(蒋静,徐志伟著),做里面的驱动程序编写的例子,原书基于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 zero
	ALL_ONE = 3, // every time got ONE
	ALTERNATIVE = 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 that
	mod_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.o

else
# normal makefile
    KDIR ?= /lib/modules/`uname -r`/build

default:
    $(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驱动程序 操作系统课程设计 最详细教程 块设备驱动 linux内核编译 本博客是本次课程设计的记录,关于这个题目的完整详细的博客内容较少,故作一篇详细教程并且写出一些报错内容、解决办法以供参考。 以下是我的课设题目要求,读者可以酌情参考: 需要重新编译Linux内核 增加一个驱动程序(使用内存模拟设备),使用模块编译方式 动态加载和卸载新的驱动 通过程序或命令行使用该驱动。 (至少能通过该驱动保存1MB的数据,还能将这些数据读取出来。可以模仿ramdisk的实现方式。) 1. 前期准备:下载和安装虚拟机以及centos 虚拟机我下载的是VMware Worksta 阅读详情

相关推荐

将驱动程序编译进linux内核

  在linux驱动开发时,一般的做法是将驱动程序编译成.ko文件,然后使用“modprobe”命令将驱动模块加载到内核中。但在驱动开发完成后,就需要将驱动程序编译进Linux内核里面。 一、直接将驱动写死进linux内核   1、以led字符设备驱动为例,首先在linux内核源码的/drivers/char/文件夹下创建一个mytest_led文件夹,将驱动文件存放在此文件夹下。   2、修改/drivers/char/文件夹下的Makefile文件,添加一句: obj-y += mytest_le

123 2万+

linker&loader

linker 用于生成可执行二进制文件 loader 用于加载可执行二进制文件到内存,并切换到可执行二进制文件的控制流 linker 和 loader 要做的事情 linker 和loader 对于工作的分工 常见的linker和loader u-boot load self u-boot load linux linux load ko ld load app-elf ...

u011011827的博客 573

linker & loader 链接器与加载器

linker & loader 链接器与加载器

十二、linux LED初始化

一、硬件原理图 我们今天的目标是将itop4412开发板上的两个led灯控制起来。先看一下原理图: 二、GPIO需要的接口和定义 1、Linux中申请GPIO的头文件 – include/linux/gpio.h 主要提供一些GPIO操作函数: 2、三星平台的GPIO配置函数头文件 – arch/arm/plat-samsung/include/plat/gpio-cfg.h – 包括三星所有处理...

qq_28938511的博客 981

STM32智能头盔DIY全攻略:从心率监测到跌倒报警的完整实现

本文提供了基于STM32的智能头盔完整DIY实现方案,涵盖从心率监测到跌倒报警的核心功能。详细解析了系统设计、硬件搭建、软件算法及系统联调,重点介绍了STM32主控、MAX30102心率传感器和MPU6050姿态传感器的应用,并分享了跌倒检测算法与功耗优化等实战经验,为嵌入式开发与物联网创新项目提供全面指导。

sony5的博客 500

ADS重新安装失败排查指南:从注册表清理到环境变量配置

本文详细解析了ADS重新安装失败的常见原因及解决方案,包括注册表残留清理、license路径锁定处理和环境变量配置。通过分步指南和实用技巧,帮助工程师高效解决安装问题,确保软件正常运行。

weixin_42524708的博客 456

linux驱动程序3.18,Demo 驱动程序——Linux-2.6.18.3

环境:操作系统:Federo core 6内核版本:Linux-2.6.18.3步骤:1.安装内核FC6操作系统自带的内核不是标准内核,内核源码有些文件没有。所以下载Linux-2.6.18.3版本的内核并安装。1)将内核源码包拷贝到/usr/src/kernels目录下,解压。# tar –zxvf linux-2.6.18.3.tar.gz2) 进入源码根目录,配置内核,编译内核,编译内核模块...

weixin_33320626的博客 454

无人机通信电台--XBee-PRO 900HP (S3B)

XBee PRO S3B也称为XBee-900HP无线模块,它是一款工作在频段900~928MHz之间,基于FHSS跳频技术的远距离无线数传电台核心模块。常用型号如下: 类别 型号 开发套件 XKB9-DMT-UHP XBee-PRO 900HP (S3B) DigiMesh模块,200Kbps,软天线 XBP9B-DMWT-002 XBee-PRO 900HP (S3B)...

low米的博客 4505

linux 安装触摸版驱动程序,添加触摸屏驱动程序 - linux-2.6.32在mini2440开发板上移植_Linux编程_Linux公社-Linux系统门户网站...

在内核中添加触摸屏驱动程序编者:linux2.6.32并没有带S3C2440触摸屏驱动程序,需要自己实现。而在此的触摸屏驱动程序时作为一个输入设备来实现的。在linux中,对于输入设备而言,内核专为其设计了输入子系统,由核心层处理公共的工作。因为对于输入设备而言,只是中断、读键值/坐标值是与设备相关的,其余的如输入事件的缓冲区的管理以及字符设备驱动的file_operations接口则是输入设备通...

weixin_42347763的博客 779

ESP32-P4开发板实战:从零配置VSCode环境到点亮MIPI-DSI屏幕(避坑指南)

本文详细介绍了ESP32-P4开发板在VSCode环境下的配置流程,包括SDK安装、idf_component.yml依赖管理,以及MIPI-DSI屏幕驱动的实现方法。通过实战指南帮助开发者避开常见配置陷阱,快速完成从环境搭建到屏幕点亮的全流程,适用于物联网和嵌入式开发场景。

pp12345的博客 742

【kubernetes】kubernetes kubeadm init 错误了 怎么回退?

因为虚拟机设置问题首先报错:然后我根据文章修改了核数。重启后,查看VIP运行所在机器,发现机器没有变化然后重新执行初始化后面报错了根据提供的信息,问题主要集中在 kubelet 未能成功启动控制平面组件,导致初始化过程超时。以下是可能的原因及相应的排查步骤:原因 1:kubelet 未运行或运行状态异常正如错误提示所指出,kubelet 可能未运行或处于不健康状态。请按照提示执行以下命令检查 kubelet 的状态和服务日志:根据输出结果,判断 kubelet 是否正常运行。后续一大堆错误。

九师兄 121

linux 内核 触摸屏,向内核里面添加触摸屏驱动(方法适合其他设备)

我最近在给2.6.13内核做设备驱动,现在写到"触摸屏",我参考的方法如下:(大家先浏览一下啊,呵呵)在linux2.6.14中没有提供s3c2410的驱动,所以我们要新建驱动文件,我们在linux2.6.14/drivers/input/touchscreen目录下建立新的文件hfrk_s3c2410_ts.c文件,驱动文件我们可以参考类似的触摸屏驱动,具体的内容参看网站或者光盘提供的源代码。首...

weixin_31601663的博客 1192

4.6 知识库使用指南:如何让AI Agent拥有专业知识

本文介绍了如何为AI Agent构建和使用知识库。知识库的作用是为Agent提供专业知识,包含文档、数据和规则,可用于专业问答和知识检索。使用方法包括创建知识库(上传文档)、向量化处理、知识检索以及在Agent中的应用。核心要点是理解知识库的价值和使用流程。文章还提出了构建方法、检索优化和应用实践等思考题,建议准备高质量数据、优化检索效果并持续更新知识库。下节将介绍用LangGraph构建复杂AI应用。

yonggeit的博客 3012

linux-2.6.32在mini2440开发板上移植---添加触摸屏驱动程序

在内核中添加触摸屏驱动程序 编者:linux2.6.32并没有带S3C2440触摸屏驱动程序,需要自己实现。而在此的触摸屏驱动程序时作为一个输入设备来实现的。在linux中,对于输入设备而言,内核专为其设计了输入子系统,由核心层处理公共的工作。因为对于输入设备而言,只是中断、读键值/坐标值是与设备相关的,其余的如输入事件的缓冲区的管理以及字符设备驱动的file_operations接口则是输

viewsky11的专栏 1352

基于SOLIDWORKS API的工业级插件开发指南

SOLIDWORKS API是基于COM技术的一套开发接口,支持C#、VB.NET等语言,用于自动化建模、属性管理、界面定制等功能。

举世誉之而不加劝,举世非之而不加沮,定乎内外之分,辩乎荣辱之境,斯已矣。 2750

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

支持v4l2 linux内核选项 s3c2440,基于S3C2440的Linux摄像头驱动开发

本文以Linux下处理视频设备的最新标准--V4L2为基准,介绍了摄像头驱动及应用程序编码方法.以s3c2440开发板为硬件平台,在linux-2.6.22版本内核下,对ov9652传感器芯片进行了代码开发工作。嵌入式Linux相对于大多数商业化嵌入式操作系统具有无可比拟的优越性,由于其开源,多任务多进程、高度模块化、支持多平台、性能稳定[1]等诸多优点,现已广泛应用于嵌入式领域阻J。ARM系列处...

weixin_29611239的博客 261

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Linux系统PWM驱动【转】

本文转载自:https://blog.csdn.net/BorntoX/article/details/51879786 硬件平台:IMX6内核版本:kernel3.0.35在linux内核中有一个规律,Linux内核开发者把通用的东西都总结出来,个性化的东西就留出接口,和GPIO驱动类似,PWM驱动在内核中也提供了对应的接口函数,内核提供的接口函数声明在include/linux/pwm.h中...

zzb2760715357的博客 117

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

linux 编译mini2440的驱动程序,按键驱动程序移植 - linux-2.6.32在mini2440开发板上移植_Linux编程_Linux公社-Linux系统门户网站...

编者:按键驱动程序涉及到linux中断程序的编写。1、按键原理图。2、驱动程序的编写移植。在/linux-2.6.32.2/drivers/char/目录下创建一个新的驱动程序文件mini2440_buttons.c,内容及详细注释如下:#include #include #include #include #include #include #include #include #include ...

weixin_36036852的博客 179

基于JZ2440自己编写4.3寸LCD驱动

简介 linux内核自带LCD驱动,这里我们自己从头写一个LCD驱动程序,编写APP的人只知道使用open、read、write······,并不清楚原理图或者寄存器,这接口函数最终调用的是我们自己在驱动中实现的驱动层open、read、write,这些代码与硬件息息相关。一般写驱动基本步骤就是: 定义主设备号 写一个文件操作集合 register_chidev进行...

笔记用 969

Linux驱动实践:带你一步一步编译内核驱动程序

在刚才的 Kconfig 参数配置中,CONFIG_HELLO 被设置为 y,于是这句话就被翻译成: obj-y += hello,表示把 hello 驱动编译进内核。虽然文章内容很漂亮,但是不知道怎么去实践、验证。打印信息的级别有好几个,从 DEBUG 到 EMERG,这里使用的是 KERN_ALERT,方便查看打印信息。CONFIG_HELLO 可以看做一个变量,在编译的时候,这个变量的值可能是:y, n 或者 m。每一篇文章的介绍都是正确的,但是如果把很多文章放在一起看,就会发现怎么说的都不一样啊?

天涯路的专栏 4345

Linux驱动程序如何编译进内核

很多刚接触Linux驱动的初学者,肯定有这样的疑问,如何

亚亚图雷的专栏 2200
上一篇: fedora16 编译 linux kernel 2.6.33
下一篇: linux学习 - 守护进程
linkerloader
博客等级 码龄14年 1粉丝 4原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值