驱动程序源代码:
/*************************************
NAME:gt2440_leds.c
COPYRIGHT:www.e-online.cc
*************************************/
#ifndef __KERNEL__
# define__KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#define DEVICE_NAME "leds"
/* 定义幻数 */
#define LED_IOC_MAGIC 'k'
/* 定义命令 */
#define IOCTL_LEDS_ON _IO(LED_IOC_MAGIC, 1)
#define IOCTL_LEDS_OFF _IO(LED_IOC_MAGIC, 2)
#define IOCTL_LED_ON _IOW(LED_IOC_MAGIC,3, int)
#define IOCTL_LED_OFF _IOW(LED_IOC_MAGIC,4, int)
#define LED_IOC_MAXNR 4
/* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */
int dev_major=0;
struct cdev cdev;
int MEMDEV_NR_DEVS=2;
/* 用来指定LED所用的GPIO引脚 */
static unsigned long led_table [] =
{
S3C2410_GPB5,
S3C2410_GPB6,
S3C2410_GPB7,
S3C2410_GPB8,
};
/* 用来指定GPIO引脚的功能:输出 */
static unsigned int led_cfg_table [] =
{
S3C2410_GPB5_OUTP,
S3C2410_GPB6_OUTP,
S3C2410_GPB7_OUTP,
S3C2410_GPB8_OUTP,
};
int leds_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg)
{
int err =0;
if(_IOC_TYPE(cmd) != LED_IOC_MAGIC){
printk(KERN_INFO"TYPE ERROR");
return-ENOTTY;
}
if(_IOC_NR(cmd) > LED_IOC_MAXNR) {
printk(KERN_INFO"NR ERROR");
return-ENOTTY;
}
if(_IOC_DIR(cmd) & _IOC_READ)
err= !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
else if(_IOC_DIR(cmd) & _IOC_WRITE)
err= !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
if (err){
printk(KERN_INFO"arg address error.");
return-EFAULT;
}
switch(cmd)
{
caseIOCTL_LEDS_ON:
s3c2410_gpio_setpin(led_table[0],0);
s3c2410_gpio_setpin(led_table[1],0);
s3c2410_gpio_setpin(led_table[2],0);
s3c2410_gpio_setpin(led_table[3],0);
break;
caseIOCTL_LEDS_OFF:
s3c2410_gpio_setpin(led_table[0],1);
s3c2410_gpio_setpin(led_table[1],1);
s3c2410_gpio_setpin(led_table[2],1);
s3c2410_gpio_setpin(led_table[3],1);
break;
caseIOCTL_LED_ON:
//设置指定引脚的输出电平为0
ret= __get_user(led_no, (int *)arg);
s3c2410_gpio_setpin(led_table[led_no],0);
return0;
caseIOCTL_LED_OFF:
//设置指定引脚的输出电平为1
ret= __get_user(led_no, (int *)arg);
s3c2410_gpio_setpin(led_table[led_no],1);
return0;
default:
return-EINVAL;
}
return 0;
}
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.ioctl = leds_ioctl,
};
/*卸载函数*/
void memdev_cleanup_module(void)
{
cdev_del(&cdev);
/*注销字符设备*/
unregister_chrdev_region(MKDEV(dev_major,0),2);
}
/*加载函数*/
int memdev_init_module(void)
{
intresult,i;
for (i = 0; i< 4; i++)
{
s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]);
s3c2410_gpio_setpin(led_table[i],0);
}
dev_t devno= MKDEV(dev_major,0);
/*静态申请设备号*/
if(dev_major)
result=register_chrdev_region(devno,2,"led");//从0开始分配两个连续的次设备号
else{/*动态申请主设备号*/
result=alloc_chrdev_region(&devno,0,2,"led");//从0开始分配两个连续的次设备号
dev_major=MAJOR(devno);
staticstruct class *led_class;//mknod
/*自动创建设备文件*/
led_class =class_create(THIS_MODULE,"led_dev"); /*在sys下创建类目录/sys/class/led_dev*/
device_create(led_class, NULL, MKDEV(dev_major,0), NULL,"led0");
}
if(result<0){
gotofail_malloc;
returnresult;
}
/*注册字符设备*/
cdev_init(&cdev,&dev_fops);
cdev.owner =THIS_MODULE;
cdev.ops=&dev_fops;
cdev_add(&cdev,MKDEV(dev_major,0),MEMDEV_NR_DEVS);/*MEMDEV_NR_DEVS添加和memdev这个设备相关联的设备数量,次设备编号从0开始,到MEMDEV_NR_DEVS*/
return 0;
fail_malloc:
memdev_cleanup_module();
returnresult;
}
module_init(memdev_init_module);
module_exit(memdev_cleanup_module);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("www.e-online.cc");
MODULE_DESCRIPTION("LEDS control for GT2440Board");b
遇到的问题:
由于刚开始我用的是Red Hat 9,大家应该都知道这个当年经典的操作系统,可惜内核是2.4.20的,而现在转向2.6的linux内核后,我是通过下面的方法实现的(这是我用ARM开发板户手则上学到的):
Step1:编辑配置文件Kconfig,加入驱动选项,使之在makemenuconfig的时候出现
打开 kernel-2.6.30.4/drivers/char/Kconfig文件,添加下面的代码所示:
config GT2440_LED
tristate "GT2440 LED Driver"
depends on ARCH_S3C2440
default y if ARCH_S3C2440
help
GT2440 User led, use GPB[5:8].
Step2:保存退出,这时在linux-2.6.30.4目录位置运行一下makemenuconfig就可以在Device Drivers -> Character devices菜单中看到刚才所添加的选项了,按下空格键将会选择为<M>,此意为要把该选项编译为模块方式;再按下空格会变为<*>,意为要把该选项编译到内核中,在此我们选择<M>。
Step3:通过上一步,我们虽然可以在配置内核的时候进行选择,但实际上此
时执行编译内核还是不能把gt2440_hello_module.c编译进去的,还需要在
Makefile 中把内核配置选项和真正的源代码联系起来,打开linux-2.6.30.4/drivers/char/Makefile,添加如下代码并保存退出:
obj-$(CONFIG_GT2440_HELLO_MODULE) +=gt2440_hello_module.o
obj-$(CONFIG_GT2440_LEDS) += gt2440_leds.o
obj-$(CONFIG_GT2440_LED) += leds.o
obj-$(CONFIG_GT2440_PWM_BEEPER) += gt2440_pwm.o
2.将驱动模块拷贝到开发板后,先别急着加载模块,先停掉开发板上的又来的自动循环led程序:
#/etc/rc.d/init.d/leds stop
3.在成功之后,卸载驱动模块出现问题
使用rmmod会出现 rmmod : chdir(/lib/modules): No such file or directory ?
原来现在的内核模块在插入卸载时都会要转到/lib/modules/(内核版本号)/这个目录里。所以只要建立这个目录并且把要使用的模块.ko文件复制到这个目录就行了。
mkdir -p /lib/modules/$(uname -r)
4.ok, 接下来,推荐大家采用IO内存映射的方法实现led驱动,这个挺有难度的,我都挂这几天了,可能自己的硬件知识太差劲了,现在好好补补,过段时间再贴出我的程序代码吧。