字元裝置驅動(四)按鍵中斷
目錄
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)
測試
載入驅動
# insmod dri.ko # lsmod Module Size Used by Not tainted dri 2748 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
以前使用測試程式呼叫
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
按鍵測試一下會列印中斷號
# 按鍵按下會列印 # irq55 irq55
可以檢視下中斷次數,這裡因為有按下中斷和彈起中斷,所以中斷次數為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
檢視下程序
ps
,檢視sh
的pid
,檢視其開啟的檔案/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
關閉檔案,可以發現檔案已經關閉,中斷也沒有了
# 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");