怎样将驱动静态的编译到内核中。

怎样将驱动静态的编译到内核中。

 

使用的开发板:tq210s5pv210开发板

内核版本: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一共有3ioctl

ioctl,unlocked_ioctlcompat_ioctl

现在只有unlocked_ioctlcompat_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)

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值