linux2.6驱动makefile的编写和驱动编写快速参考

29 篇文章 1 订阅

http://hi.baidu.com/wsdxs1234/item/5362c675ee32e1235d178943

我用的makefile

#
# Makefile for the led driver
#
ifneq ($(KERNELRELEASE),)

obj-m := led.o

else

export ARCH=arm
# 这里要写上arm交叉编译器的位置
export CROSS_COMPILE=/home/xlq/arm-linux-4.1.1/bin/arm-linux-

# 这里要写上内核树的位置,且内核必须已经编译过了
KDIR := /home/xlq/linux/src/preview-kit/linux
PWD := $(shell pwd)

#
# Include make variables (CC, etc...)
#
ASM := $(CROSS_COMPILE)as
LD := $(CROSS_COMPILE)ld
CC := $(CROSS_COMPILE)gcc
CPP := $(CROSS_COMPILE)c++
AR := $(CROSS_COMPILE)ar
STRIP   := $(CROSS_COMPILE)strip
OBJCOPY := $(CROSS_COMPILE)objcopy
OBJDUMP := $(CROSS_COMPILE)objdump

EXTRA_CFLAGS += -O3

all:
 $(MAKE) -C $(KDIR) M=$(PWD) modules
 $(CROSS_COMPILE)strip --strip-unneeded *.ko

.PHONY: clean
clean:
 rm -rf .*.swp .*.cmd *.o *.mod.c *.ko .tmp_versions *.symvers *.order

endif

 

知识汇总

1.有个前提,必须在能建立内核模块前解决。就是保证你有版本足够的新的编译器,模块工具,已经其他必要工具。

2.obj-m :=hello.o

    表明有一个模块要从目标文件hello.o建立。在从目标文件建立后结果模块命名为hello.ke。

3.如果有一个模块名位module.ko,是来自2个源文件(file1.c和file2.c),正确的书写应该是:

    obj-m :=module.o

    module-objs :=file1.o file2.o

4.假设你的内核源码数位于~/kernel-2.6目录,用来建立你的模块的make命令会是:

   make -C~/kernel-2.6 M='pwd' modules

   这个命令开始是改变它的目录到用-C选项提供的目录下(就是说,内核源码目录)。在哪里发现内核的顶层makefile.

    M=选项使makefile在试图建立模块目标前,回到你的模块源码的目录。这个目标,依次的,是指在obj-m变量中发现

    的模块列表。

 

如下书写你的makefile:

# If KERNELRELEASE is defined, we've been invoked from the  
# kernel build system and can use its language.  
ifneq ($(KERNELRELEASE),)  
  
 obj-m := hello.o   
# Otherwise we were called directly from the command  
# line; invoke the kernel build system.

else  
  
 KERNELDIR ?= /lib/modules/$(shell uname -r)/build  
 PWD := $(shell pwd)   
default:  
 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules  
  
endif  

这个 makefile 在一次典型的建立 
中要被读 2 次. 当从命令行中调用这个 makefile , 它注意到 KERNELRELEASE 变量没有 
设置. 它利用这样一个事实来定位内核源码目录, 即已安装模块目录中的符号连接指回内 
核建立树. 如果你实际上没有运行你在为其而建立的内核, 你可以在命令行提供一个  
KERNELDIR= 选项, 设置 KERNELDIR 环境变量, 或者重写 makefile 中设置 KERNELDIR 的 
那一行. 一旦发现内核源码树, makefile 调用 default: 目标, 来运行第 2 个 make 命 
令( 在 makefile 里参数化成 $(MAKE))象前面描述过的一样来调用内核建立系统. 在第 2  
次读, makefile 设置 obj-m, 并且内核的 makefile 文件完成实际的建立模块工作.

 

 

 

快速参考

insmod   
modprobe   
rmmod  

      用户空间工具, 加载模块到运行中的内核以及去除它们. 

#include <linux/init.h>   
module_init(init_function);   
module_exit(cleanup_function);  

      指定模块的初始化和清理函数的宏定义. 

__init   
__initdata   
__exit  

__exitdata  

      函数( __init 和 __exit )和数据 (__initdata 和 __exitdata)的标记, 只用在模 
      块初始化或者清理时间. 为初始化所标识的项可能会在初始化完成后丢弃; 退出的 
      项可能被丢弃如果内核没有配置模块卸载. 这些标记通过使相关的目标在可执行文 
      件的特定的 ELF 节里被替换来工作. 

#include <linux/sched.h>  

      最重要的头文件中的一个. 这个文件包含很多驱动使用的内核 API 的定义, 包括睡 
      眠函数和许多变量声明. 

struct task_struct *current;  

      当前进程. 

current->pid   
current->comm  

      进程 ID 和 当前进程的命令名. 

obj-m  

      一个 makefile 符号, 内核建立系统用来决定当前目录下的哪个模块应当被建立. 

/sys/module   
/proc/modules  

      /sys/module 是一个 sysfs 目录层次, 包含当前加载模块的信息. /proc/moudles  
      是旧式的, 那种信息的单个文件版本. 其中的条目包含了模块名, 每个模块占用的 
      内存数量, 以及使用计数. 另外的字串追加到每行的末尾来指定标志, 对这个模块 
      当前是活动的. 

vermagic.o  

      来自内核源码目录的目标文件, 描述一个模块为之建立的环境. 

#include <linux/module.h>  

      必需的头文件. 它必须在一个模块源码中包含. 

#include <linux/version.h>  

      头文件, 包含在建立的内核版本信息. 

LINUX_VERSION_CODE  

      整型宏定义, 对 #ifdef 版本依赖有用. 

EXPORT_SYMBOL (symbol);   
EXPORT_SYMBOL_GPL (symbol);  

       宏定义, 用来输出一个符号给内核. 第 2 种形式输出没有版本信息, 第 3 种限制 
       输出给 GPL 许可的模块. 

MODULE_AUTHOR(author);   
MODULE_DESCRIPTION(description);   
MODULE_VERSION(version_string);   
MODULE_DEVICE_TABLE(table_info);   
MODULE_ALIAS(alternate_name);  

       放置文档在目标文件的模块中. 

module_init(init_function);   
module_exit(exit_function);  

       宏定义, 声明一个模块的初始化和清理函数. 

#include <linux/moduleparam.h>   
module_param(variable, type, perm);  

       宏定义, 创建模块参数, 可以被用户在模块加载时调整( 或者在启动时间, 对于内 
       嵌代码). 类型可以是 bool, charp, int, invbool, short, ushort, uint, ulong,  
       或者 intarray. 

#include <linux/kernel.h>   
int printk(const char * fmt, ...);  

       内核代码的 printf 类似物. 

 

 

 

 

 

#include <linux/types.h>   
dev_t  

      dev_t 是用来在内核里代表设备号的类型. 

int MAJOR(dev_t dev);   
int MINOR(dev_t dev);  

      从设备编号中抽取主次编号的宏. 

dev_t MKDEV(unsigned int major, unsigned int minor);  

      从主次编号来建立 dev_t 数据项的宏定义. 

#include <linux/fs.h>  

      "文件系统"头文件是编写设备驱动需要的头文件. 许多重要的函数和数据结构在此 
      定义. 

int register_chrdev_region(dev_t first, unsigned int count, char *name)   
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char  
*name)   
void unregister_chrdev_region(dev_t first, unsigned int count);  

      允许驱动分配和释放设备编号的范围的函数. register_chrdev_region 应当用在事 
      先知道需要的主编号时; 对于动态分配, 使用 alloc_chrdev_region 代替. 

int register_chrdev(unsigned int major, const char *name, struct  
file_operations *fops);  

      老的( 2.6 之前) 字符设备注册函数. 它在 2.6 内核中被模拟, 但是不应当给新代 
      码使用. 如果主编号不是 0, 可以不变地用它; 否则一个动态编号被分配给这个设 
      备. 

int unregister_chrdev(unsigned int major, const char *name);  

      恢复一个由 register_chrdev 所作的注册的函数. major 和 name 字符串必须包含 
      之前用来注册设备时同样的值. 

struct file_operations;   
struct file;   
struct inode;   

大部分设备驱动使用的 3 个重要数据结构. file_operations 结构持有一个字符驱 
       动的方法; struct file 代表一个打开的文件, struct inode 代表磁盘上的一个文 
       件. 

#include <linux/cdev.h>   
struct cdev *cdev_alloc(void);   
void cdev_init(struct cdev *dev, struct file_operations *fops);   
int cdev_add(struct cdev *dev, dev_t num, unsigned int count);   
void cdev_del(struct cdev *dev);  

       cdev 结构管理的函数, 它代表内核中的字符设备. 

#include <linux/kernel.h>   
container_of(pointer, type, field);  

       一个传统宏定义, 可用来获取一个结构指针, 从它里面包含的某个其他结构的指针. 

#include <asm/uaccess.h>  

       这个包含文件声明内核代码使用的函数来移动数据到和从用户空间. 

unsigned long copy_from_user (void *to, const void *from, unsigned long count);   
unsigned long copy_to_user (void *to, const void *from, unsigned long count);  

       在用户空间和内核空间拷贝数据. 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值