1. 程式人生 > >字元裝置驅動(四)按鍵中斷

字元裝置驅動(四)按鍵中斷

目錄


title: 字元裝置驅動(四)按鍵中斷
tags: linux
date: 2018-11-23 17:26:57
toc: true
---

字元裝置驅動(四)按鍵中斷

硬體IO

 *S2      eint0   GPF0
 *S3      eint2   GPF2
 *S4      eint11  GPG3
 *S5      eint19  GPG11

程式設計

中斷配置

配置中斷引腳,配置中斷觸發方式,這是在request_irq中配置的,根據irqflags去呼叫中斷陣列中的chip結構成員進行晶片相關的操作設定desc->chip->set_type

int request_irq(unsigned int irq, irq_handler_t handler,
        unsigned long irqflags, const char *devname, void *dev_id)
    
irq:中斷號
handler:處理函式
irqflags:上升沿觸發,下降沿觸發,邊沿觸發等。指定了快速中斷或中斷共享等中斷處理屬性.
*devname:中斷名字。通常是裝置驅動程式的名稱。改值用在 /proc/interrupt 系統 (虛擬)
檔案上,或核心發生中斷錯誤時使用。
dev_id 可作為共享中斷時的中斷區別引數,也可以用來指定中斷服務函式需要參考的資料地址。也用於解除安裝action
  • 確定中斷號,可以檢視這個函式的呼叫s3c24xx_init_irq很明顯的看到能夠使用的巨集在include/asm-arm/arch/irqs.h中定義

    #define IRQ_EINT0      S3C2410_IRQ(0)     /* 16 */
    #define IRQ_EINT2      S3C2410_IRQ(2)
    #define IRQ_EINT11     S3C2410_IRQ(39)
    #define IRQ_EINT19     S3C2410_IRQ(47)
  • 中斷標誌irqflags,同樣在s3c24xx_init_irq找到相關的set_irq_chip,找到對應的chip

    //中斷0
    set_irq_chip(irqno, &s3c_irq_eint0t4);
    set_irq_handler(irqno, handle_edge_irq);
    set_irq_flags(irqno, IRQF_VALID);
    //中斷4-23
    set_irq_chip(irqno, &s3c_irqext_chip);
    set_irq_handler(irqno, handle_edge_irq);
    set_irq_flags(irqno, IRQF_VALID);
    
    //0~3
    static struct irq_chip s3c_irq_eint0t4 = {
      .name       = "s3c-ext0",
      .ack        = s3c_irq_ack,
      .mask       = s3c_irq_mask,
      .unmask     = s3c_irq_unmask,
      .set_wake   = s3c_irq_wake,
      .set_type   = s3c_irqext_type,
    };
    //4~23
    static struct irq_chip s3c_irqext_chip = {
      .name       = "s3c-ext",
      .mask       = s3c_irqext_mask,
      .unmask     = s3c_irqext_unmask,
      .ack        = s3c_irqext_ack,
      .set_type   = s3c_irqext_type,
      .set_wake   = s3c_irqext_wake
    };

    深入分析下set_type函式,可以找到對應的irqflags使用者s3c_irqext_type中的引數type也就是標誌irqflags,定義在include/asm-arm/irq.h

    #define IRQT_NOEDGE   (0)
    #define IRQT_RISING   (__IRQT_RISEDGE)
    #define IRQT_FALLING  (__IRQT_FALEDGE)
    #define IRQT_BOTHEDGE (__IRQT_RISEDGE|__IRQT_FALEDGE)
    #define IRQT_LOW  (__IRQT_LOWLVL)
    #define IRQT_HIGH (__IRQT_HIGHLVL)
    #define IRQT_PROBE    IRQ_TYPE_PROBE
    
    //s3c_irqext_type
    switch (type)
      {
          case IRQT_NOEDGE:
              printk(KERN_WARNING "No edge setting!\n");
              break;
    
          case IRQT_RISING:
              newvalue = S3C2410_EXTINT_RISEEDGE;
              break;
    
          case IRQT_FALLING:
              newvalue = S3C2410_EXTINT_FALLEDGE;
              break;
    
          case IRQT_BOTHEDGE:
              newvalue = S3C2410_EXTINT_BOTHEDGE;
              break;
    
          case IRQT_LOW:
              newvalue = S3C2410_EXTINT_LOWLEV;
              break;
    
          case IRQT_HIGH:
              newvalue = S3C2410_EXTINT_HILEV;
              break;
    
          default:
              printk(KERN_ERR "No such irq type %d", type);
              return -1;
      }
    

    這裡選擇雙邊觸發IRQT_BOTHEDGE,

  • char *devname中斷名隨便取名

  • dev_id可用作釋放中斷函式中刪除action的標識,這裡可以先寫作1

  • 綜上,程式碼為

    /* 配置GPF0,2為輸入引腳 */
    /* 配置GPG3,11為輸入引腳 */
    request_irq(IRQ_EINT0,  buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
    request_irq(IRQ_EINT2,  buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
    request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);
    request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);

中斷關閉

這裡需要增加釋放刪除action連結串列的函式

int drv_close(struct inode *inode, struct file *file)
{
    free_irq(IRQ_EINT0, &pins_desc[0]);
    free_irq(IRQ_EINT2, &pins_desc[1]);
    free_irq(IRQ_EINT11, &pins_desc[2]);
    free_irq(IRQ_EINT19, &pins_desc[3]);
    return 0;
}
static struct file_operations drv_fops = {
    .owner   =  THIS_MODULE,    /* 這是一個巨集,推向編譯模組時自動建立的__this_module變數 */
    .open    =  drv_open,     
    .read    =  drv_read,      
    .release =  drv_close,     
};

中斷函式

request_irq中會去註冊中斷函式到action連結串列,檢視使用request_irq的地方參考定義形式.比如隨便艘多一個static irqreturn_t aaci_irq(int irq, void *devid)仿照定義,列印下中斷號

static irqreturn_t buttons_irq(int irq, void *dev_id)
{
    printk("irq%d\r\n",irq);
    return IRQ_RETVAL(IRQ_HANDLED);
}

共享中斷號

這裡按鍵不要使用共享中斷號,取用單獨的中斷號區分好處理

#define IRQ_EINT4t7    S3C2410_IRQ(4)       /* 20 */
// 使用下面的
#define IRQ_EINT4      S3C2410_IRQ(32)     /* 48 */
#define IRQ_EINT5      S3C2410_IRQ(33)
#define IRQ_EINT6      S3C2410_IRQ(34)
#define IRQ_EINT7      S3C2410_IRQ(35)
#define IRQ_EINT8      S3C2410_IRQ(36)

測試

  1. 載入驅動

    # insmod dri.ko
    # lsmod
    Module                  Size  Used by    Not tainted
    dri                     2748  2
  2. 驗證驅動安裝,檢視並沒有我們的中斷

    # cat /proc/interrupts
               CPU0
     30:      95434         s3c  S3C2410 Timer Tick
     32:          0         s3c  s3c2410-lcd
     33:          0         s3c  s3c-mci
     34:          0         s3c  I2SSDI
     35:          0         s3c  I2SSDO
     37:         12         s3c  s3c-mci
     42:          0         s3c  ohci_hcd:usb1
     43:          0         s3c  s3c2440-i2c
     51:       1241     s3c-ext  eth0
     60:          0     s3c-ext  s3c-mci
     70:         70   s3c-uart0  s3c2440-uart
     71:        121   s3c-uart0  s3c2440-uart
     79:          0     s3c-adc  s3c2410_action
     80:          0     s3c-adc  s3c2410_action
     83:          0           -  s3c2410-wdt
    Err:          0
  3. 以前使用測試程式呼叫open,這裡可以使用 exec 5</dev/xyz0來開啟裝置,這個意思是開啟這個裝置到5去,接著就可以看到中斷被打開了

    # ls /dev/xyz0
    /dev/xyz0
    
    # exec 5</dev/xyz0
    
    # cat /proc/interrupts
               CPU0
     16:          0    s3c-ext0  S2
     18:          0    s3c-ext0  S3
     55:          0     s3c-ext  S4
     63:          0     s3c-ext  S5
    Err:          0
  4. 按鍵測試一下會列印中斷號

    # 按鍵按下會列印
    # irq55
    irq55
  5. 可以檢視下中斷次數,這裡因為有按下中斷和彈起中斷,所以中斷次數為2

    # cat /proc/interrupts
               CPU0
     16:          0    s3c-ext0  S2
     18:          0    s3c-ext0  S3
     55:          2     s3c-ext  S4
     63:          0     s3c-ext  S5
    Err:          0
  6. 檢視下程序ps,檢視shpid,檢視其開啟的檔案/dev/xyz0

    # ps
      PID  Uid        VSZ Stat Command
       770 0          3096 S   -sh
    
    # ls -l /proc/770/fd
    lrwx------    1 0        0              64 Jan  1 00:07 0 -> /dev/console
    lrwx------    1 0        0              64 Jan  1 00:07 1 -> /dev/console
    lrwx------    1 0        0              64 Jan  1 00:07 10 -> /dev/tty
    lrwx------    1 0        0              64 Jan  1 00:07 2 -> /dev/console
    lr-x------    1 0        0              64 Jan  1 00:07 5 -> /dev/xyz0
    
  7. 關閉檔案,可以發現檔案已經關閉,中斷也沒有了

    #  exec 5<&-
    # ls -l /proc/770/fd
    lrwx------    1 0        0              64 Jan  1 00:07 0 -> /dev/console
    lrwx------    1 0        0              64 Jan  1 00:07 1 -> /dev/console
    lrwx------    1 0        0              64 Jan  1 00:07 10 -> /dev/tty
    lrwx------    1 0        0              64 Jan  1 00:07 2 -> /dev/console
    
    # cat /proc/interrupts
    #無相關中斷了
    

完整的程式

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
//#include <linux/interrupt.h>



volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;
volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat;


static struct class *drv_class;
static struct class_device  *drv_class_dev;

static irqreturn_t buttons_irq(int irq, void *dev_id)
{
    printk("irq%d\r\n",irq);
    return IRQ_HANDLED;
}

static int drv_open(struct inode *inode, struct file *file)
{
    /* 配置GPF0,2為輸入引腳 */
    /* 配置GPG3,11為輸入引腳 */
    request_irq(IRQ_EINT0,  buttons_irq, IRQT_BOTHEDGE, "S2", 1);
    request_irq(IRQ_EINT2,  buttons_irq, IRQT_BOTHEDGE, "S3", 1);
    request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", 1);
    request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", 1);   
    return 0;
}

int drv_close(struct inode *inode, struct file *file)
{
    free_irq(IRQ_EINT0, 1);
    free_irq(IRQ_EINT2, 1);
    free_irq(IRQ_EINT11,1);
    free_irq(IRQ_EINT19,1);
    return 0;
}


static ssize_t drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
    //int minor =  MINOR(file->f_dentry->d_inode->i_rdev);
    //printk("drv_write=%d\n",minor);
    return 0;
}

static ssize_t drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
    return 0;
}


static struct file_operations drv_fops = {
    .owner  =   THIS_MODULE,    /* 這是一個巨集,推向編譯模組時自動建立的__this_module變數 */
    .open   =   drv_open,     
    .write  =   drv_write,
    .read   =   drv_read,    
    .release =  drv_close,  
};

static int major;
static int drv_init(void)
{
    int minor=0;
    major=register_chrdev(0, "drv", &drv_fops); // 註冊, 告訴核心
    drv_class = class_create(THIS_MODULE, "drv");
    drv_class_dev = class_device_create(drv_class, NULL, MKDEV(major, 0), NULL, "xyz%d", minor);

    gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
    gpfdat = gpfcon + 1;
    gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
    gpgdat = gpgcon + 1;
    return 0;
}

static void drv_exit(void)
{
    unregister_chrdev(major, "drv"); // 解除安裝
    class_device_unregister(drv_class_dev);
    class_destroy(drv_class);
    iounmap(gpfcon);
    iounmap(gpgcon);
}

module_init(drv_init);
module_exit(drv_exit);
MODULE_LICENSE("GPL");