liunx中字符驱动编写的简单模板
下面是关于字符驱动两个程序,主要是说明驱动编写的思想,理解驱动是怎么一步一步被实现的。
驱动的第一个实现程序,是相对于裸机编程的,主要是体会一下驱动编程思想:
cdev.h: 所包含的头文件
#ifndef CDEV_H_
#define CDEV_H_
#define MAX_CDEVS 1024
struct cdev {
struct file_operations *ops;
char *name;
int id;
};
fs.h : 包含的另一个头文件
#ifndef FS_H_
#define FS_H_
struct file_operations {
void (*open)(void);
void (*read)(void);
void (*write)(void);
void (*close)(void);
};
void open(int id);
void read(int id);
void write(int id);
void close(int id);
#endif /* FS_H_ */
extern struct cdev *cdev_map[MAX_CDEVS];
#endif /* CDEV_H_ */
cdev.c:
#include "cdev.h"
struct cdev *cdev_map[MAX_CDEVS];
void (struct cdev *dev, struct file_operations *fops)
{
dev->ops = fops;
}
void cdev_add(struct cdev *dev, int id)
{
cdev_map[id] = dev;
}
fs.c: 这里面是具体函数的实现,通过调用结构体执行不同的功能
/*
* fs.c
*
* Created on: 2015-4-3
* Author: Administrator
*/
#include "cdev.h"
#include "fs.h"
void open(int id)
{
struct cdev *tmp_dev = cdev_map[id];
tmp_dev->ops->open();
}
void write(int id)
{
struct cdev *tmp_dev = cdev_map[id];
tmp_dev->ops->write();
}
void read(id)
{
struct cdev *tmp_dev = cdev_map[id];
tmp_dev->ops->read();
}
void close(int id)
{
struct cdev *tmp_dev = cdev_map[id];
tmp_dev->ops->close();
}
led.c: 驱动功能的实现,将特定的功能传给结构体,并给一个标号,方便后面查找函数和调用
/*
* led.c
*
* Created on: 2015-4-3
* Author: Administrator
*/
#include "fs.h"
#include "cdev.h"
#define GPG3CON (*(volatile unsigned int *)0xe03001c0)
#define GPG3DAT (*(volatile unsigned int *)0xe03001c4)
void led_init(void)
{
GPG3CON = 0x1111;
}
void led_on(void)
{
GPG3DAT = 0xf;
}
void led_off(void)
{
GPG3DAT = 0x0;
}
struct file_operations led_ops = {
.open = led_init,
.write = led_on,
.close = led_off,
};
struct cdev led_cdev;
int led_dev_num = 0; //led设备的设备号
void led_driver_init(void)
{
//初始化led 字符设备驱动
cdev_init(&led_cdev, &led_ops);
//注册进内核
cdev_add(&led_cdev, led_dev_num);
}
kernel_main.c 主函数调用写好的驱动
/*
* kernel_main.c
*
* Created on: 2015-4-3
* Author: Administrator
*/
void start_kernel(void)
{
//内核各种机制的初始化以及模块入口初始化
led_driver_init();
//运行用户空间进程(应用)
main();
}
main.c 主函数:
#include "cdev.h"
#include "fs.h"
/*
* 延时函数
*/
void soft_delay(unsigned int loops)
{
while (loops > 0)
loops--;
}
/*
* led 测试c入口
*/
void main()
{
//获取led的设备号 0
open(0);
write(0);
}
对应于上面程序的makefile:
##############################################################
CROSS_COMPILE = arm-none-eabi-
CC = $(CROSS_COMPILE)gcc
LD = $(CROSS_COMPILE)ld
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
INCLUDE_PATH = ./include
CFLAGS = -I$(INCLUDE_PATH) -mabi=apcs-gnu -c -g -O0 -o
##############################################################
objs = init/start.o kernel/kernel_main.o cdev/fs.o cdev/cdev.o drivers/led.o main.o
pro_name = kernel
##############################################################
all:$(objs)
$(LD) $(objs) -Tmap.lds -o $(pro_name).elf #链接
$(OBJCOPY) -O binary $(pro_name).elf $(pro_name).bin #二进制提取
$(OBJDUMP) -D $(pro_name).elf > $(pro_name).dis #反汇编
%.o : %.s
$(CC) $(CFLAGS) $@ $< #-c 只编译不链接 -g 调试 -O0优化等级 -O1 -O2 -o 输出
%.o : %.c
$(CC) $(CFLAGS) $@ $< #$@:目标 $<:第一个依赖 $^: 多个依赖
clean:
rm -rf *.bin *.elf *.dis $(objs)
驱动的第二个实现,这个是在内核里面编写的程序:
hello_char.c:这个C程序里面包含了很多内核里面的语句,这个要查一下他们的用法
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
struct cdev hello_cdev; 先创建一个对象
int hello_major = 250; 主设备号 (前12位)
int hello_minor = 0; 次设备号 (后20位)
int num_of_hellos = 1; 注册这个设备的个数,这里表示就一个
dev_t hello_devno; 最终构成的设备号记做hello_devno
MODULE_LICENSE("GPL");
static int hello_open(struct inode *nodp, struct file *filp)
{
printk(KERN_INFO "hello open! \n");
return 0;
}
static int hello_release(struct inode *nodp, struct file *filp)
{
printk(KERN_INFO "hello release! \n");
return 0;
}
struct file_operations hello_ops = {
.open = hello_open,
.release = hello_release,
};
static int __init hello_init(void)
{
/*construct cdev number*/
hello_devno = MKDEV(hello_major, hello_minor); //hello_major << 20 | hello_minor; 构成设备号
register_chrdev_region(hello_devno, num_of_hellos, "hello_char");注册设备号,后面三个参数分别表示(设备号,有几个,设备名字)
/*cdev initialize*/
cdev_init(&hello_cdev, &hello_ops); 初始化设备
/*add to kernel*/
cdev_add(&hello_cdev, hello_devno, num_of_hellos); 将设备号和设备联系起来
printk(KERN_INFO "hello_init ! \n");
return 0;
}
static void __exit hello_exit(void) 卸载的函数
{
cdev_del(&hello_cdev); 卸载的时候删除对象
unregister_chrdev_region(hello_devno, num_of_hellos);
printk("hello_exit ! \n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_AUTHOR("minj@farsight.com.cn");
MODULE_DESCRIPTION("just for test!");
test.c内核调用驱动的程序:
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, const char *argv[])
{
int fd;
fd = open("/dev/hello", O_RDWR); 打开设备,在这之前linux里面创建了一个节点->sudo mknod /dev/hello c 250 0 对应前面的主次设备号
if (fd < 0) {
perror("open");
return -1;
}
close(fd);
return 0;
}
对应于上面程序的makefile :
MOD_NAME = hello_char
obj-m = $(MOD_NAME).o
#KERN_DIR = /home/linux/linux-2.6.35-farsight
KERN_DIR = /lib/modules/$(shell uname -r)/build
all:
make -C $(KERN_DIR) M=$(shell pwd) modules
clean:
rm -rf *.o *.ko *.mod.c *.order *.symvers .*.cmd .*versions
backup:
tar cvf ../$(MOD_NAME).tar.gz ../$(MOD_NAME)
cp ../$(MOD_NAME).tar.gz /mnt/hgfs/ubuntu_share
liunx中字符驱动编写的简单模板的更多相关文章
- 如何编写一个简单的Linux驱动(二)——设备操作集file_operations
前期知识 如何编写一个简单的Linux驱动(一)--驱动的基本框架 前言 在上一篇文章中,我们学习了驱动的基本框架.这一章,我们会在上一章代码的基础上,继续对驱动的框架进行完善.要下载上一篇文章的全部 ...
- 如何编写一个简单的Linux驱动(二)——完善设备驱动
前期知识 1.如何编写一个简单的Linux驱动(一)——驱动的基本框架 2.如何编写一个简单的Linux驱动(二)——设备操作集file_operations 前言 在上一篇文章中,我们编写设备驱动遇 ...
- 1、编写一个简单Makefile模板
一.Makefile简介 一个工程中的源文件不计其数,其按类型.功能.模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译, ...
- IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试
IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试 目的不是为了编写TSC2007驱动,是为了学习IIC驱动的编写,读一下TSC2007的ADC数据进行练习,, Linux主机驱动和外设驱 ...
- 程序一 用记事本建立文件src.dat,其中存放若干字符。编写程序,从文件src.dat中读取数据,统计其中的大写字母、小写字母、数字、其它字符的个数,并将这些数据写入到文件test.dat中。
用记事本建立文件src.dat,其中存放若干字符.编写程序,从文件src.dat中读取数据,统计其中的大写字母.小写字母.数字.其它字符的个数,并将这些数据写入到文件test.dat中. #inclu ...
- 基于OMAPL138的Linux字符驱动_GPIO驱动AD9833(一)之miscdevice和ioctl
基于OMAPL138的Linux字符驱动_GPIO驱动AD9833(一)之miscdevice和ioctl 0. 导语 在嵌入式的道路上寻寻觅觅很久,进入嵌入式这个行业也有几年的时间了,从2011年后 ...
- linux设备驱动编写入门
linux设备驱动是什么,我个人的理解是liunx有用户态和内核态,用户空间中是不能直接对设备的外设进行使用而内核态中却可以,这时我们需要在内核空间中将需要的外设驱动起来供用户空间使用.linux的驱 ...
- Tiny4412之串口(Uart)驱动编写
一:tiny4412串口驱动编写 1.串口通信简介 串口通信指串口按位(bit)发送和接收字节,串口通信的概念非常简单,串口按位(bit)发送和接收字节.尽管比按字节(byte)的并行通信慢,但是串口 ...
- MPU6050带字符驱动的i2c从设备驱动1
开干: 1.闲言碎语 这个驱动,越写觉的越简单,入门难,入门之后感觉还好.Linux开发还是比较友好的. 2.编写MPU6050带字符驱动的i2c从设备驱动 要实现的功能就是,将MPU6050作为字符 ...
随机推荐
- bind_module和DEFAULT_MODULE
在入口文件中定义define('BIND_MODULE', 'Admin'); 默认就会去找Admin模块. 配置文件中的 'DEFAULT_MODULE' => 'Home', // 默认模块 ...
- 从客户端(Content="<p>测试</p>")中检测到有潜在危险的 Request.Form 值
.NetFrameWork 4.0 Validaterequest="false"不起作用 要恢复到2.0的ASP.NET请求验证功能的行为,要在以下设置 Web.config中 ...
- 实现远程FTP特定时间轨道号MODIS数据的搜索
private ArrayList alst = new System.Collections.ArrayList();//建立ArrayList对象 int strLength = 0; strin ...
- jQuery对象与dom对象的转换
一直以来对于通过jQuery方式获取的对象使不能直接使用JavaScript的一些方法的,开始的时候不理解,现在此案知道,原来jQuery获得的对象并不和我们平时使用getElementById获得的 ...
- PC小技巧
一.如何在office 2010中安装 Microsoft Office Document Imaging 我用的是office 2010版本,如下操作可以把照片转换成文本:第一步:使用Microso ...
- Python:list用法
list是一种有序的集合,可以随时添加和删除其中的元素. 定义 空list >>> a_list=[] >>> a_list [] 普通 >>> ...
- discuz后台登陆 口令卡添加
1.通过根目录文件admin.php 找到 $admincp->init(); 2.指向 dz/source/class/discuz/discuz_admincp.php 这个方法funct ...
- C# List泛型集合中的GroupBy<>用法
//根据子项目id得到flowjump实体类 flowJumps = this.FlowJumps; //按工序groupby flowjumps IEnumerable<IGrouping&l ...
- Galera集群server.cnf参数调整--前言
文档安排: 前言部分会简述下galera集群,正文中会针对我们线上的环境,在不断业务的情况下,进行参数调整的话,有些参数不能够进行配置,会以#***的形式写入配置文件中,文档也会进行进一步说明. 如果 ...
- tcpdump 获取http请求url
There are tcpdump filters for HTTP GET & HTTP POST (or for both plus message body): Run man tcpd ...