《驱动学习

1.查看原理图和芯片手册

 

 

2.驱动程序分析

  2.1 init函数和exit函数,向内核注册file_operations结构体。并且创建设备信息

static struct class *thirddrv_class;
static struct device    *thirddrv_class_dev;

static struct file_operations sencod_drv_fops = {
    .owner   =  THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open    =  third_drv_open,     
    .read     =    third_drv_read,       
    .release =  third_drv_close,       
};


int major;
static int third_drv_init(void)
{
    major = register_chrdev(0, "third_drv", &sencod_drv_fops);

    thirddrv_class = class_create(THIS_MODULE, "third_drv");

    thirddrv_class_dev = device_create(thirddrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */
    return 0;
}

static void third_drv_exit(void)
{
    unregister_chrdev(major, "third_drv");
    device_unregister(thirddrv_class_dev);
    class_destroy(thirddrv_class);
    return 0;
}


module_init(third_drv_init);

module_exit(third_drv_exit);

MODULE_LICENSE("GPL");

  

  2.2 编写open函数

struct pin_desc{
    unsigned int pin;
    unsigned int key_val;
};

/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
static unsigned char key_val;

/*
 * K1,K2,K3,K4对应GPG0,GPG3,GPG5,GPG6
 */

struct pin_desc pins_desc[4] = {
    {S3C2410_GPG(0), 0x01},
    {S3C2410_GPG(3), 0x02},
    {S3C2410_GPG(5), 0x03},
    {S3C2410_GPG(6), 0x04},
};

static int third_drv_open(struct inode *inode, struct file *file)
{
    /* GPG0,GPG3,GPG5,GPG6为中断引脚: EINT8,EINT11,EINT13,EINT14 */
    request_irq(IRQ_EINT8,  buttons_irq, IRQ_TYPE_EDGE_BOTH, "K1", &pins_desc[0]);
    request_irq(IRQ_EINT11, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K2", &pins_desc[1]);
    request_irq(IRQ_EINT13, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K3", &pins_desc[2]);
    request_irq(IRQ_EINT14, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K4", &pins_desc[3]);    

    return 0;
}

  request_irq函数用于向内核申请中断。

  第一个参数:要注册中断服务函数的中断号

  第二个参数:中断服务函数

  第三个参数:触发中断的参数,比如边沿触发。这里是双边沿触发,定义在include/linux/irq.h

  第四个参数:中断程序的名字,使用cat /proc/interrupt 可以查看中断程序名字

  第五个参数:传入中断处理程序的参数,注册共享中断时不能为NULL,因为卸载时需要这个做参数,避免卸载其它中断服务函数

  2.3 close函数

int third_drv_close(struct inode *inode, struct file *file)
{
    free_irq(IRQ_EINT8,  &pins_desc[0]);
    free_irq(IRQ_EINT11, &pins_desc[1]);
    free_irq(IRQ_EINT13, &pins_desc[2]);
    free_irq(IRQ_EINT14, &pins_desc[3]);
    return 0;
}

  free_irq函数:释放分配给已定中断的内存

  第一个参数:要卸载的中断号

  第二个参数:这个是要卸载的中断action下的哪个服务函数

  2.4 read函数

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

/* 中断事件标志, 中断服务程序将它置1,third_drv_read将它清0 */
static volatile int ev_press = 0;

ssize_t third_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
    if (size != 1)
        return -EINVAL;

    /* 如果没有按键动作, 休眠 */
    wait_event_interruptible(button_waitq, ev_press);

    /* 如果有按键动作, 返回键值 */
    copy_to_user(buf, &key_val, 1);
    ev_press = 0;
    
    return 1;
}

  DECLARE_WAIT_QUEUE_HEAD是一个宏定义,来定义一个

  struct __wait_queue_head {
     spinlock_t lock;
     struct list_head task_list;
  };

  等待队列头的结构体。后面用于进程的休眠和唤醒。

  wait_event_interruptible(button_waitq, ev_press)函数:当第二个参数为0时,就会使应用程序进入休眠。

  2.5 中断服务函数

static irqreturn_t buttons_irq(int irq, void *dev_id)
{
    struct pin_desc * pindesc = (struct pin_desc *)dev_id;
    unsigned int pinval;
    
    pinval = s3c2410_gpio_getpin(pindesc->pin);

    if (pinval)
    {
        /* 松开 */
        key_val = 0x80 | pindesc->key_val;
    }
    else
    {
        /* 按下 */
        key_val = pindesc->key_val;
    }

    ev_press = 1;                  /* 表示中断发生了 */
    wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */

    
    return IRQ_RETVAL(IRQ_HANDLED);
}

  如果是由IRQ_EXIT8触发中断,第一个参数irq就是IRQ_EXIT8,第二个参数就是pins_desc[0]的地址 

  当触发中断时,就会进入中断服务函数。然后将ev_press设置为1,然后唤醒应用程序。这样就又会进入read函数,然后将数据发送给应用程序,再将ev_press设置为0,这样应用程序就又休眠。

原文地址:https://www.cnblogs.com/zhuangquan/p/11623591.html