這次要講的kernel module是運行在QT2410上的LED driver,這支driver可透過讀寫proc檔案系統的方式點亮或熄滅LED,如果要點亮LED,下達指令echo 1 > /proc/led,要熄滅LED則下echo 0 > /proc/led,交叉編譯2.6 kernel module的Makefile很簡單,KDIR就是QT2410 Linux核心所在的資料夾路徑,M是kernel module的資料夾所在位置,請參考如下Makefile,如果要縮小kernel module的size,請下指令arm-linux-strip -d led.ko(只去掉debug symbol),這樣子可以減少體積至原來1/10的大小(2007/9/17修改)
MODULE_NAME = led
KDIR = ../linux-2.6.26
obj-m := $(MODULE_NAME).o
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
LED driver的重點如下
(1)因為QT2410 Linux有MMU,所以對GPIO存取前必需先將physical address轉為virtual address,請參考requestLEDMemRegion這段函式
(2)註冊proc file system,請參考sdr_init的code中,我是如何create_proc_entry和註冊此entry的讀寫函式,雖然user space program跟kernel module溝通的方法還有ioctl,我想我就不多贅述,請多讀Linux Device Driver這本書
LED driver原始碼如下所示
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/ioport.h>
#include <asm/io.h>
#define GPIOBAdr 0x56000014
static struct proc_dir_entry *LEDProc;
static int LEDswitch=0;
static void __iomem *GPIOBBase;
static int LEDRead(char *page, char **start, off_t off, int count, int *eof,void *data)
{
char *p = page;
int len;
p += sprintf(p, "%d\n", LEDswitch);
len = (p - page) - off;
if (len < 0)
len = 0;
*eof = (len <= count) ? 1 : 0;
*start = page + off;
return len;
}
static int LEDWrite(struct file *file, const char __user *buffer,unsigned long count, void *data)
{
char mode;
if (count > 0)
{
if (get_user(mode, buffer))
return -EFAULT;
if (mode == '0')
{
LEDswitch=0;
(*(volatile unsigned *)GPIOBBase)|=0x00000002;
}
if (mode == '1')
{
LEDswitch=1;
(*(volatile unsigned *)GPIOBBase)&=~(0x00000002);
}
}
return count;
}
static int requestLEDMemRegion(void)
{
if (!request_mem_region(GPIOBAdr, 4, "GPIOBLed"))
return -EFAULT;
GPIOBBase= ioremap(GPIOBAdr, 4);
if (GPIOBBase==0) return -EFAULT;
else printk("GPIOB virtual address:0x%p\n",GPIOBBase);
return 0;
}
static int __init sdr_init(void)
{
printk("QT2410 LED driver demo\n");
if (requestLEDMemRegion()==-EFAULT) return -EFAULT;
LEDProc=create_proc_entry("led",0666,NULL);
LEDProc->read_proc=LEDRead;
LEDProc->write_proc=LEDWrite;
LEDProc->owner=THIS_MODULE;
return 0;
}
static void __exit sdr_exit(void)
{
remove_proc_entry("led", NULL);
release_mem_region(GPIOBAdr, 4);
printk("LED driver remove successfully\n");
}
module_init(sdr_init);
module_exit(sdr_exit);
MODULE_DESCRIPTION("led module");
MODULE_AUTHOR("Joey Cheng<jemicheng@gmail.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("QT2410:LED module");
編譯moduel的畫面如下
留言