单片机
返回首页

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

2025-01-09 来源:cnblogs

内核版本:Linux3.0.8


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


驱动名称:charButtons.c


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


方案1注册字符设备使用新的接口实现(需要好几个函数来实现。貌似更复杂)


方案2注册字符设备使用老的接口实现(貌似老接口更简单)


/*****************************************************************************

简    述:简单字符型驱动程序,手动静态分配设备号,手动创建设备节点

******************************************************************************/

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include


#include

#include

#include

#include


#include


#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: %08xn', 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'butsOpenrn');

    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    {

        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 %drn', devNum);

    if(OK == register_chrdev_region(devNum, subDevNum, DEVICE_NAME))

    {

        printk(KERN_EMERG'register_chrdev_region okrn');

    }

    else

    {

        printk(KERN_EMERG'register_chrdev_region errorrn');

        return ERROR;

    }

    /*if(OK == alloc_chrdev_region(&devNum, subDevNum, subDevNum,'test'))

    {

        printk(KERN_EMERG'register_chrdev_region okrn');

    }

    else

    {

        printk(KERN_EMERG'register_chrdev_region errorrn');

        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...rn');

    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');


/*****************************************************************************

简    述:简单字符型驱动程序,手动静态分配设备号,手动创建设备节点

******************************************************************************/

#include

#include

#include

#include

#include

#include

#include


#define DEVICE_NAME        'leds'


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)

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: %08xn', 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'butsOpenrn');

    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_FALLING, 

                buttons[i].name, (void *)&buttons[i]);

        if (err)

            break;

    }

    

    for(i = 0; 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;

}


static const struct file_operations gFile = 

{

    .owner = THIS_MODULE,

    .open  =  butsOpen,    

};



int charDrvInit(void)

{

    

    devNum = MKDEV(reg_major, reg_minor);

    printk(KERN_EMERG'devNum is %drn', devNum);

   

    if(OK == register_chrdev(reg_major, DEVICE_NAME, &gFile))

    {

        printk(KERN_EMERG 'register_chrdev_region okrn');

    }

    else

    {

        printk(KERN_EMERG'register_chrdev_region errorrn');

        return ERROR;

    }

    

    printk(KERN_EMERG 'button driver initial done...rn');

    return 0;

}


void __exit charDrvExit(void)

{

    int i,irq;

    unregister_chrdev(reg_major, DEVICE_NAME);

    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');

MODULE_AUTHOR('LiuB');


函数修饰符


__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    ''


如果定义了模块,即这一驱动被动态编译为模块时


驱动代码中的module_init、module_exit的内核定义(/include/linux/init.h)


/* Each module must use one module_init(). */

#define module_init(initfn)   

static inline initcall_t __inittest(void)    { return initfn; }   

int init_module(void) __attribute__((alias(#initfn)));


typedef int (*initcall_t)(void);

//static inline initcall_t __inittest(void)    { return initfn; }

//inittest这个函数返回值为一个initcall_t类型的initfn

//而initcall_t是一个函数指针,返回值为整型,输入参数为空


/* This is only required if you want to be unloadable. */

#define module_exit(exitfn)   

static inline exitcall_t __exittest(void)    { return exitfn; }   

void cleanup_module(void) __attribute__((alias(#exitfn)));


typedef void (*exitcall_t)(void);


如果未定义模块,这一驱动被静态编译进内核


驱动代码中的module_init、module_exit的内核定义(/include/linux/init.h)


/**

 * module_init() - driver initialization entry point

 * @x: function to be run at kernel boot time or module insertion

 * 

 * module_init() will either be called during do_initcalls() (if

 * builtin) or at module insertion time (if a module).  There can only

 * be one per module.

 */

#define module_init(x)    __initcall(x);

#define __initcall(fn) device_initcall(fn)

#define device_initcall(fn)    __define_initcall('6',fn,6)

/* initcalls are now grouped by functionality into separate 

 * subsections. Ordering inside the subsections is determined

 * by link order. 

 * For backwards compatibility, initcall() puts the call in 

 * the device init subsection.

 *

 * The `id' arg to __define_initcall() is needed so that multiple initcalls

 * can point at the same handler without causing duplicate-symbol build errors.

 */


#define __define_initcall(level,fn,id)

    static initcall_t __initcall_##fn##id __used

    __attribute__((__section__('.initcall' level '.init'))) = fn


通过这些段代码,我们能够看出最终的结果是将我们使用module_init修饰的函数指针链接到一个叫.initcall的段里,也就是说最终所有使用module_init修饰的函数指针都被链接在这个段里,内核在启动的时候顺序调用所有链接在这个段里的函数,实现设备的初始化


进入单片机查看更多内容>>
相关视频
  • 【TI MSPM0 应用实战】智能小车+工业角度编码器+血氧仪+烟雾探测器!硬核参考设计详解!

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

  • 直播回放: Microchip Timberwolf™ 音频处理器在线研讨会

  • 基于灵动MM32W0系列MCU的指夹血氧仪控制及OTA升级应用方案分享

精选电路图
  • 锂离子/锂聚合物USB电池充电器

  • 6晶体管H桥

  • AVR LCD温度计—LM35

  • AVR PC步进电机驱动器

  • AVR温度计TCN75

  • JDM2 PIC 18F 编程器

    相关电子头条文章