ok6410 linux的第一个驱动LED驱动
//驱动程序代码
- /****************************************************************************************************************
- * 文件名称 : led_drive.c
- * 简介 : OK6410 LED驱动
- * 作者 : 异灵元(cp1300@139.com)
- * 创建时间 : 2012/08/27 17:28
- * 修改时间 : 2012/08/27
- * 说明 : OK6410 开发板(S3C6410)LED(GPIO)驱动
- ****************************************************************************************************************/
- //系统头文件
- #include <linux/miscdevice.h>
- #include <linux/delay.h>
- #include <asm/irq.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>
- #include <asm/atomic.h>
- #include <asm/unistd.h>
- //--------------------------//
- #include <mach/map.h>
- #include <mach/regs-clock.h>
- #include <mach/regs-gpio.h>
- //--------------------------//
- #include <plat/gpio-cfg.h>
- #include <mach/gpio-bank-e.h>
- #include <mach/gpio-bank-m.h>
- ///
- //驱动模块名称
- #define DEVICE_NAME "OK6410_LED"
- //函数声明
- ///
- static long OK6410_LED_ioctl(
- struct file *file,
- unsigned int cmd,
- unsigned long arg);
- static ssize_t OK6410_LED_write(
- struct file *file,
- const char __user *buff,
- size_t size,
- loff_t *loff);
- static ssize_t OK6410_LED_read(
- struct file *file,
- char __user *buff,
- size_t size,
- loff_t *loff);
- ///
- /* 这个结构是字符设备驱动的核心
- * 当应用程序操作设备文件所提供的open,read,write等函数,
- * 最终会调用到这个结构中的对应函数
- */
- static struct file_operations dev_fops = {
- .owner = THIS_MODULE, //这是一个宏,指向编译模块时自动创建的__this_module变量
- .unlocked_ioctl = OK6410_LED_ioctl,
- .read = OK6410_LED_read,
- .write = OK6410_LED_write
- };
- //注册驱动所使用的相关信息
- static struct miscdevice misc = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = DEVICE_NAME, //驱动模块名称
- .fops = &dev_fops,
- };
- //LED设备访问信号量
- struct semaphore led_sem;
- /****************************************************************************************************************
- *函数名 : static int __init OK6410_LED_init(void)
- *功能 : LED模块初始化函数
- *参数 : 无
- *返回 : 0:成功;<0:失败
- *依赖 : linux底层宏定义
- *作者 : 异灵元(cp1300@139.com)
- *创建时间 : 2012/08/27 17:28
- *最后修改时间: 2012/08/27 17:28
- *说明 : 初始化LED硬件,注册LED驱动
- ****************************************************************************************************************/
- static int __init OK6410_LED_init(void)
- {
- int ret;
- unsigned int reg;
- //GPIOM0-3 推挽输出
- reg = readl(S3C64XX_GPMCON); //获取GPIOM寄存器数据
- reg &= (~0xffff); //清除之前设置
- reg |= 0x1111; //推挽输出
- writel(reg,S3C64XX_GPMCON); //配置IO模式
- reg = readl(S3C64XX_GPMDAT); //读取输出寄存器之前数据
- reg |= 0xf;
- writel(reg,S3C64XX_GPMDAT); //写入1,让所有的灯都熄灭
- ret = misc_register(&misc); //注册驱动
- if(ret < 0)
- {
- printk(KERN_ALERT DEVICE_NAME " can't initialized LED!\n");
- return ret;
- }
- init_MUTEX(&led_sem); //注册信号量
- printk(KERN_ALERT DEVICE_NAME " initialized\n");
- return 0; //返回成功
- }
- /****************************************************************************************************************
- *函数名 : static long OK6410_LED_ioctl(
- struct file *file,
- unsigned int cmd,
- unsigned long arg)
- *功能 : 发送命令给LED驱动模块,无实际作用,直接返回0
- *参数 : 无作用
- *返回 : 0
- *依赖 : 无
- *作者 : 异灵元(cp1300@139.com)
- *创建时间 : 2012/08/27 17:28
- *最后修改时间: 2012/08/27 17:28
- *说明 : 无
- ****************************************************************************************************************/
- static long OK6410_LED_ioctl(
- struct file *file,
- unsigned int cmd,
- unsigned long arg)
- {
- return 0;
- }
- /****************************************************************************************************************
- *函数名 : static ssize_t OK6410_LED_write(
- struct file *file,
- const char __user *buff,
- size_t size,
- loff_t *loff)
- *功能 : 写数据到LED驱动模块,低电平灯亮
- *参数 : file:文件指针(无作用);buff:数据缓冲区指针;buff:数据数量;loff:无作用
- *返回 : 0:成功;<0:失败
- *依赖 : linux底层宏
- *作者 : 异灵元(cp1300@139.com)
- *创建时间 : 2012/08/27 17:43
- *最后修改时间: 2012/08/27 17:43
- *说明 : 点灯函数,低电平亮,0-3BIT有效;对应4个LED
- ****************************************************************************************************************/
- static ssize_t OK6410_LED_write(
- struct file *file,
- const char __user *buff,
- size_t size,
- loff_t *loff)
- {
- unsigned int reg;
- if(down_interruptible(&led_sem)) //获取信号量
- return -ERESTARTSYS;
- reg = readl(S3C64XX_GPMDAT);
- reg &= (~0xf);
- reg |= buff[0] & 0xf;
- writel(reg,S3C64XX_GPMDAT);
- up(&led_sem); //释放信号量
- return 0;
- }
- /****************************************************************************************************************
- *函数名 : static ssize_t OK6410_LED_read(
- struct file *file,
- char __user *buff,
- size_t size,
- loff_t *loff)
- *功能 : 读LED状态,低电平灯亮
- *参数 : file:文件指针(无作用);buff:数据缓冲区指针;buff:数据数量;loff:无作用
- *返回 : 0:成功;<0:失败
- *依赖 : linux底层宏
- *作者 : 异灵元(cp1300@139.com)
- *创建时间 : 2012/08/27 17:48
- *最后修改时间: 2012/08/27 17:48
- *说明 : 读取灯的状态,低电平灯亮,0-3bit有效;对应4个LED
- ****************************************************************************************************************/
- static ssize_t OK6410_LED_read(
- struct file *file,
- char __user *buff,
- size_t size,
- loff_t *loff)
- {
- unsigned int reg;
- if(down_interruptible(&led_sem)) //获取信号量
- return -ERESTARTSYS;
- reg = readl(S3C64XX_GPMDAT);
- buff[0] = reg | 0xfffffff0;
- up(&led_sem); //释放信号量
- return 0;
- }
- /****************************************************************************************************************
- *函数名 : static void __exit OK6410_LED_exit(void)
- *功能 : 卸载LED驱动
- *参数 : 无
- *返回 : 无
- *依赖 : linux底层宏
- *作者 : 异灵元(cp1300@139.com)
- *创建时间 : 2012/08/27 17:50
- *最后修改时间: 2012/08/27 17:50
- *说明 : 卸载驱动
- ****************************************************************************************************************/
- static void __exit OK6410_LED_exit(void)
- {
- unsigned int reg;
- //GPIOM0-3 输入
- reg = readl(S3C64XX_GPMCON); //获取GPIOM寄存器数据
- reg &= (~0xffff); //清除之前设置
- writel(reg,S3C64XX_GPMCON); //配置IO模式
- misc_deregister(&misc); //卸载驱动
- }
- //动态加载驱动接口(必须)
- module_init(OK6410_LED_init);
- module_exit(OK6410_LED_exit);
- //其它信息(非必需)
- MODULE_AUTHOR("cp1300@139.com"); //驱动程序作者
- MODULE_DESCRIPTION("OK6410(S3C6410) LED Driver"); //一些描述信息
- MODULE_LICENSE("GPL"); //遵循的协议
//测试代码
- /****************************************************************************************************************
- * 文件名称 : led_drive_test.c
- * 简介 : OK6410 LED驱动测试程序
- * 作者 : 异灵元(cp1300@139.com)
- * 创建时间 : 2012/08/27 18:04
- * 修改时间 : 2012/08/27
- * 说明 : OK6410 开发板(S3C6410)LED(GPIO)驱动测试程序
- ****************************************************************************************************************/
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <sys/ioctl.h>
- #include <unistd.h>
- int main(void)
- {
- int fd;
- int retval;
- unsigned char led;
- //LED测试
- printf("LED test...\n");
- fd = open("/dev/OK6410_LED",O_RDWR); //open led
- if(fd == -1)
- {
- printf("open led error!\n");
- exit(-1);
- }
- else
- {
- printf("open led ok!\n");
- }
- while(1)
- {
- for(retval = 0;retval < 4;retval ++)
- {
- led = 1 << retval;
- led = ~led;
- write(fd,&led,sizeof((unsigned char)1));
- //read(fd,&led,sizeof((unsigned char)1));
- //printf("LED = 0x%X\n",led);
- usleep(1000 * 100); //100MS
- }
- for(retval = 2;retval > 0;retval --)
- {
- led = 1 << retval;
- led = ~led;
- write(fd,&led,sizeof((unsigned char)1));
- //read(fd,&led,sizeof((unsigned char)1));
- //printf("LED = 0x%X\n",led);
- usleep(1000 * 100); //100MS
- }
- }
- close(fd);
- exit(0);
- }
本驱动程序使用的是混杂设备驱动模型,主设备号为10,使用次设备号来区分不同的设备,使用宏MISC_DYNAMIC_MINOR可以自动分配次设备号。
makefile代码如下:
ifneq ($(KERNELRELEASE),)
obj-m:=led_drive.o
else
KERNELDIR:=/opt/kernel/linux-3.0.1
PWD:=$(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *.mod.c *.mod.o *~ module* Module*
endif
KERNELDIR为内核路径,led_drive.o中的led_drive来自于源码文件名led_drive.c
执行make编译完成后会在模块源码目录下生成led_drive.ko
在开发板创建目录/lib/modules/3.0.1/,3.0.1为内核版本,可以通过cat /proc/version查看。将led_drive.ko拷贝至/lib/modules/3.0.1/,执行insmod led_drive.ko,会在/dev/目录下生成设备文件OK6410_LED。执行ls -l OK6410_LED 可以看到其主设备号为10,自动分配的次设备号为54。执行应用程序led_drive_test,可观察到led出现流水灯效果。