字符设备驱动(1)驱动代码完整源码:charButtons.c

内核版本:Linux3.0.8

开发板:基于三星S5PV210处理器的Tiny210开发板

驱动名称:charButtons.c

驱动描述:按键触发中断,中断处理程序执行相应的简单LED点亮操作

代码中红色高亮标记的函数,在后续会一个个,简单剖析

/*****************************************************************************
简    述:简单字符型驱动程序,手动静态分配设备号,手动创建设备节点
******************************************************************************/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>


#include <mach/map.h>
#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>

#include <linux/slab.h>

#define DEVICE_NAME        "buttons"

struct button_desc {
    int gpio;
    int number;
    char *name;    
};

struct led_desc {
    int gpio;
    int number;
    char *name;    
};

static struct button_desc buttons[] = {
    { S5PV210_GPH2(0), 0, "KEY0" },
    { S5PV210_GPH2(1), 1, "KEY1" },
    { S5PV210_GPH2(2), 2, "KEY2" },
    { S5PV210_GPH2(3), 3, "KEY3" },
    { S5PV210_GPH3(0), 4, "KEY4" },
    { S5PV210_GPH3(1), 5, "KEY5" },
    { S5PV210_GPH3(2), 6, "KEY6" },
    { S5PV210_GPH3(3), 7, "KEY7" },
};

static struct led_desc leds[] = {
    {S5PV210_GPJ2(0),1,"LED1"},
    {S5PV210_GPJ2(1),2,"LED2"},    
    {S5PV210_GPJ2(2),3,"LED3"},
    {S5PV210_GPJ2(3),4,"LED4"},
};

#define OK            (0)
#define ERROR         (-1)
struct gpio_chip *chip;
struct cdev *gDev;
struct file_operations *gFile;
dev_t  devNum;
unsigned int subDevNum = 1;//要申请的次设备号个数
int reg_major  =  234;    
int reg_minor =   0;


static irqreturn_t button_interrupt(int irq, void *dev_id)
{
    struct button_desc *bdata = (struct button_desc *)dev_id;

    int down;
    unsigned tmp;
    tmp = gpio_get_value(bdata->gpio);

    /* active low */
    down = !tmp;
    printk("KEY %d: %08x\n", bdata->number, down);
        
    if(bdata->number < 4)
    {
        gpio_set_value(leds[bdata->number].gpio,0);
        printk("LED %d: On \n",leds[bdata->number].number);
    }
    else
    {
        gpio_set_value(leds[(bdata->number) - 4].gpio,1);
        printk("LED %d: OFF \n",leds[(bdata->number)-4].number);
    }
    
    return IRQ_HANDLED;
}

int butsOpen(struct inode *p, struct file *f)
{
    
        int irq;
    int i;
    int err = 0;
    printk(KERN_EMERG"butsOpen\r\n");
    for (i = 0; i < ARRAY_SIZE(buttons); i++) {
        if (!buttons[i].gpio)
            continue;

        irq = gpio_to_irq(buttons[i].gpio);
        // irq = IRQ_EINT(16)+i =160+i "S5PV210_GPH2(i)"
        //irq = IRQ_EINT(24)+i =168+i  "S5PV210_GPH3(i)"
        err = request_irq(irq, button_interrupt, IRQ_TYPE_EDGE_BOTH, 
                buttons[i].name, (void *)&buttons[i]);
        if (err)
            break;
    }
    
    for(i = 0; i<ARRAY_SIZE(leds);i++)
    {
        if(!leds[i].gpio)
            continue;
        gpio_direction_output(leds[i].gpio,1);
    }
    
    if (err) {
        i--;
        for (; i >= 0; i--) {
            if (!buttons[i].gpio)
                continue;

            irq = gpio_to_irq(buttons[i].gpio);
            disable_irq(irq);
            free_irq(irq, (void *)&buttons[i]);
        }

        return -EBUSY;
    }
    return 0;
}




int charDrvInit(void)
{
    
    devNum = MKDEV(reg_major, reg_minor);

    printk(KERN_EMERG"devNum is %d\r\n", devNum);
    if(OK == register_chrdev_region(devNum, subDevNum, DEVICE_NAME))
    {
        printk(KERN_EMERG"register_chrdev_region ok\r\n");
    }
    else
    {
        printk(KERN_EMERG"register_chrdev_region error\r\n");
        return ERROR;
    }
    /*if(OK == alloc_chrdev_region(&devNum, subDevNum, subDevNum,"test"))
    {
        printk(KERN_EMERG"register_chrdev_region ok\r\n");
    }
    else
    {
        printk(KERN_EMERG"register_chrdev_region error\r\n");
        return ERROR;
    }*/

    gDev = kzalloc(sizeof(struct cdev), GFP_KERNEL);
    gFile = kzalloc(sizeof(struct file_operations), GFP_KERNEL);
    
    gFile->open = butsOpen;
//注册设备函数到file_operations结构体gFile

   //gDev->owner = THIS_MODULE;
    gFile->owner = THIS_MODULE;
    cdev_init(gDev, gFile);
//在cdev结构体中添加指针指向file_operations结构体gFile
    cdev_add(gDev, devNum, 3);
//建立设备号与cdev结构体联系
    printk(KERN_EMERG"button driver initial done...\r\n");
    return 0;
}

void __exit charDrvExit(void)
{
    int i,irq;
    cdev_del(gDev);
    unregister_chrdev_region(devNum, subDevNum);
    for (i = 0; i < ARRAY_SIZE(buttons); i++) {
            if (!buttons[i].gpio)
                continue;

            irq = gpio_to_irq(buttons[i].gpio);
            disable_irq(irq);
            free_irq(irq, (void *)&buttons[i]);
        }
    
    return;
}
module_init(charDrvInit);//执行insmod时会执行此行代码并调用charDrvInit,驱动开始
module_exit(charDrvExit);//执行rmmod时,结束
MODULE_LICENSE("GPL");

 函数修饰符

__init,本质上是一个宏定义,在内核源代码中定义:#define __init __section(.init.text) __cold notrace
作用就是,将被它修饰的函数放入.init.text段中去。所以所有的内核模块的__init修饰的函数被放在一起。内核启动时统一加载,加载完后统一释放以节省内存。

__exit,同上:#define __exit __section(.exit.text) __exitused __cold notrace

printk()函数:内核封装出来的打印函数。格式:
printk( 打印级别 “打印信息”) //printk的打印级别是用来控制printk打印的信息是否在终端显示

#define KERN_EMERG    "<0>"    /* system is unusable            */
#define KERN_ALERT    "<1>"    /* action must be taken immediately    */
#define KERN_CRIT    "<2>"    /* critical conditions            */
#define KERN_ERR    "<3>"    /* error conditions            */
#define KERN_WARNING    "<4>"    /* warning conditions            */
#define KERN_NOTICE    "<5>"    /* normal but significant condition    */
#define KERN_INFO    "<6>"    /* informational            */
#define KERN_DEBUG    "<7>"    /* debug-level messages            */

/* Use the default kernel loglevel */
#define KERN_DEFAULT    "<d>"

猜你喜欢

转载自www.cnblogs.com/embeded-linux/p/11094229.html