内核编译
一、内核编译的步骤
编译步骤: (linux 内核源码的顶层目录下操作 )1. 拷贝默认配置到 .config cp config_mini2440_td35 .config2. make menuconfig 内核配置 make menuconfig3. make uImage make uImage
二、编译过程遇到的问题
1、在 make uImage编译时报错:
Can't use 'defined(@array)' (Maybe you should just omit the defined()?) at kernel/timeconst.pl line 373.
/home/linux/linux-2.6.32.2/kernel/Makefile:129: recipe for target 'kernel/timeconst.h' failed
make[1]: *** [kernel/timeconst.h] Error 255
Makefile:878: recipe for target 'kernel' failed
make: *** [kernel] Error 2
修改
2、修改入口地址
通过tftp下载到开发板后,内核还不能正常启动
原因:
加载地址 Load Address:0x30008000
入口地址 Entry Point:要改成0x30008040
因为:uImage前64字节是头文件,要偏移64字节
3、 Image zImage uImage
Image 可以直接使用的内核镜映像zImage 一段解压程序 + Image 的压缩文件uImage 64 字节的头信息+ zImage
改成下图
修改方法
再重新编译
三、向内核中加入新文件
向内核中新加文件(以向 drivers/char 下新加 test.c 文件为例)1. 在 drivers/char 目录下创建并编辑文件 test.c2. 修改同层目录下 Makefile ,新增一句obj-$(CONFIG_TEST) += test.o3. 修改同层目录下的 Kconfig ,新增一个配置选项config TESTbool “ this is a test ”default yhelphahaha, have big use, donot delelte!4. make menuconfig5. make uImage
四、加入新的目录
1、创建一个新的目录,在该目录下写一个新的Kconfig
Kconfig的格式
2、在该目录的上一级目录的Kconfig里加入
五、补充知识
1、在 Linux 内核编译过程中,
cp config_mini2440_td35 .config
命令的作用是将一个预先配置好的内核配置文件config_mini2440_td35
复制到当前目录下的.config
文件中。2、make menuconfig 可视化的配置菜单(内核活地图)配置完成后,可以保存更改,这些更改将反映在当前目录下的
.config
文件中。3、更改Kconfig的配置后,make menuconfig菜单也会被更改(Kconfig 定义make menuconfig中的选项)
4、
.config ( 存放 make menuconfig 的配置结果 )CONFIG_BT = yCONFIG_WIFI = nMakefile ( 使用 .config 中的变量 )obj-$(CONFIG_BT) += bt.oobj-$(CONFIG_WIFI) += wifi.o
设备驱动
设备驱动分类:字符设备:数据访问是顺序的 ( 字节流 )块设备:数据访问是随机的,一般是存储设备网络设备:会集成协议, 靠名字维护
计算机组成:软件(图上)、硬件(图下)
设备号: 32位高12位:主设备号,区分不通类型的设备低20位:次设备号,区分同类的不通设备
1、驱动程序开发步骤
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/module.h>
#define MAJOR_NUM 255
#define MINOR_NUM 0
#define DEV_NAME "demo"
static int open(struct inode*node,struct file*file)
{
printk("demo open ...\n");
return 0;
}
static ssize_t read(struct file*file,char __user*buf,size_t len,loff_t*offset)
{
printk("demo read...\n");
return 0;
}
static ssize_t write(struct file*file,const char __user*buf,size_t len,loff_t*offset)
{
printk("demo write ...\n");
return 0;
}
static int close(struct inode*node,struct file *file)
{
printk("demo close...\n");
return 0;
}
static dev_t dev_num;
static struct file_operations fops=
{
.owner =THIS_MODULE,
.open=open,
.read=read,
.write=write,
.release=close
};
static struct cdev dev;
static int __init demo_init(void)
{
int ret=0;
dev_num =MKDEV(MAJOR_NUM,MINOR_NUM);
ret=cdev_add(&dev,dev_num,1);
if(ret<0)
{
goto err_cdrv_add;
}
cdev_init(&dev,&fops);
ret=register_chrdev_region(dev_num,1,DEV_NAME);
if(ret<0)
goto err_register;
printk("demo_init #######################\n");
// register
return 0;
err_cdrv_add:
cdev_del(&dev);
printk("demo_init cdev_add failed ...\n");
return ret;
err_register:
unregister_chrdev_region(dev_num,1);
cdev_del(&dev);
printk("demo_init cdev_add failed ...\n");
return ret;
}
static void __exit demo_exit(void)
{
unregister_chrdev_region(dev_num,1);
cdev_del(&dev);
printk("demo_exit #######################\n");
}
module_init(demo_init);
module_exit(demo_exit);
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
int main(int argc, const char *argv[])
{
int fd=open("/dev/demo_app",O_RDWR);
if(-1==fd)
{
perror("open");
return -1;
}
char buf[10];
read(fd,buf,sizeof(buf));
write(fd,buf,sizeof(buf));
close(fd);
return 0;
}
3、在开发板下载内核、启动内核
4、在开发板上的Linux系统输入命令:mknod /dev/demo_app c 255 0
手动创建设备节点:mknod /dev/demo c 255 0/dev/demo 设备节点名称 (open 的设备名 )c 字符设备255 主设备号0 次设备号
5、最后运行应用层的程序
2、动态使用驱动模块(详细步骤)
1.修改同一目录下的Kconfig,修改形式如下
config LED3
tristate "open LED"
default y
---help---
open led!
2.make menuconfig 选为M
3.make modules(编译可动态加载的内核模块)
生成 led3.ko文件
4.复制生成的 .ko文件到 /home/linux/nfs/rootfs目录下(挂载)
5.在开发板上的操作系统上
命令:insmod led3.ko (动态加载内核模块)
6.创建设备节点
mknod dev/led c 250 0
7.运行应用层的程序
内核空间和用户空间的数据拷贝:copy_from_user();copy_to_user();
动态使用驱动模块:make menuconfig 将对应的模块配置为 Mmake modules 编译可动态加载的内核模块使用:insmod demo.ko 动态加载内核模块lsmod 查看动态加载的内核模块rmmod demo 卸载动态加载的模块make uImage 编译静态内核 -- 将选项为 y 的所有模块静态编译进内核make modules 编译动态模块 -- 将选项为 m 的所有模块编译为可动态加载的 .ko 文件make 编译静态内核和动态内核模块
3、杂项设备驱动
杂项 ( 混杂 ) 设备驱动struct miscdevice misc;misc_register(&misc);misc_deregister(&misc);主设备号为10,
以misc_led.c为例子
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/module.h>
#include <asm/string.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include<linux/miscdevice.h>
#define MAJOR_NUM 250
#define MINOR_NUM 0
#define DEV_NAME "led"
#define GPBCON 0X56000010
#define GPBDAT 0X56000014
static volatile unsigned long *gpbcon;
static volatile unsigned long *gpbdat;
static void led1_init(void)
{
*gpbcon &=~(0x3<<10);
*gpbcon |=(0x1<<10);
*gpbdat |=(0x1<<5);
}
static void led_on(void)
{
*gpbdat &=~(1<<5);
}
static void led_off(void)
{
*gpbdat |=(0x1<<5);
}
static int open(struct inode*node,struct file*file)
{
led1_init();
printk("led open ...\n");
return 0;
}
static ssize_t write(struct file*file,const char __user*buf,size_t len,loff_t*offset)
{
char data[48]={0};
size_t len_cp=(sizeof(data)<len)?(sizeof(data)):len;
copy_from_user(data,buf,len_cp);
if(strcmp("ledon",data)==0)
led_on();
else if(strcmp("ledoff",data)==0)
led_off();
else
return -EINVAL;
printk("led write ...\n");
return len_cp;
}
static int close(struct inode*node,struct file *file)
{
led_off();
printk("led close...\n");
return 0;
}
static struct file_operations fops=
{
.owner =THIS_MODULE,
.open=open,
.write=write,
.release=close
};
static struct miscdevice misc=
{
.minor=MISC_DYNAMIC_MINOR,
.name=DEV_NAME,
.fops=&fops
};
static int __init led_init(void)
{
int ret=0;
ret=misc_register(&misc);
if(ret<0)
goto err_register;
gpbcon=ioremap(GPBCON,sizeof(*gpbcon));
gpbdat=ioremap(GPBDAT,sizeof(*gpbdat));
printk("led_init #######################\n");
// register
return 0;
err_register:
misc_deregister(&misc);
printk("led_init cdev_add failed ...\n");
return ret;
}
static void __exit led_exit(void)
{
iounmap(gpbcon);
iounmap(gpbdat);
misc_deregister(&misc);
printk("led_exit #######################\n");
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
步骤和上述(第2)一样,但该驱动程序不需要 mknod dev/led c 250 0 创建设备结点
只需要insmod demo.ko 动态加载内核模块 运行应用层的程序即可