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! ");
return 0;
}
static int hello_release(struct inode *nodp, struct file *filp)
{
printk(KERN_INFO "hello release! ");
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 ! ");
return 0;
}


static void __exit hello_exit(void) 卸载的函数
{
cdev_del(&hello_cdev); 卸载的时候删除对象
unregister_chrdev_region(hello_devno, num_of_hellos);
printk("hello_exit ! ");
}

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

原文地址:https://www.cnblogs.com/cnlg/p/4394154.html