头像

也许

广州医科大学




离线:19小时前


最近来访(65)
用户头像
李京泽
用户头像
MJgopher
用户头像
NZX
用户头像
鳕鱼饭
用户头像
听雨家族--无尘Txc.
用户头像
surfing_7
用户头像
15630138557
用户头像
Mondayhmd
用户头像
白月光L
用户头像
承影
用户头像
HAMRUS
用户头像
Malaimooooh
用户头像
Joanna
用户头像
Atacama_Dessert
用户头像
关注whale77
用户头像
封禁用户
用户头像
OI
用户头像
洗衣机里的纸巾
用户头像
lanqiaobeizmy
用户头像
XXMM


也许
2天前

类对象的组合方式

生成子类继承

关联:

    构造函数传参(参数是另一个类的对象)

    直接在类中生成另一个类的对象

代理模式

无多态

适合于:为其他对象提供一种代理以控制对这个对象的访问。

构造一个代理类,代理类中使用另一个对象方法

模板:a是代理类,b类实现协议类protocol;a中包含b类,a可以使用b类的接口

好处:main函数不需要修改了。只需要修改协议实现类

装饰模式

有多态

功能:扩展对象的功能,是继承关系的一个替换方案。

把要添加的附加功能分别放在单独的类中,并让这个类包含它要装饰的对象

当需要执行时,客户端就可以有选择地、按顺序地使用装饰功能包装对象

适用于:

    动态(自由装饰)的给一个对象添加一些额外的职责。就增加功能来说,此模式比生成子类更为灵活。

适配器模式

没有多态,只是个接口中转站

适用于:

    是将一个类的接口转换成客户希望的另外一个接口。使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

构造一个适配器类,继承自客户需求的类,将实际类的对象指针传入适配器类中,在适配器类中实现客户需要的接口,使用

在客户需求的接口中使用实际类对象的方法,从而实现了将一个类的接口转换成客户希望的另外一个接口

桥接模式

适用于:桥接模式是将抽象部分与实现部分分离(解耦合),使它们都可以独立的变化。

车 安装 发动机 ;不同型号的车,安装不同型号的发动机

继承方法实现:不同的车型,不同型号,安装不同类型的发动机,组合很多,会引起子类的泛滥。

需要把“安装发动机”这个事,做很好的分解;把抽象 和 行为实现 分开

车抽象类,发动机抽象类----有多态,不同类型车调用不同的安装发动机方法

发动机类:有安装发动机方法,具体实现

车类中:有安装发动机方法,不具体实现。使用发动机使用的方法,通过构造函数传入发动机类对象的指针来关联发动机类

客户端可以自由组合      不同型号的车和不同型号的发动机自由组合




也许
3天前

工厂模式与建造者模式的区别

Factory模式不考虑对象的组装过程,而直接生成一个我想要的对象。

Builder模式先一个个的创建对象的每一个部件,再统一组装成一个对象

建造者模式

适用情况:

    一个对象的构建比较复杂,将一个对象的构建(?)和对象的表示(?)进行分离。

对象的表示:对象的属性

对象的构建:对象的属性赋值

房子(对象):属性有:墙,地板,门

如何构建不同的房子(公寓,别墅的属性不同)

Builder:为创建产品各个部分(墙,地板,门),统一抽象接口。

ConcreteBuilder:继承自Builder,具体的创建产品的各个部分(实现),部分A, 部分B,部分C。并返回组装完成的对象

Director:构造一个使用Builder接口的对象。

Product:这个例子中表示房子,表示被构造的复杂对象。

构建公寓,别墅 案例代码

#include <iostream>
using namespace std;
#include "string"
class House
{
public:
    void setFloor(string floor)
    {
        this->m_floor = floor;
    }
    void setWall(string wall)
    {
        this->m_wall = wall;
    }
    void setDoor(string door)
    {
        this->m_door = door;
    }

    //
    string getFloor()
    {
        return m_floor;
    }
    string setWall()
    {
        return  m_wall;
    }
    string setDoor()
    {
        return m_door;
    }

protected:
private:
    string  m_floor;
    string  m_wall;
    string  m_door;
};

class Builder
{
public:
    virtual void makeFloor() = 0;
    virtual void makeWall() =  0;
    virtual void makeDoor() = 0;
    virtual House *GetHouse() = 0;
};

//公寓
class FlatBuild : public Builder
{
public:
    FlatBuild()
    {
        pHouse = new House;
    }
    virtual void makeFloor()
    {
        pHouse->setFloor("flat Door");
    }
    virtual void makeWall()
    {
        pHouse->setWall("flat Wall");
    }
    virtual void makeDoor()
    {
        pHouse->setDoor("flat Door");
    }
    virtual House *GetHouse()
    {
        return pHouse;
    }

private:
    House *pHouse;
};

//别墅
class VillaBuild : public Builder
{
public:
    VillaBuild()
    {
        pHouse = new House;
    }
    virtual void makeFloor()
    {
        pHouse->setFloor("villa floor");
    }
    virtual void makeWall()
    {
        pHouse->setWall("villa Wall");
    }
    virtual void makeDoor()
    {
        pHouse->setDoor("villa Door");
    }
    virtual House *GetHouse()
    {
        return pHouse;
    }
private:
    House *pHouse;
};

class Director
{
public:
    void Construct(Builder *builder)
    {
        builder->makeFloor();
        builder->makeWall();
        builder->makeDoor();
    }
protected:
private:
};


void main()
{
    //客户直接造房子
    House *pHose = new House;
    pHose->setDoor("wbm门");
    pHose->setFloor("wbmFloor");
    pHose->setWall("wbmWall");
    delete pHose;


    /* //工程队直接造房子 
    Builder *builder = new FlatBuild;
    builder->makeFloor();
    builder->makeWall();
    builder->makeDoor();
    */

    //指挥者(设计师)指挥 工程队 和 建房子
    Director *director = new Director;

    //建公寓
    Builder *builder = new FlatBuild;
    director->Construct(builder); //设计师 指挥 工程队干活
    House *house = builder->GetHouse();
    cout << house->getFloor() << endl;
    delete house;
    delete builder;

    //建别墅
    builder = new VillaBuild;
    director->Construct(builder); //设计师 指挥 工程队干活
    house = builder->GetHouse();
    cout << house->getFloor() << endl;
    delete house;
    delete builder;

    delete director;

    system("pause");
    return ;
}



也许
3天前

简单工厂模式

class Fruit
{
public:
    virtual void getFruit() = 0;

protected:
private:
};


class Banana : public Fruit
{
public:
    virtual void getFruit()
    {
        cout<<"香蕉"<<endl;

    }
protected:
private:
};


class Pear : public Fruit
{
public:
    virtual void getFruit()
    {
        cout<<"梨子"<<endl;

    }
protected:
private:
};

class Factory 
{
public:
    static Fruit* Create(char *name)
    {
        Fruit *tmp = NULL;
        if (strcmp(name, "pear") == 0)
        {
            tmp = new Pear();
        }
        else if (strcmp(name, "banana") == 0)
        {
            tmp = new Banana();
        }
        else
        {
            return NULL;
        }
        return tmp;
    }
protected:
private:
};

void main()
{
    Fruit *pear =  Factory::Create("pear");
    if (pear == NULL)
    {
        cout<<"创建pear失败\n";
    }
    pear->getFruit();

    Fruit *banana =  Factory::Create("banana");
    banana->getFruit();

    system("pause");
}

工厂模式

简单工厂模式,当有新的对象需要创建时,需要修改factory类的方法,不符合开闭原则

工厂模式:当系统扩展需要添加新的产品对象时,仅仅需要添加一个具体对象以及一个具体工厂对象,原有工厂对象不需要

进行任何修改

#include "iostream"
using namespace std;

class Fruit
{
public:
    virtual void sayname()
    {
        cout<<"fruit\n";
    }
};

class FruitFactory
{
public:
    virtual Fruit* getFruit()
    {
        return new Fruit();
    }
};


//香蕉
class Banana : public Fruit
{
public:
    virtual void sayname()
    {
        cout<<"Banana \n"<<endl;
    }
};

//香蕉工厂
class BananaFactory : public  FruitFactory
{
public:
    virtual Fruit* getFruit()
    {
        return new Banana;
    }
};


//苹果
class Apple : public Fruit
{
public:
    virtual void sayname()
    {
        cout<<"Apple \n"<<endl;
    }
};

//苹果工厂
class AppleFactory : public  FruitFactory
{
public:
    virtual Fruit* getFruit()
    {
        return new Apple;
    }
};

void main()
{
    FruitFactory * ff  = NULL;
    Fruit *fruit = NULL;

    //1
    ff = new BananaFactory();
    fruit = ff->getFruit();
    fruit->sayname();

    delete fruit;
    delete ff;

    //2苹果
    ff = new AppleFactory();
    fruit = ff->getFruit();
    fruit->sayname();

    delete fruit;
    delete ff;

    cout<<"hello....\n";
    system("pause");
}

抽象工厂模式

工厂模式只能生产一个产品。(要么香蕉、要么苹果)

抽象工厂可以一下生产一个产品族(里面有很多产品组成)

class Fruit
{
public:
    virtual void sayname()
    {
        cout<<"fruit\n";
    }
};

class FruitFactory
{
public:
    virtual Fruit* getApple()
    {
        return new Fruit();
    }
    virtual Fruit* getBanana()
    {
        return new Fruit();
    }
};

//南方香蕉
class SouthBanana : public Fruit
{
public:
    virtual void sayname()
    {
        cout<<"South Banana \n"<<endl;
    }
};


//南方苹果
class SouthApple : public Fruit
{
public:
    virtual void sayname()
    {
        cout<<"South Apple \n"<<endl;
    }
};


//北方香蕉
class NorthBanana : public Fruit
{
public:
    virtual void sayname()
    {
        cout<<"North Banana \n"<<endl;
    }
};


//北方苹果
class NorthApple : public Fruit
{
public:
    virtual void sayname()
    {
        cout<<"North Apple \n"<<endl;
    }
};

class SourthFruitFactory : public FruitFactory
{
public:
    virtual Fruit* getApple()
    {
        return new SouthApple();
    }
    virtual Fruit* getBanana()
    {
        return new SouthBanana();
    }
};

class NorthFruitFactory : public FruitFactory
{
public:
    virtual Fruit* getApple()
    {
        return new NorthApple();
    }
    virtual Fruit* getBanana()
    {
        return new NorthBanana();
    }
};

void main()
{   
    FruitFactory * ff  = NULL;
    Fruit *fruit = NULL;

    ff = new SourthFruitFactory();
    fruit = ff->getApple();
    fruit->sayname();
    fruit = ff->getBanana();
    fruit->sayname();

    delete fruit;
    delete ff;

    ff = new NorthFruitFactory();
    fruit = ff->getApple();
    fruit->sayname();
    fruit = ff->getBanana();
    fruit->sayname();

    delete fruit;
    delete ff;

    cout<<"hello....\n";
    system("pause");
}



也许
3天前

设计模式基本原则

开放封闭原则 

    类的改动是通过增加代码进行的,而不是修改源代码。

依赖倒置原则

    依赖于抽象(接口),不要依赖具体的实现(类),也就是针对接口编程。

单例模式

目的:保证为一个类只生成唯一的实例对象

应用场景:多个线程共享同一个资源或者操作同一个对象

实现单例模式步骤:

    构造函数私有化

    提供一个全局的静态方法(全局访问点) //使用静态方法不需要对类进行实例化

    在类中定义一个静态指针,指向本类的变量的静态变量指针   // 初始化不需要依赖具体对象 

懒汉式单例模式

在全局的静态方法中创建对象

class  Singelton
{
private:
    Singelton()
    {
        m_singer = NULL;
        m_count = 0;
        cout << "构造函数Singelton ... do" << endl;
    }

public:
    static Singelton *getInstance()
    {
        if (m_singer == NULL )  //懒汉式:1 每次获取实例都要判断 2 多线程会有问题
        {
            m_singer = new Singelton;
        }
        return m_singer;
    }
    static void printT()
    {
        cout << "m_count: " << m_count << endl;
    }

private:
    static Singelton *m_singer;
    static int m_count;
};

Singelton *Singelton::m_singer = NULL;  //懒汉式 并没有创建单例对象
int Singelton::m_count = 0;


void main01_1()
{
    cout << "演示 懒汉式" << endl;
    Singelton *p1 = Singelton::getInstance(); //只有在使用的时候,才去创建对象。
    Singelton *p2 = Singelton::getInstance();
    if (p1 != p2)
    {
        cout << "不是同一个对象" << endl;
    }
    else
    {
        cout << "是同一个对象" << endl;
    }
    p1->printT();
    p2->printT();

    system("pause");
    return ;
}

饿汉式单例模式

不在静态方法中创建对象,而是在全局区直接创建对象

class  Singelton2
{
private:
    Singelton2()
    {
        m_singer = NULL;
        m_count = 0;
        cout << "构造函数Singelton ... do" << endl;
    }

public:
    static Singelton2 *getInstance()
    {
//      if (m_singer == NULL )
//      {
//          m_singer = new Singelton2;
//      }
        return m_singer;
    }
    static void Singelton2::FreeInstance()
    {
        if (m_singer != NULL)
        {
            delete m_singer;
            m_singer = NULL;
            m_count = 0;
        }
    }
    static void printT()
    {
        cout << "m_count: " << m_count << endl;
    }

private:
    static Singelton2 *m_singer;
    static int m_count;
};

Singelton2 *Singelton2::m_singer = new Singelton2; //不管你创建不创建实例,均把实例new出来
int Singelton2::m_count = 0;

void main()
{
    cout << "演示 饿汉式" << endl;

    Singelton2 *p1 = Singelton2::getInstance(); //只有在使用的时候,才去创建对象。
    Singelton2 *p2 = Singelton2::getInstance();
    if (p1 != p2)
    {
        cout << "不是同一个对象" << endl;
    }
    else
    {
        cout << "是同一个对象" << endl;
    }
    p1->printT();
    p2->printT();
    Singelton2::FreeInstance();
    Singelton2::FreeInstance();


    system("pause");
}



也许
10天前

中断线程化处理引入

复杂、耗时的事情,尽量使用内核线程来处理。上节视频介绍的工作队列用起来挺简单,但是它有一个缺点:工作队列中有

多个work,前一个work没处理完会影响后面的work。解决方法有很多种,比如干脆自己创建一个内核线程,不跟别的work凑

在一块了。

对于中断处理,还有另一种方法:threadedirq,线程化的中断处理。中断的处理仍然可以认为分为上半部、下半部。上半

部用来处理紧急的事情,下半部用一个内核线程来处理,这个内核线程专用于这个中断。

函数

request_threaded_irq(unsigned int irq, irq_handler_t handler,  irq_handler_t thread_fn,unsigned long flags, 

const char *name, void *dev);

作用:将中断线程化,中断将作为内核线程运行

参数:

irq             中断号,所申请的中断向量

handler         中断处理函数,在驱动中一般这个参数是NULL,为NULL时使用默认的处理,这个相当于中断的上半段

thread_fn       中断发生时,如果handler为NULL,就直接将thread_fn扔到内核线程中去执行

flags           指定中断属性、中断触发方式(一般用宏定义表示)等,定义在linux/interrupt.h中

name            指定中断名字,用命令cat /proc/interrupts可查看系统中断申请与使用情况

dev             传入中断处理程序的参数,可以为NULL,但在注册共享中断时,此参数不能为NULL。该参数可作为共享中
                断时的中断区别参数,还可以把其传给一个结构体变量,用于保存一个设备的信息,使中断处理函数可以获得该设备的信息



编程

probe 函数中将request_irq() 换成 request_threaded_irq()



也许
10天前

工作队列引入

前面讲的定时器、下半部tasklet,它们都是在中断上下文中执行,它们无法休眠(不能被调度)。当要处理更复杂的事情

时,往往更耗时这些更耗时的工作放在定时器或是下半部中,会使得系统很卡;并且循环等待某件事情完成也太浪费CPU资

源了。

如果使用线程来处理这些耗时的工作,那就可以解决系统卡顿的问题:因为线程可以休眠。

在内核中,我们并不需要自己去创建线程,可以使用“工作队列”(workqueue)。内核初始化工作队列就为它创建了内核线程

常用函数

初始化工作队列:#define INIT_WORK(_work, _func) 

调用schedule_work

    就会把work_struct结构体放入队列中,并唤醒对应的内核线程。内核线程就会从队列里把work_struct结构体取出来,

    执行里面的函数。

编程


    probe 函数中初始化work_queue

    中断服务程序中调度work_queue(创建内核线程)

    任务处理函数中打印key值




也许
10天前

内核函数

定义tasklet:

    中断下半部使用结构体tasklet_struct来表示

    struct tasklet_struct
    {
        struct tasklet_struct *next;   //下一个节点的地址
        unsigned long state;      // bit0 == TASKLET_STATE_SCHED = 1 表示放入链表中了
                                  // bit1 == TASKLET_STATE_RUN = 1 表示重在运行
        atomic_t count;           // 0 :使能 1:禁止
        void (*func)(unsigned long);  // 中断下半部函数
        unsigned long data;          //函数参数
    };

初始化tasklet:

    extern void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);

使能/禁止tasklet

    static inline void tasklet_enable(struct tasklet_struct *t);

    static inline void tasklet_disable(struct tasklet_struct *t);

调度tasklet:发出软中断

    static inline void tasklet_schedule(struct tasklet_struct *t);    

kill tasklet

    extern void tasklet_kill(struct tasklet_struct *t);

tasklet使用方法

先定义tasklet,需要使用时调用tasklet_schedule,驱动卸载前调用tasklet_kill。

tasklet_schedule只是把tasklet放入内核队列,它的func函数会在软件中断的执行过程中被调用。

调用过程:

    asklet属于TASKLET_SOFTIRQ软件中断,入口函数为tasklet_action

    当发生硬件中断时,内核处理完硬件中断后,会处理软件中断。对于TASKLET_SOFTIRQ软件中断,会调用tasklet_actio

    n函数。

总结    

    执行过程还是挺简单的:从队列中找到tasklet,进行状态判断后执行func函数,从队列中删除tasklet

    tasklet_schedule调度tasklet时,其中的函数并不会立刻执行,而只是把tasklet放入队列;

    调用一次tasklet_schedule,只会导致tasklnet的函数被执行一次;

    如果tasklet的函数尚未执行,多次调用tasklet_schedule也是无效的,只会放入队列一次。


编程

将中断服务程序中打印key值操作放到中断下半部中

    probe 函数中初始化tasklet

    中断服务程序中调度tasklet(触发软中断)

    任务处理函数中打印key值




也许
10天前

定时器相关函数

setup_timer(timer, fn, data):设置定时器,主要是初始化timer_list结构体,设置其中的函数、参数。

void add_timer(struct timer_list *timer):向内核添加定时器。

    timer->expires表示超时时间。 

    当超时时间到达,内核就会调用这个函数:timer->function(timer->data)。

int mod_timer(struct timer_list *timer, unsigned long expires): 修改定时器的超时时间,

    它等同于:del_timer(timer); timer->expires = expires; add_timer(timer); 但是更加高效。

int del_timer(struct timer_list *timer):删除定时器。

使用定时器处理按键抖动

按键抖动.png

在实际的按键操作中,可能会有机械抖动:

按下或松开一个按键,它的GPIO电平会反复变化,最后才稳定。一般是几十毫秒才会稳定。

如果不处理抖动的话,用户只操作一次按键,中断程序可能会上报多个数据。

怎么处理?

    1. 在按键中断程序中,可以循环判断几十亳秒,发现电平稳定之后再上报

    2. 使用定时器

    显然第1种方法太耗时,违背“中断要尽快处理”的原则,你的系统会很卡。

怎么使用定时器?看下图:

消抖.png

核心在于:在GPIO中断中并不立刻记录按键值,而是修改定时器超时时间,10ms后再处理。

如果10ms内又发生了GPIO中断,那就认为是抖动,这时再次修改超时时间为10ms。

只有10ms之内再无GPIO中断发生,那么定时器的函数才会被调用。

在定时器函数中记录按键值。

编程思路

probe函数中调用 setup_timer(), add_timer()

中断服务程序中不记录按键值,而是修改超时时间mod_timer()

定时器超时函数中记录按键值,唤醒线程,发送信号




也许
10天前

read函数阻塞与非阻塞

读常规文件是不会阻塞的, 从字符设备或网络读则不一定

注意:对于普通文件、块设备文件,O_NONBLOCK不起作用。

注意:对于字符设备文件,O_NONBLOCK起作用的前提是驱动程序针对O_NONBLOCK做了处理。

应用编程可以设置非阻塞read字符设备

1. open函数传入参数: flag |= O_NONBLOCK

    int  fd = open(“/dev/xxx”, O_RDWR | O_NONBLOCK);  /* 非阻塞方式 */

    int  fd = open(“/dev/xxx”, O_RDWR );  /* 阻塞方式 */

2. fcntl()函数修改

    int flags = fcntl(fd, F_GETFL);

    fcntl(fd, F_SETFL, flags | O_NONBLOCK);  /* 非阻塞方式 */

    fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);  /* 阻塞方式 */

驱动程序针对O_NONBLOCK的处理

驱动程序要根据这个标记位决定事件未就绪时是休眠和还是立刻返回。

static ssize_t drv_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos)

    if (queue_empty(&as->queue) && fp->f_flags & O_NONBLOCK)
            return -EAGAIN;

        wait_event_interruptible(apm_waitqueue, !queue_empty(&as->queue));
        ……
}



也许
10天前

异步机制

使用休眠-唤醒、POLL机制时,都需要休眠等待某个事件发生时,它们的差别在于后者可以指定休眠的时长。

同步:主动等待结果,等着结果到来,不去干其他事

异步:被动等待结果,去干其他事

驱动程序怎么通知APP:发信号,这只有3个字,却可以引发很多问题:

    谁发:驱动程序发

    发什么信号:SIGIO

    怎么发:内核里提供有函数

    发给谁:APP,APP要把自己告诉驱动

    APP收到后做什么:执行信号处理函数

    信号处理函数和信号,之间怎么挂钩:APP注册信号处理函数

APP还要做什么事?想想这几个问题:

内核里有那么多驱动,你想让哪一个驱动给你发SIGIO信号?

    APP要打开驱动程序的设备节点。

驱动程序怎么知道要发信号给你而不是别人?

    APP要把自己的进程ID告诉驱动程序。

APP有时候想收到信号,有时候又不想收到信号:

    应该可以把APP的意愿告诉驱动。

驱动程序要做什么?发信号。

    APP设置进程ID时,驱动程序要记录下进程ID;

    APP还要使能驱动程序的异步通知功能,驱动中有对应的函数:

        APP打开驱动程序时,内核会创建对应的file结构体,file中有f_flags;

        f_flags中有一个FASYNC位,它被设置为1时表示使能异步通知功能。

        当f_flags中的FASYNC位发生变化时,驱动程序的fasync函数被调用。

    发生中断时,有数据时,驱动程序调用内核辅助函数发信号。这个辅助函数名为kill_fasync


内核调用流程

异步通知.png

重点从2开始:
2 APP给SIGIO这个信号注册信号处理函数func,以后APP收到SIGIO信号时,这个函数会被自动调用;

3 把APP的PID(进程ID)告诉驱动程序,这个调用不涉及驱动程序,在内核的文件系统层次记录PID;

4 读取驱动程序文件Flag;

5 设置Flag里面的FASYNC位为1:当FASYNC位发生变化时,会导致驱动程序的fasync被调用;

67 调用faync_helper,它会根据FAYSNC的值决定是否设置button_async->fa_file=驱动文件filp:

驱动文件filp结构体里面含有之前设置的PID。

8 APP可以做其他事;

9,10 按下按键,发生中断,驱动程序的中断服务程序被调用,里面调用kill_fasync发信号;

11, 12 , 13 APP收到信号后,它的信号处理函数被自动调用,可以在里面调用read函数读取按键。

异步实现按键中断实现

应用编程:设置标志位

驱动编程:实现fasyn函数

#include <linux/module.h>
#include <linux/poll.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>

struct gpio_key{
    int gpio;
    struct gpio_desc *gpiod;
    int flag;
    int irq;
} ;

static struct gpio_key *gpio_keys_100ask;

/* 主设备号                                                                 */
static int major = 0;
static struct class *gpio_key_class;

/* 环形缓冲区 */
#define BUF_LEN 128
static int g_keys[BUF_LEN];
static int r, w;

struct fasync_struct *button_fasync;

#define NEXT_POS(x) ((x+1) % BUF_LEN)

static int is_key_buf_empty(void)
{
    return (r == w);
}

static int is_key_buf_full(void)
{
    return (r == NEXT_POS(w));
}

static void put_key(int key)
{
    if (!is_key_buf_full())
    {
        g_keys[w] = key;
        w = NEXT_POS(w);
    }
}

static int get_key(void)
{
    int key = 0;
    if (!is_key_buf_empty())
    {
        key = g_keys[r];
        r = NEXT_POS(r);
    }
    return key;
}


static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);

/* 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t gpio_key_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
    //printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    int err;
    int key;

    wait_event_interruptible(gpio_key_wait, !is_key_buf_empty());
    key = get_key();
    err = copy_to_user(buf, &key, 4);

    return 4;
}

static unsigned int gpio_key_drv_poll(struct file *fp, poll_table * wait)
{
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    poll_wait(fp, &gpio_key_wait, wait);
    return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
}

static int gpio_key_drv_fasync(int fd, struct file *file, int on)
{
    if (fasync_helper(fd, file, on, &button_fasync) >= 0)
        return 0;
    else
        return -EIO;
}


/* 定义自己的file_operations结构体                                              */
static struct file_operations gpio_key_drv = {
    .owner   = THIS_MODULE,
    .read    = gpio_key_drv_read,
    .poll    = gpio_key_drv_poll,
    .fasync  = gpio_key_drv_fasync,
};


static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{
    struct gpio_key *gpio_key = dev_id;
    int val;
    int key;

    val = gpiod_get_value(gpio_key->gpiod);


    printk("key %d %d\n", gpio_key->gpio, val);
    key = (gpio_key->gpio << 8) | val;
    put_key(key);
    wake_up_interruptible(&gpio_key_wait);
    kill_fasync(&button_fasync, SIGIO, POLL_IN);

    return IRQ_HANDLED;
}

/* 1. 从platform_device获得GPIO
 * 2. gpio=>irq
 * 3. request_irq
 */
static int gpio_key_probe(struct platform_device *pdev)
{
    int err;
    struct device_node *node = pdev->dev.of_node;
    int count;
    int i;
    enum of_gpio_flags flag;

    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

    count = of_gpio_count(node);
    if (!count)
    {
        printk("%s %s line %d, there isn't any gpio available\n", __FILE__, __FUNCTION__, __LINE__);
        return -1;
    }

    gpio_keys_100ask = kzalloc(sizeof(struct gpio_key) * count, GFP_KERNEL);
    for (i = 0; i < count; i++)
    {
        gpio_keys_100ask[i].gpio = of_get_gpio_flags(node, i, &flag);
        if (gpio_keys_100ask[i].gpio < 0)
        {
            printk("%s %s line %d, of_get_gpio_flags fail\n", __FILE__, __FUNCTION__, __LINE__);
            return -1;
        }
        gpio_keys_100ask[i].gpiod = gpio_to_desc(gpio_keys_100ask[i].gpio);
        gpio_keys_100ask[i].flag = flag & OF_GPIO_ACTIVE_LOW;
        gpio_keys_100ask[i].irq  = gpio_to_irq(gpio_keys_100ask[i].gpio);
    }

    for (i = 0; i < count; i++)
    {
        err = request_irq(gpio_keys_100ask[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "100ask_gpio_key", &gpio_keys_100ask[i]);
    }

    /* 注册file_operations    */
    major = register_chrdev(0, "100ask_gpio_key", &gpio_key_drv);  /* /dev/gpio_key */

    gpio_key_class = class_create(THIS_MODULE, "100ask_gpio_key_class");
    if (IS_ERR(gpio_key_class)) {
        printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
        unregister_chrdev(major, "100ask_gpio_key");
        return PTR_ERR(gpio_key_class);
    }

    device_create(gpio_key_class, NULL, MKDEV(major, 0), NULL, "100ask_gpio_key"); /* /dev/100ask_gpio_key */

    return 0;

}

static int gpio_key_remove(struct platform_device *pdev)
{
    //int err;
    struct device_node *node = pdev->dev.of_node;
    int count;
    int i;

    device_destroy(gpio_key_class, MKDEV(major, 0));
    class_destroy(gpio_key_class);
    unregister_chrdev(major, "100ask_gpio_key");

    count = of_gpio_count(node);
    for (i = 0; i < count; i++)
    {
        free_irq(gpio_keys_100ask[i].irq, &gpio_keys_100ask[i]);
    }
    kfree(gpio_keys_100ask);
    return 0;
}


static const struct of_device_id ask100_keys[] = {
    { .compatible = "100ask,gpio_key" },
    { },
};

/* 1. 定义platform_driver */
static struct platform_driver gpio_keys_driver = {
    .probe      = gpio_key_probe,
    .remove     = gpio_key_remove,
    .driver     = {
        .name   = "100ask_gpio_key",
        .of_match_table = ask100_keys,
    },
};

/* 2. 在入口函数注册platform_driver */
static int __init gpio_key_init(void)
{
    int err;

    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

    err = platform_driver_register(&gpio_keys_driver); 

    return err;
}

/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
 *     卸载platform_driver
 */
static void __exit gpio_key_exit(void)
{
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

    platform_driver_unregister(&gpio_keys_driver);
}


/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(gpio_key_init);
module_exit(gpio_key_exit);

MODULE_LICENSE("GPL");