韦东山第12课-字符设备、中断方式查询驱动

1 裸板中断方式查询按键

(详情看嵌入式linux应用开发完全手册 P143)

源码见:百问网JZ2440v2主光盘\hardware\int

疑问点:期间有很多基础器没有设置说是用的默认配置,但是技术手册中并没有看到默认设置。

比如说你要设置EINT0为中断触发功能,在技术手册中,将设计到EINT0的寄存器设置一遍,内容看一遍就会了。

中断模式图:


最主要代码:

//head.S
@******************************************************************************
@ File:head.S
@ 功能:初始化,设置中断模式、管理模式的栈,设置好中断处理函数
@******************************************************************************       
   
.extern     main
.text 
.global _start 
_start:
@******************************************************************************       
@ 中断向量,本程序中,除Reset和HandleIRQ外,其它异常都没有使用
@******************************************************************************       
    b   Reset

@ 0x04:   炊ㄒ逯噶钪兄鼓J降南蛄康
HandleUndef:
    b   HandleUndef 
 
@ 0x08: 管理模式的向量地址,通过SWI指令进入此模式
HandleSWI:
    b   HandleSWI

@ 0x0c: 指令预取终止导致的异常的向量地址
HandlePrefetchAbort:
    b   HandlePrefetchAbort

@ 0x10: 数据访问终止导致的异常的向量地址
HandleDataAbort:
    b   HandleDataAbort

@ 0x14: 保留
HandleNotUsed:
    b   HandleNotUsed

@ 0x18: 中断模式的向量地址
    b   HandleIRQ

@ 0x1c: 快中断模式的向量地址
HandleFIQ:
    b   HandleFIQ

Reset:                  
    ldr sp, =4096           @ 设置栈指针,以下都是C函数,调用前需要设好栈
    bl  disable_watch_dog   @ 关闭WATCHDOG,否则CPU会不断重启
    
    msr cpsr_c, #0xd2       @ 进入中断模式
    ldr sp, =3072           @ 设置中断模式栈指针

    msr cpsr_c, #0xd3       @ 进入管理模式
    ldr sp, =4096           @ 设置管理模式栈指针,
                            @ 其实复位之后,CPU就处于管理模式,
                            @ 前面的“ldr sp, =4096”完成同样的功能,此句可省略

    bl  init_led            @ 始化LED的GPIO管脚
    bl  init_irq            @ 用中断初始化函数,在init.c中
    msr cpsr_c, #0x5f       @ 置I-bit=0,开IRQ中断
    
    ldr lr, =halt_loop      @ 置返回地址
    ldr pc, =main           @ 用main函数
halt_loop:
    b   halt_loop

HandleIRQ:
    sub lr, lr, #4                  @ 计算返回地址
    stmdb   sp!,    { r0-r12,lr }   @ 保存使用到的寄存器
                                    @ 注意,此时的sp是中断模式的sp
                                    @ 初始值是上面设置的3072
    
    ldr lr, =int_return             @ 设置调用ISR即EINT_Handle函数后的返回地址  
    ldr pc, =EINT_Handle            @ 调用中断服务函数,在interrupt.c中
int_return:
    ldmia   sp!,    { r0-r12,pc }^  @ 中断返回, ^表示将spsr的值复制到cpsr
    


// init.c
/*
 * init.c: 进行一些初始化
 */ 

#include "s3c24xx.h"

/*
 * LED1,LED2,LED4对应GPF4、GPF5、GPF6
 */
#define	GPF4_out	(1<<(4*2))
#define	GPF5_out	(1<<(5*2))
#define	GPF6_out	(1<<(6*2))

#define	GPF4_msk	(3<<(4*2))
#define	GPF5_msk	(3<<(5*2))
#define	GPF6_msk	(3<<(6*2))

/*
 * S2,S3,S4对应GPF0、GPF2、GPG3
 */
#define GPF0_eint     (0x2<<(0*2))
#define GPF2_eint     (0x2<<(2*2))
#define GPG3_eint     (0x2<<(3*2))

#define GPF0_msk    (3<<(0*2))
#define GPF2_msk    (3<<(2*2))
#define GPG3_msk    (3<<(3*2))

/*
 * 关闭WATCHDOG,否则CPU会不断重启
 */
void disable_watch_dog(void)
{
    WTCON = 0;  // 关闭WATCHDOG很简单,往这个寄存器写0即可
}

void init_led(void)
{
    // LED1,LED2,LED4对应的3根引脚设为输出
    GPFCON &= ~(GPF4_msk | GPF5_msk | GPF6_msk);
    GPFCON |= GPF4_out | GPF5_out | GPF6_out;
}

/*
 * 初始化GPIO引脚为外部中断
 * GPIO引脚用作外部中断时,默认为低电平触发、IRQ方式(不用设置INTMOD)
 */ 
void init_irq( )
{
    // S2,S3对应的2根引脚设为中断引脚 EINT0,ENT2
    GPFCON &= ~(GPF0_msk | GPF2_msk);
    GPFCON |= GPF0_eint | GPF2_eint;

    // S4对应的引脚设为中断引脚EINT11
    GPGCON &= ~GPG3_msk;
    GPGCON |= GPG3_eint;
    
    // 对于EINT11,需要在EINTMASK寄存器中使能它
    EINTMASK &= ~(1<<11);
        
    /*
     * 设定优先级:
     * ARB_SEL0 = 00b, ARB_MODE0 = 0: REQ1 > REQ3,即EINT0 > EINT2
     * 仲裁器1、6无需设置
     * 最终:
     * EINT0 > EINT2 > EINT11即K2 > K3 > K4
     */
    PRIORITY = (PRIORITY & ((~0x01) | (0x3<<7))) | (0x0 << 7) ;

    // EINT0、EINT2、EINT8_23使能
    INTMSK   &= (~(1<<0)) & (~(1<<2)) & (~(1<<5));
}
//interrupt.c
#include "s3c24xx.h"

void EINT_Handle()
{
    unsigned long oft = INTOFFSET;
    unsigned long val;
    
    switch( oft )
    {
        // S2被按下
        case 0: 
        {   
            GPFDAT |= (0x7<<4);   // 所有LED熄灭
            GPFDAT &= ~(1<<4);      // LED1点亮
            break;
        }
        
        // S3被按下
        case 2:
        {   
            GPFDAT |= (0x7<<4);   // 所有LED熄灭
            GPFDAT &= ~(1<<5);      // LED2点亮
            break;
        }

        // K4被按下
        case 5:
        {   
            GPFDAT |= (0x7<<4);   // 所有LED熄灭
            GPFDAT &= ~(1<<6);      // LED4点亮                
            break;
        }

        default:
            break;
    }

    //清中断
    if( oft == 5 ) 
        EINTPEND = (1<<11);   // EINT8_23合用IRQ5
    SRCPND = 1<<oft;
    INTPND = 1<<oft;
}

2 字符驱动设备 - 按键中断查询

2.1 中断处理体系结构

     每一个中断用一个irq_desc结构体表示:其中irq_desc.irq_chip结构用于操作底层硬件,屏蔽、使能、清除中断等;irq_desc.irqaction 结构用来注册中断处理函数



     可见,中断体系结构的初始化即使构造这些数据结构,比如:irq_desc数组项中的handle_irq、chip等成员;用户注册中断时就是构造action链表;用户卸载中断是就是从action链表中除去不需要的项。
struct irq_desc {
unsigned int           irq;    //中断号 
irq_flow_handler_t  handle_irq;   //系统中断处理的入口函数  
struct irq_chip*chip;   // 对应的irq_chip结构,定义了与中断处理有关的函数,详细讲解如下
struct msi_desc *msi_desc;
void *handler_data;
void *chip_data;
struct irqaction *action; /* IRQ action list */  //用户的中断处理函数,调用r equ est_irq时添加  
unsigned int status; /* IRQ status */


unsigned int depth; /* nested irq disables */
unsigned int wake_depth; /* nested wake enables */
unsigned int irq_count; /* For detecting broken IRQs */
unsigned int irqs_unhandled;
spinlock_t lock;
#ifdef CONFIG_SMP
cpumask_t affinity;
unsigned int cpu;
#endif
#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)
cpumask_t pending_mask;
#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
const char *name;
} ____cacheline_internodealigned_in_smp;
 
struct irq_chip {
	const char	*name;
	unsigned int	(*startup)(unsigned int irq);
	void		(*shutdown)(unsigned int irq);
	void		(*enable)(unsigned int irq);
	void		(*disable)(unsigned int irq);

	void		(*ack)(unsigned int irq);
	void		(*mask)(unsigned int irq);
	void		(*mask_ack)(unsigned int irq);
	void		(*unmask)(unsigned int irq);
	void		(*eoi)(unsigned int irq);

	void		(*end)(unsigned int irq);
	void		(*set_affinity)(unsigned int irq, cpumask_t dest);
	int		(*retrigger)(unsigned int irq);
	int		(*set_type)(unsigned int irq, unsigned int flow_type);
	int		(*set_wake)(unsigned int irq, unsigned int on);

	/* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHOD
	void		(*release)(unsigned int irq, void *dev_id);
#endif
	/*
	 * For compatibility, ->typename is copied into ->name.
	 * Will disappear.
	 */
	const char	*typename;
};



2.2 用户注册中断处理函数的过程、

     用户用request_irq注册中断函数:
     request_irq(IRQ_EINT0,  buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
     request_irq函数调用setup_irq函数
     setup_irq()函数完成三个功能:
    1,将新建的irqaction结构联入irq_desc[irq]结构的action链表中;
    2,设置irq_desc[irq]结构中chip成员的还没有设置的指针,让它们指向一些默认设置;
    3,设置中断触发方式;
    4,启动中断(中断使能的意思)


    int request_irq(unsigned int irq, irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id)
               action->handler = handler;
action->flags = irqflags;
cpus_clear(action->mask);
action->name = devname;
action->next = NULL;
action->dev_id = dev_id;
                retval = setup_irq(irq, action);

2.3 中断处理过程

P404 看书很无聊 但是书上讲的还是清楚。 s3c24xx_init_irq()设置了中断的初始化函数,irq_desc.irq_chip 设置了有关寄存器的设置。
    asm_do_IRQ是中断的C语言的入口函数
asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
    asm_do_IRQ函数调用desc_handle_irq(irq, desc)函数, desc_handle_irq函数直接调用desc结构中的handle_irq成员函数,他就是irq_desc[irq].handle_irq.

函数调用连接过程:
asm_do_IRQ
       desc_handle_irq( irq, desc)
               struct irq_desc *desc = irq_desc + irq;  
              desc->handle_irq(irq, desc);              // irq=16+5,由s3c24xx_init_irq()知set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);
                                                                         //所以调用s3c_irq_demux_extint8
                                                                        //所以不同中断号的 .handle_irq()是不一样的及__set_irq_handler()不一样
                                                                        //级联中断用set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);
                                                                         //IRQ_EINT8t23=S3C2410_IRQ(5);
                                                                        //irq=S3C2410_IRQ(5) 有 set_irq_chained_handler(S3C2410_IRQ(5), s3c_irq_demux_extint8);
                                                                        
               s3c_irq_demux_extint8
                   irq += (IRQ_EINT4 - 4);                         //irq=44
        desc_handle_irq(irq, irq_desc + irq);    //
                                  desc->handle_irq(irq, desc);   // 因为irq=44由s3c24xx_init_irq()知set_irq_handler(irqno, handle_edge_irq);
                                                                                   //所以调用handle_edge_irq
                                   handle_edge_irq
      desc->chip->ack(irq);  //在init_IRQ中被设置为s3c_irqext_chip
      action_ret = handle_IRQ_event(irq, action);  //该函数来逐个执行action链表中用户注册的中断处理函数;
        do {
      ret = action->handler(irq, action->dev_id);  //执行用户注册的中断处理函数
      if (ret == IRQ_HANDLED)
           status |= action->flags;
       retval |= ret;
               action = action->next;           //下一个
            } while (action);

在工程中搜索__set_irq_handler函数,发现中断处理函数是被定义在 arch/arm/plat-s3c24xx/irq.c/s3c24xx_init_irq()中:
。。。
        /* setup the cascade irq handlers */ //设置各个irq的ird_desc[irq].handle_irq()函数如:ird_desc[irq].handle_irq()=s3c_irq_demux_extint8();
set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7);
set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);
set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0);
set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1);
set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2);
set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc);
/* external interrupts */


for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
irqdbf("registering irq %d (ext int)\n", irqno);
set_irq_chip(irqno, &s3c_irq_eint0t4);
set_irq_handler(irqno, handle_edge_irq);
set_irq_flags(irqno, IRQF_VALID);
}
for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
irqdbf("registering irq %d (extended s3c irq)\n", irqno);
set_irq_chip(irqno, &s3c_irqext_chip);
set_irq_handler(irqno, handle_edge_irq);
set_irq_flags(irqno, IRQF_VALID);
}

注: 从以上代码中可以看出,注册中断主要是注册中断服务程序入口。Linux中将所有的中断号用一个stuctirq_desc数据结构进行统一管理,每个中断号或者一组中断号(如级联的中断),对应一个structirq_desc, 所以根据相应的中断号,可以获取对应的中断描述结构irq_desc:
。。。
    所以 s3c24xx_init_irq函数用来构造irq_desc结构的各项。

例如:s3c_irq_demux_extint8()函数:




       IRQ_ENINT8--IRQ_EINT23这几个中断的处理函数入口,在init_IRQ函数初始化中断体系结构的时候已经被设置为handle_edge_ipq函数。上面代码的

第575行的代码就是调用这个函数(?)。

handle_edge_irq()

      desc->chip->ack(irq);  //在init_IRQ中被设置为s3c_irqext_chip
      action_ret = handle_IRQ_event(irq, action);  //该函数来逐个执行action链表中用户注册的中断处理函数;
        do {
      ret = action->handler(irq, action->dev_id);  //执行用户注册的中断处理函数
      if (ret == IRQ_HANDLED)
           status |= action->flags;
       retval |= ret;
               action = action->next;           //下一个
            } while (action);

和裸板相似的寄存器设置在 struct irq_chip *chip; 中
附录:
s3c24xx_init_irq源码:
void __init s3c24xx_init_irq(void)
{
	unsigned long pend;
	unsigned long last;
	int irqno;
	int i;

	irqdbf("s3c2410_init_irq: clearing interrupt status flags\n");

	/* first, clear all interrupts pending... */

	last = 0;
	for (i = 0; i < 4; i++) {
		pend = __raw_readl(S3C24XX_EINTPEND);

		if (pend == 0 || pend == last)
			break;

		__raw_writel(pend, S3C24XX_EINTPEND);
		printk("irq: clearing pending ext status %08x\n", (int)pend);
		last = pend;
	}

	last = 0;
	for (i = 0; i < 4; i++) {
		pend = __raw_readl(S3C2410_INTPND);

		if (pend == 0 || pend == last)
			break;

		__raw_writel(pend, S3C2410_SRCPND);
		__raw_writel(pend, S3C2410_INTPND);
		printk("irq: clearing pending status %08x\n", (int)pend);
		last = pend;
	}

	last = 0;
	for (i = 0; i < 4; i++) {
		pend = __raw_readl(S3C2410_SUBSRCPND);

		if (pend == 0 || pend == last)
			break;

		printk("irq: clearing subpending status %08x\n", (int)pend);
		__raw_writel(pend, S3C2410_SUBSRCPND);
		last = pend;
	}

	/* register the main interrupts */

	irqdbf("s3c2410_init_irq: registering s3c2410 interrupt handlers\n");

	for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) {
		/* set all the s3c2410 internal irqs */

		switch (irqno) {
			/* deal with the special IRQs (cascaded) */
            /* 这几个中断是级联的,所以中断例程也不一样.*/

		case IRQ_EINT4t7:
		case IRQ_EINT8t23:
		case IRQ_UART0:
		case IRQ_UART1:
		case IRQ_UART2:
		case IRQ_ADCPARENT:
			set_irq_chip(irqno, &s3c_irq_level_chip);
			set_irq_handler(irqno, handle_level_irq);  /*这个中断例程在后面会被重新设置*/
			break;

		case IRQ_RESERVED6:
		case IRQ_RESERVED24:
			/* no IRQ here */
			break;

		default:
			//irqdbf("registering irq %d (s3c irq)\n", irqno);
			set_irq_chip(irqno, &s3c_irq_chip);
			set_irq_handler(irqno, handle_edge_irq); /*对于不是级联的中断,这个就是中断例程.*/
			set_irq_flags(irqno, IRQF_VALID);
		}
	}
    /* 重新设置级联的中断例程*/

	/* setup the cascade irq handlers */
	set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7);
	set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);

	set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0);
	set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1);
	set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2);
	set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc);
	/* external interrupts */
    /* 为外部中断设置中断例程*/

	for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
		irqdbf("registering irq %d (ext int)\n", irqno);
		set_irq_chip(irqno, &s3c_irq_eint0t4);
		set_irq_handler(irqno, handle_edge_irq);
		set_irq_flags(irqno, IRQF_VALID);
	}

	for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
		irqdbf("registering irq %d (extended s3c irq)\n", irqno);
		set_irq_chip(irqno, &s3c_irqext_chip);
		set_irq_handler(irqno, handle_edge_irq);
		set_irq_flags(irqno, IRQF_VALID);
	}

	/* register the uart interrupts */
    /* 为UART设置中断例程,*/

	irqdbf("s3c2410: registering external interrupts\n");

	for (irqno = IRQ_S3CUART_RX0; irqno <= IRQ_S3CUART_ERR0; irqno++) {
		irqdbf("registering irq %d (s3c uart0 irq)\n", irqno);
		set_irq_chip(irqno, &s3c_irq_uart0);
		set_irq_handler(irqno, handle_level_irq);
		set_irq_flags(irqno, IRQF_VALID);
	}

	for (irqno = IRQ_S3CUART_RX1; irqno <= IRQ_S3CUART_ERR1; irqno++) {
		irqdbf("registering irq %d (s3c uart1 irq)\n", irqno);
		set_irq_chip(irqno, &s3c_irq_uart1);
		set_irq_handler(irqno, handle_level_irq);
		set_irq_flags(irqno, IRQF_VALID);
	}

	for (irqno = IRQ_S3CUART_RX2; irqno <= IRQ_S3CUART_ERR2; irqno++) {
		irqdbf("registering irq %d (s3c uart2 irq)\n", irqno);
		set_irq_chip(irqno, &s3c_irq_uart2);
		set_irq_handler(irqno, handle_level_irq);
		set_irq_flags(irqno, IRQF_VALID);
	}

	for (irqno = IRQ_TC; irqno <= IRQ_ADC; irqno++) {
		irqdbf("registering irq %d (s3c adc irq)\n", irqno);
		set_irq_chip(irqno, &s3c_irq_adc);
		set_irq_handler(irqno, handle_edge_irq);
		set_irq_flags(irqno, IRQF_VALID);
	}

	irqdbf("s3c2410: registered interrupt handlers\n");
}


3 字符驱动按键中断查询源码

// third_drv.c

#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>


static struct class *thirddrv_class;
static struct class_device	*thirddrv_class_dev;

volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;

volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat;


static DECLARE_WAIT_QUEUE_HEAD(button_waitq);  //生成一个等待队列头wait_queue_head_t,名字为button_waitq

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

//作为设备id  及 dev_id,作为request_irq函数 的参数void *dev_id
struct pin_desc{
	unsigned int pin;
	unsigned int key_val;
};


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

struct pin_desc pins_desc[4] = {
	{S3C2410_GPF0, 0x01},
	{S3C2410_GPF2, 0x02},
	{S3C2410_GPG3, 0x03},
	{S3C2410_GPG11, 0x04},
};


/*
  * 确定按键值
  */
  //中断处理函数第一个被触发的中断的ID,第二个被触发的设备id及dev_id,
  //dev_id 在request_irq中输入
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); 
/*
s3c2410_gpio_getpin()的返回值是GPxDAT寄存器的值与所要读取的 
GPIO对应的bit mask相与以后的值,0表示该GPIO对应的bit为0, 
非0表示该bit为1,所以s3c2410_gpio_getpin(S3C2410_GPG(9))如果GPG9为   
低电平则返回的是0,如果是高电平则返回的是GPxDAT中的
GPG9对应位的值为0x0100而不是0x0001,查处问题后修改也很
简单了。
*/
	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);
}

static int third_drv_open(struct inode *inode, struct file *file)
{
	/* 配置GPF0,2为输入引脚 */
	/* 配置GPG3,11为输入引脚 */
    //int request_irq(unsigned int irq, irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id)
     //     中断号,中断入口函数,中断触发方式,申请的中断名用cat /proc/interrupts命令查看,自定义的设备id
    //dev_id 随意给可以是1 或0 等,韦东山给了结构体

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

	return 0;
}

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

	/* 如果没有按键动作,ev_press=0 休眠 */
    /*休眠 将进程放入button_waitq等待队列中*/
      /*唤醒休眠ake_up_interruptible(&button_waitq);  */
	wait_event_interruptible(button_waitq, ev_press);

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


int third_drv_close(struct inode *inode, struct file *file)
{
    //注销申请的中断,执行后调用cat /proc/interrupts命令将看不到中断名
	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 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 = class_device_create(thirddrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */

	gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
	gpfdat = gpfcon + 1;

	gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
	gpgdat = gpgcon + 1;

	return 0;
}

static void third_drv_exit(void)
{
	unregister_chrdev(major, "third_drv");
	class_device_unregister(thirddrv_class_dev);
	class_destroy(thirddrv_class);
	iounmap(gpfcon);
	iounmap(gpgcon);
	return 0;
}


module_init(third_drv_init);

module_exit(third_drv_exit);

MODULE_LICENSE("GPL");



//thirddrvtest.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

/* thirddrvtest 
  */
int main(int argc, char **argv)
{
	int fd;
	unsigned char key_val;
	
	fd = open("/dev/buttons", O_RDWR);
	if (fd < 0)
	{
		printf("can't open!\n");
	}

	while (1)
	{
		read(fd, &key_val, 1);
		printf("key_val = 0x%x\n", key_val);
	}
	
	return 0;
}





lsmod:查看驱动

打开中断就会注册中断:

cat /proc/interrupts 查看中断

打开设备 exec 5</dev/buttons  //打开这个设备放入5

cat /proc/interrupts //看到有中断s2 s3 s4 s5

ps: 查看当前进程是771 -sh

ls -l /proc/771/fd  //查看 发现5指向 /dev/buttons

关闭设备:exec 5<&-

注销驱动 :rmmod third_drv


ps;

kill -9 787

lsmod

rmmod third_drv

./thirddrvtest & 在后台运行程序

ps //查看 在slip状态

cat /proc/interrupts

top //查看占用cpu资源






lsmod:查看驱动

打开中断就会注册中断:

cat /proc/interrupts 查看中断

打开设备 exec 5</dev/buttons  //打开这个设备放入5

cat /proc/interrupts //看到有中断s2 s3 s4 s5

ps: 查看当前进程是771 -sh

ls -l /proc/771/fd  //查看 发现5指向 /dev/buttons

关闭设备:exec 5<&-

注销驱动 :rmmod third_drv


ps;

kill -9 787

lsmod

rmmod third_drv

./thirddrvtest & 在后台运行程序

ps //查看 在slip状态

cat /proc/interrupts

top //查看占用cpu资源

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值