按鍵(jian)字(zi)符設備(bei)的file_operations結(jie)構定(ding)義為:
	static struct file_operations button_fops =
	    {
	        .owner = THIS_MODULE,
	        .ioctl = button_ioctl,
	        .open = button_open,
	        .read = button_read,
	        .release = button_release,
	    };
	以下為open和release函數接口的(de)實現。
	/* 打開文件, 申請中斷 */
	    static int button_open(struct inode *inode,struct file *filp) 
	    {
	        int ret = nonseekable_open(inode, filp);
	        if (ret < 0) 
	        {
	             return ret;
	        }
	        init_gpio(); /* 相關GPIO端口的初始化*/
	        request_irqs(); /* 申請4個中斷 */
	        if (ret < 0) 
	        {
	             return ret;
	        }
	        init_keybuffer(); /* 初始化按鍵緩沖數據結構 */
	        return ret;
	    }
	    /* 關閉文件, 屏蔽中斷 */
	    static int button_release(struct inode *inode,struct file *filp)
	    {
	        free_irqs(); /* 屏蔽中斷 */
	        return 0;
	    }
	在open函數接口(kou)(kou)中(zhong)(zhong)(zhong),進行了GPIO端口(kou)(kou)的(de)初始化(hua)、申(shen)請(qing)硬(ying)件(jian)中(zhong)(zhong)(zhong)斷(duan)以(yi)及按鍵緩(huan)沖的(de)初始化(hua)等工作。在以(yi)前的(de)章(zhang)節(jie)中(zhong)(zhong)(zhong)提過(guo),中(zhong)(zhong)(zhong)斷(duan)端口(kou)(kou)是比較寶貴而(er)且數量有限的(de)資源(yuan)。因此需要注意,好要在第一(yi)(yi)次打開設(she)備時(shi)申(shen)請(qing)(調用(yong)request_irq函數)中(zhong)(zhong)(zhong)斷(duan)端口(kou)(kou),而(er)不(bu)(bu)是在驅動模塊加載的(de)時(shi)候申(shen)請(qing)。如果(guo)已加載的(de)設(she)備驅動占用(yong)而(er)在一(yi)(yi)定時(shi)間(jian)段內不(bu)(bu)使(shi)用(yong)某些中(zhong)(zhong)(zhong)斷(duan)資源(yuan),則(ze)這些資源(yuan)不(bu)(bu)會(hui)被其(qi)他驅動所(suo)使(shi)用(yong),只能白白浪費(fei)掉(diao)。而(er)在打開設(she)備的(de)時(shi)候(調用(yong)open函數接口(kou)(kou))申(shen)請(qing)中(zhong)(zhong)(zhong)斷(duan),則(ze)不(bu)(bu)同的(de)設(she)備驅動可以(yi)共享這些寶貴的(de)中(zhong)(zhong)(zhong)斷(duan)資源(yuan)。
	以下(xia)為中斷申請(qing)和釋放的部分(fen)以及中斷處理(li)函數。
	/* 中斷處理函數,其中irq為中斷號 */
	    static irqreturn_t button_irq(int irq, void *dev_id, struct pt_regs *regs)
	    {
	        unsigned char ucKey = 0;
	        disable_irqs(); /* 屏蔽中斷 */
	        /* 延遲50毫秒, 屏蔽按鍵毛刺 */
	        udelay(50000);
	        ucKey = button_scan(irq); /* 掃描按鍵,獲得進行操作的按鍵的ID */
	        if ((ucKey >= 1) && (ucKey <= 16))
	        {
	            /* 如果緩沖區已滿, 則不添加 */
	            if (((key_buffer.head + 1) & (MAX_KEY_COUNT - 1)) != key_buffer.tail)
	            {
	                 spin_lock_irq(&buffer_lock);
	                 key_buffer.jiffy[key_buffer.tail] = get_tick_count();
	               key_buffer.tail ++;
	               key_buffer.tail &= (MAX_KEY_COUNT -1);
	                 spin_unlock_irq(&buffer_lock);
	             }
	        }
	        init_gpio(); /* 初始化GPIO端口,主要是為了恢復中斷端口配置 */
	        enable_irqs(); /* 開啟中斷 */
	        return IRQ_HANDLED;/* 2.6內核返回值一般是這個宏 */
	    }
	    /* 申請4個中斷 */
	    static int request_irqs(void)
	    {
	        int ret, i, j;
	        for (i = 0; i < MAX_COLUMN; i++)
	    {
	             ret = request_irq(key_info_matrix[i][0].irq_no, 
	                      button_irq, SA_INTERRUPT, BUTTONS_DEVICE_NAME, NULL);
	             if (ret < 0)
	             {
	                     for (j = 0; j < i; j++)
	                     {
	                         free_irq(key_info_matrix[j][0].irq_no, NULL); 
	                     }
	                     return -EFAULT;
	             }
	         }
	         return 0;
	    }
	    /* 釋放中斷 */
	    static __inline void free_irqs(void)
	    {
	        int i;
	        for (i = 0; i < MAX_COLUMN; i++)
	        {
	            free_irq(key_info_matrix[i][0].irq_no, NULL);
	        }
	    }
	中(zhong)斷(duan)(duan)處理(li)函數(shu)(shu)在(zai)每次中(zhong)斷(duan)(duan)產(chan)生的時(shi)候會被調用(yong),因此它的執行時(shi)間(jian)要盡(jin)可能(neng)得短。通常中(zhong)斷(duan)(duan)處理(li)函數(shu)(shu)只是(shi)簡單地喚醒等待資源(yuan)的任務,而(er)復(fu)雜且耗時(shi)的工(gong)作則(ze)讓這個任務去完(wan)成。中(zhong)斷(duan)(duan)處理(li)函數(shu)(shu)不能(neng)向用(yong)戶(hu)空間(jian)發送數(shu)(shu)據或者接(jie)收數(shu)(shu)據,不能(neng)做(zuo)任何可能(neng)發生睡眠的操作,而(er)且不能(neng)調用(yong)schedule()函數(shu)(shu)。
	為了簡單(dan)起見(jian),而且考(kao)慮到按(an)鍵操作的(de)(de)時(shi)間(jian)比較長,在本實例中的(de)(de)中斷(duan)處理函(han)數(shu)button_irq()里,通(tong)過(guo)調(diao)用睡眠函(han)數(shu)來消除毛刺信號(hao)。讀者可以根據(ju)以上介紹(shao)的(de)(de)對中斷(duan)處理函(han)數(shu)的(de)(de)要求改進該部分代碼。
	按鍵(jian)(jian)掃(sao)描(miao)函數(shu)如下所示。首(shou)先根據中斷號(hao)確定操作按鍵(jian)(jian)所在行的位(wei)置(zhi),然后采(cai)用逐列掃(sao)描(miao)法終確定操作按鍵(jian)(jian)所在的位(wei)置(zhi)。
	/* 
	    ** 進入中斷后, 掃描銨鍵碼 
	    ** 返回: 按鍵碼(1-16), 0xff表示錯誤 
	    */
	    static __inline unsigned char button_scan(int irq)
	    {
	        unsigned char key_id = 0xff;
	        unsigned char column = 0xff, row = 0xff;
	        s3c2410_gpio_cfgpin(S3C2410_GPF0, S3C2410_GPF0_INP); /* GPF0 */
	       s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_INP); /* GPF2 */
	       s3c2410_gpio_cfgpin(S3C2410_GPG3, S3C2410_GPG3_INP); /* GPG3 */
	       s3c2410_gpio_cfgpin(S3C2410_GPG11, S3C2410_GPG11_INP); /* GPG11 */
	        switch (irq)
	        { /* 根據irq值確定操作按鍵所在行的位置*/
	            case IRQ_EINT0:
	            {
	                  column = 0;
	            }
	            break;
	            case IRQ_EINT2:
	            {
	                  column = 1;
	            }
	            break;
	            case IRQ_EINT11:
	            {
	                  column = 2;
	            }
	            break;
	            case IRQ_EINT19:
	            {
	                  column = 3;
	            }
	            break;
	        } 
	        if (column != 0xff)
	        { /* 開始逐列掃描, 掃描第0列 */
	             s3c2410_gpio_setpin(S3C2410_GPE11, 0); /* 將KSCAN0置為低電平 */
	             s3c2410_gpio_setpin(S3C2410_GPG6, 1);
	             s3c2410_gpio_setpin(S3C2410_GPE13, 1);
	             s3c2410_gpio_setpin(S3C2410_GPG2, 1);
	         if(!s3c2410_gpio_getpin(key_info_matrix[column][0].irq_gpio_port))
	             { /* 觀察對應的中斷線的輸入端口值 */
	                  key_id = key_info_matrix[column][0].key_id;
	                  return key_id;
	             }
	             /* 掃描第1列*/
	             s3c2410_gpio_setpin(S3C2410_GPE11, 1);
	             s3c2410_gpio_setpin(S3C2410_GPG6, 0); /* 將KSCAN1置為低電平 */
	             s3c2410_gpio_setpin(S3C2410_GPE13, 1);
	             s3c2410_gpio_setpin(S3C2410_GPG2, 1);
	             if(!s3c2410_gpio_getpin(key_info_matrix[column][1].irq_gpio_port))
	             {
	                  key_id = key_info_matrix[column][1].key_id; 
	                  return key_id;
	             }
	             /* 掃描第2列*/
	             s3c2410_gpio_setpin(S3C2410_GPE11, 1);
	             s3c2410_gpio_setpin(S3C2410_GPG6, 1);
	             s3c2410_gpio_setpin(S3C2410_GPE13, 0); /* 將KSCAN2置為低電平 */
	             s3c2410_gpio_setpin(S3C2410_GPG2, 1); 
	             if(!s3c2410_gpio_getpin(key_info_matrix[column][2].irq_gpio_port))
	             {
	                  key_id = key_info_matrix[column][2].key_id;
	                  return key_id;
	             }
	             /* 掃描第3列*/
	             s3c2410_gpio_setpin(S3C2410_GPE11, 1);
	             s3c2410_gpio_setpin(S3C2410_GPG6, 1);
	             s3c2410_gpio_setpin(S3C2410_GPE13, 1);
	             s3c2410_gpio_setpin(S3C2410_GPG2, 0); /* 將KSCAN3置為低電平 */
	             if(!s3c2410_gpio_getpin(key_info_matrix[column][3].irq_gpio_port))
	             {
	                   key_id = key_info_matrix[column][3].key_id;
	                   return key_id;
	             }
	         } 
	         return key_id;
	     }
	以下是read函(han)數接口(kou)的實現(xian)。首先在按(an)鍵(jian)緩(huan)沖中刪除已經過時(shi)的按(an)鍵(jian)操作信息(xi)(xi),接下來,從按(an)鍵(jian)緩(huan)沖中讀取一條信息(xi)(xi)(按(an)鍵(jian)ID)并傳遞給用戶層。
	/* 從緩沖刪除過時數據(5秒前的按鍵值) */
	    static void remove_timeoutkey(void)
	    {
	        unsigned long tick;
	        spin_lock_irq(&buffer_lock); /* 獲得一個自旋鎖 */
	        while(key_buffer.head != key_buffer.tail)
	        {
	             tick = get_tick_count() - key_buffer.jiffy[key_buffer.head];
	             if (tick < 5000) /* 5秒 */
	             break;
	             key_buffer.buf[key_buffer.head] = 0;
	             key_buffer.jiffy[key_buffer.head] = 0;
	             key_buffer.head ++;
	             key_buffer.head &= (MAX_KEY_COUNT -1);
	        }
	        spin_unlock_irq(&buffer_lock); /* 釋放自旋鎖 */
	    }
	    /* 讀鍵盤 */
	    static ssize_t button_read(struct file *filp, 
	                            char *buffer, size_t count, loff_t *f_pos)
	    {
	         ssize_t ret = 0;
	         remove_timeoutkey(); /* 刪除過時的按鍵操作信息 */
	         spin_lock_irq(&buffer_lock);
	         while((key_buffer.head != key_buffer.tail) && (((size_t)ret) < count))
	         {
	               put_user((char)(key_buffer.buf[key_buffer.head]), &buffer[ret]);
	               key_buffer.buf[key_buffer.head] = 0;
	               key_buffer.jiffy[key_buffer.head] = 0;
	               key_buffer.head ++;
	               key_buffer.head &= (MAX_KEY_COUNT -1);
	               ret ++;
	         }
	         spin_unlock_irq(&buffer_lock);
	         return ret;
	    }
	以(yi)上介紹了按鍵驅(qu)動(dong)程(cheng)序(xu)中的主要內容。