智汇工业-智慧工业、智能制造及工业智能、工业互联门户网站,专业的工业“互联网+”传媒

uClinux下中斷驅動的I/O設備驅動開發

來源:網絡

點擊:1398

A+ A-

所屬頻道:新聞中心

關鍵詞: uClinux,I/O設備驅動,

      引 言

      在32位微處理器逐漸成為嵌入式系統主流的同時,嵌入式應用也變得越來越復雜。許多嵌入式系統都不得不借助于專用的操作系統來支撐自己的應用。uClinux作為類Unix操作系統,繼承了Linux的各種優秀的品質,成為首選的嵌入式系統的操作系統。

      為自己的設備在操作系統下添加驅動程序,是嵌入式設計必不可少的部分。針對不同的設備類型,選擇合適的驅動程序的模式,同樣也是十分重要的。通常的設備驅動采用直接I/O的方式,如存儲器、看門狗等;而對于象網絡這樣的數據流設備的驅動,則應該用到中斷機制。

      本文以uClinux為背景,以一種數據流設備為目標,介紹中斷驅動的I/O設備驅動的開發。

      1 應用背景

      1.1 硬件描述

      本文介紹的驅動程序是應用在一種電信E1線路和以太網互聯設備上的。它是旁路接收E1數據并將其發送到以太網的某一臺服務器上,在服務器上對E1的話路和信令時隙分析。

      該設備中的處理器是采用三星公司出品的網絡型ARM 處理器S3C4510B。E1線路接口采用Dallas半導體公司的專用El接口單元(LIU)芯片DS2148,它完成波形整理、時鐘恢復和HDB3解碼。DS2148將整理后的E1數據流送給一片Altera公司的Cyclone系列的FPGA(EPlC3T144C8),它將串行的E1數據流存入到 FIFO,再通過ARM的32位外部總線將數據傳送給ARM。ARM將數據打包通過以太網發送到服務器上。圖l所示是本系統的硬件框圖。本文主要介紹接在 ARM的外部總線上的FPGA,在uClinux下的驅動程序中斷機制的設計。

    uClinux下中斷驅動的I/O設備驅動開發

      1.2硬件連接

      S3C4510B處理器和FPGA的連接電路如圖2所示。

    uClinux下中斷驅動的I/O設備驅動開發

      1.3 FPGA內FIFO的結構

      在FPGA內部設置了兩個FIFO。為了防止ARM和FPGA操作的沖突,ARM和FPGA對兩個FIFO操作采用乒乓方式,這

      樣ARM和FPGA就可以同時操作不同的FIFO,而不需要等待。FIFO的大小是4096位,能容納一個E1復幀的數據量。當FPGA將一個FIFO填滿后,會用中斷的方式通知 ARM來讀FIFO,同時FPGA會置內部的F1FO狀態寄存器。FIFO)狀態寄存器命名為fpga_imf,是一個32位的寄存器,用其中某幾位置 ,表示對應的FIFO需要讀取。

      2 軟件設計

      中斷驅動的I/O是指,輸人數據在中斷期間被填充到緩沖區內,并由讀取該設備的進程取走緩沖區內的數據;輸出緩沖區由寫設備的進程填充,并在中斷期間取走數據。數據緩沖可以將數據的發送和接收與write及read系統調用分離開來,提高系統的整體性能。下面是uCllnux下的中斷程序的設計。

      2.1 uClinux下的中斷程序

      在uClinux系統中,通過調用下面這個函數向系統申請一個中斷通道(或中斷請求IRQ),并在處理完以后釋放掉它。

      mt reqLIest_irq(unsigned int irq,void(*handler)(int,vold*,

      struct pt_regs*),unsigned 10ng flags,const chat*device,

      vold*dev_id);

      void free_irq(unstgned int lrq,VOid*dev_id);

      其中,irq是中斷號。在本系統中它對應于S3C4510B的21個中斷源。這里用的是中斷源O。handler指向要安裝的中斷處理函數的指針。 flags是一個與中斷管理有關的各種選項的字節掩碼。device傳遞給request_irq的字符串,在/proc/interrupts中用于顯示中斷的擁有者。dev_id指針用于共享的中斷信號線。函數的返回值為O時表示成功,或者返回一個負的錯誤碼。函數返回一EBUJSY通知另一個設備驅動程序已經使用了要申請的中斷信號線。下面是FPGA的設備中斷申請函數。這個函數是在驅動中的fpga_open函數中被調用的。

      int fpga_open(struct inode*inocle,stuct_file*file){

      int result;

      result="request"_irq(FPGA_IRQ,δfpga_isr,SA_INTER-RUPT,″fpga″,NULL);

      if(resuIt!=O){

      printk(KERN_INFO”Can not register FPGA ISR!\n”);}else{

      printk(KERN_INFO″FPGA ISR Register successfully!\n”);

      }

      }

      在申請了中斷通道后,系統會響應外部中斷0,而進入中斷處理程序。中斷處理程序的第一步是要先清除S3C4510B的中斷懸掛寄存器的外部中斷O位。這是為了讓FPGA可以產生新的中斷。在uClinux系統中是調用下面的宏來實現的。

      #deflne CLEAR_PEND_INT(n) IntPend=(1<<(n))

      中斷處理程序功能就是將有關中斷接收的信息反饋給設備,并根據要服務的中斷的不同含義相應地對數據進行讀寫。所以FPGA的中斷處理的主要任務是,讀取 FPGA中FIFO狀態寄存器的值,獲取需要讀取的FIFO的信息并安排接收數據。在程序中用到了系統提供的inl函數。

      unmgned mt status

      status="inl"(FPGA_IMF);

      中斷處理程序的執行應盡可能的短,而從FPGA中接收數據,一次必須讀完一個FIFO及128字。這是一個需要較長時間的外部I/O操作,所以把這個操作放到中斷處理的底半部(bottom-haIf)來完成。下面介紹中斷處理的底半部的設計。

      2.2 BH機制

      底半部處理程序和上半部最大的不同就在于,在執行BH時所有的中斷都是打開的,所以說它是在&ldquo;更安全&rdquo;時間內

      運行。2.4版本的uClinux內核有三種機制來實現底半部的處理:軟中斷、tasklet和BH。在這里選用了較為簡單的BH機制。

      BH機制實際上是一個任務隊列,中斷處理程序將要處理的任務插到特定的任務隊列中等待內核執行。內核維護著多個任務隊列,但驅動程序只能用前三種:

      ①tq_scheduler隊列。當調度器被運行時,該隊列就會被處理。因為此時調度器在被調度出的進程的上下文中運行,所以該隊列中的任務幾乎可以做任何事。它們不會在中斷時運行。

      ②tq_timer隊列。該隊列由定時器隊列處理程序(timertick)運行,因為該處理程序是在中斷時問運行的。該隊列中的所有任務就也是在中斷時間內運行的。

      ③tu_lmmediate隊列。立即隊列在系統調用返回時或調度器運行時盡快得到處理的(不管兩種情況誰先發生了)。該隊列是在中斷時間內得到處理的。

      隊列元素由下面的結構來描述:

      structtq_struct{

      structq_struct*mext; /*激活的BH的鏈接表*/

      unsigned 1ong sync; /*必須初始化為零*/

      void(*outine)(vold*); /*調用的函數*/

      void*data; /*傳遞給函數的參數*/

      };

      上面的數據結構中最重要的字段是rotltine和data。將要延遲的任務插入隊列,必須先設置好結構的這些字段,并把next和sync兩個字段清零。結構中的sync標志位用于避免同一任務被插人多次,這會破壞next指針。一旦任務被排人隊列,該數據結構就被認為是內核&ldquo;擁有&rdquo;了,不能再被修改。

      在FPGA的驅動中,定義了一個任務隊列元素用于完成底半部分:

      struct tq_struct el_task;

      unsigned int el_line;

      el_line數組用來保存傳遞給任務的參數。在打開FPGA時要對任務隊列結構賦值:

      el_task.routine=fpga_bh;

      e1 task.data=&e1_line:

      上面的fpga_bh是底半部分處理函數void fpga_bh(unsigned int*line)的函數名,el_line是傳遞給fpga_bh函數的實參。

      與任務隊列有關的還有下面的函數:

      void queue_task(struct tq_struet*task,task_queue*List);

      正如該函數的名字,本函數用于將任務排進隊列中。它關閉了中斷,避免了競爭,因此可以被模塊中任一函數調用。FPGA的任務被插入到tq_immediate隊列中,所以,list被賦值為&tq_immediate。

      當某段代碼需要調度運行下半部處理時,只要調用mark_bh即可:

      void mark_bh(intnr);

      這里,nr是激活的BH的類型。這個數是在頭文件中定義的一個符號常數。每個下半部BH相應的處理函數由擁有它的那個驅動程序提供。

      完成任務隊列元素設置后,中斷處理函數中就可以啟用BH機制。在讀得fpga_imf的值后將其賦給el_line,然后調用queue_task將任務插入到tq_immediate隊列中,再調用mark_bh(IMMEDIATE_BH),啟動底半部分處理。到此,中斷處理程序就可以退出了。

      2.3底半部分處理程序和緩沖區

      uClinux操作系統退出中斷處理程序后,會立即將tq_immediate隊列中任務投入運行,其中也有fpga_bh函數。在進入fpga_bh同時,系統會將el_line的地址作為實參傳遞給形參line。也就是將FIFO狀態寄存器(fpga_imf)的值間接傳給了底半部處理程序。底半部分程序中會檢查這個值的每一位,據此決定需要讀的FIFO。

      從FIFO中讀上來的數據都是存放在內核的緩沖區中的。因為每一個FIFO的容量是一個E1的復幀,所以內核的緩沖也是以E1復幀的大小為一個緩沖塊。緩沖塊用鏈表串連起來。緩沖單元的數據結構如下:

      struct buf_struct{

      struct list_head list; /*鏈表頭*/

      unsigned int buf_size; /*數據塊的大小*/

      unsigned int*buLhead; /*緩沖塊的指針*/

      unsigned int*buL_curl; /*緩沖塊當前指針*/

      };

      buf_size說明了數據塊的大小。這是一個以&ldquo;字&rdquo;為單位的數值。緩沖塊在內核堆區開辟,buf_head指向實際的緩沖塊的首地址,而buf_cur指向緩沖塊中正在操作的單元。為了使用鏈表機制,驅動必須包含頭文件。其中定義了list_head類型結構:

      struct list_head{

      struct list_head*next.*prev;

      為了訪問緩沖塊鏈表,還要建立一個鏈表頭,在驅動 中定義全局變量:

      struct list_head read_list;

      鏈表頭必須是一個獨立的list_head結構。在使用之前,必須用INIT_LIST_HEAD宏來初始化鏈表頭:

      INIT_LIST_HEAD(&readlist);

      Linux系統提供了鏈表的操作函數,在頭文件中:

      list_add(struet list_head*new,struct list_head*head); /*在鏈表頭后插入一個新項*/

      list_add_tail(stuot list_head*new,struet list_head*head); /*在鏈表尾部添加一個新項*/ 

       nbsp; 

       list_del(struet_list_head*entry); /*將給定項從鏈表中刪除*/

      list_empty(struct list_head*head)??????????????????????????? /*判斷鏈表是否為空*/

      list_entry(struct list_head。ptr,type_of_struet,field_ name); /*訪問包含鏈表頭的結構*/

      其中list_entry的作用是一個1ist_head結構指針映射回一個指向包含它的大結構的指針。ptr是指向structlist_head結構的指針,type_of_struct是包含ptr的結構類型,field_name是結構中鏈表字段的名字。如可以用這個宏將指向數據緩沖塊的鏈表指針 (readl)映射為緩沖塊結構指針(buf):

      struetbuf_strcut*buf=list_entry(real,struct buf_struct,list);

      底半部分處理程序中,內核緩沖塊是動態分配的。因為驅動程序是內核的一部分,所以在內核堆區開辟緩沖區就要用專用的函數,在頭文件定義了如下函數:

      void*kmalloc(size t size,int flags);/*在內核堆中分配size大小的空問*/

      void kfree(void*obi/*釋放kmalloc分配的空間*/

      kmalloc函數的第1個參數是size(大小),第2個參數是優先權。最常用的優先權是GFP_KERNEL,它的意思是該內存

      分配是由運行在內核態的進程調用的。有時kmalloc是在進程上下文之外調用的,比如在中斷處理、任務隊列處理和內核定時器處理時發生。這些情況下,current進程就不應該進入睡眠狀態,這時應該就使用優先權GFP_ATOMIC。

      不要過于頻繁地用kmalloc在內核堆中分配空間,因為在分配空間時可能有中斷到來,這樣是不安全的。在驅動中建立另一個鏈表用于回收使用過的緩沖塊。在驅動中用free_1ist作為回收緩沖塊的鏈表頭:

      struct list_head free_list;

      這樣就存在兩個鏈表:一個是裝載著數據的鏈表,一個是已經使用過的緩沖塊的鏈表(稱為自由鏈表)。那么只要自由鏈表中還有表項,在需要緩沖塊時就可以直接從自由鏈表中取出一個使用,而不用kmalloc再去分配。

      2.4 阻塞型I/O和自旋鎖的使用

      在驅動程序中,read的工作是將內核緩沖區中拷貝到用戶空間。在進行這種操作時有兩種情況是應該注意的:

      ①當read時發現讀鏈表是空,也就是還沒有數據可讀。

      這種情況下,可以讓read立即返回一EAGAIN,告知用戶進程沒有讀到數據;另一個辦法就是實現阻塞型I/O,在沒有數據可讀時讓用戶進程進入睡眠狀態并等待數據。

      有幾種處理和喚醒的方法,都要處理同一個基本的數據類型&mdash;&mdash;等待隊列(walt_queue_head_t),就是由正在等待某事件發生的進程組成的一個隊列。使用之前必須聲明和初始化,在驅動程序中是如下聲明的:

      wait_queue_head_t read_Jqueue;

      init_waitqueue_head(&read_queue);

      可以調用如下函數之一讓進程進入睡眠狀態:

      void wait_evet(wait_queue_head_ queue,int condition);

      int wait_evem_interruptible(Walt_queue_hean_t queue,int condition);

      這兩個函數把等待事件和測試事件是否發生合并起來。調用之后,進程會一直睡眠到C布爾表達式condition為真時為止。在驅動中的read函數中,判斷讀鏈表為空,就調用它進入睡眠:

      while(1ist_efnpty(&read_list)){

      If(filp一>f_flags &delta;O_NoNBLOCK)/*如果設置成非阻塞I/o*/

      return&mdash;EAGAIN;

      ;    if(wait_evert_interruptible(read_queue,!list_empty(&delta;read_list))) return&mdash;ERESTARTSYS;

      }

      對應上面的函數,要喚醒進程可以調用下面的函數:

      wake_up(wait_queue_gead_t*queue);

      wake_up_jnterruptlbk(wait_queue_head_t*queue);

      驅動程序應該在數據到來后及時喚醒進程,也就是從FIFO讀取數據后,在退出底半部處理程序前執行:

      wake_up_mterIuptible(&read_queue);

      要指出的是被喚醒并不保證等待的事件發生了,所以從睡眠態返回后,應該循環測試condition。

      ②當read操作正在訪問某一個鏈表時,底半程序也要訪問同一個鏈表。這樣是比較危險的,應該避免。

      為了避免這種情況的發生,這里使用自旋鎖。在read操作訪問鏈表前獲得鎖,訪問結束時解鎖。底半部要訪問鏈表時先要檢查自旋鎖是否已上鎖,如果有,則等待到鎖可用。

      自旋鎖使用類型spinlock_t來描述。自旋鎖被聲明和初始化為不加鎖狀態方式如下:

      spinlock_t1ist_10ck=SPIN_LoCK_UNLOCKED;

      處理自旋鎖的函數如下:

      spill_1ock_bh(Spllalock-t*1ock);

      spin_unloek_bh(splnlock_t*lock);

      這里使用獲得自旋鎖并且阻止底半部執行的函數,就可以完全保證底半部程序不會在read操作訪問鏈表時來訪問鏈表。程序中如下實現:

      spln_lock_bh(&list_lock);

      list_del(readl); /*將使用后的緩沖塊從讀鏈表中刪除*/

      list_add_tail(readI,&free_list);/*將使用后的緩沖塊插入自由鏈表中*/

      spin_unlock_bh(&list_lock);

      2.5中斷驅動的I/O

      至此,可以完整地描述ARM與FPGA之間數據流動的過程:當FPGA的一個FIFO滿后,向ARM發出中斷,ARM進入中斷處理程序后,讀取FPGA中的FlFO狀態寄存器(fpga_imf)的值,然后把一個任務插到立即隊列(tq_imrnediate)中,啟動底半部分(BH),同時將FIFO) 狀態寄存器的值傳遞給底半部分處理程序(fpga_bh),完成這些工作后退出中斷處理程序。進入底半部分處理程序后,根據FIFO狀態寄存器的值確定要處理的F1F0。從FIFO中將數據讀出存人到內核緩沖塊中,這個緩沖塊可能是從自由隊列(free_list)中取出來的一個。如果自由隊列中是空的,就新分配一個緩沖塊。接下來將填好的緩沖塊加到讀隊列(read-list)中,并喚醒睡眠的進程,這樣底半部分的工作也完成了。當用戶進程對FPGA設備進行讀操作時,驅動中的read函數檢查讀鏈表。如果讀鏈表為空,則進入睡眠并等待數據到來。有數據后將從讀隊列中取出的緩沖塊的數據拷貝到用戶空間,然后將使用過的緩沖塊插到自由隊列中,等待以后再次使用。內核緩沖區的操作過程如圖3所示。圖3上半部分是在底半部分程序中,下半部分是在read函數中。

    uClinux下中斷驅動的I/O設備驅動開發

      結語

      連續數據流設備在uClinux下的驅動,通常會用到中斷機制。本文討論的中斷驅動的I/O式為這種應用提供了一種實用的方法。文中所涉及的鏈表、阻塞型I/O、自旋鎖等技術在驅動程序的開發中也經常得到使用。
     

    (審核編輯: 智匯李)

    聲明:除特別說明之外,新聞內容及圖片均來自網絡及各大主流媒體。版權歸原作者所有。如認為內容侵權,請聯系我們刪除。

    主站蜘蛛池模板: 狠狠穞A片一區二區三區-免费网站在线观看人数更新时间-欧洲尺码日本尺码专线不卡顿,国产大尺度禁片未删减版,baomaav | 首页_01精密设备吊装,气垫搬运,半导体设备安装_苏州大方起重吊装公司 | 雷达液位计_耐磨热电偶_蒸汽_柴油,汽油_天然气流量计_巴歇尔槽_一体化温度变送器-江苏翔腾仪表有限公司 | 金雷诺机柜,GLN机柜,户外机柜,电力机柜,服务器机柜 | 竖豆网-网站建设、小程序商城、软件定制开发| 郑州空调维修_郑州中央空调维修_空调清洗维保-郑州大晟机电设备安装工程有限公司 | 湖南净声源环保科技有限公司是一家专业从事噪声治理和建筑声学设计生态环境综合治理服务的企业,专业从事株洲电梯隔音治理,湘潭中央空调降噪处理,衡阳邵阳冷却塔噪音治理,岳阳常德大型风机噪声隔音降噪,张家界空压机噪声治理,益阳配电房变压器噪声治理,专业郴州永州工厂企业车间噪声治理,怀化娄底专业机械设备减振降治理,武汉噪音治理隔音降噪公司,孝感噪音治理,立式球磨机的噪声控制,专业隔音降噪公司,、以及各类机械动力设备减振降噪噪声治理的公司,同时为客户提供咨询与解决方案 | 深圳家具网-家具展会-家具检测-家具品牌—深圳市家具行业协会官网 | 箱包定制_广州箱包厂_双肩电脑背包_双肩旅行包_拉杆箱包_商务公文包_包包批发,深圳爱自由,礼品箱包定制,电话:400-0061-690 | 亿企商贸-亿万企业的商务贸易平台-B2B企业产品发布供求信息平台,一带一路中国企业及产品展示平台,免费企业智能自助建站网络营销推广平台,打造B2B企业黄页产品信息发布推广专业综合电子商务平台! | 景县泉兴永塔业有限公司-广播电视塔、通信塔、电力塔、交通设施、监控杆塔、气象塔、森林防火瞭望塔、避雷塔、烟筒塔、训练塔 | 真石漆设备-干粉砂浆生产线-保温砂浆机械-郑州屹成机械设备 | 熊猫家装-装修公司,上海装修、室内设计、家装、别墅装修、办公室装修、全屋定制就上熊猫家装 | 上进电缆(嘉兴)股份有限公司官网 - 光伏电缆|防火电缆|电力电缆|铝合金电缆专业生产厂家 | 新中式家具,广东新中式家具,广州新中式家具,佛山新中式家具,顺德新中式家具,乐从新中式家具,新中式家具厂家直销--唐明雅居 | 熊猫家装-装修公司,上海装修、室内设计、家装、别墅装修、办公室装修、全屋定制就上熊猫家装 | 行星式球磨机-实验室球磨仪[东方天净]小型高能研磨筛分仪直销厂家 | 银联POS机_银联微信支付宝刷卡POS机_外币POS机_移动POS机办理安装——谷骐科技 | 心理咨询室设备_音乐放松椅_心理测评系统_情绪宣泄设备厂家 | 资质代办-企业施工资质代办,湖南建筑资质代办公司-[小凯企服] | 展馆周边酒店_会展中心附近酒店_展览旅游酒店预订官网-盟友云 | 上进电缆(嘉兴)股份有限公司官网 - 光伏电缆|防火电缆|电力电缆|铝合金电缆专业生产厂家 | 著名刑事诉讼律师_刑事辩护律师★王平聚【清华博士/刑法教授】 | 山东国新起重机械有限公司,国新起重,起重设备,起重机械,山东起重机厂家,行车,龙门吊 | 重庆消杀公司-重庆斗哥环保科技-灭鼠公司-重庆灭蟑螂-除四害-灭老鼠-灭虫-重庆灭白蚁公司 | 萍乡市宏运特种陶瓷有限公司| 学汽修_汽修学校_汽修学校哪家好-江西万通汽车学院官网 | 戒网瘾学校-陕西正规戒网瘾-叛逆青少年教育学校-重生教育官网 | 景观灯-庭院灯-多功能路灯-高杆灯-智慧灯杆生产厂家-扬州景尚光电 | 在线腐蚀率仪,在线污垢热阻仪,靶式光源仪-北京同德创业科技有限公司 | 潍坊晨硕机械设备有限公司 | 同兴科技-安徽同兴科技发展有限责任公司 | 炸鸡汉堡设备厂家-开汉堡炸鸡店需要的设备全套-广州英迪尔电器有限公司 | 智能试剂柜-疾控|高校实验室|医院药品智能试剂管理柜-北京晶品赛思 | 木材粉碎机,树枝粉碎机,木材破碎机厂家| 盆底肌修复仪器-产后康复脉冲磁训练仪-南京佳澜健康管理有限公司 | 千斤顶-超薄电动千斤顶-电动液压千斤顶-液压螺母扳手-泰州杰克液压机械制造有限公司 | 在线腐蚀率仪,在线污垢热阻仪,靶式光源仪-北京同德创业科技有限公司 | 武汉东湖高新集团股份有限公司官网 | 长型材数控钻孔攻牙机-自动数控热熔钻孔机-东莞市利速数控机械有限公司 | 苏州氮气弹簧厂家_江浙沪氮气弹簧价格_江苏氮气弹簧规格_BelleFlex碟形弹簧_昆山三虑五金机械有限公司 |