轻轻松松编写自己linux驱动

环境:

编译平台:虚拟机上安装linux操作系统(fedroa 10)

应用平台:TQ2440移植linux(内核2.6.30.4)

编译器:交叉编译器arm-linu-gcc(4.3.3)

首先说明下编写驱动所涉及的的目录和文件:

1、TQ2440移植的系统内核在我PC上存放的路径:/opt/EmbedSky/linux2.6.30.4

2、编译器存放在:/opt/EmbedSky/4.3.3/bin

3、我们编写文件的工作目录在PC机的:/home/test

4、TQ2440启动NFS文件系统是挂载虚拟机linux目录:/opt /EmbedSky/root_nfs

5、我们将编译好对驱动模块*.ko,这放在:/opt/EmbedSky/root_nfs/lib 即放在TQ2440文件系统目录下/lib下

6、我们将编译好对驱动测试软件程序执行文件,放在:/opt/EmbedSky/root_nfs/usr/sbin/

驱动程序是用于连接硬件与系统内核:驱动编译方式有两种,一是编译入内核,二是编译成模块(可以像我们windows一样运行做系统下载驱动程序)。

这我们用编译模块驱动来介绍,因为这种方式比编译内核好;编译入内核需要重新编译内核文件,重新移植内核入TQ2440(开发板),对flash烧写次数多伤害大。

首先知道我们要编写4个文件:2个C文件,2个Makefile文件;分别是驱动源程序C文件(testdev.c),驱动源程序编译相关的Makefile文件,测试驱动程序的"软件程序"(testsoft.c),编译测试软件程序的Makefile。

开始编写:

第一步:在/home/test/testdev目录下建立testdev.c

#cat testdev.c      //该命令是查看testdev.c的内容,且当前目录下运行。

[root@EmbedSky testdev]# cat testdev.c
/*************************************

NAME:testdev.c
COPYRIGHT:http://www.cnblogs.com/CJye/

*************************************/
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>

#define DEVICE_NAME "testdev"
#define LED_MAJOR 241
/* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */
#define IOCTL_LED_ON    1
#define IOCTL_LED_OFF    0

/* 用来指定LED所用的GPIO引脚 */
static unsigned long led_table [] =
{
    S3C2410_GPB5,
    S3C2410_GPB6,
    S3C2410_GPB7,
    S3C2410_GPB8,
};

/* 用来指定GPIO引脚的功能:输出 */
static unsigned int led_cfg_table [] =
{
    S3C2410_GPB5_OUTP,
    S3C2410_GPB6_OUTP,
    S3C2410_GPB7_OUTP,
    S3C2410_GPB8_OUTP,
};

static int testdev_open(struct inode *inode,struct file *file)
{
        return 0;
}

static int testdev_ioctl(struct inode *inode,     struct file *file,     unsigned int cmd,     unsigned long arg)
{
    if (arg > 4)
    {
        return -EINVAL;
    }
    switch(cmd)
    {
        case IOCTL_LED_ON:
            // 设置指定引脚的输出电平为0
            s3c2410_gpio_setpin(led_table[arg], 0);
            return 0;
        case IOCTL_LED_OFF:
            // 设置指定引脚的输出电平为1
            s3c2410_gpio_setpin(led_table[arg], 1);
            return 0;
        default:
            return -EINVAL;
    }
}

static struct file_operations testdev_fops = {        //给内核结构体赋值
        .owner    =     THIS_MODULE,
        .open     =       testdev_open,
        .ioctl      =       testdev_ioctl,
};

static char __initdata banner[] = "TQ2440/DIY LEDS,http://www.cnblogs.com/CJye/\n";

static int __init testdev_init(void)
{
    int ret;
    int i;
        for (i = 0; i < 4; i++)
    {
        s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]); //将GPB5~8脚作为输出
        s3c2410_gpio_setpin(led_table[i], 0);    //GPB5~8输出低电平4个灯全部点亮
    }
    printk(banner);
            ret=register_chrdev(LED_MAJOR,DEVICE_NAME,&testdev_fops);  //向内核写入驱动模块
    if(ret<0)
    {
        printk(DEVICE_NAME"can not register major number\n");
            return ret;
        }     
}

static void __exit testdev_exit(void)
{
    unregister_chrdev(LED_MAJOR,DEVICE_NAME);
    printk(DEVICE_NAME"give up/cancel\n");
}

module_init(testdev_init);              //insmod *.ko函数调用入口
module_exit(testdev_exit);        //rmmod *.ko 函数调用入口

MODULE_LICENSE("GPL");
MODULE_AUTHOR("http://www.cnblogs.com/CJye/");
MODULE_DESCRIPTION("TQ2440/DIY LEDS Driver");

第二步:在/home/test/testdev目录下建立Makefile

#cat Makefile    //该命令是查看testdev.c的内容,且当前目录下运行。

[root@EmbedSky testdev]# cat Makefile

PWD    = $(shell pwd)

KERNELDIR =/opt/EmbedSky/linux-2.6.30.4

obj-m    =testdev.o

modules:
    make -C $(KERNELDIR) M=$(PWD) modules
    cp -f testdev.ko /opt/EmbedSky/root_nfs/lib/.
    
clean:
    make -C $(KERNELDIR) M=$(PWD) clean
    rm -f $(PWD)/*~

#ls                  //该命令是查看编译生成的文件,在当前目录运行。

[root@EmbedSky testdev]# ls
Makefile       Module.symvers  testdev.ko     testdev.mod.o
modules.order  testdev.c       testdev.mod.c  testdev.o

第三步:在/home/test/testsoft目录下建立testsoft.c

#cat testdev.c      //该命令是查看testdev.c的内容,且当前目录下运行。

[root@EmbedSky testsoft]# cat testsoft.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>          //open() close()
#include <unistd.h>         //read() write()

#define DEVICE_NAME "/dev/testdev"  //this is dev name
#define LED_ON 1
#define LED_OFF 0

int main(void)
{
    int fd;
    int ret;
    char *i;
    printf("\nstart gpio_led_driver test\n\n");
    fd = open(DEVICE_NAME,O_RDWR);
    printf("fd = %d\n",fd);
    if(fd==-1)
       {
             printf("open device %s error\n",DEVICE_NAME);
       }
    else
       {
          while(1)        //对GPB5~8的LED控制操作
        {
        ioctl(fd,LED_ON,0);      
        ioctl(fd,LED_ON,2);        //点亮GPB5,GPB7
        ioctl(fd,LED_OFF,1);        //关闭GPB6,GPB8
        ioctl(fd,LED_OFF,3);       
        sleep(1);
        ioctl(fd,LED_ON,1);
        ioctl(fd,LED_ON,3);        //点亮GPB6,GPB8
        ioctl(fd,LED_OFF,0);        //点亮GPB5,GPB7
        ioctl(fd,LED_OFF,2);
        sleep(1);
        }  
           ret = close(fd);
           printf("ret=%d\n",ret);
           printf("close gpio_led_drier test\n");
       }
    return 0;
}

第四步:在/home/test/testsoft目录下建立Makefile

#cat Makefile    //该命令是查看Makefile的内容,且当前目录下运行。

[root@EmbedSky testsoft]# cat Makefile

CC  =/opt/EmbedSky/4.3.3/bin/arm-linux-gcc

OBJ  =testsoft.o

EXEC  =testsoft

$(EXEC):$(OBJ)

  $(CC) -o $@ $^

copy :

  cp -f  $(EXEC) /opt/EmbedSky/root_nfs/usr/sbin/.

clean:

  rm -f $(EXEC) *.o *~

  rm -f /opt/EmbedSky/root_nfs/usr/sbin/$(EXEC)

#ls                  //该命令是查看编译生成的文件,在当前目录运行。

 [root@EmbedSky testsoft]# ls
Makefile        testsoft.c      testsoft.o    testsoft

到此,驱动文件编译工作完成了。

下面通过windons上secureCRT终端进入TQ2440操作。

TQ2440如果有相关的GPIO控制LED的驱动,可以先停用,然后再加载我们自己的驱动。

#/etc/rc.d/init.d/ledctl stop      ///etc/rc.d/init.d目录下是一些TQ2440启动文件系统自启动的应用程序,根据这命令格式可以停用相应的应用程序。

  ledctl这是我启动文件系统加载的一个LED跑马灯,要加载下面我们的应用程序,所以我就先停用这个ledctl。

#cd /lib

#insmod testdev.ko        //加载这个模块到内核

#lsmod                         //查看是否有这个模块

#cd /dev                       //进入系统驱动文件目录

#mknod testdev c 241 0          //给testdev.ko建议一个设备节点,字符型驱动文件,主设备号241与驱动文件里一致,次设备号0

#cd /usr/sbin               //进入测试驱动软件程序执行文件存放目录

#ls                              //查看是否有testsoft执行文件。

# ./testsoft                  //此时你实现对LED灯控制效果就出现在你眼前了。

是不是很轻松,很有成就,那就去开只香槟庆祝下。

 



posted @ 2013-05-08 08:55  梦幻乐园  阅读(442)  评论(0编辑  收藏  举报