【字符设备驱动】
一、gpio
gpio驱动:
/********************************gpio.h****************************************/
#ifndef __GPIO_H
#define __GPIO_H
#include
#define MAGIC 0xd0
#define SET _IO(MAGIC,0)
#define CLR _IO(MAGIC,1)
#define OUT _IO(MAGIC,2)
#define MAXNR 3
#endif
/********************************gpio.c****************************************/
#include
#include
#include
#include
#include
#include
#include
#include "gpio.h"
#define GPIO_MAJOR126
#define DEVICE_NAME "gpio"
#define MAX_PORT 1
#define PORTC_CTL_ADDR 0x01d20010
#define PORTC_DAT_ADDR 0x01d20014
static int gpio_open(struct inode*inode,struct file *filp)
{
MOD_INC_USE_COUNT;
return0;
}
static int gpio_release(struct inode*inode,struct file *filp)
{
MOD_DEC_USE_COUNT;
return0;
}
static int gpio_ioctl(struct inode*inode,struct file *filp,unsigned int cmd,unsigned long arg)
{
intnum;
volatileu32 *regctl,*regdat;
num= MINOR(inode->i_rdev);
if(num >=MAX_PORT) return -ENODEV;
if(_IOC_TYPE(cmd)!=MAGIC)
return-ENOTTY;
if(_IOC_NR(cmd)>=MAXNR)
return-ENOTTY;
regctl=(volatileu32 *)(PORTC_CTL_ADDR);
regdat=(volatileu32 *)(PORTC_DAT_ADDR);
switch(cmd)
{
case SET:
if(arg< 16)
*regdat|= 1u<
分析:
#define module_init(x) __initcall(x);
#define __initcall(fn) \
staticinitcall_t __initcall_##fn __init_call = fn
typedef int (*initcall_t)(void);
#define __init_call __attribute__ ((unused,__section__ (".initcall.init")))
module_init(gpio_init);
替换:
#define__initcall(gpio_init) \
static initcall_t __initcall_ gpio_init \
__attribute__ ((unused,__section__ (".initcall.init"))) = gpio_init
如果没有属性,该宏定义为:
static initcall_t__initcall_ gpio_init = gpio_init; //这就是一般的静态变量定义并初始化其值。
在vmlinux.lds中:
……
__initcall_start = .;
*(.initcall.init)
__initcall_end = .;
static void __init do_initcalls(void)
{
initcall_t*call;
call= &__initcall_start;
do{
(*call)();
call++;
}while (call < &__initcall_end);
flush_scheduled_tasks();
}
static void __init do_basic_setup(void)
{
……
sock_init();
start_context_thread();
do_initcalls();
……
}
static int init(void* unused)
{
structfiles_struct *files;
lock_kernel();
do_basic_setup();
……
}
添加模块到内核中:
在2.4内核下的配置(在2.6中不是config.in而是kconfig)
a、 在对应驱动程序所在目录下的config.in文件中添加配置菜单项
格式:编译类型菜单项名称配置变量如:bool ‘gpio_drive’ CONFIG_GPIO
配置结果保存在对应版本linux内核目录(/uCLinux-xxx/linux-2.4.x)下的.config文件中
如:CONFIG_GPIO=y/CONFIG_GPIO=n/CONFIG_GPIO=M(编译成模块)
b、 在对应驱动程序所在目录下的Makefile文件中添加对结果CONFIG_GPIO编译处理
如:obj-$(CONFIG_GPIO) +=gpio.o
如果懒得加这些七七八八的东西直接写这样也OK:obj-y +=gpio.o
注释:CONFIG_xxx表示一个全局变量,只是起到传递参数的作用。 在/uClinux-dist/vendors/Samsung/44B0的Makefile文件中添加设备节点: zero,c,1,5 random,c,1,8 urandom,c,1,9 \ \ gpioDev,c,126,0 \ \ ram0,b,1,0 ram1,b,1,1 \ |
动态加载模块到内核:
insmod与modprobe区别:
modprobe会到相应目录下找到对模块并执行insmod安装模块。
命令还有lsmod与rmmod
Gpio应用程序:
/********************************iotest.c****************************************/
#include
#include
#include "gpio.h"
int main(int argc,char *argv[])
{
intfd;
inti;
fd= open("/dev/gpioDev",O_RDONLY);
if(fd < 0)
{
printf("openerror\n");
return0;
}
ioctl(fd,OUT,1);
ioctl(fd,OUT,2);
ioctl(fd,OUT,3);
for(;;)
{
ioctl(fd,SET,3);
ioctl(fd,CLR,2);
ioctl(fd,CLR,1);
sleep(1);
ioctl(fd,SET,2);
ioctl(fd,CLR,1);
ioctl(fd,CLR,3);
sleep(1);
ioctl(fd,SET,1);
ioctl(fd,CLR,2);
ioctl(fd,CLR,3);
sleep(1);
}
close(fd);
return0;
}
/******************************Makefile**************************************/
EXEC = iotest
OBJS = iotest.o
all: $(EXEC)
$(EXEC): $(OBJS)
$(CC)$(LDFLAGS) -o $@ $(OBJS) $(LDLIBS)
romfs:
$(ROMFSINST)/bin/$(EXEC)
clean:
-rm-f $(EXEC) *.elf *.gdb *.o
添加应用程序到内核中:
a、 在/uClinux-xxx/config/目录中的config.in文件中添加配置菜单项
如:bool ‘iotest-app’ CONFIG_USER_IOTEST
b、 配置结果是保存在/uClinux-xxx/config/目录中的.config文件中,对其结果进行处理在Makefile文件在user目录下
如:dir_$( CONFIG_USER_IOTEST) += iotest // iotest为应用程序目录名称
注释:应用程序源代码是放在/uClinux-xxx/user目录下的 dirà表示目录文件夹 objà表示目标文件 |
在上面这些参数都被设置后,最终被编入到目录/uClinux-dist/vendors/Samsung/44B0/中的对应的config.xxx文件中,以使其中的Makefile进行对整个工程编译成最终的镜像文件。