一、驱动源码
#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
选中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)如果要实现开机启动,可在网页中进行设置,
a、查看进程
b、杀死进程(这样才能手动控制,否则一直在后台运行,没法结束)
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/
相关源码下载地址: