這次要講的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的畫面如下

最後修改日期: 3 6 月, 2022

作者

留言

撰寫回覆或留言

發佈留言必須填寫的電子郵件地址不會公開。