linux驱动模块编译安装时,makefile出现的问题

lipv 2016-02-21 03:03:09
系统为ubuntu kylin 15.10,内核版本4.2.0-27
自己编译安装一个字符驱动模块,驱动源码如下:

my_chrdev.c
#include <linux/init.h>//初始化头文件
#include <linux/module.h>//动态的将模块加载到内核中去常用的宏定义如 MODULE_LICESENCE(),MODULE_AUTHOR(),
#include <linux/kernel.h>
#include <linux/cdev.h>//包含了cdev 结构及相关函数的定义
#include <linux/types.h>//对一些特殊类型的定义,例如dev_t, off_t, pid_t
#include <linux/fs.h> //包含文件操作相关struct的定义,例struct file_operations
#include <linux/errno.h>//包含了对返回值的宏定义,这样用户程序可以用perror输出错误信息。
#include <linux/sched.h>//内核等待队列中要使用的TASK_NORMAL、TASK_INTERRUPTIBLE包含在这个头文件
#include <linux/mm.h>//内存管理头文件,含有页面大小定义和一些页面释放函数原型
#include <linux/slab.h>//包含了kcalloc、kzalloc内存分配函数的定义
#include <asm/io.h>//I/O头文件,以宏的嵌入汇编程序形式定义对I/O端口操作的函数
#include <asm/system.h>//系统头文件,定义了设置或修改描述符/中断门等的嵌入式汇编宏
#include <asm/uaccess.h>//与处理器相关的入口

#define MY_CHRDEV_SIZE 1024 //设备申请的内存空间1k

struct cdev cdev; //字符设备结构体
static int mychrdev_major=0; //主设备号变量
struct Device
{
char *data; //char数组指针,用于存放从用户读入的数据
long data_size; //存储的数据长度
} *pdev;

static int my_open(struct inode *inode, struct file *file)
{
file->private_data = pdev;
return 0;
}

static loff_t my_llseek(struct file *file, loff_t offset, int orig)
{
loff_t cfo=0; //current file offset
switch(orig)
{
case 0: cfo = offset; //SEEK_SET
break;
case 1: cfo = file->f_pos + offset; //SEEK_CUR
break;
case 2: cfo = MY_CHRDEV_SIZE - 1 + offset; //SEEK_END
break;
}
if ((cfo < 0) || (cfo > MY_CHRDEV_SIZE))
{
return -EINVAL;
}
file->f_pos = cfo;
return cfo;
}

//对应用户态的read,写数据到用户空间
static ssize_t my_read(struct file *file, const char __user *buf,
size_t count, loff_t *p_cfo)
{
int val=0;
struct Device *dev = file->private_data;//设备描述结构体指针,获取设备信息
if(copy_to_user(buf, (void *)(dev->data + *p_cfo), count))
val = -EFAULT;
else
{
*p_cfo += count;
val = count;
}
return val;
}

//对应用户态的write,从用户空间读入数据
static ssize_t my_write(struct file *file, const char __user *buf,
size_t count,loff_t *p_cfo)
{
int val = 0;
struct Device *dev = file->private_data;
if(copy_from_user(dev->data + *p_cfo), buf, count))
val = -EFAULT;
else
{
*p_cfo += count;
val = count;
}
return val;
}

struct int file_operations fops =
{
.owner = THIS_MODULE,
.llseek = my_llseek,
.read = my_read,
.write = my_write;
.open = my_open,
}

int __init init_module(void)
{
int dev_no;
if((dev_no= register_chrdev(0, "MyChrDev", &fops) < 0)
{
printk(KERN_INFO "MyChrDev: Fail to get major num.\n");
return dev_no;
}
if(mychrdev_major == 0)
mychrdev_major = dev_no;

cdev_init(&cdev, &fops);
cdev.owner = THIS_MODULE;
cdev.ops = &fops;
cdev_add(&cdev, MKDEV(mychrdev_major, 0), 1); //注册1个字符设备

//为设备描述结构体分配内存
pdev = kmalloc(sizeof(struct Device), GFP_KERNEL);
if(!pdev)
{
dev_no = -ENOMEM;
printk(KERN_INFO "MyChrDev: Fail to get memory.\n");
return dev_no;
}
(*pdev).size = MY_CHRDEV_SIZE;
(*pdev).data = kmalloc(MY_CHRDEV_SIZE, GFP_KERNEL);
memset((*pdev).data, 0, (*pdev).size);
return 0;
}

void __exit cleanup_module(void)
{
cdev_del(&cdev);//注销设备
kfree(pdev);//注销设备结构体
kfree((*pdev).data);//注销设备内存空间
unregister_chrdev(mychrdev_major, "MyChrDev");//卸载主设备号
}

MODULE_LICESENCE("Dual BSD/GLPL");
MODULE_AUTHOR("Lipeng Wan");

module_init(init_module);
module_exit(cleanup_module);


同时,自己编写的Makefile如下

ifneq  ($(KERNELRELEASE),)
#kbuild syntax
#the source file
mymodule-objs := my_chrdev.o
#the target file
obj-m := my_chrdev.o
else
#def some macros
PWD := $(shell pwd)
KVER ?= $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build
all:
$(MAKE) -C $(KDIR) M=$(PWD)
clean:
rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions
endif


第一次写makefile,开始不知道要注意tab键,折腾了半天才明白。但是编译的时候还是出错:
Makefile:1: *** recipe commences before first target。 停止。

新手在这里求问了。
...全文
854 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
lipv 2016-02-27
  • 打赏
  • 举报
回复
引用 11 楼 nswcfd 的回复:
出现recipe commences before first target的时候的makefile的确切内容是什么?(注意排除粘贴的影响)
ifneq ($(KERNELRELEASE),) #kbuild syntax #the source file mymodule-objs := my_chrdev.o #the target file obj-m := my_chrdev.o else #def some macros PWD := $(shell pwd) KVER ?= $(shell uname -r) KDIR := /lib/modules/$(KVER)/build all: $(MAKE) -C $(KDIR) M=$(PWD) clean: rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions endif 就是上面第一个makefile
lipv 2016-02-27
  • 打赏
  • 举报
回复
引用 10 楼 nswcfd 的回复:
贴的有问题吧? 如果把#ifneq, #else, #endif之前的注释去掉的话,endif前面有个tab,属于clean target的一部分,导致整个makefile由于缺少endif不完整?
没注意这个tab,但是没有出现之前的错误了,成功产生.ko文件
nswcfd 2016-02-26
  • 打赏
  • 举报
回复
出现recipe commences before first target的时候的makefile的确切内容是什么?(注意排除粘贴的影响)
nswcfd 2016-02-26
  • 打赏
  • 举报
回复
贴的有问题吧? 如果把#ifneq, #else, #endif之前的注释去掉的话,endif前面有个tab,属于clean target的一部分,导致整个makefile由于缺少endif不完整?
图灵转世 2016-02-24
  • 打赏
  • 举报
回复
网上百度一下,应该可以解决额,
nswcfd 2016-02-23
  • 打赏
  • 举报
回复
估计还是tab引起的问题 http://www.gnu.org/software/make/manual/html_node/Error-Messages.html ‘recipe commences before first target. Stop.’ ‘missing rule before recipe. Stop.’ This means the first thing in the makefile seems to be part of a recipe: it begins with a recipe prefix character and doesn’t appear to be a legal make directive (such as a variable assignment). Recipes must always be associated with a target. The second form is generated if the line has a semicolon as the first non-whitespace character; make interprets this to mean you left out the "target: prerequisite" section of a rule. See Rule Syntax. http://www.gnu.org/software/make/manual/html_node/Recipe-Syntax.html 5.1 Recipe Syntax Makefiles have the unusual property that there are really two distinct syntaxes in one file. Most of the makefile uses make syntax (see Writing Makefiles). However, recipes are meant to be interpreted by the shell and so they are written using shell syntax. The make program does not try to understand shell syntax: it performs only a very few specific translations on the content of the recipe before handing it to the shell. Each line in the recipe must start with a tab (or the first character in the value of the .RECIPEPREFIX variable; see Special Variables), except that the first recipe line may be attached to the target-and-prerequisites line with a semicolon in between. Any line in the makefile that begins with a tab and appears in a “rule context” (that is, after a rule has been started until another rule or variable definition) will be considered part of a recipe for that rule. Blank lines and lines of just comments may appear among the recipe lines; they are ignored. Some consequences of these rules include: A blank line that begins with a tab is not blank: it’s an empty recipe (see Empty Recipes). A comment in a recipe is not a make comment; it will be passed to the shell as-is. Whether the shell treats it as a comment or not depends on your shell. A variable definition in a “rule context” which is indented by a tab as the first character on the line, will be considered part of a recipe, not a make variable definition, and passed to the shell. A conditional expression (ifdef, ifeq, etc. see Syntax of Conditionals) in a “rule context” which is indented by a tab as the first character on the line, will be considered part of a recipe and be passed to the shell.
nswcfd 2016-02-23
  • 打赏
  • 举报
回复
语法上倒是没有什么问题。
lipv 2016-02-23
  • 打赏
  • 举报
回复
引用 6 楼 nswcfd 的回复:
语法上倒是没有什么问题。

	#ifneq  ($(KERNELRELEASE),)
	#kbuild syntax
	#the source file
	mymodule-objs := my_chrdev.o
	#the target file
	obj-m := my_chrdev.o
	#else
	#def some macros
	PWD := $(shell pwd)
	KVER := $(shell uname -r)
	KDIR := /lib/modules/$(KVER)/build
all:
		$(MAKE) -C $(KDIR) M=$(PWD)
clean:
		rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions
	#endif
我之前已经注意了tab键的问题,检查后确认不是tab键的问题 我把那个判断注释掉之后,貌似可以消除前面的错误,但是又出现了一堆新的报错 我看到提示里面是Makefile,我重建的是makefile,于是更改过来(即把makefile重命名为Makefile)。 执行make后产生了一个build-in.o文件,仍然报错,但错误不一样了,报的都是一堆源码错误(这个我可以再改)。 觉得诡异的地方就是:为什么加了ifneq那个部分就有问题?难道是和版本有关?还是说这ubuntu kylin系统的原因? 总之,还是感谢回答。
常书 2016-02-22
  • 打赏
  • 举报
回复
不是的,KDIR是你放内核的目录,用下面这种方式编译的话 上上面的的编译就要拷贝到内核目录去编译 你内核没编译过,编译驱动会报错的
lipv 2016-02-22
  • 打赏
  • 举报
回复
引用 2 楼 r_Jimy 的回复:
到/lib/modules/$(KVER)/build这里编译一下
是直接把makefile和.c文件复制过去,存放在一个文件夹里,切换到文件夹目录进行编译么?试了,还是那个错误!
lipv 2016-02-22
  • 打赏
  • 举报
回复
引用 1 楼 r_Jimy 的回复:
内核先编译再编译你的模块
内核是编译过了的,有两个内核:linux-4.2.0 和4.2.0.7。用gcc直接编译时,提示找不到linux/module.h, linux/init.h头文件也有问题。
常书 2016-02-22
  • 打赏
  • 举报
回复
到/lib/modules/$(KVER)/build这里编译一下
常书 2016-02-22
  • 打赏
  • 举报
回复
内核先编译再编译你的模块
《Android系统源代码情景分析》随书光盘内容(源代码) 目录如下: 第1篇 初识Android系统 第1章 准备知识 1.1 Linux内核参考书籍 1.2 Android应用程序参考书籍 1.3 下载、编译和运行Android源代码 1.3.1 下载Android源代码 1.3.2 编译Android源代码 1.3.3 运行Android模拟器 1.4 下载、编译和运行Android内核源代码 1.4.1 下载Android内核源代码 1.4.2 编译Android内核源代码 1.4.3 运行Android模拟器 1.5 开发第一个Android应用程序 1.6 单独编译和打包Android应用程序模块 1.6.1 导入单独编译模块的mmm命令 1.6.2 单独编译Android应用程序模块 1.6.3 重新打包Android系统镜像文件 第2章 硬件抽象层 2.1 开发Android硬件驱动程序 2.1.1 实现内核驱动程序模块 2.1.2 修改内核Kconfig文件 2.1.3 修改内核Makefile文件 2.1.4 编译内核驱动程序模块 2.1.5 验证内核驱动程序模块 2.2 开发C可执行程序验证Android硬件驱动程序 2.3 开发Android硬件抽象层模块 2.3.1 硬件抽象层模块编写规范 2.3.2 编写硬件抽象层模块接口 2.3.3 硬件抽象层模块的加载过程 2.3.4 处理硬件设备访问权限问题 2.4 开发Android硬件访问服务 2.4.1 定义硬件访问服务接口 2.4.2 实现硬件访问服务 2.4.3 实现硬件访问服务的JNI方法 2.4.4 启动硬件访问服务 2.5 开发Android应用程序来使用硬件访问服务 第3章 智能指针 3.1 轻量级指针 3.1.1 实现原理分析 3.1.2 应用实例分析 3.2 强指针和弱指针 3.2.1 强指针的实现原理分析 3.2.2 弱指针的实现原理分析 3.2.3 应用实例分析 第2篇 Android专用驱动系统 第4章 Logger日志系统 4.1 Logger日志格式 4.2 Logger日志驱动程序 4.2.1 基础数据结构 4.2.2 日志设备的初始化过程 4.2.3 日志设备文件的打开过程 4.2.4 日志记录的读取过程 4.2.5 日志记录的写入过程 4.3 运行库层日志库 4.4 C/C++日志写入接口 4.5 Java日志写入接口 4.6 Logcat工具分析 4.6.1 相关数据结构 4.6.2 初始化过程 4.6.3 日志记录的读取过程 4.6.4 日志记录的输出过程 第5章 Binder进程间通信系统 5.1 Binder驱动程序 5.1.1 基础数据结构 5.1.2 Binder设备的初始化过程 5.1.3 Binder设备文件的打开过程 5.1.4 Binder设备文件的内存映射过程 5.1.5 内核缓冲区管理 5.2 Binder进程间通信库 5.3 Binder进程间通信应用实例 5.4 Binder对象引用计数技术 5.4.1 Binder本地对象的生命周期 5.4.2 Binder实体对象的生命周期 5.4.3 Binder引用对象的生命周期 5.4.4 Binder代理对象的生命周期 5.5 Binder对象死亡通知机制 5.5.1 注册死亡接收通知 5.5.2 发送死亡接收通知 5.5.3 注销死亡接收通知 5.6 Service Manager的启动过程 5.6.1 打开和映射Binder设备文件 5.6.2 注册为Binder上下文管理者 5.6.3 循环等待Client进程请求 5.7 Service Manager代理对象的获取过程 5.8 Service组件的启动过程 5.8.1 注册Service组件 5.8.2 启动Binder线程池 5.9 Service代理对象的获取过程 5.10 Binder进程间通信机制的Java接口 5.10.1 Service Manager的Java代理对象的获取过程 5.10.2 Java服务接口的定义和解析 5.10.3 Java服务的启动过程 5.10.4 Java服务代理对象的获取过程 5.10.5 Java服务的调用过程 第6章 Ashmem匿名共享内存系统 6.1 Ashmem驱动程序 6.1.1 基础数据结构 6.1.2 匿名共享内存设备的初始化过程 6.1.3 匿名共享内存设备文件的打开过程 6.1.4 匿名共享内存设备文件的内存映射过程 6.1.5 匿名共享内存块的锁定和解锁过程 6.1.6 匿名共享内存块的回收过程 6.2 运行库cutils的匿名共享内存访问接口 6.3 匿名共享内存的C++访问接口 6.3.1 MemoryHeapBase 6.3.2 MemoryBase 6.3.3 应用实例 6.4 匿名共享内存的Java访问接口 6.4.1 MemoryFile 6.4.2 应用实例 6.5 匿名共享内存的共享原理 第3篇 Android应用程序框架 第7章 Activity组件的启动过程 7.1 Activity组件应用实例 7.2 根Activity组件的启动过程 7.3 子Activity组件在进程内的启动过程 7.4 子Activity组件在新进程中的启动过程 第8章 Service组件的启动过程 8.1 Service组件应用实例 8.2 Service组件在新进程中的启动过程 8.3 Service组件在进程内的绑定过程 第9章 Android系统广播机制 9.1 广播机制应用实例 9.2 广播接收者的注册过程 9.3 广播的发送过程 第10章 Content Provider组件的实现原理 10.1 Content Provider组件应用实例 10.1.1 ArticlesProvider 10.1.2 Article 10.2 Content Provider组件的启动过程 10.3 Content Provider组件的数据共享原理 10.3.1 数据共享模型 10.3.2 数据传输过程 10.4 Content Provider组件的数据更新通知机制 10.4.1 注册内容观察者 10.4.2 发送数据更新通知 第11章 Zygote和System进程的启动过程 11.1 Zygote进程的启动脚本 11.2 Zygote进程的启动过程 11.3 System进程的启动过程 第12章 Android应用程序进程的启动过程 12.1 应用程序进程的创建过程 12.2 Binder线程池的启动过程 12.3 消息循环的创建过程 第13章 Android应用程序的消息处理机制 13.1 创建线程消息队列 13.2 线程消息循环过程 13.3 线程消息发送过程 13.4 线程消息处理过程 第14章 Android应用程序的键盘消息处理机制 14.1 键盘消息处理模型 14.2 InputManager的启动过程 14.2.1 创建InputManager 14.2.2 启动InputManager 14.2.3 启动InputDispatcher 14.2.4 启动InputReader 14.3 InputChannel的注册过程 14.3.1 创建InputChannel 14.3.2 注册Server端InputChannel 14.3.3 注册系统当前激活的应用程序窗口 14.3.4 注册Client端InputChannel 14.4 键盘消息的分发过程 14.4.1 InputReader获得键盘事件 14.4.2 InputDispatcher分发键盘事件 14.4.3 系统当前激活的应用程序窗口获得键盘消息 14.4.4 InputDispatcher获得键盘事件处理完成通知 14.5 InputChannel的注销过程 14.5.1 销毁应用程序窗口 14.5.2 注销Client端InputChannel 14.5.3 注销Server端InputChannel 第15章 Android应用程序线程的消息循环模型 15.1 应用程序主线程消息循环模型 15.2 与界面无关的应用程序子线程消息循环模型 15.3 与界面相关的应用程序子线程消息循环模型 第16章 Android应用程序的安装和显示过程 16.1 应用程序的安装过程 16.2 应用程序的显示过程

1,318

社区成员

发帖
与我相关
我的任务
社区描述
主要是开发驱动技术
社区管理员
  • 驱动程序开发区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧