怎样将驱动静态的编译到内核中。
使用的开发板:tq210的s5pv210开发板
内核版本:linux-3.8.3
编译器:arm-linux-gcc 4.4.3
相关下载地址:
一、准备好可以正常编译好引导开发板的源码(
怎样实现正确配置和编译好内核源码的链接地址;http://blog.csdn.net/girlkoo/article/details/8719828)
二、在内核源码kernel文件夹的driver目录下,创建led_arm文件夹
mkdir drivers/led_arm
三、将“LED驱动实验”的驱动程序拷贝到led_arm目录下(详情见上一次LED驱动博客)
将led.c拷贝在该文件夹下面
以下是led.c驱动的编写
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <asm/gpio.h>
#include <plat/gpio-cfg.h>
#define LED_ON 0x100001
#define LED_OFF 0x100002
//硬件资源
struct led_resource{
int gpio; //gpio口
char *name; //设备名称。在/proc/devices/显示
};
static struct led_resource led_info[] = {
[0] ={
.gpio = S5PV210_GPC0(3),
.name = "LED1"
},
[1] ={
.gpio = S5PV210_GPC0(4),
.name = "LED2"
}
};
//软件管理
static dev_t dev; //设备号
struct cdev led_cdev;//创建设备类
static struct class *cls; //创建设备类指针
static int led_open(struct inode *inode, struct file *file)
{
printk("%s\n",__func__);
return 0;
}
static int led_close(struct inode *inode, struct file *file)
{
printk("%s\n",__func__);
return 0;
}
//static int led_ioctl(struct inode *inode, struct file *file,
// unsigned int cmd, unsigned long arg)
static int led_ioctl( struct file *file,
unsigned int cmd, unsigned long arg)
{
int kindex;
//拷贝数据到内核空间
copy_from_user(&kindex,
(int *)arg, sizeof(kindex));
switch(cmd){
case LED_ON:
gpio_set_value(led_info[kindex-1].gpio,1);
break;
case LED_OFF:
gpio_set_value(led_info[kindex-1].gpio,0);
break;
default:
return -1;
}
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.release = led_close,
.unlocked_ioctl = led_ioctl
};
static int led_init(void)
{
int i;
//1.分配字符设备
alloc_chrdev_region(&dev, 0, 1, "leds");
//2.初始化字符设备
cdev_init(&led_cdev, &led_fops);
//3.注册字符设备
cdev_add(&led_cdev, dev, 1);
//4.添加设备文件
//4.1主设备类
// 结果是: sys/class/myleds
cls = class_create(THIS_MODULE,"myleds");
//4.2建立设备文件
device_create(cls, NULL, dev, NULL, "myleds");
//5.申请gpio口资源
for(i=0; i < ARRAY_SIZE(led_info);i++)
{
gpio_request(led_info[i].gpio, led_info[i].name);
gpio_direction_output(led_info[i].gpio, 0 );
}
return 0;
}
static void led_exit(void)
{
int i;
//删除设备文件和设备类
device_destroy(cls, dev);
class_destroy(cls);
//1.卸载字符设备对象
cdev_del(&led_cdev);
//2.释放gpio资源
for(i=0; i< ARRAY_SIZE(led_info);i++)
{
gpio_set_value(led_info[i].gpio, 0);
gpio_free(led_info[i].gpio);
}
//3.释放设备号
unregister_chrdev_region(dev, 1);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
由于Kconfig就是对应着内核的配置菜单。假如要想添加新的驱动到内核的源码中,可以通过修改Kconfig来增加对我们驱动的配置菜单,这样就有途径选择我们的驱动。
每个config菜单项都要有类型定义,bool:布尔类型,tristate三态:内建、模块、移除,string:字符串,hex:十六进制,integer:整型
例如config HELLO_MODULE
bool "hello test module"
bool类型的只能选中或不选中,tristate类型的菜单项多了编译成内核模块的选项,假如选择编译成内核模块,则会在.config中生成一个 CONFIG_HELLO_MODULE=m的配置,假如选择内建,就是直接编译成内核影响,就会在.config中生成一个 CONFIG_HELLO_MODULE=y的配置.
下面在led_arm下面进行以下的操作:
①添加一个Kconfig文件
[cpp] view plain copy
1. config MYLED
2. tristate "MYLED"
3. default n
4. help
5. this is a LED driver
②添加一个Makefile
[cpp] view plain copy
1. obj-$(CONFIG_LEDTEST)+=led.o
四、将我们编写的驱动支持选项增加进内核的配置菜单中。
回到drivers,并且修改drivers目录中的Kconfig文件
在最后一行添加语句
[cpp] view plain copy
1. source "drivers/led_arm/Kconfig"
五、修改drivers/Makefile文件
增加以下语句
obj-$(CONFIG_LEDTEST) +=led_arm/
六、重新配置内核
在源码根目录输入
#make Menuconfig
可以在菜单的最后,看到我们新加入的MYLED选项。
这里可以选择编译的内核的型,*代表为静态编译驱动到内核中,M为将驱动编译成模块驱动,这里我们将驱动静态编译成内核中。
Make zImage
将arch/arm/boot/kernel/zImage 下载到开发板进行测试。
以下是led_test.c程序的编写;
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define LED_ON 0x100001
#define LED_OFF 0x100002
int main(int argc, char *argv[])
{
int fd;
int cmd;
if(argc < 3){
printf("Usage:\n %s <on|off> <1|2>\n", argv[0]);
return -1;
}
fd= open("/dev/myleds", O_RDWR);
if(fd < 0){
printf("open led device failed. \n");
return -1;
}
cmd= strtoul(argv[2], NULL, 10);
if(!strcmp(argv[1]),"on")
ioctl(fd,LED_ON,&cmd);
else if(!strcmp(argv[1],"off"))
ioctl(fd,LED_OFF,&cmd);
close(fd);
return 0;
}
问题总结:
编译过程中如果使用的内核为linux-2.6.36之后的内核版本,并且在其中使用ioctl,就会出现以下的错误。
error: unknown field 'ioctl' specified in initializer
问题是由于2.6.36内核之后 去掉了原来的ioctl,添加两个新的成员,所以会出错
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
所以修改需要在驱动的源文件中file_operations内.ioctl改为 .compat_ioctl即可
OK,编译通过,警告咱就忽略了
kernel 2.6.35 及之前的版本中struct file_operations一共有3个ioctl:
ioctl,unlocked_ioctl和compat_ioctl
现在只有unlocked_ioctl和compat_ioctl了
在kernel 2.6.36 中已经完全删除了struct file_operations中的ioctl函数指针,取而代之的是unlocked_ioctl。
但是这个指针函数变了之后最大的影响是参数中少了inode ,
在kernel 2.6.36 中已经完全删除了struct file_operations中的ioctl函数指针,取而代之的是unlocked_ioctl。
这个指针函数变了之后最大的影响是参数中少了inode , 不过这个不是问题,因为用户程序中的ioctl对应的系统调用接口没有变化,所以用户程序不需要改变,一切都交给内核处理了,如果想在unlocked_ioctl中获得inode等信息可以用如下方法:
struct inode *inode = file->f_mapping->host;
struct block_device *bdev = inode->i_bdev;
struct gendisk *disk = bdev->bd_disk;
fmode_t mode = file->f_mode;
struct backing_dev_info *bdi;
参数变化参考 file_operations这个结构体,最重要的是这个cmd参数,这个变化很大。
例如我们驱动中编写的参数设置:
Linux-2.6.36之前使用的ioctl参数
//static int led_ioctl(struct inode *inode, struct file *file,
// unsigned int cmd, unsigned long arg)
Linux-2.6.36之后使用的参数
static int led_ioctl( struct file *file,
unsigned int cmd, unsigned long arg)