/*所需的头文件定义*/
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/ioctl.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <asm/atomic.h>
#include <asm/irq.h>
#include <asm/unistd.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
/*定义LED驱动的设备名"*/
#define DEVICE_NAME "leds"
/*定义LED对应的GPIO端口列表,对应定义设备号0~3*/
static unsigned long leds_table[] = {
S3C2410_GPB(5),
S3C2410_GPB(6),
S3C2410_GPB(7),
S3C2410_GPB(8),
};
/*定义LED对应端口将要输出的状态列表*/
static unsigned int leds_cfg_table[]=
{
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
};
/*ioctl 函数的实现
* 在应用/用户层将通过ioctl 函数向内核传递参数,以控制LED的输出状态
*ioctl(fd, on, led_number);
*inode和file指针是对应应用程序传递的文件描述符fd的值,
*和传递给 open 方法的相同参数
*/
static int leds_ioctl(
struct inode *inode,
struct file *file,
/*cmd为0接受为0则灯灭,因为下面取值为!cmd,s3c2440定义GPIO输出为低电平时有效*/
unsigned int cmd,
/*arg为输入的自定义设备号,设备号为0~3*/
unsigned long arg)
{
switch(cmd){
case 0:
case 1:
if (arg > 4){
return -EINVAL;
}
/*s3c2440定义GPIO输出为低电平时有效,这里cmd取反值
*通过s3c2410_gpio_setpin()来做,此函数为驱动函数的核心操作*/
s3c2410_gpio_setpin(leds_table[arg],!cmd);
return 0;
default:
return -EINVAL;
}
}
/*
* 设备函数操作集,在此只有ioctl 函数,通常还有read,write,open,close等,因为本LED驱动在下面已经
* 注册为misc设备,因此也可以不用open/close
*/
static struct file_operations leds_fops=
{
/*.owner只有在编译为模块的时候才有实际用处.*/
.owner = THIS_MODULE,
.ioctl = leds_ioctl,
};
/*
*把LED 驱动注册为MISC设备
*Minor为指定次设备号,等于MISC_DYNAMIC_MINOR表示为动态获取次设备号.
*Name为设备名.
*Fops为file_operations结构,设备的操作函数指针集合.
*/
static struct miscdevice leds=
{
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &leds_fops,
};
/*
*模块初始化
*GPIO口的设置:
*S3C2440设置GPIO口主要是设置3个寄存器.分别为GPxCON,GPxDAT,GPxUP(x=A….J),
*GPxCON用于设置IO口的功能,每两位对应一个IO口,输入=00,输出=01;特殊功能=10;
*GPxDAT为IO口的数据寄存器,每一位对应一个IO口.GPxUP为是否使用上拉电阻,0为使用,
*注意的是并不是每组IO口都有内部集成上拉电阻,如果没有集成,则没该寄存器.
*设置这三个寄存器相对应的函数为:s3c2410_gpio_cfgpin,s3c2410_gpio_setpin.
*/
static int __init leds_init(void)
{
int ret;
int i;
/*下面为设置LED初始化后4个LED全处于发光状态*/
for (i=1; i<4; i++)
{
/*设置4个LED对应的端口寄存器为输出(OUTPUT)*/
s3c2410_gpio_cfgpin(leds_table[i],leds_cfg_table[i]);
/*设置LED 对应的端口寄存器为低电平输出,
在模块加载结束后,四个LED 应该是全部都是发光状态*/
s3c2410_gpio_setpin(leds_table[i],0);
};
/*
*misc设备注册
*非标准设备使用misc_register,
*即一些字符设备不符合预先确定的字符设备范畴,
*这些设备就用主编号10一起归于"其他类型",
*misc_register()用主编号10调用register_chrdev(),
*设备名称和函数表指针通过miscdevice数据结构获得.
*同样,miscdevice数据结构还保存设备驱动程序所使用的次要号码.
*/
ret = misc_register(&leds);
printk (DEVICE_NAME"\tis initialized\n");
return ret;
}
/*设备卸载*/
static void __exit leds_exit(void)
{
/*
*misc(混合,其他类型,不能严格划分的设备类型)
*类设备的注销函数,成功返回为0,错误返回一个错误代码
*/
misc_deregister(&leds);
}
/*模块初始化,仅当使用insmod/podprobe命令加载时有用,
如果设备不是通过模块方式加载,此处将不会被调用*/
module_init(leds_init);
/*卸载模块,当该设备通过模块方式加载后,
可以通过rmmod 命令卸载,将调用此函数*/
module_exit(leds_exit);