溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

中斷處理 I/O內(nèi)存

發(fā)布時間:2020-07-22 22:51:10 來源:網(wǎng)絡(luò) 閱讀:2491 作者:毛散人 欄目:移動開發(fā)

回顧:內(nèi)核競態(tài)與并發(fā)

什么情況下會產(chǎn)生競態(tài)

1)SMP

2)單CPU支持任務(wù)搶占

3)中斷和進(jìn)程之間

4)中斷和中斷之間

解決競態(tài)的方法

1)中斷屏蔽

2)原子操作

位原子操作

×××原子操作 atomic{     int。。。。}

3)自旋鎖

  優(yōu)點:一旦可以獲取鎖,立即獲取

  缺點:長時間獲取鎖不成功,會消耗CPU資源

  它所保護(hù)的臨界資源(代碼段)通常比較短

4)信號量

   down(。。。)會導(dǎo)致睡眠

等等看前一章中

自旋鎖只允許一個持有者,信號量可以有多個持有者

信號量保護(hù)的臨界資源(代碼段)通常比較長

2,等待隊列      #include<linux/sched.h>

Read() recv()

Wait_event_interrptible //阻塞

Wake_up_interruptible  //喚醒



阻塞/非阻塞

實際上,應(yīng)用程序并不關(guān)心驅(qū)動里面read/write具體實現(xiàn),只管調(diào)用并獲取返回值

如果設(shè)備沒有準(zhǔn)備好數(shù)據(jù)給應(yīng)用程序讀或者沒有準(zhǔn)備好接受用戶程序?qū)懀?qū)動程序應(yīng)當(dāng)阻塞進(jìn)程,使它進(jìn)入睡眠,直到請求可以得到滿足

阻塞讀

     在阻塞型驅(qū)動程序中,如果進(jìn)程調(diào)用read設(shè)備操作,但是設(shè)備沒有數(shù)據(jù)或數(shù)據(jù)不足,進(jìn)程應(yīng)該被阻塞,當(dāng)有新數(shù)據(jù)到達(dá)后,喚醒被阻塞進(jìn)程

阻塞寫

在阻塞型驅(qū)動程序中,如果進(jìn)程調(diào)用write設(shè)備操作,但是設(shè)備沒有足夠的空間供其寫入,進(jìn)程該被阻塞,但設(shè)備中的數(shù)據(jù)讀走后,緩沖區(qū)中空出部分空間,應(yīng)該喚醒被阻塞進(jìn)程

應(yīng)用程序非阻塞讀

阻塞方式是文件讀寫操作的默認(rèn)方式

應(yīng)用程序可以通過使用O_NONBLOCK標(biāo)志來人為的設(shè)置讀寫操作為非阻塞方式

     定義在<asm-generic/fcntl.h>

     如果設(shè)置了O_NONBLOCK標(biāo)志,read和write的處理行為相同

if(0==pcdevp->)&&(O_NONBLICK&file->f_flags){

   printk(KERN_ALET “Char......”);

 return -EAGAIN;











---------------------------------------------------------------

多路監(jiān)聽偵測 select    #include <linux/poll.h>

int select(int nfds, fd_set *readfds, fd_set *writefds,

                  fd_set *exceptfds, struct timeval *timeout);

Nfds 要監(jiān)聽的文件描述符最大值+1

Readfds,要監(jiān)聽的讀文件描述符集合

Writefds要監(jiān)聽的寫文件描述符集合

Exceptfds 要監(jiān)聽的異常文件描述集合

Timeout 監(jiān)聽的超時時間


void FD_CLR(int fd, fd_set *set);

       int  FD_ISSET(int fd, fd_set *set);

       void FD_SET(int fd, fd_set *set);

       void FD_ZERO(fd_set *set);

內(nèi)核中要實現(xiàn)的函數(shù)

Unsigned int(*poll)()

Fd_set rfds;

Struct timeval tv

FD_ZERO()

加入集合FD_SET()


POLLIN

有數(shù)據(jù)可讀。

POLLRDNORM

有普通數(shù)據(jù)可讀。

POLLRDBAND

有優(yōu)先數(shù)據(jù)可讀。

POLLPRI

有緊迫數(shù)據(jù)可讀。

POLLOUT

寫數(shù)據(jù)不會導(dǎo)致阻塞。

POLLWRNORM

寫普通數(shù)據(jù)不會導(dǎo)致阻塞。

POLLWRBAND

寫優(yōu)先數(shù)據(jù)不會導(dǎo)致阻塞。

POLLMSG

SIGPOLL 消息可用。

此外,revents域中還可能返回下列事件:

POLLER

指定的文件描述符發(fā)生錯誤。

POLLHUP

指定的文件描述符掛起事件。

POLLNVAL

指定的文件描述符非法。

這些事件在events域中無意義,因為它們在合適的時候總是會從revents中返回。使用poll()和select()不一樣,你不需要顯式地請求異常情況報告。

POLLIN | POLLPRI等價于select()的讀事件,POLLOUT |POLLWRBAND等價于select()的寫事件。POLLIN等價于POLLRDNORM |POLLRDBAND,而POLLOUT則等價于POLLWRNORM。




中斷


驅(qū)動需要包含 #include <linux/gpio.h>

判斷一個IO是否合法:int gpio_is_valid(int number);

設(shè)置GPIO的方向,如果是輸出同時設(shè)置電平:

/* set as input or output, returning 0 or negative errno */

int gpio_direction_input(unsigned gpio);

int gpio_direction_output(unsigned gpio, int value);

獲取輸入引腳的電平:

/* GPIO INPUT: return zero or nonzero */

int gpio_get_value(unsigned gpio);

/* GPIO OUTPUT */

void gpio_set_value(unsigned gpio, int value);

int gpio_cansleep(unsigned gpio);

To access such GPIOs, a different set of accessors is defined:

/* GPIO INPUT: return zero or nonzero, might sleep */

int gpio_get_value_cansleep(unsigned gpio);

/* GPIO OUTPUT, might sleep */

void gpio_set_value_cansleep(unsigned gpio, int value);

獲取一個GPIO并聲明標(biāo)簽:

/* request GPIO, returning 0 or negative errno.

* non-null labels may be useful for diagnostics.

*/

int gpio_request(unsigned gpio, const char *label);

/* release previously-claimed GPIO */

void gpio_free(unsigned gpio);


將GPIO映射為IRQ中斷:

/* map GPIO numbers to IRQ numbers */

int gpio_to_irq(unsigned gpio);

/* map IRQ numbers to GPIO numbers (avoid using this) */

int irq_to_gpio(unsigned irq);


設(shè)置GPIO IRQ中斷類型:


if (!sw->both_edges) {

if (gpio_get_value(sw->gpio))

set_irq_type(gpio_to_irq(sw->gpio), IRQ_TYPE_EDGE_FALLING);

else

set_irq_type(gpio_to_irq(sw->gpio), IRQ_TYPE_EDGE_RISING);

在驅(qū)動中使用延時函數(shù)mdelay,需要包含<linux/delay.h>文件




對于硬件產(chǎn)生的變化,需要CPI進(jìn)行處理,通過情況下CPU獲取該變化的方式有種

1)輪詢

2)中斷  #include <linux/interrup>

Request irq(...)

注冊中斷request_irq(...)

Irq 要注冊的中斷號  arm/mach-xxxxx/include/mach/irqs.h

Gpio_to_irq()

handle  中斷處理函數(shù)

Irqreturn_t xxxxx(int irq,void *dev_id)

Irqflags 中斷標(biāo)志

Cat /proc/interrupts

IRQF_SHARED

flag定義 描述

IRQF_TRIGGER_XXX 描述該interrupt line觸發(fā)類型的flag

IRQF_DISABLED 首先要說明的是這是一個廢棄的flag,在新的內(nèi)核中,該flag沒有任何的作用了。具體可以參考:Disabling IRQF_DISABLED 

舊的內(nèi)核(2.6.35版本之前)認(rèn)為有兩種interrupt handler:slow handler和fast handle。在request irq的時候,對于fast handler,需要傳遞IRQF_DISABLED的參數(shù),確保其中斷處理過程中是關(guān)閉CPU的中斷,因為是fast handler,執(zhí)行很快,即便是關(guān)閉CPU中斷不會影響系統(tǒng)的性能。但是,并不是每一種外設(shè)中斷的handler都是那么快(例如磁盤),因此就有 slow handler的概念,說明其在中斷處理過程中會耗時比較長。對于這種情況,在執(zhí)行interrupt handler的時候不能關(guān)閉CPU中斷,否則對系統(tǒng)的performance會有影響。 

新的內(nèi)核已經(jīng)不區(qū)分slow handler和fast handle,都是fast handler,都是需要關(guān)閉CPU中斷的,那些需要后續(xù)處理的內(nèi)容推到threaded interrupt handler中去執(zhí)行。

IRQF_SHARED 這是flag用來描述一個interrupt line是否允許在多個設(shè)備中共享。如果中斷控制器可以支持足夠多的interrupt source,那么在兩個外設(shè)間共享一個interrupt request line是不推薦的,畢竟有一些額外的開銷(發(fā)生中斷的時候要逐個詢問是不是你的中斷,軟件上就是遍歷action list),因此外設(shè)的irq handler中最好是一開始就啟動判斷,看看是否是自己的中斷,如果不是,返回IRQ_NONE,表示這個中斷不歸我管。 早期PC時代,使用8259中斷控制器,級聯(lián)的8259最多支持15個外部中斷,但是PC外設(shè)那么多,因此需要irq share?,F(xiàn)在,ARM平臺上的系統(tǒng)設(shè)計很少會采用外設(shè)共享IRQ方式,畢竟一般ARM SOC提供的有中斷功能的GPIO非常的多,足夠用的。 當(dāng)然,如果確實需要兩個外設(shè)共享IRQ,那也只能如此設(shè)計了。對于HW,中斷控制器的一個interrupt source的引腳要接到兩個外設(shè)的interrupt request line上,怎么接?直接連接可以嗎?當(dāng)然不行,對于低電平觸發(fā)的情況,我們可以考慮用與門連接中斷控制器和外設(shè)。

IRQF_PROBE_SHARED IRQF_SHARED用來表示該interrupt action descriptor是允許和其他device共享一個interrupt line(IRQ number),但是實際上是否能夠share還是需要其他條件:例如觸發(fā)方式必須相同。有些驅(qū)動程序可能有這樣的調(diào)用場景:我只是想scan一個irq table,看看哪一個是OK的,這時候,如果即便是不能和其他的驅(qū)動程序share這個interrupt line,我也沒有關(guān)系,我就是想scan看看情況。這時候,caller其實可以預(yù)見sharing mismatche的發(fā)生,因此,不需要內(nèi)核打印“Flags mismatch irq……“這樣冗余的信息

IRQF_PERCPU 在SMP的架構(gòu)下,中斷有兩種mode,一種中斷是在所有processor之間共享的,也就是global的,一旦中斷產(chǎn)生,interrupt controller可以把這個中斷送達(dá)多個處理器。當(dāng)然,在具體實現(xiàn)的時候不會同時將中斷送達(dá)多個CPU,一般是軟件和硬件協(xié)同處理,將中斷送達(dá)一個CPU處理。但是一段時間內(nèi)產(chǎn)生的中斷可以平均(或者按照既定的策略)分配到一組CPU上。這種interrupt mode下,interrupt controller針對該中斷的operational register是global的,所有的CPU看到的都是一套寄存器,一旦一個CPU ack了該中斷,那么其他的CPU看到的該interupt source的狀態(tài)也是已經(jīng)ack的狀態(tài)。 

和global對應(yīng)的就是per cpu interrupt了,對于這種interrupt,不是processor之間共享的,而是特定屬于一個CPU的。例如GIC中interrupt ID等于30的中斷就是per cpu的(這個中斷event被用于各個CPU的local timer),這個中斷號雖然只有一個,但是,實際上控制該interrupt ID的寄存器有n組(如果系統(tǒng)中有n個processor),每個CPU看到的是不同的控制寄存器。在具體實現(xiàn)中,這些寄存器組有兩種形態(tài),一種是banked,所有CPU操作同樣的寄存器地址,硬件系統(tǒng)會根據(jù)訪問的cpu定向到不同的寄存器,另外一種是non banked,也就是說,對于該interrupt source,每個cpu都有自己獨特的訪問地址。

IRQF_NOBALANCING 這也是和multi-processor相關(guān)的一個flag。對于那些可以在多個CPU之間共享的中斷,具體送達(dá)哪一個processor是有策略的,我們可以在多個CPU之間進(jìn)行平衡。如果你不想讓你的中斷參與到irq balancing的過程中那么就設(shè)定這個flag

IRQF_IRQPOLL  

IRQF_ONESHOT one shot本身的意思的只有一次的,結(jié)合到中斷這個場景,則表示中斷是一次性觸發(fā)的,不能嵌套。對于primary handler,當(dāng)然是不會嵌套,但是對于threaded interrupt handler,我們有兩種選擇,一種是mask該interrupt source,另外一種是unmask該interrupt source。一旦mask住該interrupt source,那么該interrupt source的中斷在整個threaded interrupt handler處理過程中都是不會再次觸發(fā)的,也就是one shot了。這種handler不需要考慮重入問題。 

具體是否要設(shè)定one shot的flag是和硬件系統(tǒng)有關(guān)的,我們舉一個例子,比如電池驅(qū)動,電池里面有一個電量計,是使用HDQ協(xié)議進(jìn)行通信的,電池驅(qū)動會注冊一個threaded interrupt handler,在這個handler中,會通過HDQ協(xié)議和電量計進(jìn)行通信。對于這個handler,通過HDQ進(jìn)行通信是需要一個完整的HDQ交互過程,如果中間被打斷,整個通信過程會出問題,因此,這個handler就必須是one shot的。

IRQF_NO_SUSPEND 這個flag比較好理解,就是說在系統(tǒng)suspend的時候,不用disable這個中斷,如果disable,可能會導(dǎo)致系統(tǒng)不能正常的resume。

IRQF_FORCE_RESUME 在系統(tǒng)resume的過程中,強(qiáng)制必須進(jìn)行enable的動作,即便是設(shè)定了IRQF_NO_SUSPEND這個flag。這是和特定的硬件行為相關(guān)的。

IRQF_NO_THREAD 有些low level的interrupt是不能線程化的(例如系統(tǒng)timer的中斷),這個flag就是起這個作用的。另外,有些級聯(lián)的interrupt controller對應(yīng)的IRQ也是不能線程化的(例如secondary GIC對應(yīng)的IRQ),它的線程化可能會影響一大批附屬于該interrupt controller的外設(shè)的中斷響應(yīng)延遲。

IRQF_EARLY_RESUME  

IRQF_TIMER





如何判斷哪個按鍵觸發(fā)的中斷

如何判斷怎么觸發(fā)(上升沿,下降沿,高電平,低電平)中斷的

          使用管腳的輸入功能(配置成輸入功能)判斷電平

          重新配置為外部中斷功能

如何去抖動

Linux內(nèi)核中中斷處理程序的一般結(jié)構(gòu)

頂半部:完成盡可能少的緊急功能,往往是簡單的讀取寄存器,清除中斷標(biāo)志。登記底半部。

底半部:完成中斷處理程序中絕大部分工作,通常這部分都比較耗時。

1)軟中斷

2)tasklet(利用了軟中斷的機(jī)制)

struct  tasklet_struct{

Void(*func)(unsigned  long);//底半部完成函數(shù)

Unsigned long data;

。。。 

}

tasklet_scedule(。。。)//完成底半部的登記

DECLARE_TASKLET 定義并初始化

3)工作者隊列

Struct work_struct{


}

INIT_WAORK//初始化work

Schrdiule_work  //登記work

flush_work//

Tasklet和工作者隊列有啥區(qū)別

Asklet中的func函數(shù)工作于中斷上下文,func不允許睡眠的,work中的func工作于進(jìn)程上下文

IO與內(nèi)存:

統(tǒng)一編址(ARM):

MOV 

獨立編址(X86):

MOV R0 [0X100]

IN/OUT 0X100

ARM PowePC MPIS 都是用統(tǒng)一編址

X86使用獨立編址

Linux編程使用到的都是虛擬地址,驅(qū)動開發(fā)時,從芯片手冊得到的物理地址,需要轉(zhuǎn)換成虛擬地址后再使用






/*包含初始化宏定義的頭文件,代碼中的module_init和module_exit在此文件中*/

#include <linux/init.h>

/*包含初始化加載模塊的頭文件,代碼中的MODULE_LICENSE在此頭文件中*/

#include <linux/module.h>

/*定義module_param module_param_array的頭文件*/

#include <linux/moduleparam.h>

/*定義module_param module_param_array中perm的頭文件*/

#include <linux/stat.h>

/*三個字符設(shè)備函數(shù)*/

#include <linux/fs.h>

/*MKDEV轉(zhuǎn)換設(shè)備號數(shù)據(jù)類型的宏定義*/

#include <linux/kdev_t.h>

/*定義字符設(shè)備的結(jié)構(gòu)體*/

#include <linux/cdev.h>

/*分配內(nèi)存空間函數(shù)頭文件*/

#include <linux/slab.h>

/*包含函數(shù)device_creatchar_driver_ledse 結(jié)構(gòu)體class等頭文件*/

#include <linux/device.h>

#include <linux/wait.h>

/*自定義頭文件*/

#include "char_driver_leds.h"

#include <linux/sched.h>


//#include <stdio.h> 

#include <linux/delay.h>




#include <linux/interrupt.h>

#include <asm/gpio.h>

#include <plat/gpio-cfg.h>


/*Linux中申請GPIO的頭文件*/

#include <linux/gpio.h>

/*三星平臺的GPIO配置函數(shù)頭文件*/

/*三星平臺EXYNOS系列平臺,GPIO配置參數(shù)宏定義頭文件*/

#include <plat/gpio-cfg.h>

/*三星平臺4412平臺,GPIO宏定義頭文件*/

#include <mach/gpio-exynos4.h>



MODULE_LICENSE("Dual BSD/GPL");

/*聲明是開源的,沒有內(nèi)核版本限制*/

MODULE_AUTHOR("songmao");

/*聲明作者*/





static int led_gpios[] = {

EXYNOS4_GPL2(0),EXYNOS4_GPK1(1),

};

#define LED_NUM ARRAY_SIZE(led_gpios)


static int irq_gpio[] ={

EXYNOS4_GPX1(1),EXYNOS4_GPX1(2),

    EXYNOS4_GPX1(0),EXYNOS4_GPX1(1)

};


#define IRQ_NUM ARRAY_SIZE(irq_gpio)

int led_num[4];


int numdev_major = DEV_MAJOR;

int numdev_minor = DEV_MINOR;


/*輸入主設(shè)備號*/

module_param(numdev_major,int,S_IRUSR);

/*輸入次設(shè)備號*/

module_param(numdev_minor,int,S_IRUSR);





static struct class *myclass;

struct reg_dev *my_devices;


/*打開操作*/

static int chardevnode_open(struct inode *inode, struct file *file)

{

    struct reg_dev *reg_devp =NULL;


reg_devp =container_of(inode->i_cdev,struct reg_dev,cdev);

file->private_data=reg_devp;

   

printk(KERN_EMERG "chardevnode_open is success!\n");

/* if(!atomic_dec_and_test(&(reg_devp->atc)))

{

  printk(KERN_ERR "atomic:device can open only once!");

  atomic_inc(&(reg_devp->atc));

  return -EBUSY;

}*/

spin_lock(&(reg_devp->lock));

if(OPEN_NUM<=reg_devp->open_num){

      spin_unlock(&(reg_devp->lock));

   printk(KERN_ERR "atomic:device can open over num!");

return -EBUSY;

}

reg_devp->open_num++;

spin_unlock(&(reg_devp->lock));

return 0;

}

/*關(guān)閉操作*/

static int chardevnode_release(struct inode *inode, struct file *file)

{

    struct reg_dev *reg_devp =NULL;

reg_devp=file->private_data;

printk(KERN_EMERG "chardevnode_release is success!\n");

//atomic_inc(&(reg_devp->atc));

/*spin_lock(&(reg_devp->lock));

reg_devp->open_num--;

spin_unlock(&(reg_devp->lock));*/

up(&(reg_devp->sem_open));

return 0;

}

/*IO操作*/

static long chardevnode_ioctl(struct file *file, unsigned int cmd, unsigned long arg){

struct reg_dev *reg_devp =NULL;

reg_devp=file->private_data;

switch(cmd)

{

case 0:

case 1:

if (arg > LED_NUM) {

return -EINVAL;

}


gpio_set_value(led_gpios[arg], cmd);

break;


default:

return -EINVAL;

}

printk(KERN_EMERG "chardevnode_ioctl is success! cmd is %d,arg is %d \n",cmd,arg);


up(&(reg_devp->sem_read));

up(&(reg_devp->sem_write));

return 0;

}


ssize_t chardevnode_read(struct file *file, char __user *buf, size_t count, loff_t *f_ops)

{

    struct reg_dev *reg_devp =NULL;

reg_devp=file->private_data;

//down_interruptible(&(reg_devp->sem_read));

wait_event_interruptible(reg_devp->wqh,0!=reg_devp->led);

reg_devp->led=0;

printk(KERN_INFO"chardevnode_read success");

return 0;

}


ssize_t chardevnode_write(struct file *file, const char __user *buf, size_t count, loff_t *f_ops)

     struct reg_dev *reg_devp =NULL;

reg_devp=file->private_data;

//down_interruptible(&(reg_devp->sem_write));

reg_devp->led=1;

wake_up_interruptible(&(reg_devp->wqh));

printk(KERN_INFO"chardevnode_write success");

return 0;

}


loff_t chardevnode_llseek(struct file *file, loff_t offset, int ence){

return 0;

}

struct file_operations my_fops = {

.owner = THIS_MODULE,

.open = chardevnode_open,

.release = chardevnode_release,

.unlocked_ioctl = chardevnode_ioctl,

.read = chardevnode_read,

.write = chardevnode_write,

.llseek = chardevnode_llseek,

};


static void chardeviced_cdd_work_func(struct work_struct *work)

{

      int i = 0,j,ledss;

      struct reg_dev *pcdevp = container_of(work,struct reg_dev,cdd_work);

 printk(KERN_INFO" CharDeviceDriver:  Entry cdd_work");

 printk(KERN_ALERT"CharDeviceDriver:CDD work name:%s \n",pcdevp->cdd_work_name);

 for(i=0;i<4;i++)

 { 

      if(led_num[i]>0)

      {

          j=i%2;

if(i>1)

ledss=1;

else

ledss=0;

                 gpio_set_value(led_gpios[j],ledss);

      }  

}


static void chardevicedriver_cdd_delayed_work_func(struct work_struct *work)

{

     struct reg_dev *pcdevp = container_of(work,struct reg_dev,cdd_delayed_work);

printk(KERN_INFO" CharDeviceDriver:  Entry chardeviceddriver_cdd_delayed_work_func");

printk(KERN_ALERT"CharDeviceDriver:CDD delayed work namename:%s \n",pcdevp->cdd_delayed_work_name);

}


static irq_handler_t chardevnode_irq(int irq,void *dev_id)

{

    struct reg_dev *reg_devp = (struct reg_dev *)dev_id;

for(i=0;i<IRQ_NUM;i++){

s3c_gpio_cfgpin(irq_gpio[i], S3C_GPIO_SFN(0X0));

        led_num[i] = gpio_get_value(irq_gpio[i]);

   s3c_gpio_cfgpin(irq_gpio[i], S3C_GPIO_SFN(0X0));

}



tasklet_schedule(&(my_devices));


schedule_work(&(reg_devp->cdd_work));


queue_delayed_work(reg_devp->cdd_workqueue,&(reg_devp->cdd_delayed_work),3*HZ);


return IRQ_HANDLED;

}


/*設(shè)備注冊到系統(tǒng)*/

static void reg_init_cdev(struct reg_dev *dev,int index){

int err;

int devno = MKDEV(numdev_major,numdev_minor+index);


/*數(shù)據(jù)初始化*/

cdev_init(&dev->cdev,&my_fops);

dev->cdev.owner = THIS_MODULE;

dev->cdev.ops = &my_fops;

/*注冊到系統(tǒng)*/

err = cdev_add(&dev->cdev,devno,1);

if(err){

printk(KERN_EMERG "cdev_add %d is fail! %d\n",index,err);

}

else{

printk(KERN_EMERG "cdev_add %d is success!\n",numdev_minor+index);

}

}


static int gpio_init(void){

int i=0,ret;

for(i=0;i<LED_NUM;i++){

ret = gpio_request(led_gpios[i], "LED");

if (ret) {

printk("%s: request GPIO %d for LED failed, ret = %d\n", DEVICE_NAME,i,ret);

return -1;

}

else{

s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);

gpio_set_value(led_gpios[i], 1);

}

}


for(i=0;i<IRQ_NUM;i++){

ret = gpio_request(irq_gpio[i], "IRQ");

if (ret) {

printk("%s: request GPIO %d for IRQ failed, ret = %d\n", DEVICE_NAME,i,ret);

return -1;

}

else{

s3c_gpio_cfgpin(irq_gpio[i], S3C_GPIO_SFN(0X0F));

}

}

     ret = request_irq(IRQ_EINT(9),chardevnode_irq,IRQF_SAMPLE_RANDOM|IRQF_TRIGGER_LOW,"interrupt0",my_devices);

     if(0>ret){

          printk(KERN_ERR "Chardevnode_irq:Failure is IRQ_EINT(9)");

 goto FAIL_IRQ_EINT;

}

 ret = request_irq(IRQ_EINT(10),chardevnode_irq,IRQF_SAMPLE_RANDOM|IRQF_TRIGGER_LOW,"interrupt1",my_devices);

     if(0>ret){

          printk(KERN_ERR "Chardevnode_irq:Failure is IRQ_EINT(10)");

}

 

      ret = request_irq(IRQ_EINT(17),chardevnode_irq,IRQF_SAMPLE_RANDOM|IRQF_TRIGGER_LOW,"interrupt2",my_devices);

     if(0>ret){

          printk(KERN_ERR "Chardevnode_irq:Failure is IRQ_EINT(17)");

}

      ret = request_irq(IRQ_EINT(18),chardevnode_irq,IRQF_SAMPLE_RANDOM|IRQF_TRIGGER_LOW,"interrupt3",my_devices);

     if(0>ret){

          printk(KERN_ERR "Chardevnode_irq:Failure is IRQ_EINT(17)");

}


printk(KERN_INFO"Chardevnode_irq:Success is IRQ_EINT");

return 0;

FAIL_IRQ_EINT: 

     free_irq(IRQ_EINT(9),my_devices);

free_irq(IRQ_EINT(10),my_devices);

free_irq(IRQ_EINT(17),my_devices);

free_irq(IRQ_EINT(18),my_devices);

 for(i=0;i<LED_NUM;i++)

gpio_free(led_gpios[i]);

for(i=0;i<IRQ_NUM;i++){

ret = gpio_free(irq_gpio[i]);

}



static int __init scdev_init(void)

{

int ret = 0,i;

dev_t num_dev;

printk(KERN_EMERG "numdev_major is %d!\n",numdev_major);

printk(KERN_EMERG "numdev_minor is %d!\n",numdev_minor);

if(numdev_major){

num_dev = MKDEV(numdev_major,numdev_minor);

ret = register_chrdev_region(num_dev,DEVICE_MINOR_NUM,DEVICE_NAME);

}

else{

/*動態(tài)注冊設(shè)備號*/

ret = alloc_chrdev_region(&num_dev,numdev_minor,DEVICE_MINOR_NUM,DEVICE_NAME);

/*獲得主設(shè)備號*/

numdev_major = MAJOR(num_dev);

printk(KERN_EMERG "adev_region req %d !\n",numdev_major);

}

if(ret<0){

printk(KERN_EMERG "register_chrdev_region req %d is failed!\n",numdev_major);

}

myclass = class_create(THIS_MODULE,DEVICE_NAME);

my_devices = kmalloc(DEVICE_MINOR_NUM * sizeof(struct reg_dev),GFP_KERNEL);

if(!my_devices){

ret = -ENOMEM;

goto fail;

}

memset(my_devices,0,DEVICE_MINOR_NUM * sizeof(struct reg_dev));

/*設(shè)備初始化*/

for(i=0;i<DEVICE_MINOR_NUM;i++){

my_devices[i].data = kmalloc(REGDEV_SIZE,GFP_KERNEL);

memset(my_devices[i].data,0,REGDEV_SIZE);

/*設(shè)備注冊到系統(tǒng)*/

reg_init_cdev(&my_devices[i],i);

/*創(chuàng)建設(shè)備節(jié)點*/

device_create(myclass,NULL,MKDEV(numdev_major,numdev_minor+i),NULL,DEVICE_NAME"%d",i);

//atomic_inc(&(my_devices[i].atc));

/*spin_lock_init(&(my_devices[i].lock));*/

my_devices[i].open_num=0;


/*sema_init(&(my_devices[i].sem_open,2);

    sema_init(&(my_devices[i].sem_read,1);

sema_init(&(my_devices[i].sem_write,0);*/

//init_waitqueue_head(&(my_devices[i].wqh));


//strcpy( &(my_devices[i].cdd_work_name),"cdd_work_name");

}

ret = gpio_init();

if(ret){

printk(KERN_EMERG "gpio_init failed!\n");

}

INIT_WORK(&(my_devices->cdd_work,chardeviced_cdd_work_func,NULL);

//my_devices[i].cdd_workqueue;

create_workqueue("cdd_workqueue");

if(IS_ERR(my_devices->cdd_workqueue)){

        print(KERN_ERR"ChardeviceDriver: Failure to create work_queue!\n");

ret=PTR_ERR(my_devices->cdd_workqueue);

goto failure_creat_work;

 

    }

printk(KERN_INFO"ChardeviceDriver:Success to create work_queue!\n");

strcpy( &(my_devices->cdd_work_name),"cdd_delayed_work_name");

INIT_DELAYED_WORK(my_devices->cdd_delayed_work,chardevicedriver_cdd_delayed_work_func);

printk(KERN_EMERG "scdev_init!\n");

    

/*打印信息,KERN_EMERG表示緊急信息*/

return 0;

failure_creat_work:

     free_irq(IRQ_EINT(9),my_devices);

free_irq(IRQ_EINT(10),my_devices);

free_irq(IRQ_EINT(17),my_devices);

free_irq(IRQ_EINT(18),my_devices);

 for(i=0;i<LED_NUM;i++)

gpio_free(led_gpios[i])

for(i=0;i<IRQ_NUM;i++){

ret = gpio_free(irq_gpio[i]);


fail:

/*注銷設(shè)備號*/

unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);

printk(KERN_EMERG "kmalloc is fail!\n");

return ret;

}


static void __exit scdev_exit(void)

{

int i;

printk(KERN_EMERG "scdev_exit!\n");


    free_irq(IRQ_EINT(9),my_devices);

free_irq(IRQ_EINT(10),my_devices);

free_irq(IRQ_EINT(17),my_devices);

free_irq(IRQ_EINT(18),my_devices);

/*除去字符設(shè)備*/

for(i=0;i<DEVICE_MINOR_NUM;i++){

cdev_del(&(my_devices[i].cdev));

/*摧毀設(shè)備節(jié)點函數(shù)d*/

device_destroy(myclass,MKDEV(numdev_major,numdev_minor+i));

}

/*釋放設(shè)備class*/

class_destroy(myclass);

/*釋放內(nèi)存*/

kfree(my_devices);

/*釋放GPIO*/

for(i=0;i<LED_NUM;i++){

gpio_free(led_gpios[i]);

}

unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);

}



module_init(scdev_init);

/*初始化函數(shù)*/

module_exit(scdev_exit);

/*卸載函數(shù)*/














1)申請I/O內(nèi)存   #include <asm/io.h>

  request_mem_region(start,n,name)物理地址

  Start 待申請的起始地址(物理地址)

  n,從start開始地址的字節(jié)數(shù)

  Name,

2)映射ioremap(phys,addr,size)

   phys_addr,等同于1)中的start

   size等同于n

   return value:映射后的虛擬地址 vir_addr

   如:vir_addr=ioremap(0xE0200008,4)

  ioread8   ioread16   ioread32

  ioreite8   iowrite16  iowrite32

  ((volatile unsigned int *)vir_addr) =0xXXXX;

  volatile

  寄存器變量,硬件地址,中斷或者多線程中共享的全局變量,防止編譯器錯誤優(yōu)化

  4)取消映射

     ioumap(*addr)

         addr,   ioremap返回的虛擬地址vir_addr

 5)釋放I/O內(nèi)存

  release_mem_region(start,n)

  回顧:內(nèi)核競態(tài)與并發(fā)

什么情況下會產(chǎn)生競態(tài)

1)SMP

2)單CPU支持任務(wù)搶占

3)中斷和進(jìn)程之間

4)中斷和中斷之間

解決競態(tài)的方法

1)中斷屏蔽

2)原子操作

位原子操作

×××原子操作 atomic{     int。。。。}

3)自旋鎖

  優(yōu)點:一旦可以獲取鎖,立即獲取

  缺點:長時間獲取鎖不成功,會消耗CPU資源

  它所保護(hù)的臨界資源(代碼段)通常比較短

4)信號量

   down(。。。)會導(dǎo)致睡眠

等等看前一章中

自旋鎖只允許一個持有者,信號量可以有多個持有者

信號量保護(hù)的臨界資源(代碼段)通常比較長

2,等待隊列      #include<linux/sched.h>

Read() recv()

Wait_event_interrptible //阻塞

Wake_up_interruptible  //喚醒



阻塞/非阻塞

實際上,應(yīng)用程序并不關(guān)心驅(qū)動里面read/write具體實現(xiàn),只管調(diào)用并獲取返回值

如果設(shè)備沒有準(zhǔn)備好數(shù)據(jù)給應(yīng)用程序讀或者沒有準(zhǔn)備好接受用戶程序?qū)?,?qū)動程序應(yīng)當(dāng)阻塞進(jìn)程,使它進(jìn)入睡眠,直到請求可以得到滿足

阻塞讀

     在阻塞型驅(qū)動程序中,如果進(jìn)程調(diào)用read設(shè)備操作,但是設(shè)備沒有數(shù)據(jù)或數(shù)據(jù)不足,進(jìn)程應(yīng)該被阻塞,當(dāng)有新數(shù)據(jù)到達(dá)后,喚醒被阻塞進(jìn)程

阻塞寫

在阻塞型驅(qū)動程序中,如果進(jìn)程調(diào)用write設(shè)備操作,但是設(shè)備沒有足夠的空間供其寫入,進(jìn)程該被阻塞,但設(shè)備中的數(shù)據(jù)讀走后,緩沖區(qū)中空出部分空間,應(yīng)該喚醒被阻塞進(jìn)程

應(yīng)用程序非阻塞讀

阻塞方式是文件讀寫操作的默認(rèn)方式

應(yīng)用程序可以通過使用O_NONBLOCK標(biāo)志來人為的設(shè)置讀寫操作為非阻塞方式

     定義在<asm-generic/fcntl.h>

     如果設(shè)置了O_NONBLOCK標(biāo)志,read和write的處理行為相同

if(0==pcdevp->)&&(O_NONBLICK&file->f_flags){

   printk(KERN_ALET “Char......”);

 return -EAGAIN;











---------------------------------------------------------------

多路監(jiān)聽偵測 select    #include <linux/poll.h>

int select(int nfds, fd_set *readfds, fd_set *writefds,

                  fd_set *exceptfds, struct timeval *timeout);

Nfds 要監(jiān)聽的文件描述符最大值+1

Readfds,要監(jiān)聽的讀文件描述符集合

Writefds要監(jiān)聽的寫文件描述符集合

Exceptfds 要監(jiān)聽的異常文件描述集合

Timeout 監(jiān)聽的超時時間


void FD_CLR(int fd, fd_set *set);

       int  FD_ISSET(int fd, fd_set *set);

       void FD_SET(int fd, fd_set *set);

       void FD_ZERO(fd_set *set);

內(nèi)核中要實現(xiàn)的函數(shù)

Unsigned int(*poll)()

Fd_set rfds;

Struct timeval tv

FD_ZERO()

加入集合FD_SET()


POLLIN

有數(shù)據(jù)可讀。

POLLRDNORM

有普通數(shù)據(jù)可讀。

POLLRDBAND

有優(yōu)先數(shù)據(jù)可讀。

POLLPRI

有緊迫數(shù)據(jù)可讀。

POLLOUT

寫數(shù)據(jù)不會導(dǎo)致阻塞。

POLLWRNORM

寫普通數(shù)據(jù)不會導(dǎo)致阻塞。

POLLWRBAND

寫優(yōu)先數(shù)據(jù)不會導(dǎo)致阻塞。

POLLMSG

SIGPOLL 消息可用。

此外,revents域中還可能返回下列事件:

POLLER

指定的文件描述符發(fā)生錯誤。

POLLHUP

指定的文件描述符掛起事件。

POLLNVAL

指定的文件描述符非法。

這些事件在events域中無意義,因為它們在合適的時候總是會從revents中返回。使用poll()和select()不一樣,你不需要顯式地請求異常情況報告。

POLLIN | POLLPRI等價于select()的讀事件,POLLOUT |POLLWRBAND等價于select()的寫事件。POLLIN等價于POLLRDNORM |POLLRDBAND,而POLLOUT則等價于POLLWRNORM。




中斷


驅(qū)動需要包含 #include <linux/gpio.h>

判斷一個IO是否合法:int gpio_is_valid(int number);

設(shè)置GPIO的方向,如果是輸出同時設(shè)置電平:

/* set as input or output, returning 0 or negative errno */

int gpio_direction_input(unsigned gpio);

int gpio_direction_output(unsigned gpio, int value);

獲取輸入引腳的電平:

/* GPIO INPUT: return zero or nonzero */

int gpio_get_value(unsigned gpio);

/* GPIO OUTPUT */

void gpio_set_value(unsigned gpio, int value);

int gpio_cansleep(unsigned gpio);

To access such GPIOs, a different set of accessors is defined:

/* GPIO INPUT: return zero or nonzero, might sleep */

int gpio_get_value_cansleep(unsigned gpio);

/* GPIO OUTPUT, might sleep */

void gpio_set_value_cansleep(unsigned gpio, int value);

獲取一個GPIO并聲明標(biāo)簽:

/* request GPIO, returning 0 or negative errno.

* non-null labels may be useful for diagnostics.

*/

int gpio_request(unsigned gpio, const char *label);

/* release previously-claimed GPIO */

void gpio_free(unsigned gpio);


將GPIO映射為IRQ中斷:

/* map GPIO numbers to IRQ numbers */

int gpio_to_irq(unsigned gpio);

/* map IRQ numbers to GPIO numbers (avoid using this) */

int irq_to_gpio(unsigned irq);


設(shè)置GPIO IRQ中斷類型:


if (!sw->both_edges) {

if (gpio_get_value(sw->gpio))

set_irq_type(gpio_to_irq(sw->gpio), IRQ_TYPE_EDGE_FALLING);

else

set_irq_type(gpio_to_irq(sw->gpio), IRQ_TYPE_EDGE_RISING);

在驅(qū)動中使用延時函數(shù)mdelay,需要包含<linux/delay.h>文件




對于硬件產(chǎn)生的變化,需要CPI進(jìn)行處理,通過情況下CPU獲取該變化的方式有種

1)輪詢

2)中斷  #include <linux/interrup>

Request irq(...)

注冊中斷request_irq(...)

Irq 要注冊的中斷號  arm/mach-xxxxx/include/mach/irqs.h

Gpio_to_irq()

handle  中斷處理函數(shù)

Irqreturn_t xxxxx(int irq,void *dev_id)

Irqflags 中斷標(biāo)志

Cat /proc/interrupts

IRQF_SHARED

flag定義 描述

IRQF_TRIGGER_XXX 描述該interrupt line觸發(fā)類型的flag

IRQF_DISABLED 首先要說明的是這是一個廢棄的flag,在新的內(nèi)核中,該flag沒有任何的作用了。具體可以參考:Disabling IRQF_DISABLED 

舊的內(nèi)核(2.6.35版本之前)認(rèn)為有兩種interrupt handler:slow handler和fast handle。在request irq的時候,對于fast handler,需要傳遞IRQF_DISABLED的參數(shù),確保其中斷處理過程中是關(guān)閉CPU的中斷,因為是fast handler,執(zhí)行很快,即便是關(guān)閉CPU中斷不會影響系統(tǒng)的性能。但是,并不是每一種外設(shè)中斷的handler都是那么快(例如磁盤),因此就有 slow handler的概念,說明其在中斷處理過程中會耗時比較長。對于這種情況,在執(zhí)行interrupt handler的時候不能關(guān)閉CPU中斷,否則對系統(tǒng)的performance會有影響。 

新的內(nèi)核已經(jīng)不區(qū)分slow handler和fast handle,都是fast handler,都是需要關(guān)閉CPU中斷的,那些需要后續(xù)處理的內(nèi)容推到threaded interrupt handler中去執(zhí)行。

IRQF_SHARED 這是flag用來描述一個interrupt line是否允許在多個設(shè)備中共享。如果中斷控制器可以支持足夠多的interrupt source,那么在兩個外設(shè)間共享一個interrupt request line是不推薦的,畢竟有一些額外的開銷(發(fā)生中斷的時候要逐個詢問是不是你的中斷,軟件上就是遍歷action list),因此外設(shè)的irq handler中最好是一開始就啟動判斷,看看是否是自己的中斷,如果不是,返回IRQ_NONE,表示這個中斷不歸我管。 早期PC時代,使用8259中斷控制器,級聯(lián)的8259最多支持15個外部中斷,但是PC外設(shè)那么多,因此需要irq share?,F(xiàn)在,ARM平臺上的系統(tǒng)設(shè)計很少會采用外設(shè)共享IRQ方式,畢竟一般ARM SOC提供的有中斷功能的GPIO非常的多,足夠用的。 當(dāng)然,如果確實需要兩個外設(shè)共享IRQ,那也只能如此設(shè)計了。對于HW,中斷控制器的一個interrupt source的引腳要接到兩個外設(shè)的interrupt request line上,怎么接?直接連接可以嗎?當(dāng)然不行,對于低電平觸發(fā)的情況,我們可以考慮用與門連接中斷控制器和外設(shè)。

IRQF_PROBE_SHARED IRQF_SHARED用來表示該interrupt action descriptor是允許和其他device共享一個interrupt line(IRQ number),但是實際上是否能夠share還是需要其他條件:例如觸發(fā)方式必須相同。有些驅(qū)動程序可能有這樣的調(diào)用場景:我只是想scan一個irq table,看看哪一個是OK的,這時候,如果即便是不能和其他的驅(qū)動程序share這個interrupt line,我也沒有關(guān)系,我就是想scan看看情況。這時候,caller其實可以預(yù)見sharing mismatche的發(fā)生,因此,不需要內(nèi)核打印“Flags mismatch irq……“這樣冗余的信息

IRQF_PERCPU 在SMP的架構(gòu)下,中斷有兩種mode,一種中斷是在所有processor之間共享的,也就是global的,一旦中斷產(chǎn)生,interrupt controller可以把這個中斷送達(dá)多個處理器。當(dāng)然,在具體實現(xiàn)的時候不會同時將中斷送達(dá)多個CPU,一般是軟件和硬件協(xié)同處理,將中斷送達(dá)一個CPU處理。但是一段時間內(nèi)產(chǎn)生的中斷可以平均(或者按照既定的策略)分配到一組CPU上。這種interrupt mode下,interrupt controller針對該中斷的operational register是global的,所有的CPU看到的都是一套寄存器,一旦一個CPU ack了該中斷,那么其他的CPU看到的該interupt source的狀態(tài)也是已經(jīng)ack的狀態(tài)。 

和global對應(yīng)的就是per cpu interrupt了,對于這種interrupt,不是processor之間共享的,而是特定屬于一個CPU的。例如GIC中interrupt ID等于30的中斷就是per cpu的(這個中斷event被用于各個CPU的local timer),這個中斷號雖然只有一個,但是,實際上控制該interrupt ID的寄存器有n組(如果系統(tǒng)中有n個processor),每個CPU看到的是不同的控制寄存器。在具體實現(xiàn)中,這些寄存器組有兩種形態(tài),一種是banked,所有CPU操作同樣的寄存器地址,硬件系統(tǒng)會根據(jù)訪問的cpu定向到不同的寄存器,另外一種是non banked,也就是說,對于該interrupt source,每個cpu都有自己獨特的訪問地址。

IRQF_NOBALANCING 這也是和multi-processor相關(guān)的一個flag。對于那些可以在多個CPU之間共享的中斷,具體送達(dá)哪一個processor是有策略的,我們可以在多個CPU之間進(jìn)行平衡。如果你不想讓你的中斷參與到irq balancing的過程中那么就設(shè)定這個flag

IRQF_IRQPOLL  

IRQF_ONESHOT one shot本身的意思的只有一次的,結(jié)合到中斷這個場景,則表示中斷是一次性觸發(fā)的,不能嵌套。對于primary handler,當(dāng)然是不會嵌套,但是對于threaded interrupt handler,我們有兩種選擇,一種是mask該interrupt source,另外一種是unmask該interrupt source。一旦mask住該interrupt source,那么該interrupt source的中斷在整個threaded interrupt handler處理過程中都是不會再次觸發(fā)的,也就是one shot了。這種handler不需要考慮重入問題。 

具體是否要設(shè)定one shot的flag是和硬件系統(tǒng)有關(guān)的,我們舉一個例子,比如電池驅(qū)動,電池里面有一個電量計,是使用HDQ協(xié)議進(jìn)行通信的,電池驅(qū)動會注冊一個threaded interrupt handler,在這個handler中,會通過HDQ協(xié)議和電量計進(jìn)行通信。對于這個handler,通過HDQ進(jìn)行通信是需要一個完整的HDQ交互過程,如果中間被打斷,整個通信過程會出問題,因此,這個handler就必須是one shot的。

IRQF_NO_SUSPEND 這個flag比較好理解,就是說在系統(tǒng)suspend的時候,不用disable這個中斷,如果disable,可能會導(dǎo)致系統(tǒng)不能正常的resume。

IRQF_FORCE_RESUME 在系統(tǒng)resume的過程中,強(qiáng)制必須進(jìn)行enable的動作,即便是設(shè)定了IRQF_NO_SUSPEND這個flag。這是和特定的硬件行為相關(guān)的。

IRQF_NO_THREAD 有些low level的interrupt是不能線程化的(例如系統(tǒng)timer的中斷),這個flag就是起這個作用的。另外,有些級聯(lián)的interrupt controller對應(yīng)的IRQ也是不能線程化的(例如secondary GIC對應(yīng)的IRQ),它的線程化可能會影響一大批附屬于該interrupt controller的外設(shè)的中斷響應(yīng)延遲。

IRQF_EARLY_RESUME  

IRQF_TIMER





如何判斷哪個按鍵觸發(fā)的中斷

如何判斷怎么觸發(fā)(上升沿,下降沿,高電平,低電平)中斷的

          使用管腳的輸入功能(配置成輸入功能)判斷電平

          重新配置為外部中斷功能

如何去抖動

Linux內(nèi)核中中斷處理程序的一般結(jié)構(gòu)

頂半部:完成盡可能少的緊急功能,往往是簡單的讀取寄存器,清除中斷標(biāo)志。登記底半部。

底半部:完成中斷處理程序中絕大部分工作,通常這部分都比較耗時。

1)軟中斷

2)tasklet(利用了軟中斷的機(jī)制)

struct  tasklet_struct{

Void(*func)(unsigned  long);//底半部完成函數(shù)

Unsigned long data;

。。。 

}

tasklet_scedule(。。。)//完成底半部的登記

DECLARE_TASKLET 定義并初始化

3)工作者隊列

Struct work_struct{


}

INIT_WAORK//初始化work

Schrdiule_work  //登記work

flush_work//

Tasklet和工作者隊列有啥區(qū)別

Asklet中的func函數(shù)工作于中斷上下文,func不允許睡眠的,work中的func工作于進(jìn)程上下文

IO與內(nèi)存:

統(tǒng)一編址(ARM):

MOV 

獨立編址(X86):

MOV R0 [0X100]

IN/OUT 0X100

ARM PowePC MPIS 都是用統(tǒng)一編址

X86使用獨立編址

Linux編程使用到的都是虛擬地址,驅(qū)動開發(fā)時,從芯片手冊得到的物理地址,需要轉(zhuǎn)換成虛擬地址后再使用






/*包含初始化宏定義的頭文件,代碼中的module_init和module_exit在此文件中*/

#include <linux/init.h>

/*包含初始化加載模塊的頭文件,代碼中的MODULE_LICENSE在此頭文件中*/

#include <linux/module.h>

/*定義module_param module_param_array的頭文件*/

#include <linux/moduleparam.h>

/*定義module_param module_param_array中perm的頭文件*/

#include <linux/stat.h>

/*三個字符設(shè)備函數(shù)*/

#include <linux/fs.h>

/*MKDEV轉(zhuǎn)換設(shè)備號數(shù)據(jù)類型的宏定義*/

#include <linux/kdev_t.h>

/*定義字符設(shè)備的結(jié)構(gòu)體*/

#include <linux/cdev.h>

/*分配內(nèi)存空間函數(shù)頭文件*/

#include <linux/slab.h>

/*包含函數(shù)device_creatchar_driver_ledse 結(jié)構(gòu)體class等頭文件*/

#include <linux/device.h>

#include <linux/wait.h>

/*自定義頭文件*/

#include "char_driver_leds.h"

#include <linux/sched.h>


//#include <stdio.h> 

#include <linux/delay.h>




#include <linux/interrupt.h>

#include <asm/gpio.h>

#include <plat/gpio-cfg.h>


/*Linux中申請GPIO的頭文件*/

#include <linux/gpio.h>

/*三星平臺的GPIO配置函數(shù)頭文件*/

/*三星平臺EXYNOS系列平臺,GPIO配置參數(shù)宏定義頭文件*/

#include <plat/gpio-cfg.h>

/*三星平臺4412平臺,GPIO宏定義頭文件*/

#include <mach/gpio-exynos4.h>



MODULE_LICENSE("Dual BSD/GPL");

/*聲明是開源的,沒有內(nèi)核版本限制*/

MODULE_AUTHOR("songmao");

/*聲明作者*/





static int led_gpios[] = {

EXYNOS4_GPL2(0),EXYNOS4_GPK1(1),

};

#define LED_NUM ARRAY_SIZE(led_gpios)


static int irq_gpio[] ={

EXYNOS4_GPX1(1),EXYNOS4_GPX1(2),

    EXYNOS4_GPX1(0),EXYNOS4_GPX1(1)

};


#define IRQ_NUM ARRAY_SIZE(irq_gpio)

int led_num[4];


int numdev_major = DEV_MAJOR;

int numdev_minor = DEV_MINOR;


/*輸入主設(shè)備號*/

module_param(numdev_major,int,S_IRUSR);

/*輸入次設(shè)備號*/

module_param(numdev_minor,int,S_IRUSR);





static struct class *myclass;

struct reg_dev *my_devices;


/*打開操作*/

static int chardevnode_open(struct inode *inode, struct file *file)

{

    struct reg_dev *reg_devp =NULL;


reg_devp =container_of(inode->i_cdev,struct reg_dev,cdev);

file->private_data=reg_devp;

   

printk(KERN_EMERG "chardevnode_open is success!\n");

/* if(!atomic_dec_and_test(&(reg_devp->atc)))

{

  printk(KERN_ERR "atomic:device can open only once!");

  atomic_inc(&(reg_devp->atc));

  return -EBUSY;

}*/

spin_lock(&(reg_devp->lock));

if(OPEN_NUM<=reg_devp->open_num){

      spin_unlock(&(reg_devp->lock));

   printk(KERN_ERR "atomic:device can open over num!");

return -EBUSY;

}

reg_devp->open_num++;

spin_unlock(&(reg_devp->lock));

return 0;

}

/*關(guān)閉操作*/

static int chardevnode_release(struct inode *inode, struct file *file)

{

    struct reg_dev *reg_devp =NULL;

reg_devp=file->private_data;

printk(KERN_EMERG "chardevnode_release is success!\n");

//atomic_inc(&(reg_devp->atc));

/*spin_lock(&(reg_devp->lock));

reg_devp->open_num--;

spin_unlock(&(reg_devp->lock));*/

up(&(reg_devp->sem_open));

return 0;

}

/*IO操作*/

static long chardevnode_ioctl(struct file *file, unsigned int cmd, unsigned long arg){

struct reg_dev *reg_devp =NULL;

reg_devp=file->private_data;

switch(cmd)

{

case 0:

case 1:

if (arg > LED_NUM) {

return -EINVAL;

}


gpio_set_value(led_gpios[arg], cmd);

break;


default:

return -EINVAL;

}

printk(KERN_EMERG "chardevnode_ioctl is success! cmd is %d,arg is %d \n",cmd,arg);


up(&(reg_devp->sem_read));

up(&(reg_devp->sem_write));

return 0;

}


ssize_t chardevnode_read(struct file *file, char __user *buf, size_t count, loff_t *f_ops)

{

    struct reg_dev *reg_devp =NULL;

reg_devp=file->private_data;

//down_interruptible(&(reg_devp->sem_read));

wait_event_interruptible(reg_devp->wqh,0!=reg_devp->led);

reg_devp->led=0;

printk(KERN_INFO"chardevnode_read success");

return 0;

}


ssize_t chardevnode_write(struct file *file, const char __user *buf, size_t count, loff_t *f_ops)

     struct reg_dev *reg_devp =NULL;

reg_devp=file->private_data;

//down_interruptible(&(reg_devp->sem_write));

reg_devp->led=1;

wake_up_interruptible(&(reg_devp->wqh));

printk(KERN_INFO"chardevnode_write success");

return 0;

}


loff_t chardevnode_llseek(struct file *file, loff_t offset, int ence){

return 0;

}

struct file_operations my_fops = {

.owner = THIS_MODULE,

.open = chardevnode_open,

.release = chardevnode_release,

.unlocked_ioctl = chardevnode_ioctl,

.read = chardevnode_read,

.write = chardevnode_write,

.llseek = chardevnode_llseek,

};


static void chardeviced_cdd_work_func(struct work_struct *work)

{

      int i = 0,j,ledss;

      struct reg_dev *pcdevp = container_of(work,struct reg_dev,cdd_work);

 printk(KERN_INFO" CharDeviceDriver:  Entry cdd_work");

 printk(KERN_ALERT"CharDeviceDriver:CDD work name:%s \n",pcdevp->cdd_work_name);

 for(i=0;i<4;i++)

 { 

      if(led_num[i]>0)

      {

          j=i%2;

if(i>1)

ledss=1;

else

ledss=0;

                 gpio_set_value(led_gpios[j],ledss);

      }  

}


static void chardevicedriver_cdd_delayed_work_func(struct work_struct *work)

{

     struct reg_dev *pcdevp = container_of(work,struct reg_dev,cdd_delayed_work);

printk(KERN_INFO" CharDeviceDriver:  Entry chardeviceddriver_cdd_delayed_work_func");

printk(KERN_ALERT"CharDeviceDriver:CDD delayed work namename:%s \n",pcdevp->cdd_delayed_work_name);

}


static irq_handler_t chardevnode_irq(int irq,void *dev_id)

{

    struct reg_dev *reg_devp = (struct reg_dev *)dev_id;

for(i=0;i<IRQ_NUM;i++){

s3c_gpio_cfgpin(irq_gpio[i], S3C_GPIO_SFN(0X0));

        led_num[i] = gpio_get_value(irq_gpio[i]);

   s3c_gpio_cfgpin(irq_gpio[i], S3C_GPIO_SFN(0X0));

}



tasklet_schedule(&(my_devices));


schedule_work(&(reg_devp->cdd_work));


queue_delayed_work(reg_devp->cdd_workqueue,&(reg_devp->cdd_delayed_work),3*HZ);


return IRQ_HANDLED;

}


/*設(shè)備注冊到系統(tǒng)*/

static void reg_init_cdev(struct reg_dev *dev,int index){

int err;

int devno = MKDEV(numdev_major,numdev_minor+index);


/*數(shù)據(jù)初始化*/

cdev_init(&dev->cdev,&my_fops);

dev->cdev.owner = THIS_MODULE;

dev->cdev.ops = &my_fops;

/*注冊到系統(tǒng)*/

err = cdev_add(&dev->cdev,devno,1);

if(err){

printk(KERN_EMERG "cdev_add %d is fail! %d\n",index,err);

}

else{

printk(KERN_EMERG "cdev_add %d is success!\n",numdev_minor+index);

}

}


static int gpio_init(void){

int i=0,ret;

for(i=0;i<LED_NUM;i++){

ret = gpio_request(led_gpios[i], "LED");

if (ret) {

printk("%s: request GPIO %d for LED failed, ret = %d\n", DEVICE_NAME,i,ret);

return -1;

}

else{

s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);

gpio_set_value(led_gpios[i], 1);

}

}


for(i=0;i<IRQ_NUM;i++){

ret = gpio_request(irq_gpio[i], "IRQ");

if (ret) {

printk("%s: request GPIO %d for IRQ failed, ret = %d\n", DEVICE_NAME,i,ret);

return -1;

}

else{

s3c_gpio_cfgpin(irq_gpio[i], S3C_GPIO_SFN(0X0F));

}

}

     ret = request_irq(IRQ_EINT(9),chardevnode_irq,IRQF_SAMPLE_RANDOM|IRQF_TRIGGER_LOW,"interrupt0",my_devices);

     if(0>ret){

          printk(KERN_ERR "Chardevnode_irq:Failure is IRQ_EINT(9)");

 goto FAIL_IRQ_EINT;

}

 ret = request_irq(IRQ_EINT(10),chardevnode_irq,IRQF_SAMPLE_RANDOM|IRQF_TRIGGER_LOW,"interrupt1",my_devices);

     if(0>ret){

          printk(KERN_ERR "Chardevnode_irq:Failure is IRQ_EINT(10)");

}

 

      ret = request_irq(IRQ_EINT(17),chardevnode_irq,IRQF_SAMPLE_RANDOM|IRQF_TRIGGER_LOW,"interrupt2",my_devices);

     if(0>ret){

          printk(KERN_ERR "Chardevnode_irq:Failure is IRQ_EINT(17)");

}

      ret = request_irq(IRQ_EINT(18),chardevnode_irq,IRQF_SAMPLE_RANDOM|IRQF_TRIGGER_LOW,"interrupt3",my_devices);

     if(0>ret){

          printk(KERN_ERR "Chardevnode_irq:Failure is IRQ_EINT(17)");

}


printk(KERN_INFO"Chardevnode_irq:Success is IRQ_EINT");

return 0;

FAIL_IRQ_EINT: 

     free_irq(IRQ_EINT(9),my_devices);

free_irq(IRQ_EINT(10),my_devices);

free_irq(IRQ_EINT(17),my_devices);

free_irq(IRQ_EINT(18),my_devices);

 for(i=0;i<LED_NUM;i++)

gpio_free(led_gpios[i]);

for(i=0;i<IRQ_NUM;i++){

ret = gpio_free(irq_gpio[i]);

}



static int __init scdev_init(void)

{

int ret = 0,i;

dev_t num_dev;

printk(KERN_EMERG "numdev_major is %d!\n",numdev_major);

printk(KERN_EMERG "numdev_minor is %d!\n",numdev_minor);

if(numdev_major){

num_dev = MKDEV(numdev_major,numdev_minor);

ret = register_chrdev_region(num_dev,DEVICE_MINOR_NUM,DEVICE_NAME);

}

else{

/*動態(tài)注冊設(shè)備號*/

ret = alloc_chrdev_region(&num_dev,numdev_minor,DEVICE_MINOR_NUM,DEVICE_NAME);

/*獲得主設(shè)備號*/

numdev_major = MAJOR(num_dev);

printk(KERN_EMERG "adev_region req %d !\n",numdev_major);

}

if(ret<0){

printk(KERN_EMERG "register_chrdev_region req %d is failed!\n",numdev_major);

}

myclass = class_create(THIS_MODULE,DEVICE_NAME);

my_devices = kmalloc(DEVICE_MINOR_NUM * sizeof(struct reg_dev),GFP_KERNEL);

if(!my_devices){

ret = -ENOMEM;

goto fail;

}

memset(my_devices,0,DEVICE_MINOR_NUM * sizeof(struct reg_dev));

/*設(shè)備初始化*/

for(i=0;i<DEVICE_MINOR_NUM;i++){

my_devices[i].data = kmalloc(REGDEV_SIZE,GFP_KERNEL);

memset(my_devices[i].data,0,REGDEV_SIZE);

/*設(shè)備注冊到系統(tǒng)*/

reg_init_cdev(&my_devices[i],i);

/*創(chuàng)建設(shè)備節(jié)點*/

device_create(myclass,NULL,MKDEV(numdev_major,numdev_minor+i),NULL,DEVICE_NAME"%d",i);

//atomic_inc(&(my_devices[i].atc));

/*spin_lock_init(&(my_devices[i].lock));*/

my_devices[i].open_num=0;


/*sema_init(&(my_devices[i].sem_open,2);

    sema_init(&(my_devices[i].sem_read,1);

sema_init(&(my_devices[i].sem_write,0);*/

//init_waitqueue_head(&(my_devices[i].wqh));


//strcpy( &(my_devices[i].cdd_work_name),"cdd_work_name");

}

ret = gpio_init();

if(ret){

printk(KERN_EMERG "gpio_init failed!\n");

}

INIT_WORK(&(my_devices->cdd_work,chardeviced_cdd_work_func,NULL);

//my_devices[i].cdd_workqueue;

create_workqueue("cdd_workqueue");

if(IS_ERR(my_devices->cdd_workqueue)){

        print(KERN_ERR"ChardeviceDriver: Failure to create work_queue!\n");

ret=PTR_ERR(my_devices->cdd_workqueue);

goto failure_creat_work;

 

    }

printk(KERN_INFO"ChardeviceDriver:Success to create work_queue!\n");

strcpy( &(my_devices->cdd_work_name),"cdd_delayed_work_name");

INIT_DELAYED_WORK(my_devices->cdd_delayed_work,chardevicedriver_cdd_delayed_work_func);

printk(KERN_EMERG "scdev_init!\n");

    

/*打印信息,KERN_EMERG表示緊急信息*/

return 0;

failure_creat_work:

     free_irq(IRQ_EINT(9),my_devices);

free_irq(IRQ_EINT(10),my_devices);

free_irq(IRQ_EINT(17),my_devices);

free_irq(IRQ_EINT(18),my_devices);

 for(i=0;i<LED_NUM;i++)

gpio_free(led_gpios[i])

for(i=0;i<IRQ_NUM;i++){

ret = gpio_free(irq_gpio[i]);


fail:

/*注銷設(shè)備號*/

unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);

printk(KERN_EMERG "kmalloc is fail!\n");

return ret;

}


static void __exit scdev_exit(void)

{

int i;

printk(KERN_EMERG "scdev_exit!\n");


    free_irq(IRQ_EINT(9),my_devices);

free_irq(IRQ_EINT(10),my_devices);

free_irq(IRQ_EINT(17),my_devices);

free_irq(IRQ_EINT(18),my_devices);

/*除去字符設(shè)備*/

for(i=0;i<DEVICE_MINOR_NUM;i++){

cdev_del(&(my_devices[i].cdev));

/*摧毀設(shè)備節(jié)點函數(shù)d*/

device_destroy(myclass,MKDEV(numdev_major,numdev_minor+i));

}

/*釋放設(shè)備class*/

class_destroy(myclass);

/*釋放內(nèi)存*/

kfree(my_devices);

/*釋放GPIO*/

for(i=0;i<LED_NUM;i++){

gpio_free(led_gpios[i]);

}

unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);

}



module_init(scdev_init);

/*初始化函數(shù)*/

module_exit(scdev_exit);

/*卸載函數(shù)*/














1)申請I/O內(nèi)存   #include <asm/io.h>

  request_mem_region(start,n,name)物理地址

  Start 待申請的起始地址(物理地址)

  n,從start開始地址的字節(jié)數(shù)

  Name,

2)映射ioremap(phys,addr,size)

   phys_addr,等同于1)中的start

   size等同于n

   return value:映射后的虛擬地址 vir_addr

   如:vir_addr=ioremap(0xE0200008,4)

  ioread8   ioread16   ioread32

  ioreite8   iowrite16  iowrite32

  ((volatile unsigned int *)vir_addr) =0xXXXX;

  volatile

  寄存器變量,硬件地址,中斷或者多線程中共享的全局變量,防止編譯器錯誤優(yōu)化

  4)取消映射

     ioumap(*addr)

         addr,   ioremap返回的虛擬地址vir_addr

 5)釋放I/O內(nèi)存

  release_mem_region(start,n)

  回顧:內(nèi)核競態(tài)與并發(fā)

什么情況下會產(chǎn)生競態(tài)

1)SMP

2)單CPU支持任務(wù)搶占

3)中斷和進(jìn)程之間

4)中斷和中斷之間

解決競態(tài)的方法

1)中斷屏蔽

2)原子操作

位原子操作

×××原子操作 atomic{     int。。。。}

3)自旋鎖

  優(yōu)點:一旦可以獲取鎖,立即獲取

  缺點:長時間獲取鎖不成功,會消耗CPU資源

  它所保護(hù)的臨界資源(代碼段)通常比較短

4)信號量

   down(。。。)會導(dǎo)致睡眠

等等看前一章中

自旋鎖只允許一個持有者,信號量可以有多個持有者

信號量保護(hù)的臨界資源(代碼段)通常比較長

2,等待隊列      #include<linux/sched.h>

Read() recv()

Wait_event_interrptible //阻塞

Wake_up_interruptible  //喚醒



阻塞/非阻塞

實際上,應(yīng)用程序并不關(guān)心驅(qū)動里面read/write具體實現(xiàn),只管調(diào)用并獲取返回值

如果設(shè)備沒有準(zhǔn)備好數(shù)據(jù)給應(yīng)用程序讀或者沒有準(zhǔn)備好接受用戶程序?qū)?,?qū)動程序應(yīng)當(dāng)阻塞進(jìn)程,使它進(jìn)入睡眠,直到請求可以得到滿足

阻塞讀

     在阻塞型驅(qū)動程序中,如果進(jìn)程調(diào)用read設(shè)備操作,但是設(shè)備沒有數(shù)據(jù)或數(shù)據(jù)不足,進(jìn)程應(yīng)該被阻塞,當(dāng)有新數(shù)據(jù)到達(dá)后,喚醒被阻塞進(jìn)程

阻塞寫

在阻塞型驅(qū)動程序中,如果進(jìn)程調(diào)用write設(shè)備操作,但是設(shè)備沒有足夠的空間供其寫入,進(jìn)程該被阻塞,但設(shè)備中的數(shù)據(jù)讀走后,緩沖區(qū)中空出部分空間,應(yīng)該喚醒被阻塞進(jìn)程

應(yīng)用程序非阻塞讀

阻塞方式是文件讀寫操作的默認(rèn)方式

應(yīng)用程序可以通過使用O_NONBLOCK標(biāo)志來人為的設(shè)置讀寫操作為非阻塞方式

     定義在<asm-generic/fcntl.h>

     如果設(shè)置了O_NONBLOCK標(biāo)志,read和write的處理行為相同

if(0==pcdevp->)&&(O_NONBLICK&file->f_flags){

   printk(KERN_ALET “Char......”);

 return -EAGAIN;











---------------------------------------------------------------

多路監(jiān)聽偵測 select    #include <linux/poll.h>

int select(int nfds, fd_set *readfds, fd_set *writefds,

                  fd_set *exceptfds, struct timeval *timeout);

Nfds 要監(jiān)聽的文件描述符最大值+1

Readfds,要監(jiān)聽的讀文件描述符集合

Writefds要監(jiān)聽的寫文件描述符集合

Exceptfds 要監(jiān)聽的異常文件描述集合

Timeout 監(jiān)聽的超時時間


void FD_CLR(int fd, fd_set *set);

       int  FD_ISSET(int fd, fd_set *set);

       void FD_SET(int fd, fd_set *set);

       void FD_ZERO(fd_set *set);

內(nèi)核中要實現(xiàn)的函數(shù)

Unsigned int(*poll)()

Fd_set rfds;

Struct timeval tv

FD_ZERO()

加入集合FD_SET()


POLLIN

有數(shù)據(jù)可讀。

POLLRDNORM

有普通數(shù)據(jù)可讀。

POLLRDBAND

有優(yōu)先數(shù)據(jù)可讀。

POLLPRI

有緊迫數(shù)據(jù)可讀。

POLLOUT

寫數(shù)據(jù)不會導(dǎo)致阻塞。

POLLWRNORM

寫普通數(shù)據(jù)不會導(dǎo)致阻塞。

POLLWRBAND

寫優(yōu)先數(shù)據(jù)不會導(dǎo)致阻塞。

POLLMSG

SIGPOLL 消息可用。

此外,revents域中還可能返回下列事件:

POLLER

指定的文件描述符發(fā)生錯誤。

POLLHUP

指定的文件描述符掛起事件。

POLLNVAL

指定的文件描述符非法。

這些事件在events域中無意義,因為它們在合適的時候總是會從revents中返回。使用poll()和select()不一樣,你不需要顯式地請求異常情況報告。

POLLIN | POLLPRI等價于select()的讀事件,POLLOUT |POLLWRBAND等價于select()的寫事件。POLLIN等價于POLLRDNORM |POLLRDBAND,而POLLOUT則等價于POLLWRNORM。




中斷


驅(qū)動需要包含 #include <linux/gpio.h>

判斷一個IO是否合法:int gpio_is_valid(int number);

設(shè)置GPIO的方向,如果是輸出同時設(shè)置電平:

/* set as input or output, returning 0 or negative errno */

int gpio_direction_input(unsigned gpio);

int gpio_direction_output(unsigned gpio, int value);

獲取輸入引腳的電平:

/* GPIO INPUT: return zero or nonzero */

int gpio_get_value(unsigned gpio);

/* GPIO OUTPUT */

void gpio_set_value(unsigned gpio, int value);

int gpio_cansleep(unsigned gpio);

To access such GPIOs, a different set of accessors is defined:

/* GPIO INPUT: return zero or nonzero, might sleep */

int gpio_get_value_cansleep(unsigned gpio);

/* GPIO OUTPUT, might sleep */

void gpio_set_value_cansleep(unsigned gpio, int value);

獲取一個GPIO并聲明標(biāo)簽:

/* request GPIO, returning 0 or negative errno.

* non-null labels may be useful for diagnostics.

*/

int gpio_request(unsigned gpio, const char *label);

/* release previously-claimed GPIO */

void gpio_free(unsigned gpio);


將GPIO映射為IRQ中斷:

/* map GPIO numbers to IRQ numbers */

int gpio_to_irq(unsigned gpio);

/* map IRQ numbers to GPIO numbers (avoid using this) */

int irq_to_gpio(unsigned irq);


設(shè)置GPIO IRQ中斷類型:


if (!sw->both_edges) {

if (gpio_get_value(sw->gpio))

set_irq_type(gpio_to_irq(sw->gpio), IRQ_TYPE_EDGE_FALLING);

else

set_irq_type(gpio_to_irq(sw->gpio), IRQ_TYPE_EDGE_RISING);

在驅(qū)動中使用延時函數(shù)mdelay,需要包含<linux/delay.h>文件




對于硬件產(chǎn)生的變化,需要CPI進(jìn)行處理,通過情況下CPU獲取該變化的方式有種

1)輪詢

2)中斷  #include <linux/interrup>

Request irq(...)

注冊中斷request_irq(...)

Irq 要注冊的中斷號  arm/mach-xxxxx/include/mach/irqs.h

Gpio_to_irq()

handle  中斷處理函數(shù)

Irqreturn_t xxxxx(int irq,void *dev_id)

Irqflags 中斷標(biāo)志

Cat /proc/interrupts

IRQF_SHARED

flag定義 描述

IRQF_TRIGGER_XXX 描述該interrupt line觸發(fā)類型的flag

IRQF_DISABLED 首先要說明的是這是一個廢棄的flag,在新的內(nèi)核中,該flag沒有任何的作用了。具體可以參考:Disabling IRQF_DISABLED 

舊的內(nèi)核(2.6.35版本之前)認(rèn)為有兩種interrupt handler:slow handler和fast handle。在request irq的時候,對于fast handler,需要傳遞IRQF_DISABLED的參數(shù),確保其中斷處理過程中是關(guān)閉CPU的中斷,因為是fast handler,執(zhí)行很快,即便是關(guān)閉CPU中斷不會影響系統(tǒng)的性能。但是,并不是每一種外設(shè)中斷的handler都是那么快(例如磁盤),因此就有 slow handler的概念,說明其在中斷處理過程中會耗時比較長。對于這種情況,在執(zhí)行interrupt handler的時候不能關(guān)閉CPU中斷,否則對系統(tǒng)的performance會有影響。 

新的內(nèi)核已經(jīng)不區(qū)分slow handler和fast handle,都是fast handler,都是需要關(guān)閉CPU中斷的,那些需要后續(xù)處理的內(nèi)容推到threaded interrupt handler中去執(zhí)行。

IRQF_SHARED 這是flag用來描述一個interrupt line是否允許在多個設(shè)備中共享。如果中斷控制器可以支持足夠多的interrupt source,那么在兩個外設(shè)間共享一個interrupt request line是不推薦的,畢竟有一些額外的開銷(發(fā)生中斷的時候要逐個詢問是不是你的中斷,軟件上就是遍歷action list),因此外設(shè)的irq handler中最好是一開始就啟動判斷,看看是否是自己的中斷,如果不是,返回IRQ_NONE,表示這個中斷不歸我管。 早期PC時代,使用8259中斷控制器,級聯(lián)的8259最多支持15個外部中斷,但是PC外設(shè)那么多,因此需要irq share?,F(xiàn)在,ARM平臺上的系統(tǒng)設(shè)計很少會采用外設(shè)共享IRQ方式,畢竟一般ARM SOC提供的有中斷功能的GPIO非常的多,足夠用的。 當(dāng)然,如果確實需要兩個外設(shè)共享IRQ,那也只能如此設(shè)計了。對于HW,中斷控制器的一個interrupt source的引腳要接到兩個外設(shè)的interrupt request line上,怎么接?直接連接可以嗎?當(dāng)然不行,對于低電平觸發(fā)的情況,我們可以考慮用與門連接中斷控制器和外設(shè)。

IRQF_PROBE_SHARED IRQF_SHARED用來表示該interrupt action descriptor是允許和其他device共享一個interrupt line(IRQ number),但是實際上是否能夠share還是需要其他條件:例如觸發(fā)方式必須相同。有些驅(qū)動程序可能有這樣的調(diào)用場景:我只是想scan一個irq table,看看哪一個是OK的,這時候,如果即便是不能和其他的驅(qū)動程序share這個interrupt line,我也沒有關(guān)系,我就是想scan看看情況。這時候,caller其實可以預(yù)見sharing mismatche的發(fā)生,因此,不需要內(nèi)核打印“Flags mismatch irq……“這樣冗余的信息

IRQF_PERCPU 在SMP的架構(gòu)下,中斷有兩種mode,一種中斷是在所有processor之間共享的,也就是global的,一旦中斷產(chǎn)生,interrupt controller可以把這個中斷送達(dá)多個處理器。當(dāng)然,在具體實現(xiàn)的時候不會同時將中斷送達(dá)多個CPU,一般是軟件和硬件協(xié)同處理,將中斷送達(dá)一個CPU處理。但是一段時間內(nèi)產(chǎn)生的中斷可以平均(或者按照既定的策略)分配到一組CPU上。這種interrupt mode下,interrupt controller針對該中斷的operational register是global的,所有的CPU看到的都是一套寄存器,一旦一個CPU ack了該中斷,那么其他的CPU看到的該interupt source的狀態(tài)也是已經(jīng)ack的狀態(tài)。 

和global對應(yīng)的就是per cpu interrupt了,對于這種interrupt,不是processor之間共享的,而是特定屬于一個CPU的。例如GIC中interrupt ID等于30的中斷就是per cpu的(這個中斷event被用于各個CPU的local timer),這個中斷號雖然只有一個,但是,實際上控制該interrupt ID的寄存器有n組(如果系統(tǒng)中有n個processor),每個CPU看到的是不同的控制寄存器。在具體實現(xiàn)中,這些寄存器組有兩種形態(tài),一種是banked,所有CPU操作同樣的寄存器地址,硬件系統(tǒng)會根據(jù)訪問的cpu定向到不同的寄存器,另外一種是non banked,也就是說,對于該interrupt source,每個cpu都有自己獨特的訪問地址。

IRQF_NOBALANCING 這也是和multi-processor相關(guān)的一個flag。對于那些可以在多個CPU之間共享的中斷,具體送達(dá)哪一個processor是有策略的,我們可以在多個CPU之間進(jìn)行平衡。如果你不想讓你的中斷參與到irq balancing的過程中那么就設(shè)定這個flag

IRQF_IRQPOLL  

IRQF_ONESHOT one shot本身的意思的只有一次的,結(jié)合到中斷這個場景,則表示中斷是一次性觸發(fā)的,不能嵌套。對于primary handler,當(dāng)然是不會嵌套,但是對于threaded interrupt handler,我們有兩種選擇,一種是mask該interrupt source,另外一種是unmask該interrupt source。一旦mask住該interrupt source,那么該interrupt source的中斷在整個threaded interrupt handler處理過程中都是不會再次觸發(fā)的,也就是one shot了。這種handler不需要考慮重入問題。 

具體是否要設(shè)定one shot的flag是和硬件系統(tǒng)有關(guān)的,我們舉一個例子,比如電池驅(qū)動,電池里面有一個電量計,是使用HDQ協(xié)議進(jìn)行通信的,電池驅(qū)動會注冊一個threaded interrupt handler,在這個handler中,會通過HDQ協(xié)議和電量計進(jìn)行通信。對于這個handler,通過HDQ進(jìn)行通信是需要一個完整的HDQ交互過程,如果中間被打斷,整個通信過程會出問題,因此,這個handler就必須是one shot的。

IRQF_NO_SUSPEND 這個flag比較好理解,就是說在系統(tǒng)suspend的時候,不用disable這個中斷,如果disable,可能會導(dǎo)致系統(tǒng)不能正常的resume。

IRQF_FORCE_RESUME 在系統(tǒng)resume的過程中,強(qiáng)制必須進(jìn)行enable的動作,即便是設(shè)定了IRQF_NO_SUSPEND這個flag。這是和特定的硬件行為相關(guān)的。

IRQF_NO_THREAD 有些low level的interrupt是不能線程化的(例如系統(tǒng)timer的中斷),這個flag就是起這個作用的。另外,有些級聯(lián)的interrupt controller對應(yīng)的IRQ也是不能線程化的(例如secondary GIC對應(yīng)的IRQ),它的線程化可能會影響一大批附屬于該interrupt controller的外設(shè)的中斷響應(yīng)延遲。

IRQF_EARLY_RESUME  

IRQF_TIMER





如何判斷哪個按鍵觸發(fā)的中斷

如何判斷怎么觸發(fā)(上升沿,下降沿,高電平,低電平)中斷的

          使用管腳的輸入功能(配置成輸入功能)判斷電平

          重新配置為外部中斷功能

如何去抖動

Linux內(nèi)核中中斷處理程序的一般結(jié)構(gòu)

頂半部:完成盡可能少的緊急功能,往往是簡單的讀取寄存器,清除中斷標(biāo)志。登記底半部。

底半部:完成中斷處理程序中絕大部分工作,通常這部分都比較耗時。

1)軟中斷

2)tasklet(利用了軟中斷的機(jī)制)

struct  tasklet_struct{

Void(*func)(unsigned  long);//底半部完成函數(shù)

Unsigned long data;

。。。 

}

tasklet_scedule(。。。)//完成底半部的登記

DECLARE_TASKLET 定義并初始化

3)工作者隊列

Struct work_struct{


}

INIT_WAORK//初始化work

Schrdiule_work  //登記work

flush_work//

Tasklet和工作者隊列有啥區(qū)別

Asklet中的func函數(shù)工作于中斷上下文,func不允許睡眠的,work中的func工作于進(jìn)程上下文

IO與內(nèi)存:

統(tǒng)一編址(ARM):

MOV 

獨立編址(X86):

MOV R0 [0X100]

IN/OUT 0X100

ARM PowePC MPIS 都是用統(tǒng)一編址

X86使用獨立編址

Linux編程使用到的都是虛擬地址,驅(qū)動開發(fā)時,從芯片手冊得到的物理地址,需要轉(zhuǎn)換成虛擬地址后再使用






/*包含初始化宏定義的頭文件,代碼中的module_init和module_exit在此文件中*/

#include <linux/init.h>

/*包含初始化加載模塊的頭文件,代碼中的MODULE_LICENSE在此頭文件中*/

#include <linux/module.h>

/*定義module_param module_param_array的頭文件*/

#include <linux/moduleparam.h>

/*定義module_param module_param_array中perm的頭文件*/

#include <linux/stat.h>

/*三個字符設(shè)備函數(shù)*/

#include <linux/fs.h>

/*MKDEV轉(zhuǎn)換設(shè)備號數(shù)據(jù)類型的宏定義*/

#include <linux/kdev_t.h>

/*定義字符設(shè)備的結(jié)構(gòu)體*/

#include <linux/cdev.h>

/*分配內(nèi)存空間函數(shù)頭文件*/

#include <linux/slab.h>

/*包含函數(shù)device_creatchar_driver_ledse 結(jié)構(gòu)體class等頭文件*/

#include <linux/device.h>

#include <linux/wait.h>

/*自定義頭文件*/

#include "char_driver_leds.h"

#include <linux/sched.h>


//#include <stdio.h> 

#include <linux/delay.h>




#include <linux/interrupt.h>

#include <asm/gpio.h>

#include <plat/gpio-cfg.h>


/*Linux中申請GPIO的頭文件*/

#include <linux/gpio.h>

/*三星平臺的GPIO配置函數(shù)頭文件*/

/*三星平臺EXYNOS系列平臺,GPIO配置參數(shù)宏定義頭文件*/

#include <plat/gpio-cfg.h>

/*三星平臺4412平臺,GPIO宏定義頭文件*/

#include <mach/gpio-exynos4.h>



MODULE_LICENSE("Dual BSD/GPL");

/*聲明是開源的,沒有內(nèi)核版本限制*/

MODULE_AUTHOR("songmao");

/*聲明作者*/





static int led_gpios[] = {

EXYNOS4_GPL2(0),EXYNOS4_GPK1(1),

};

#define LED_NUM ARRAY_SIZE(led_gpios)


static int irq_gpio[] ={

EXYNOS4_GPX1(1),EXYNOS4_GPX1(2),

    EXYNOS4_GPX1(0),EXYNOS4_GPX1(1)

};


#define IRQ_NUM ARRAY_SIZE(irq_gpio)

int led_num[4];


int numdev_major = DEV_MAJOR;

int numdev_minor = DEV_MINOR;


/*輸入主設(shè)備號*/

module_param(numdev_major,int,S_IRUSR);

/*輸入次設(shè)備號*/

module_param(numdev_minor,int,S_IRUSR);





static struct class *myclass;

struct reg_dev *my_devices;


/*打開操作*/

static int chardevnode_open(struct inode *inode, struct file *file)

{

    struct reg_dev *reg_devp =NULL;


reg_devp =container_of(inode->i_cdev,struct reg_dev,cdev);

file->private_data=reg_devp;

   

printk(KERN_EMERG "chardevnode_open is success!\n");

/* if(!atomic_dec_and_test(&(reg_devp->atc)))

{

  printk(KERN_ERR "atomic:device can open only once!");

  atomic_inc(&(reg_devp->atc));

  return -EBUSY;

}*/

spin_lock(&(reg_devp->lock));

if(OPEN_NUM<=reg_devp->open_num){

      spin_unlock(&(reg_devp->lock));

   printk(KERN_ERR "atomic:device can open over num!");

return -EBUSY;

}

reg_devp->open_num++;

spin_unlock(&(reg_devp->lock));

return 0;

}

/*關(guān)閉操作*/

static int chardevnode_release(struct inode *inode, struct file *file)

{

    struct reg_dev *reg_devp =NULL;

reg_devp=file->private_data;

printk(KERN_EMERG "chardevnode_release is success!\n");

//atomic_inc(&(reg_devp->atc));

/*spin_lock(&(reg_devp->lock));

reg_devp->open_num--;

spin_unlock(&(reg_devp->lock));*/

up(&(reg_devp->sem_open));

return 0;

}

/*IO操作*/

static long chardevnode_ioctl(struct file *file, unsigned int cmd, unsigned long arg){

struct reg_dev *reg_devp =NULL;

reg_devp=file->private_data;

switch(cmd)

{

case 0:

case 1:

if (arg > LED_NUM) {

return -EINVAL;

}


gpio_set_value(led_gpios[arg], cmd);

break;


default:

return -EINVAL;

}

printk(KERN_EMERG "chardevnode_ioctl is success! cmd is %d,arg is %d \n",cmd,arg);


up(&(reg_devp->sem_read));

up(&(reg_devp->sem_write));

return 0;

}


ssize_t chardevnode_read(struct file *file, char __user *buf, size_t count, loff_t *f_ops)

{

    struct reg_dev *reg_devp =NULL;

reg_devp=file->private_data;

//down_interruptible(&(reg_devp->sem_read));

wait_event_interruptible(reg_devp->wqh,0!=reg_devp->led);

reg_devp->led=0;

printk(KERN_INFO"chardevnode_read success");

return 0;

}


ssize_t chardevnode_write(struct file *file, const char __user *buf, size_t count, loff_t *f_ops)

     struct reg_dev *reg_devp =NULL;

reg_devp=file->private_data;

//down_interruptible(&(reg_devp->sem_write));

reg_devp->led=1;

wake_up_interruptible(&(reg_devp->wqh));

printk(KERN_INFO"chardevnode_write success");

return 0;

}


loff_t chardevnode_llseek(struct file *file, loff_t offset, int ence){

return 0;

}

struct file_operations my_fops = {

.owner = THIS_MODULE,

.open = chardevnode_open,

.release = chardevnode_release,

.unlocked_ioctl = chardevnode_ioctl,

.read = chardevnode_read,

.write = chardevnode_write,

.llseek = chardevnode_llseek,

};


static void chardeviced_cdd_work_func(struct work_struct *work)

{

      int i = 0,j,ledss;

      struct reg_dev *pcdevp = container_of(work,struct reg_dev,cdd_work);

 printk(KERN_INFO" CharDeviceDriver:  Entry cdd_work");

 printk(KERN_ALERT"CharDeviceDriver:CDD work name:%s \n",pcdevp->cdd_work_name);

 for(i=0;i<4;i++)

 { 

      if(led_num[i]>0)

      {

          j=i%2;

if(i>1)

ledss=1;

else

ledss=0;

                 gpio_set_value(led_gpios[j],ledss);

      }  

}


static void chardevicedriver_cdd_delayed_work_func(struct work_struct *work)

{

     struct reg_dev *pcdevp = container_of(work,struct reg_dev,cdd_delayed_work);

printk(KERN_INFO" CharDeviceDriver:  Entry chardeviceddriver_cdd_delayed_work_func");

printk(KERN_ALERT"CharDeviceDriver:CDD delayed work namename:%s \n",pcdevp->cdd_delayed_work_name);

}


static irq_handler_t chardevnode_irq(int irq,void *dev_id)

{

    struct reg_dev *reg_devp = (struct reg_dev *)dev_id;

for(i=0;i<IRQ_NUM;i++){

s3c_gpio_cfgpin(irq_gpio[i], S3C_GPIO_SFN(0X0));

        led_num[i] = gpio_get_value(irq_gpio[i]);

   s3c_gpio_cfgpin(irq_gpio[i], S3C_GPIO_SFN(0X0));

}



tasklet_schedule(&(my_devices));


schedule_work(&(reg_devp->cdd_work));


queue_delayed_work(reg_devp->cdd_workqueue,&(reg_devp->cdd_delayed_work),3*HZ);


return IRQ_HANDLED;

}


/*設(shè)備注冊到系統(tǒng)*/

static void reg_init_cdev(struct reg_dev *dev,int index){

int err;

int devno = MKDEV(numdev_major,numdev_minor+index);


/*數(shù)據(jù)初始化*/

cdev_init(&dev->cdev,&my_fops);

dev->cdev.owner = THIS_MODULE;

dev->cdev.ops = &my_fops;

/*注冊到系統(tǒng)*/

err = cdev_add(&dev->cdev,devno,1);

if(err){

printk(KERN_EMERG "cdev_add %d is fail! %d\n",index,err);

}

else{

printk(KERN_EMERG "cdev_add %d is success!\n",numdev_minor+index);

}

}


static int gpio_init(void){

int i=0,ret;

for(i=0;i<LED_NUM;i++){

ret = gpio_request(led_gpios[i], "LED");

if (ret) {

printk("%s: request GPIO %d for LED failed, ret = %d\n", DEVICE_NAME,i,ret);

return -1;

}

else{

s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);

gpio_set_value(led_gpios[i], 1);

}

}


for(i=0;i<IRQ_NUM;i++){

ret = gpio_request(irq_gpio[i], "IRQ");

if (ret) {

printk("%s: request GPIO %d for IRQ failed, ret = %d\n", DEVICE_NAME,i,ret);

return -1;

}

else{

s3c_gpio_cfgpin(irq_gpio[i], S3C_GPIO_SFN(0X0F));

}

}

     ret = request_irq(IRQ_EINT(9),chardevnode_irq,IRQF_SAMPLE_RANDOM|IRQF_TRIGGER_LOW,"interrupt0",my_devices);

     if(0>ret){

          printk(KERN_ERR "Chardevnode_irq:Failure is IRQ_EINT(9)");

 goto FAIL_IRQ_EINT;

}

 ret = request_irq(IRQ_EINT(10),chardevnode_irq,IRQF_SAMPLE_RANDOM|IRQF_TRIGGER_LOW,"interrupt1",my_devices);

     if(0>ret){

          printk(KERN_ERR "Chardevnode_irq:Failure is IRQ_EINT(10)");

}

 

      ret = request_irq(IRQ_EINT(17),chardevnode_irq,IRQF_SAMPLE_RANDOM|IRQF_TRIGGER_LOW,"interrupt2",my_devices);

     if(0>ret){

          printk(KERN_ERR "Chardevnode_irq:Failure is IRQ_EINT(17)");

}

      ret = request_irq(IRQ_EINT(18),chardevnode_irq,IRQF_SAMPLE_RANDOM|IRQF_TRIGGER_LOW,"interrupt3",my_devices);

     if(0>ret){

          printk(KERN_ERR "Chardevnode_irq:Failure is IRQ_EINT(17)");

}


printk(KERN_INFO"Chardevnode_irq:Success is IRQ_EINT");

return 0;

FAIL_IRQ_EINT: 

     free_irq(IRQ_EINT(9),my_devices);

free_irq(IRQ_EINT(10),my_devices);

free_irq(IRQ_EINT(17),my_devices);

free_irq(IRQ_EINT(18),my_devices);

 for(i=0;i<LED_NUM;i++)

gpio_free(led_gpios[i]);

for(i=0;i<IRQ_NUM;i++){

ret = gpio_free(irq_gpio[i]);

}



static int __init scdev_init(void)

{

int ret = 0,i;

dev_t num_dev;

printk(KERN_EMERG "numdev_major is %d!\n",numdev_major);

printk(KERN_EMERG "numdev_minor is %d!\n",numdev_minor);

if(numdev_major){

num_dev = MKDEV(numdev_major,numdev_minor);

ret = register_chrdev_region(num_dev,DEVICE_MINOR_NUM,DEVICE_NAME);

}

else{

/*動態(tài)注冊設(shè)備號*/

ret = alloc_chrdev_region(&num_dev,numdev_minor,DEVICE_MINOR_NUM,DEVICE_NAME);

/*獲得主設(shè)備號*/

numdev_major = MAJOR(num_dev);

printk(KERN_EMERG "adev_region req %d !\n",numdev_major);

}

if(ret<0){

printk(KERN_EMERG "register_chrdev_region req %d is failed!\n",numdev_major);

}

myclass = class_create(THIS_MODULE,DEVICE_NAME);

my_devices = kmalloc(DEVICE_MINOR_NUM * sizeof(struct reg_dev),GFP_KERNEL);

if(!my_devices){

ret = -ENOMEM;

goto fail;

}

memset(my_devices,0,DEVICE_MINOR_NUM * sizeof(struct reg_dev));

/*設(shè)備初始化*/

for(i=0;i<DEVICE_MINOR_NUM;i++){

my_devices[i].data = kmalloc(REGDEV_SIZE,GFP_KERNEL);

memset(my_devices[i].data,0,REGDEV_SIZE);

/*設(shè)備注冊到系統(tǒng)*/

reg_init_cdev(&my_devices[i],i);

/*創(chuàng)建設(shè)備節(jié)點*/

device_create(myclass,NULL,MKDEV(numdev_major,numdev_minor+i),NULL,DEVICE_NAME"%d",i);

//atomic_inc(&(my_devices[i].atc));

/*spin_lock_init(&(my_devices[i].lock));*/

my_devices[i].open_num=0;


/*sema_init(&(my_devices[i].sem_open,2);

    sema_init(&(my_devices[i].sem_read,1);

sema_init(&(my_devices[i].sem_write,0);*/

//init_waitqueue_head(&(my_devices[i].wqh));


//strcpy( &(my_devices[i].cdd_work_name),"cdd_work_name");

}

ret = gpio_init();

if(ret){

printk(KERN_EMERG "gpio_init failed!\n");

}

INIT_WORK(&(my_devices->cdd_work,chardeviced_cdd_work_func,NULL);

//my_devices[i].cdd_workqueue;

create_workqueue("cdd_workqueue");

if(IS_ERR(my_devices->cdd_workqueue)){

        print(KERN_ERR"ChardeviceDriver: Failure to create work_queue!\n");

ret=PTR_ERR(my_devices->cdd_workqueue);

goto failure_creat_work;

 

    }

printk(KERN_INFO"ChardeviceDriver:Success to create work_queue!\n");

strcpy( &(my_devices->cdd_work_name),"cdd_delayed_work_name");

INIT_DELAYED_WORK(my_devices->cdd_delayed_work,chardevicedriver_cdd_delayed_work_func);

printk(KERN_EMERG "scdev_init!\n");

    

/*打印信息,KERN_EMERG表示緊急信息*/

return 0;

failure_creat_work:

     free_irq(IRQ_EINT(9),my_devices);

free_irq(IRQ_EINT(10),my_devices);

free_irq(IRQ_EINT(17),my_devices);

free_irq(IRQ_EINT(18),my_devices);

 for(i=0;i<LED_NUM;i++)

gpio_free(led_gpios[i])

for(i=0;i<IRQ_NUM;i++){

ret = gpio_free(irq_gpio[i]);


fail:

/*注銷設(shè)備號*/

unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);

printk(KERN_EMERG "kmalloc is fail!\n");

return ret;

}


static void __exit scdev_exit(void)

{

int i;

printk(KERN_EMERG "scdev_exit!\n");


    free_irq(IRQ_EINT(9),my_devices);

free_irq(IRQ_EINT(10),my_devices);

free_irq(IRQ_EINT(17),my_devices);

free_irq(IRQ_EINT(18),my_devices);

/*除去字符設(shè)備*/

for(i=0;i<DEVICE_MINOR_NUM;i++){

cdev_del(&(my_devices[i].cdev));

/*摧毀設(shè)備節(jié)點函數(shù)d*/

device_destroy(myclass,MKDEV(numdev_major,numdev_minor+i));

}

/*釋放設(shè)備class*/

class_destroy(myclass);

/*釋放內(nèi)存*/

kfree(my_devices);

/*釋放GPIO*/

for(i=0;i<LED_NUM;i++){

gpio_free(led_gpios[i]);

}

unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);

}



module_init(scdev_init);

/*初始化函數(shù)*/

module_exit(scdev_exit);

/*卸載函數(shù)*/














1)申請I/O內(nèi)存   #include <asm/io.h>

  request_mem_region(start,n,name)物理地址

  Start 待申請的起始地址(物理地址)

  n,從start開始地址的字節(jié)數(shù)

  Name,

2)映射ioremap(phys,addr,size)

   phys_addr,等同于1)中的start

   size等同于n

   return value:映射后的虛擬地址 vir_addr

   如:vir_addr=ioremap(0xE0200008,4)

  ioread8   ioread16   ioread32

  ioreite8   iowrite16  iowrite32

  ((volatile unsigned int *)vir_addr) =0xXXXX;

  volatile

  寄存器變量,硬件地址,中斷或者多線程中共享的全局變量,防止編譯器錯誤優(yōu)化

  4)取消映射

     ioumap(*addr)

         addr,   ioremap返回的虛擬地址vir_addr

 5)釋放I/O內(nèi)存

  release_mem_region(start,n)

  


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI