led的驱动及测试程序

一、驱动源码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
//#include <asm/irq.h>
//#include <mach/regs-gpio.h>
//#include <mach/hardware.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/miscdevice.h>
#include <linux/types.h>
#include <linux/ioctl.h>

#include <linux/cdev.h>

#define led_pin 0
#define DEVICE_NAME "mydriver"
//#define DEVICE_NAME "dragino2:red:wlan"
static int MYDRIVER_Major = 12;

static int mydriver_open(struct inode *inode,struct file *filp)
{
    printk("My Driver Open Called!
");
    return 0;
}

static int mydriver_release(struct inode *inode,struct file *filp)
{
    printk("My Driver Release Called!
");
    return 0;
}

static int mydriver_read(struct file *filp,char *buf,size_t count, loff_t *f_pos)
{
    printk("My Driver Read Called!
");
    return 0;
}

static int mydriver_write(struct file *filp,char *buf,size_t count, loff_t *f_pos)
{
    printk("My Driver Writet Called!
");
    return 0;
}

static long op_mydriver_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    switch(cmd) {
        case 0:
            gpio_set_value(led_pin, 0);
            break;
        case 1:
            //if (arg > 2) {
            //    return -EINVAL;//判读用户的参数是否有误
            //}

            //gpio_set_value(led_pin, !cmd);//用户选定的LED并设置值
            gpio_set_value(led_pin, 1);
            //printk(DEVICE_NAME": %d %d
", arg, cmd);
            break;
        default:
            return -EINVAL;
    }
    return 0;
}



/*
static int mydriver_probe()
{
    printk("My Driver Probe Called!
");
}

static int mydriver_remove()
{
    printk("My Driver Remove Called!
");
}
*/

//??
/*
static struct device_driver  mydriver_driver = {
    //.driver = {
    //    .name        = DEVICE_NAME,
    //    .owner        = THIS_MODULE,
    //},
    .name        = DEVICE_NAME,
    .probe        = mydriver_probe,
    .remove        = mydriver_remove,
};
*/


static struct file_operations mydriver_fops =
{
    .owner = THIS_MODULE,
    .open = mydriver_open,
    .release = mydriver_release,
    .read = mydriver_read,
    .write =mydriver_write,
    .unlocked_ioctl = op_mydriver_unlocked_ioctl,
};

static struct miscdevice mydriver_miscdev =
{
    .minor = MISC_DYNAMIC_MINOR,//由系统自动配置,次设备号
    .name = DEVICE_NAME,
    .fops = &mydriver_fops,
};



static int __init mydriver_init(void)
{
   int ret;
     //ret =  ath79_register_leds_gpio(0,DEVICE_NAME,&mydriver_driver);
    //ret = driver_register(&mydriver_driver);
    ret = gpio_request(led_pin,"LED");//申请IO引脚gpio_request
    //ret = gpio_request_one(led_pin,10,"LED");
    ret = register_chrdev(MYDRIVER_Major, DEVICE_NAME, &mydriver_fops);
    if (ret) {
        printk("request GPIO for led error
");
        return ret;
    }
    gpio_direction_output(led_pin, 0);
    //gpio_set_value(led_pin, 1);
    
    
    ret = misc_register(&mydriver_miscdev);//创建设备节点
    if (ret) {
        printk("misc_unregister error
");
        return ret;
    }
    printk("MY DRIVER MODULE INIT
");
    return 0;//没有这句,那默认返回值是多少?
    
}

static void __exit mydriver_exit(void)
{
    int ret;
    
    //gpio_free(led_pin);
    unregister_chrdev(MYDRIVER_Major, DEVICE_NAME);
    ret = misc_deregister(&mydriver_miscdev);
    if (ret) 
    {
        printk("misc_register error
");
        return ret;
     }

    printk("My DRIVER MODULE EXIT
");
}

module_init(mydriver_init);
module_exit(mydriver_exit);

MODULE_AUTHOR("www.txmcu.com");
MODULE_DESCRIPTION("My Driver");
MODULE_LICENSE("GPL");

说明:

1、在驱动初始化及驱动退出函数中,

static int __init mydriver_init(void){...}

static void __exit mydriver_exit(void){...}

双下划线表示模块在内核启动和关闭时自动运行和退出,注意不是单下滑线。

2、该设备注册用杂项(misc)设备方式进行注册,如果一个字符设备驱动要驱动多个设备,那么它就不应该用misc设备来实现。

3、led管脚的选择,参考《AR9331.pdf》的第17页,选择led的管脚为LED0即GPIO0,所以

#define led_pin 0

该管脚为高电平点亮led灯,低电平为熄灭led灯。

4、该驱动实现对led的控制是在以下函数中:

static long op_mydriver_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg){...}

以下两个函数实现对led管脚设置高低电平:

gpio_set_value(led_pin, 0);

gpio_set_value(led_pin, 1);

5、初始化要return 0,否则加载驱动时,会输出很多乱起八糟的东西。

二、驱动编译

1、Kconfig文件的书写

config DRIVER-MODEL
    tristate "Custom GPIO-based led driver"
    depends on GENERIC_GPIO
    select SPI_GPIO //this option would also be choosed
    help
      This is an led driver to register 1 
      GPIO lines. 
      The devices will be exposed to userspace as /dev/spidevX.X
      
      This module is maily intended to interface microcontrollers
      and other led devices without a specific kernel driver.

      This support is also available as a module.

2、Makefile文件

obj-${CONFIG_DRIVER_MODEL}    += driver-model.o
#obj-m += driver-model.o

说明:

其中${CONFIG_DRIVER_MODEL}看作一个整体,一个变量,其值是由上级目录的Makefile文件确定。

3、上级Makefile文件

#
# Copyright (C) 2008 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#

include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk

PKG_NAME:=driver-model
PKG_RELEASE:=1

include $(INCLUDE_DIR)/package.mk

define KernelPackage/driver-model
  SUBMENU:=LED modules
  TITLE:=gpio control led
#  DEPENDS:=@GPIO_SUPPORT +kmod-spi-bitbang +kmod-spi-gpio
  DEPENDS:=@GPIO_SUPPORT
  FILES:=$(PKG_BUILD_DIR)/driver-model.ko
#  KCONFIG:=CONFIG_DRIVER_MODEL=m 
#      CONFIG_SPI_BITBANG=y
  KCONFIG:=
  AUTOLOAD:=$(call AutoLoad,50,driver-model,1)
endef

define KernelPackage/driver-model/description
  Kernel module for control a led.
endef

EXTRA_KCONFIG:= 
    CONFIG_DRIVER_MODEL=m

EXTRA_CFLAGS:= 
    $(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=m,%,$(filter %=m,$(EXTRA_KCONFIG)))) 
    $(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=y,%,$(filter %=y,$(EXTRA_KCONFIG)))) 

MAKE_OPTS:= 
    ARCH="$(LINUX_KARCH)" 
    CROSS_COMPILE="$(TARGET_CROSS)" 
    SUBDIRS="$(PKG_BUILD_DIR)" 
    EXTRA_CFLAGS="$(EXTRA_CFLAGS)" 
    $(EXTRA_KCONFIG)

define Build/Prepare
    mkdir -p $(PKG_BUILD_DIR)
    $(CP) ./src/* $(PKG_BUILD_DIR)/
endef

define Build/Compile
    $(MAKE) -C "$(LINUX_DIR)" 
        $(MAKE_OPTS) 
        modules
endef

$(eval $(call KernelPackage,driver-model))

说明:

AUTOLOAD:=$(call AutoLoad,50,driver-model,1)当把驱动编译进内核,即选择Y时,该函数实现开机自动安装驱动。

4、make menuconfig

clip_image002

选中kmod-driver-model。

三、测试源码

#include <stdio.h>
#include <unistd.h>

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <linux/ioctl.h>

/*
int main(int argc, char **argv)
{
int fd;
char i;
//char buff[1024];
fd = open("/dev/mydriver", O_RDWR);
if (fd < 0) {
    perror("open");
    return 1;
}
printf("open success!
");
for(i=5;i>0;i--)
{
ioctl(fd,1,1);
sleep(1);
ioctl(fd,0,1);
sleep(1);
}
close(fd);
return 0;
}
*/


int main(int argc, char **argv)
{
        int on;
        int led_no;
        int fd;

        if(argc != 3 ||sscanf(argv[1],"%d",&led_no) !=1 ||
                       sscanf(argv[2],"%d",&on) !=1 ||
                on < 0 || on > 1 || led_no <0 || led_no>3)
        {
                fprintf(stderr,"Usage:led led_no 0|1
");
                exit(1);
        }

        //打开/dev/leds0设备文件
       fd = open("/dev/mydriver",0);
       if(fd <0 )
       {
                perror("/dev/mydriver");
                exit(1);
       }

       //通过系统调用ioctl和输入的参数控制led
 if(led_no ==0)//第一个参数为功能选择,0为手动控制功能;第二个参数为控制灭还是亮;
{     
    ioctl(fd,on,led_no);
}
else//第一个参数为功能选择,1为自动控制功能;
{
    while(1)
    {
    ioctl(fd,1,led_no);
    usleep(500000);
    ioctl(fd,0,led_no);
    usleep(500000);
    }
}
      
       //关闭设备句柄
       close(fd);
       return 0;
}

说明:

1、该测试程序需要外部输入两个参数;

2、usleep的最大值不能超过1000000。

四、应用程序编译

1、Makefile文件

all: led

led: led.c
    $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<

clean:
    rm -f led

2、上级目录的Makefile文件

#
# Copyright (C) 2010-2012 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#

include $(TOPDIR)/rules.mk

PKG_NAME:=led
PKG_VERSION:=1
PKG_RELEASE:=1

include $(INCLUDE_DIR)/package.mk

define Package/led
  SECTION:=utils
  CATEGORY:=Utilities
  TITLE:=led 
  MAINTAINER:=tingpan
  PKGARCH:=all
endef

define Package/led/description
    led world!
endef

define Build/Prepare
    mkdir -p $(PKG_BUILD_DIR)
    $(CP) ./src/* $(PKG_BUILD_DIR)/
endef

define Build/Configure
endef

define Build/Compile
    $(MAKE) -C $(PKG_BUILD_DIR) 
        CC="$(TARGET_CC)" 
        CFLAGS="$(TARGET_CFLAGS) -Wall" 
        LDFLAGS="$(TARGET_LDFLAGS)"
endef

define Package/led/install
    $(INSTALL_DIR) $(1)/usr/sbin
    $(INSTALL_BIN) $(PKG_BUILD_DIR)/led $(1)/usr/sbin/
endef

$(eval $(call BuildPackage,led))

五、烧写固件及测试

1、烧写固件

(1)连接串口、网线;

(2)打开SecureCRT ,两秒内按下键盘任意键,输入http

(3)打开浏览器,输入192.168.0.250

(4)开始刷机,自动重启。

2、查看驱动

在终端输入lsmod,存在driver-model的驱动,也可以进入dev文件查看存在mydriver设备。

3、测试代码进行测试

(1)led 0 1开启led灯

(2)led 0 0 关闭led灯

(3)led 1 0或led 1 1 实现led每秒闪一次

(4)如果要实现开机启动,可在网页中进行设置,

clip_image002[4]

a、查看进程

clip_image004

b、杀死进程(这样才能手动控制,否则一直在后台运行,没法结束)

clip_image006

4、测试

应用程序return 0 或中断执行会执行release函数。

补充:开机启动

可以再etc文件夹中添加,如果是系统服务加入init.d文件夹下,写入名字就好;

如果是自己编写的应用程序,或需要网络启动后再启动,可以再rc.local文件中添加。

可在makefile中添加:

$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_DIR) $(1)/etc/config
$(CP) ./files/scws $(1)/etc/config/
$(INSTALL_BIN) $(PKG_BUILD_DIR)/scws $(1)/usr/sbin/ 

相关源码下载地址:

http://pan.baidu.com/s/1sDikY

原文地址:https://www.cnblogs.com/smbx-ztbz/p/4418404.html