驱动程序:
/*whjled.c*/
#include<mach/regs-gpio.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/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#define LED_ON 1
#define LED_OFF 0
#define DEVICE_NAME "whjled"
#define LED_MAJOR 98
static unsigned long led_table [] =
{
S3C2410_GPB(5),
S3C2410_GPB(6),
S3C2410_GPB(7),
S3C2410_GPB(8),
};
static unsigned int led_cfg_table [] =
{
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
};
static int s3c2440_leds_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
if(arg>3)
{
printk("Led's number error,pleasecheck!");
return -EINVAL;
}
switch(cmd)
{
case LED_ON:
s3c2410_gpio_setpin(led_table[arg],0);//led low light
return 0;
case LED_OFF:
s3c2410_gpio_setpin(led_table[arg], 1);
return 0;
default:
return -EINVAL;
}
}
static struct file_operations dev_fops =
{
.owner = THIS_MODULE,
.ioctl = s3c2440_leds_ioctl,
};
static int __init dev_init(void)
{
int ret;
int i;
ret = register_chrdev(LED_MAJOR,DEVICE_NAME,&dev_fops);
if(ret<0)
{
printk(DEVICE_NAME"uninitialized\n");
return ret;
}
for (i = 0; i < 4; i++)
{
s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]);
s3c2410_gpio_setpin(led_table[i], 1);
}
printk (DEVICE_NAME"initialized\n");
return 0;
}
static void __exit dev_exit(void)
{
unregister_chrdev(LED_MAJOR,DEVICE_NAME);
}
module_init(dev_init);
module_exit(dev_exit);
驱动文件对应的Makefile为:
obj-m:=whjled.o
PWD:=$(shell pwd)
KDIR:=/home/linux-2.6.32.2c
all:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
rm -rf *.o *.ko *.mod.c *.mod.o *.symvers
将whjled.c与Makefile放在同一文件夹内(linux环境)执行make,生成whjled.ko。将whjled.ko拷贝至根文件系统的tmp文件夹下(我使用的是nfs根文件系统)。
在/tmp文件夹下加载驱动模块:输入insmod whjled.ko,若创建成功,可通过cat /proc/devices查看到设备whjled与其主设备号98.
在/dev文件夹下建立设备节点:输入mknod whjled c 98 1,若成功,可通过ls /dev查看到设备文件
测试程序如下:
/*cpp.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define LED_DEVICE "/dev/whjled"
int main(int argc,char *argv[])
{
int fd,cmd,num;
fd = open(LED_DEVICE,0);
num=atoi(argv[1]);
cmd=atoi(argv[2]);
if(fd < 0)
{
printf("can't open/dev/leds!\n");
exit(0);
}
ioctl(fd,cmd,num);
exit(0);
}
将驱动程序放在linux环境内,执行arm-linux-gcc-static cpp.c -o cpp,可得到可执行文件cpp。将cpp拷贝至根文件系统的/tmp文件夹下,就可通过
./cpp 0 0//led0灭
./cpp 0 1//led0亮
./cpp 1 0
./cpp 1 1
.....
./cpp 3 1
操作 LED 。
注意:
1、注册设备的时候,有两种方式:一种是使用register_chrdev(LED_MAJOR,DEVICE_NAME,&dev_fops),LED_MAJOR为定义的主设备号,DEVICE_NAME为定义的设备名称,dev_fops为定义的文件操作结构体。使用该函数向系统注册字符型设备驱动程序,主设备号LED_MAJOR自己定义,如该值为0则系统自动分配主设备号;另一种是使用misc_register(&misc)。如果是非标准设备则使用 misc_register,即一些字符设备不符合预先确定的字符设备范畴,就用这种方式,它固定使用主设备号10注册,如果多个设备次设备号不同。
2、使用register_chrdev(LED_MAJOR,DEVICE_NAME,&dev_fops)注册字符设备驱动程序时,如果有多个设备使用该函数注册驱动程序,LED_MAJOR不能相同,否则几个设备都无法注册(我已在友善的板子上验证)。如果模块使用该方式注册并且LED_MAJOR为0(自动分配主设备号),使用insmod命令加载模块时会在终端显示分配的主设备号和次设备号,在/dev目录下建立该节点,比如设备leds,如果加载该模块时分配的主设备号和次设备号为253和0,则建立节点:mknodleds c 253 0。使用register_chrdev(LED_MAJOR,DEVICE_NAME,&dev_fops)注册字符设备驱动程序时都要手动建立节点,否则在应用程序无法打开该设备。
3、由于我使用的是静态链接的根文件系统所以要加“-static”选项。