Chinaunix首页 | 论坛 | 博客
  • 博客访问: 17195
  • 博文数量: 6
  • 博客积分: 1481
  • 博客等级: 上尉
  • 技术积分: 55
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-31 00:24
文章分类
文章存档

2010年(6)

我的朋友
最近访客

分类:

2010-06-22 19:17:53

初始问题:  键盘中断是怎么设置的??
 

/*
 * Keyboard driver
 * Copyright (c) 2001,2004 David H. Hovemeyer
 * $Revision: 1.14 $
 *
 * This is free software. You are permitted to use,
 * redistribute, and modify it as specified in the file "COPYING".
 */


....

....

/*
 * Handler for keyboard interrupts.
 */

static void Keyboard_Interrupt_Handler(struct Interrupt_State* state)
{
    uchar_t status, scanCode;
    unsigned flag = 0;
    bool release = false, shift;
    Keycode keycode;

    Begin_IRQ(state);

    status = In_Byte(KB_CMD);
    IO_Delay();

    if ((status & KB_OUTPUT_FULL) != 0) {
    /* There is a byte available */
    scanCode = In_Byte(KB_DATA);
    IO_Delay();
/*
 *    Print("code=%x%s\n", scanCode, (scanCode&0x80) ? " [release]" : "");
 */


    if (scanCode & KB_KEY_RELEASE) {
     release = true;
     scanCode &= ~(KB_KEY_RELEASE);
    }

    if (scanCode >= SCAN_TABLE_SIZE) {
     Print("Unknown scan code: %x\n", scanCode);
     goto done;
    }

    /* Process the key */
    shift = ((s_shiftState & SHIFT_MASK) != 0);
    keycode = shift ? s_scanTableWithShift[scanCode] : s_scanTableNoShift[scanCode];

    /* Update shift, control and alt state */
    switch (keycode) {
    case KEY_LSHIFT:
     flag = LEFT_SHIFT;
     break;
    case KEY_RSHIFT:
     flag = RIGHT_SHIFT;
     break;
    case KEY_LCTRL:
     flag = LEFT_CTRL;
     break;
    case KEY_RCTRL:
     flag = RIGHT_CTRL;
     break;
    case KEY_LALT:
     flag = LEFT_ALT;
     break;
    case KEY_RALT:
     flag = RIGHT_ALT;
     break;
    default:
     goto noflagchange;
    }

    if (release)
     s_shiftState &= ~(flag);
    else
     s_shiftState |= flag;
            
    
/*
     * Shift, control and alt keys don't have to be
     * queued, flags will be set!
     */

    goto done;

noflagchange:
    /* Format the new keycode */
    if (shift)
     keycode |= KEY_SHIFT_FLAG;
    if ((s_shiftState & CTRL_MASK) != 0)
     keycode |= KEY_CTRL_FLAG;
    if ((s_shiftState & ALT_MASK) != 0)
     keycode |= KEY_ALT_FLAG;
    if (release)
     keycode |= KEY_RELEASE_FLAG;
        
    /* Put the keycode in the buffer */
    Enqueue_Keycode(keycode);

    /* Wake up event consumers */
    Wake_Up(&s_waitQueue);

    
/*
     * Pick a new thread upon return from interrupt
     * (hopefully the one waiting for the keyboard event)
     */

    g_needReschedule = true;
    }

done:
    End_IRQ(state);
}


/* ----------------------------------------------------------------------
 * Public functions
 * ---------------------------------------------------------------------- */


void Init_Keyboard(void)
{
    ushort_t irqMask;

    Print("Initializing keyboard...\n");

    /* Start out with no shift keys enabled. */
    s_shiftState = 0;

    /* Buffer is initially empty. */
    s_queueHead = s_queueTail = 0;

    /* Install interrupt handler */
    Install_IRQ(KB_IRQ, Keyboard_Interrupt_Handler);

    /* Enable IRQ1 (keyboard) */
    irqMask = Get_IRQ_Mask();
    irqMask &= ~(1 << KB_IRQ);
    Set_IRQ_Mask(irqMask);
}

1.中断产生过程 

(1)如果IR引脚上有信号,会使中断请求寄存器(Interrupt Request Register,IRR)相应的位置位,比如图中, IR3, IR4, IR5上有信号,那么IRR的3,4,5为1 

(2)如果这些IRR中有一个是允许的,也就是没有被屏蔽,那么就会通过INT向CPU发出中断请求信号。屏蔽是由中断屏蔽寄存器(Interrupt Mask Register,IMR)来控制的,比如图中位3被置1,也就是IRR位3的信号被屏蔽了。在图中,还有4,5的信号没有被屏蔽,所以,会向CPU发出请求信号。 

(3)如果CPU处于开中断状态,那么在执行指令的最后一个周期,在INTA上做出回应,并且关中断 

(4)8259A收到回应后,将中断服务寄存器(In-Service Register)置位,而将相应的IRR复位: 
8259芯片会比较IRR中的中断的优先级,如上图中,由于IMR中位3处于屏蔽状态,所以实际上只是比较IR4,I5,缺省情况下,IR0最高,依次往下,IR7最低(这种优先级可以被设置),所以上图中,ISR被设置为4. 
相应的,IRR中位4被复位(我不太清楚,复位是清0,在未来某个时刻检测IR4上是否又有中断,还是在复位的同时检测IR4上是否又产生了中断) 

(5)在CPU发出下一个INTA信号时,8259将中断号送到数据线上,从而能被CPU接收到,这里有个问题:比如在上图中,8259获得的是数4,但是CPU需要的是中断号(并不为4),从而可以到idt找相应的向量。所以有一个从ISR的信号到中断号的转换。在Linux的设置中,4对应的中断号是0x24. 

(6)如果8259处于自动结束中断(Automatic End of Interrupt AEOI)状态,那么在刚才那个INTA信号结束前,8259的ISR复位(也就是清0),如果不处于这个状态,那么直到CPU发出EOI指令,它才会使得ISR复位。 


2.一些相关专题 

(1)从8259:在x86单CPU的机器上采用两个8259芯片,主芯片如上图所示,x86模式规定,从8259将它的INT脚与主8259的IR2相连,这样,如果从8259芯片的引脚IR8-IR15上有中断,那么会在INT上产生信号,主8259在IR2上产生了一个硬件信号,当它如上面的步骤处理后将IR2的中断传送给CPU,收到应答后,会通过CAS通知从8259芯片,从8259芯片将IRQ中断号送到数据线上,从而被CPU接收。 
由此,我猜测它产生的所有中断在主8259上优先级为2,不知道对不对。 

(2)关于屏蔽 

从上面可以看出,屏蔽有两种方法,一种作用于CPU, 通过清除IF标记,使得CPU不去响应8259在INT上的请求。也就是所谓关中断。 
另一种方法是,作用于8259,通过给它指令设置IMR,使得相应的IRR不参与ISR(见上面的(4)),被称为禁止(disable),反之,被称为允许(enable). 
每次设置IMR只需要对端口0x21(主)或0xA1(从)输出一个字节即可,字节每位对应于IMR每位,例如: 

outb(cached_21,0x21); 

为了统一处理16个中断,Linux用一个16位cached_irq_mask变量来记录这16个中断的屏蔽情况: 

static unsigned int cached_irq_mask = 0xffff; 

为了分别对应于主从芯片的8位IMR,将这16位cached_irq_mask分成两个8位的变量: 

#define __byte(x,y) (((unsigned char *)&(y))[x]) 
#define cached_21 (__byte(0,cached_irq_mask)) 
#define cached_A1 (__byte(1,cached_irq_mask)) 

在禁用某个irq的时候,调用下面的函数: 

void disable_8259A_irq(unsigned int irq) 

unsigned int mask = 1 << irq; 
unsigned long flags; 

spin_lock_irqsave(&i8259A_lock, flags); 
cached_irq_mask |= mask;/*-- 对这16位变量设置 */ 
if (irq & 8)/*-- 看是对主8259设置还是对从8259芯片设置 */ 
outb(cached_A1,0xA1);/*-- 对从8259芯片设置 */ 
else 
outb(cached_21,0x21);/*-- 对主8259芯片设置 */ 
spin_unlock_irqrestore(&i8259A_lock, flags); 

(3)关于中断号的输出 

8259在ISR里保存的只是irq的ID,但是它告诉CPU的是中断向量ID,比如ISR保存时钟中断的ID 0,但是在通知CPU却是中断号0x20.因此需要建立一个映射。在8259芯片产生的IRQ号必须是连续的,也就是如果irq0对应的是中断向量0x20,那么irq1对应的就是0x21,... 

在i8259.c/init_8259A()中,进行设置: 

/* 
* outb_p - this has to work on a wide range of PC hardware. 
*/ 
outb_p(0x11, 0x20); /* ICW1: select 8259A-1 init */ 
outb_p(0x20 + 0, 0x21); /* ICW2: 8259A-1 IR0-7 mapped to 0x20-0x27 */ 
outb_p(0x04, 0x21); /* 8259A-1 (the master) has a slave on IR2 */ 
if (auto_eoi) 
outb_p(0x03, 0x21); /* master does Auto EOI */ 
else 
outb_p(0x01, 0x21); /* master expects normal EOI */ 

outb_p(0x11, 0xA0); /* ICW1: select 8259A-2 init */ 
outb_p(0x20 + 8, 0xA1); /* ICW2: 8259A-2 IR0-7 mapped to 0x28-0x2f */ 
outb_p(0x02, 0xA1); /* 8259A-2 is a slave on master's IR2 */ 
outb_p(0x01, 0xA1); /* (slave's support for AEOI in flat mode 
is to be investigated) */ 



这样,在IDT的向量0x20-0x2f可以分别填入相应的中断处理函数的地址了。 

http://netcourse.cug.edu.cn:7310/netclass/weixingjisuanji/content/chapter5/5-1/5-1-0.htm

 


3.Intel8259的编程结构 
  8259的内部结构如图5-1所示,其内部结构逻辑主要由以下三部分组成: 

(1)控制逻辑 

(2)中断优先权判优及其屏蔽

(3)辅助电路


  编程结构框架如图5-2动画演示,8259的编程结构由三组共10个寄存器构成,每个寄存器均为8位。
第一组:IRR、PR、ISR
IRR:中断请求寄存器(Interrupt Request Register) 该寄存器的8位(D7~D0)分别存放IR7~IR0输入线上的中断请求。当某输入线有请求时,IRR对应位置1,该寄存器具有锁存功能。
ISR:当前中断服务寄存器(In Service Register) 该寄存器用于存放正在被服务的所有中断级,包括尚未服务完而中途被别的中断打端了的中断级。
PR:优先级裁决器(Priority Resolver) 当IR输入线上有请求时,IRR对应位置1,同时,PR将该中断的优先级与ISR中的优先级比较,若该中断的优先级高于ISR中的最高优先级,则PR就使INT信号变为高电平,把该中断送给CPU,同时,在ISR相应位置1。否则,PR不为该中断提出申请。
第二组:ICW1、ICW2、ICW3、ICW4
  这四个寄存器用来存放初始化命令字(Initialization CommandWord)。初始化命令字一般在系统启动时由程序设置,一旦设定,一般在系统工作过程中就不再改变。



阅读(1255) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:[GeekOS/Project1] Loading Executable Files

给主人留下些什么吧!~~