OK6410 linux 内核模块加载--LED内核模块编译加载

来源:互联网 发布:有朋自远方来不亦说乎 编辑:程序博客网 时间:2024/05/16 08:52
开发环境:VMware虚拟机下的Ubuntu 12.0.4
linux内核:linux-3.0.1
试验平台:OK6410
实验现象:加载模块时,开发板上的4个LED灯组成流水灯。
 
 
1.解压光盘中的linux-3.0.1-2012-09-23.tar.gz到/home/linux-3.0.1/下
 
2.由于此内核源代码顶层目录中的Makefile中的ARCH=arm,CROSS_COMPILE=arm-linux-已被修改,所以在此不用重复此步骤.
 
3.编写led_driver.c文件.程序代码如下
 
/****************************************************************************************************************
 * 文件名称 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(DEVICE_NAME " can't initialized LED!\n");
return ret;
}
init_MUTEX(&led_sem);//注册信号量
printk(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"); //遵循的协议
///////////////////////////////////////////////////////////////////////////
4、编写APP程序
/****************************************************************************************************************
 * ???? : led_teset.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;

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 by Sandy!\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);
}
///////////////////////////////////////////////////////////////////////////
/************************************************************************

5.将编写好的led_driver.c文件放到内核源代码的/drivers/char/目录下,然后修改/drivers/char/Makefile文件,如下图.
              
6.编译内核模块.make modules在/home/linux-(你的目录)/下
  编译结束后,将会在源代码目录下的/drivers/char/下生成我们所需要的led_driver.ko文件,将其通过串口下载到OK6410的开发板上.(具体有关串口下载请查看用户手册,这里千万不要用ftp传送,经试验用ftp传递的文件大小会发生变化).
 
7.串口终端下操作开发板,将led_driver.ko文件转移到/lib/modules/3.0.1/目录下加载(没有的话创建该目录)
 
8.加载模块.insmod led_driver.ko,这时将会看到LED都亮了!
 
9.查看挂载的模块. lsmod
 
10.查看模块信息.modinfo led_driver(由于开发析上的buybox里没有加入modinfo命令,所以这里不可用),LED全灭!
 
11.arm-linux-gcc -o led_test_arm led_test.c编译应用程序然后串口下载上开发板上上。


12.在串口调试助手里运行led_test  ./led_test_arm
13.卸载模块.rmmod led_driver(注意这里不是led_driver.ko)