linux设备驱动之LED驱动测试

上篇对LED驱动代码进行分析,现在来写个LED测试测序测试该驱动。

1.驱动文件
  [weiming@Huangweiming driver]$mkdir s3c_led
[weiming@Huangweiming driver]$ cd s3c_led/
[weiming@Huangweiming s3c_led]$ vim s3c_led.c
/*********************************************************************************
 *      Copyright:  (C) 2012 Guo Wenxue<guowenxue@gmail.com>  
 *                  All rights reserved.
 *
 *       Filename:  s3c_led.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(07/26/2012~)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "07/26/2012 10:03:40 PM"
 *                 
 ********************************************************************************/

#include <linux/module.h>   /* Every Linux kernel module must include this head */
#include <linux/init.h>     /* Every Linux kernel module must include this head */
#include <linux/kernel.h>   /* printk() */
#include <linux/fs.h>       /* struct fops */
#include <linux/errno.h>    /* error codes */
#include <linux/cdev.h>     /* cdev_alloc()  */
#include <asm/io.h>         /* ioremap()  */
#include <linux/ioport.h>   /* request_mem_region() */

#include <asm/ioctl.h>      /* Linux kernel space head file for macro _IO() to generate ioctl command  */
#ifndef __KERNEL__
#include <sys/ioctl.h>      /* User space head file for macro _IO() to generate ioctl command */
#endif
//#include <linux/printk.h> /* Define log level KERN_DEBUG, no need include here */


#define DRV_AUTHOR                "Guo Wenxue <guowenxue@gmail.com>"
#define DRV_DESC                  "S3C24XX LED driver"

#define DEV_NAME                  "led"			//定义设备名称
#define LED_NUM                   4				//定义设备数量

/* Set the LED dev major number */
//#define LED_MAJOR                 79
#ifndef LED_MAJOR
#define LED_MAJOR                 0	/*如果没有定义主设备好,则默认为0,一般这个定义的设备号是固定不可用的,但是这为自动分配主设备号的逻辑提供了方便。*/
#endif

#define DRV_MAJOR_VER             1
#define DRV_MINOR_VER             0
#define DRV_REVER_VER             0

#define DISABLE                   0	
#define ENABLE                    1

#define GPIO_INPUT                0x00	//宏定义00为输入模式
#define GPIO_OUTPUT               0x01	//宏定义01为输出模式


#define PLATDRV_MAGIC             0x60	//定义了一个魔数
#define LED_OFF                   _IO (PLATDRV_MAGIC, 0x18)
#define LED_ON                    _IO (PLATDRV_MAGIC, 0x19) 				/*魔数有着特殊的功能。我们定义了led_on和led_off,但是这个宏定义可能和系统的别的重复,因此我们采用魔术机制,定义一个系统未用的魔数,然后让魔术生成我们定义的led_on和led_off,这样就不会和系统相同了*/

#define S3C_GPB_BASE              0x56000010	//GPB引脚基地址
#define GPBCON_OFFSET             0				//GPBCON偏移地址
#define GPBDAT_OFFSET             4				//GPBDAT偏移地址
#define GPBUP_OFFSET              8
#define S3C_GPB_LEN               0x10        /* 0x56000010~0x56000020  */

int led[LED_NUM] = {5,6,8,10};  /* Four LEDs use GPB5,GPB6,GPB8,GPB10 */

static void __iomem *s3c_gpb_membase;	
/* 定义s3c_gpb_membase为void __iomem*类型指针 ,在后面用来保存映射后的虚拟空间基地址。*/


#define s3c_gpio_write(val, reg) __raw_writel((val), (reg)+s3c_gpb_membase)		//设置当前寄存器的值	 
#define s3c_gpio_read(reg)       __raw_readl((reg)+s3c_gpb_membase)				//读取当前虚拟地址寄存器的值  


int dev_count = ARRAY_SIZE(led);	//设备个数赋值给dev_count 
int dev_major = LED_MAJOR;			//主设备号赋值给dev_major
int dev_minor = 0;					//次设备号赋值0  
int debug = DISABLE;				//出错定义赋值 

static struct cdev      *led_cdev;	
/*定义一个cdev类型的结构体指针,cdev是内核中表示字符设备的一个结构体    静态变量存放在数据段*/

static int s3c_hw_init(void)	//static只能在本C文件使用
{
    int          i;
    volatile unsigned long  gpb_con, gpb_dat, gpb_up;  //volatile防止编译器优化代码

    if(!request_mem_region(S3C_GPB_BASE, S3C_GPB_LEN, "s3c2440 led"))  /*向内核申请内存资源,其起始地址为S3C_GPB_BASE,大小为s3c_GPB_LEN;如果申请失败,返回-EBUSY*/
    {
        return -EBUSY;
    }

    if( !(s3c_gpb_membase=ioremap(S3C_GPB_BASE, S3C_GPB_LEN)) )	 /*申请成功后,将I/O内存资源的物理地址映射成核心虚地址。以后操作寄存器一律操作虚拟地址即s3c_gpb_membase的地址*/		
    {
        release_mem_region(S3C_GPB_BASE, S3C_GPB_LEN);
        return -ENOMEM;
    }

    for(i=0; i<dev_count; i++)
    {
        /* Set GPBCON register, set correspond GPIO port as input or output mode  */
        gpb_con = s3c_gpio_read(GPBCON_OFFSET);		//读取GPBCON寄存器的值 
        gpb_con &= ~(0x3<<(2*led[i]));   /* Clear the currespond LED GPIO configure register先将相应的led的gpio引脚复位清零*/   
        gpb_con |= GPIO_OUTPUT<<(2*led[i]); /* Set the currespond LED GPIO as output mode 然后将相应的led的gpio引脚设置为输出模式*/ 
        s3c_gpio_write(gpb_con, GPBCON_OFFSET);		/*再将设置好的GPBCON引脚信息写入寄存器中 */

        /* Set GPBUP register, set correspond GPIO port pull up resister as enable or disable  */
        gpb_up = s3c_gpio_read(GPBUP_OFFSET);		//读取GPBUP的虚拟地址并保存在gpb_up中  
        //gpb_up &= ~(0x1<<led[i]); /* Enable pull up resister */
        gpb_up |= (0x1<<led[i]);  /* Disable pull up resister */
        s3c_gpio_write(gpb_up, GPBUP_OFFSET);		//将设置好的GPBUP引脚信息写入寄存器中 

        /* Set GPBDAT register, set correspond GPIO port power level as high level or low level */
        gpb_dat = s3c_gpio_read(GPBDAT_OFFSET);		//读取GPBDAT的虚拟地址并保存在gpb_dat中  
        //gpb_dat &= ~(0x1<<led[i]); /* This port set to low level, then turn LED on */
        gpb_dat |= (0x1<<led[i]);  /* This port set to high level, then turn LED off */
        s3c_gpio_write(gpb_dat, GPBDAT_OFFSET);		//将设置好的GPBDAT引脚信息写入寄存器中  
    }

    return 0;
}


static void turn_led(int which, unsigned int cmd)
{
    volatile unsigned long  gpb_dat;

    gpb_dat = s3c_gpio_read(GPBDAT_OFFSET);			//设置gpb_dat变量来存储GPBDAT的在内存中的虚拟地址 

    if(LED_ON == cmd)
    {
        gpb_dat &= ~(0x1<<led[which]); /*  Turn LED On */
    }
    else if(LED_OFF == cmd)
    {
        gpb_dat |= (0x1<<led[which]);  /*  Turn LED off */
    }

    s3c_gpio_write(gpb_dat, GPBDAT_OFFSET);			//设置好的GPBDAT引脚信息写入内存  
}

static void s3c_hw_term(void)
{
    int                     i;
    volatile unsigned long  gpb_dat;

    for(i=0; i<dev_count; i++)
    {
        gpb_dat = s3c_gpio_read(GPBDAT_OFFSET);
        gpb_dat |= (0x1<<led[i]);  /* Turn LED off */
        s3c_gpio_write(gpb_dat, GPBDAT_OFFSET);
    }

    release_mem_region(S3C_GPB_BASE, S3C_GPB_LEN);	//释放已申请的内存资源
    iounmap(s3c_gpb_membase);						//取消物理地址到虚拟地址的映射关系  
}


static int led_open(struct inode *inode, struct file *file)	
{
    int minor = iminor(inode);							//获取次设备号  

    file->private_data = (void *)minor;	/* 在file结构体中,private_data是一个空类型指针,此处用强制类型转换 */

    printk(KERN_DEBUG "/dev/led%d opened.\n", minor);	//打印成功信息
    return 0;
}

static int led_release(struct inode *inode, struct file *file)
{
    printk(KERN_DEBUG "/dev/led%d closed.\n", iminor(inode));

    return 0;
}

static void print_help(void)
{
    printk("Follow is the ioctl() commands for %s driver:\n", DEV_NAME);
    //printk("Enable Driver debug command: %u\n", SET_DRV_DEBUG);
    printk("Turn LED on command  : %u\n", LED_ON);
    printk("Turn LED off command : %u\n", LED_OFF);

    return;
}

static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)	//参数为用户程序空间传过来的参数 
{
    int which = (int)file->private_data;			//获取次设备号

    switch (cmd)
    {
        case LED_ON:

            turn_led(which, LED_ON);
            break;

        case LED_OFF:
            turn_led(which, LED_OFF);
            break;
    
        default:
            printk(KERN_ERR "%s driver don't support ioctl command=%d\n", DEV_NAME, cmd);
            print_help();
            break;
    }

    return 0;
}


static struct file_operations led_fops = 		//定义fop结构体,针对该驱动提供的系统调用和操作 
{
    .owner = THIS_MODULE,
    .open = led_open,							//open为指向led_open()的函数指针
    .release = led_release,
    .unlocked_ioctl = led_ioctl,
};

static int __init s3c_led_init(void)
{
    int                    result;
    dev_t                  devno;
	
	 /*如果硬件初始化失败,打印如下信息并返回-ENODEV*/ 
    if( 0 != s3c_hw_init() )
    {
        printk(KERN_ERR "s3c2440 LED hardware initialize failure.\n");
        return -ENODEV;
    }

    /*  Alloc the device for driver */
    if (0 != dev_major) /*  Static */ /* 如果已经有了设备号 静态获取主次设备号 */ 
    {
        devno = MKDEV(dev_major, 0);				//主次设备号和设备名绑定
        result = register_chrdev_region (devno, dev_count, DEV_NAME);
    }
    else							//动态获取主次设备号 
    {
        result = alloc_chrdev_region(&devno, dev_minor, dev_count, DEV_NAME);
        dev_major = MAJOR(devno);
    }

    /*  Alloc for device major failure */	 /*  如果设备号申请失败,打印如下信息并返回-ENODEV */ 
    if (result < 0)									//返回值小于0表示分配失败
    {
        printk(KERN_ERR "S3C %s driver can't use major %d\n", DEV_NAME, dev_major);
        return -ENODEV;
    } 
    printk(KERN_DEBUG "S3C %s driver use major %d\n", DEV_NAME, dev_major);

    if(NULL == (led_cdev=cdev_alloc()) )	//分配cdev结构体,如果分配失败,打印如下信息 
    {
        printk(KERN_ERR "S3C %s driver can't alloc for the cdev.\n", DEV_NAME);
        unregister_chrdev_region(devno, dev_count);
        return -ENOMEM;
    }
    
    led_cdev->owner = THIS_MODULE;			//指明设备所属模块  
    cdev_init(led_cdev, &led_fops);			//用于初始化cdev结构体,并建立cdev与file_operations 之间的连接

    result = cdev_add(led_cdev, devno, dev_count);  //提交给内核,向内核注册一个cdev结构
    if (0 != result)
    {   
        printk(KERN_INFO "S3C %s driver can't reigster cdev: result=%d\n", DEV_NAME, result); 
        goto ERROR;
    }

            
    printk(KERN_ERR "S3C %s driver[major=%d] version %d.%d.%d installed successfully!\n", 
            DEV_NAME, dev_major, DRV_MAJOR_VER, DRV_MINOR_VER,DRV_REVER_VER);
    return 0;


ERROR:
    printk(KERN_ERR "S3C %s driver installed failure.\n", DEV_NAME);
    cdev_del(led_cdev);								//注销设备,通常发生在驱动模块的卸载函数中  
    unregister_chrdev_region(devno, dev_count);		//释放注册的设备号  
    return result;
}

/*s3c_led_exit()执行功能与s3c_led_init()函数的功能完全相反。该函数主要的功能为卸载驱动,释放内存,释放设备节点和设备号*/
static void __exit s3c_led_exit(void)
{
    dev_t devno = MKDEV(dev_major, dev_minor);

    s3c_hw_term();

    cdev_del(led_cdev);							//移除字符设备
    unregister_chrdev_region(devno, dev_count);  //释放设备编号

    printk(KERN_ERR "S3C %s driver version %d.%d.%d removed!\n", 
            DEV_NAME, DRV_MAJOR_VER, DRV_MINOR_VER,DRV_REVER_VER);

    return ;
}



/* These two functions defined in <linux/init.h> */
module_init(s3c_led_init);
module_exit(s3c_led_exit);

module_param(debug, int, S_IRUGO);
module_param(dev_major, int, S_IRUGO);

MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION(DRV_DESC);
MODULE_LICENSE("GPL");
2.Makefile文件
[weiming@Huangweiming s3c_led ]$ vim Makefile
 Makefile                                                                                             
  1 LINUX_SRC?=../../kernel/linux-3.0
  2 CROSS_COMPILE=/opt/buildroot-2012.08/ARM920t/usr/bin/arm-linux-
  3
  4 obj-m := s3c_led.o
  5
  6 modules: 
  7     @make -C $(LINUX_SRC) M=`pwd` modules 
  8     @make clean
  9
 10 clean:
 11     rm -f  *.ko.* *.o *mod.c *.order *.symvers 
make之后会生成我们所需要的.ko的驱动模块
3.测试文件
[weiming@Huangweiming s3c_led]$ vim test_s3c_led.c
/*********************************************************************************
 *      Copyright:  (C) 2017 Huang Weiming<710564672@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  test_s3c_led.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(2017年04月17日)
 *         Author:  Huang Weiming <710564672@qq.com>
 *      ChangeLog:  1, Release initial version on "2017年04月17日 19时40分14秒"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>

#define LED_CNT     4
#define DEVNAME_LEN 10

#define PLATDRV_MAGIC             0x60
#define LED_OFF                   _IO (PLATDRV_MAGIC, 0x18)
#define LED_ON                    _IO (PLATDRV_MAGIC, 0x19)
int main (int argc, char **argv)
{
    int     i;
    int     fd[LED_CNT];
    char    dev_name[DEVNAME_LEN] = {0,0,0,0};

    for(i=0; i<LED_CNT; i++)
    {
        snprintf(dev_name, sizeof(dev_name), "/dev/led%d", i);
        fd[i] = open(dev_name, O_RDWR, 0755);
        if(fd[i] < 0)
            goto err;
    }


    while(1)
    {
        for(i=0; i<LED_CNT; i++)
        {
            ioctl(fd[i], LED_ON);
        }
        sleep(1);

        for(i=0; i<LED_CNT; i++)
        {
            ioctl(fd[i], LED_OFF);
        }
        sleep(1);
    
    }

    for(i=0; i<LED_CNT; i++)
    {
        close(fd[i]);
    }
    return 0;

err:
    for(i=0; i<LED_CNT; i++)
    {
        if(fd[i] > 0)
        {
            close(fd[i]);
        }
    }
    return -1;
}
 编写完后对测试程序进行编译

4.开发板烧录
通过tftp命令把文件上传到开发板

接着进行加载模块测试

执行完./a.out就能看到led0~3每隔1秒进行亮灭闪烁。符合测试测序所实现的功能,测试驱动成功。

所遇问题

此情况是没有安装网卡驱动,可进行如下解决:
1.修改内核源码
[weiming@Huangweiming linux-3.0]$ vim arch/arm/mach-s3c2440/mach-smdk2440.c
#include <linux/dm9000.h>  //添加DM9000网卡的头文件 
并添加如下代码 
/* add DM9000 ethernet drivers ,whitch is bodify by liuchengdeng */ 
#define DM9000_BASE    (S3C2410_CS4 + 0x300) 
static struct resource s3c_dm9000_resource[] = { 
     [0] = { 
        .start = DM9000_BASE, 
        .end   = DM9000_BASE + 3, 
        .flags = IORESOURCE_MEM 
    }, 
    [1] = { 
        .start = DM9000_BASE + 4, 
        .end   = DM9000_BASE + 7, 
        .flags = IORESOURCE_MEM 
    }, 
    [2] = { 
        .start = IRQ_EINT7, 
        .end   = IRQ_EINT7, 
        .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE, 
    } 
}; 
/*         
 * The DM9000 has no eeprom, and it's MAC address is set by 
 * the bootloader before starting the kernel. 
 */ 
static struct dm9000_plat_data s3c_dm9000_pdata = { 
    .flags      = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM), 
}; 
static struct platform_device s3c_device_dm9000 = { 
    .name       = "dm9000", 
    .id     = -1, 
    .num_resources  = ARRAY_SIZE(s3c_dm9000_resource), 
    .resource   = s3c_dm9000_resource, 
    .dev        = { 
        .platform_data  = &s3c_dm9000_pdata, 
    }, 
};
2.添加启动DM9000
[weiming@Huangweiming linux-3.0]$ vim arch/arm/mach-s3c2440/mach-smdk2440.c

3.添加<linux/io.h>头文件
[weiming@Huangweiming linux-3.0]$ vim include/linux/dm9000.h

4.make生成新的内核文件,该文件就支持dm9000网卡了。








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值