1. 程式人生 > >嵌入式Linux驅動程式開發

嵌入式Linux驅動程式開發

嵌入式Linux驅動程式開發

系統呼叫是作業系統核心和應用程式之間的介面,裝置驅動程式是作業系統核心和機器硬體之間的介面。裝置驅動程式為應用程式遮蔽了硬體的細節,這樣在應用程式看來,硬體裝置只是一個裝置檔案,應用程式可以象操作普通檔案一樣對硬體裝置進行操作。裝置驅動程式是核心的一部分。

Linux將裝置主要分成兩大類:一類是塊裝置,類似磁碟以記錄塊或扇區為單位,成塊進行輸入/輸出的裝置;另一類是字元裝置,類似鍵盤以字元為單位,逐個進行輸入/輸出的裝置。網路裝置是介於塊裝置和字元裝置之間的一種特殊裝置。

塊裝置介面僅支援面向塊的I/O操作,所有I/O操作都通過在核心地址空間中的I/O緩衝區進行,它可以支援隨機存取的功能。檔案系統通常都建立在塊裝置上。

字元裝置介面支援面向字元的I/O操作,由於它們不經過系統的快速快取,所以它們負責管理自己的緩衝區結構。字元裝置介面只支援順序存取的功能,一般不能進行任意長度的I/O請求,而是限制I/O請求的長度必須是裝置要求的基本塊長的倍數。

1.裝置驅動程式的概念

裝置驅動程式實際是處理和操作硬體控制器的軟體,從本質上講,是核心中具有最高特權級的、駐留記憶體的、可共享的底層硬體處理例程。驅動程式是核心的一部分,是作業系統核心與硬體裝置的直接介面,驅動程式遮蔽了硬體的細節,完成以下功能:

— 對裝置初始化和釋放;

— 對裝置進行管理,包括實時引數設定,以及提供對裝置的操作介面;

— 讀取應用程式傳送給裝置檔案的資料或者回送應用程式請求的資料;

— 檢測和處理裝置出現的錯誤。

Linux作業系統將所有的裝置全部看成檔案,並通過檔案的操作介面進行操作。對使用者程式而言,裝置驅動程式隱藏了裝置的具體細節,對各種不同裝置提供了一致的介面,一般來說,是把裝置對映為一個特殊的裝置檔案,使用者程式可以像對其他檔案一樣對此裝置檔案進行操作。這意味著:

— 由於每一個裝置至少由檔案系統的一個檔案代表,因而都有一個“檔名”。

— 應用程式通常可以通過系統呼叫open()開啟裝置檔案,建立起與目標裝置的連線。

— 打開了代表著目標裝置的檔案,即建立起與裝置的連線後,可以通過read()、write()、ioctl()等常規的檔案操作對目標裝置進行操作。

裝置檔案的屬性由三部分資訊組成:第一部分是檔案的型別,第二部分是一個主裝置號,第三部分是一個次裝置號。其中型別和主裝置號結合在一起惟一地確定了裝置檔案驅動程式及其介面,而次裝置號則說明目標裝置是同類裝置中的第幾個。

由於Linux 中將裝置當做檔案處理,所以對裝置進行操作的呼叫格式與對檔案的操作類似,主要包括open()、read()、write()、ioctl()、close()等。應用程式發出系統呼叫命令後,會從使用者態轉到核心態,通過核心將open()這樣的系統呼叫轉換成對物理裝置的操作。

2.處理器與裝置間資料交換方式

處理器與外設之間傳輸資料的控制方式通常有3種:查詢方式、中斷方式和直接記憶體存取(DMA)方式。

21.查詢方式

裝置驅動程式通過裝置的I/O埠空間,以及儲存器空間完成資料的交換。例如,網絡卡一般將自己的內部暫存器對映為裝置的I/O埠,而顯示卡則利用大量的儲存器空間作為視訊資訊的儲存空間。利用這些地址空間,驅動程式可以向外設傳送指定的操作指令。通常來講,由於外設的操作耗時較長,因此,當處理器實際執行了操作指令之後,驅動程式可採用查詢方式等待外設完成操作。

2.2.中斷方式

查詢方式白白浪費了大量的處理器時間,而中斷方式才是多工作業系統中最有效利用處理器的方式。當CPU進行主程式操作時,外設的資料已存入埠的資料輸入暫存器,或埠的資料輸出暫存器已空,此時由外設通過介面電路向CPU發出中斷請求訊號。CPU在滿足一定條件下,暫停執行當前正在執行的主程式,轉入執行相應能夠進行輸入/輸出操作的子程式,待輸入/輸出操作執行完畢之後,CPU再返回並繼續執行原來被中斷的主程式。這樣,CPU就避免了把大量時間耗費在等待、查詢外設狀態的操作上,使其工作效率得以大大提高。中斷方式的原理示意圖如圖6.1所示。

                                                                                                                                                   

2.3.直接訪問記憶體(DMA)方式

利用中斷,系統和裝置之間可以通過裝置驅動程式傳送資料,但是,當傳送的資料量很大時,因為中斷處理上的延遲,利用中斷方式的效率會大大降低。而直接記憶體訪問(DMA)可以解決這一問題。DMA可允許裝置和系統記憶體間在沒有處理器參與的情況下傳輸大量資料。裝置驅動程式在利用DMA之前,需要選擇DMA通道並定義相關暫存器,以及資料的傳輸方向,即讀取或寫入,然後將裝置設定為利用該DMA通道傳輸資料。裝置完成設定之後,可以立即利用該DMA通道在裝置和系統的記憶體之間傳輸資料,傳輸完畢後產生中斷以便通知驅動程式進行後續處理。在利用DMA進行資料傳輸的同時,處理器仍然可以繼續執行指令。

3.驅動程式結構 

3.1一個裝置驅動程式模組的基本框架

裝置驅動程式流程圖

在系統內部,I/O裝置的存取通過一組固定的入口點來進行,入口點也可以理解為裝置的控制代碼,就是對裝置進行操作的基本函式。字元型裝置驅動程式提供如下幾個入口點:

—  open入口點。開啟裝置準備I/O操作。對字元裝置檔案進行開啟操作,都會呼叫裝置的open入口點。open子程式必須對將要進行的I/O操作做好必要的準備工作,如清除緩衝區等。如果裝置是獨佔的,即同一時刻只能有一個程式訪問此裝置,則open子程式必須設定一些標誌以表示裝置處於忙狀態。

—  close入口點。關閉一個裝置。當最後一次使用裝置完成後,呼叫close子程式。獨佔裝置必須標記裝置方可再次使用。

—  read入口點。從裝置上讀資料。對於有緩衝區的I/O操作,一般是從緩衝區裡讀資料。對字元裝置檔案進行讀操作將呼叫read子程式。

—  write入口點。往裝置上寫資料。對於有緩衝區的I/O操作,一般是把資料寫入緩衝區裡。對字元裝置檔案進行寫操作將呼叫write子程式。

—  ioctl入口點。執行讀、寫之外的操作。

—       select入口點。檢查裝置,看資料是否可讀或裝置是否可用於寫資料。select系統呼叫在檢查與裝置檔案相關的檔案描述符時使用select入口點。

3.1. file_operations結構體

struct file_operations {

structmodule *owner;

loff_t(*llseek) (struct file *, loff_t, int);

ssize_t(*read) (struct file *, char *, size_t, loff_t *);

ssize_t(*write) (struct file *, const char *, size_t, loff_t *);

int(*readdir) (struct file *, void *, filldir_t);

unsignedint (*poll) (struct file *, struct poll_table_struct *);

int(*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

int (*mmap)(struct file *, struct vm_area_struct *);

int (*open)(struct inode *, struct file *);

int(*flush) (struct file *);

int(*release) (struct inode *, struct file *);

int(*fsync) (struct file *, struct dentry *, int datasync);

int(*fasync) (int, struct file *, int);

int (*lock)(struct file *, int, struct file_lock *);

ssize_t(*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);

ssize_t(*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);

ssize_t(*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);

unsignedlong (*get_unmapped_area)(

struct file*,

unsignedlong,

unsignedlong,

unsignedlong,

unsignedlong

);

};

—  lseek,移動檔案指標的位置,只能用於可以隨機存取的裝置。

—  read,進行讀操作,buf為存放讀取結果的緩衝區,count為所要讀取的資料長度。

—  write,進行寫操作,與read類似。

—  select,進行選擇操作。

—  ioctl,進行讀、寫以外的其他操作。

—  mmap,用於把裝置的內容對映到地址空間,一般只有塊裝置驅動程式使用。

—  open,開啟裝置進行I/O操作。返回0表示成功,返回負數表示失敗。

—  release,即close操作

3.2.inode{}和file{}結構體

inode資料結構體提供關於特別裝置檔案的資訊。file結構體主要是與檔案系統對應的裝置驅動程式使用。

struct file主要用於與檔案系統相關的裝置驅動程式,可提供關於被開啟的檔案的資訊,定義如下:

struct file {

struct list_head   f_list;

struct dentry              *f_dentry;

struct vfsmount        *f_vfsmnt;

struct file_operations  *f_op;

atomic_t            f_count;

unsigned int              f_flags;

mode_t                     f_mode;

loff_t                 f_pos;

unsigned long           f_reada,f_ramax, f_raend, f_ralen, f_rawin;

struct fown_struct      f_owner;

unsigned int              f_uid,f_gid;

int                     f_error;

unsigned long            f_version;

/* needed for tty driver, and maybe others */

void                  *private_data;

/* preallocated helper kiobuf to speedup O_DIRECT */

struct kiobuf              *f_iobuf;

long                  f_iobuf_lock;

};

在使用者自己的驅動程式中,首先要根據驅動程式的功能,完成file_operations結構中函式的實現。不需要的函式介面可以直接在file_operations結構中初始化為NULL。file_operations中的變數會在驅動程式初始化時,註冊到系統內部。每個程序對裝置的操作,都會根據主次裝置號,轉換成對file_operations結構的訪問。

4.設備註冊和初始化

裝置的驅動程式在載入的時候首先需要呼叫入口函式init_module(),該函式最重要的一個工作就是向核心註冊該裝置,對於字元裝置呼叫register_chrdev()完成註冊。register_chrdev 的定義為:int register_chrdev(unsignedint major, const char *name, struct file_ operations *fops);

其中,major是為裝置驅動程式向系統申請的主裝置號,如果為0,則系統為此驅動程式動態分配一個主裝置號。name是裝置名,fops是對各個呼叫的入口點說明。此函式返回0時表示成功;返回-EINVAL,表示申請的主裝置號非法,主要原因是主裝置號大於系統所允許的最大裝置號;返回-EBUSY,表示所申請的主裝置號正在被其他裝置程式使用。如果動態分配主裝置號成功,此函式將返回所分配的主裝置號。如果register_chrdev()操作成功,裝置名就會出現在/proc/dvices檔案中。

Linux在/dev目錄中為每個裝置建立一個檔案,用ls –l命令列出函式返回值,若小於0,則表示註冊失敗;返回0或者大於0的值表示註冊成功。註冊以後,Linux將裝置名與主、次裝置號聯絡起來。當有對此裝置名的訪問時,Linux通過請求訪問的裝置名得到主、次裝置號,然後把此訪問分發到對應的裝置驅動,裝置驅動再根據次裝置號呼叫不同的函式。

當裝置驅動模組從Linux核心中解除安裝,對應的主裝置號必須被釋放。字元裝置在cleanup_module()函式中呼叫unregister_chrdev()來完成裝置的登出。unregister_chrdev()的定義為:int unregister_chrdev(unsignedint major, const char *name);

此函式的引數為主裝置號major和裝置名name。Linux核心把name和major在核心註冊的名稱對比,如果不相等,解除安裝失敗,並返回-EINVAL;如果major大於最大的裝置號,也返回-EINVAL。

包括設備註冊在內,裝置驅動的初始化函式主要完成的功能是有以下5項。

(1)對驅動程式管理的硬體進行必要的初始化。

對硬體暫存器進行設定。比如,設定中斷掩碼,設定串列埠的工作方式、並口的資料方向等。

(2)初始化裝置驅動相關的引數。

一般說來,每個裝置都要定義一個裝置變數,用以儲存裝置相關的引數。在這一步驟裡對裝置變數中的項進行初始化。

(3)在核心註冊裝置。

呼叫register_chrdev()函式來註冊裝置。

(4)註冊中斷。

如果裝置需要IRQ支援,則要使用request_irq()函式註冊中斷。

(5)其他初始化工作。

初始化部分一般還負責給裝置驅動程式申請包括記憶體、時鐘、I/O埠等在內的系統資源,這些資源也可以在open子程式或者其他地方申請。這些資源不用時,應該釋放,以利於資源的共享。

若驅動程式是核心的一部分,初始化函式則要按如下方式宣告:

int __init chr_driver_init(void);

其中__init是必不可少的,在系統啟動時會由核心呼叫chr_driver_init,完成驅動程式的初始化。

當驅動程式是以模組的形式編寫時,則要按照如下方式宣告:

int init_module(void)

當執行後面介紹的insmod命令插入模組時,會呼叫init_module函式完成初始化工作。

5.中斷管理

裝置驅動程式通過呼叫request_irq函式來申請中斷,通過free_irq來釋放中斷。它們在linux/sched.h中的定義如下:

int request_irq(

unsigned int irq,

void (*handler)(int irq,void dev_id,structpt_regs *regs),

unsigned long flags,

const char *device,

void *dev_id

);

void free_irq(unsigned int irq, void*dev_id);

通常從request_irq函式返回的值為0時,表示申請成功;負值表示出現錯誤。

— irq表示所要申請的硬體中斷號。

— handler為向系統登記的中斷處理子程式,中斷產生時由系統來呼叫,呼叫時所帶引數irq為中斷號,dev_id為申請時告訴系統的裝置標識,regs為中斷髮生時暫存器內容。

— device為裝置名,將會出現在/proc/interrupts檔案裡。

— flag是申請時的選項,它決定中斷處理程式的一些特性,其中最重要的是決定中斷處理程式是快速處理程式(flag裡設定了SA_INTERRUPT)還是慢速處理程式(不設定SA_INTERRUPT)。

下面的程式碼將在SBC-2410X的Linux中註冊外部中斷2。

eint_irq = IRQ_EINT2;

set_external_irq (eint_irq, EXT_FALLING_EDGE,GPIO_PULLUP_DIS);

ret_val =request_irq(eint_irq,eint2_handler, “S3C2410Xeint2”,0);

if(ret_val < 0){

return ret_val;

}

用來開啟和關閉中斷的函式如下:

#define cli() _asm_ _volatile_("cli"::)

#define sli() _asm_ _volatile_("sli"::) 。

6.裝置驅動程式的開發過程

由於嵌入式裝置由於硬體種類非常豐富,在預設的核心釋出版中不一定包括所有驅動程式。所以進行嵌入式Linux系統的開發,很大的工作量是為各種裝置編寫驅動程式。除非系統不使用作業系統,程式直接操縱硬體。嵌入式Linux系統驅動程式開發與普通Linux開發沒有區別。可以在硬體生產廠家或者Internet上尋找驅動程式,也可以根據相近的硬體驅動程式來改寫,這樣可以加快開發速度。實現一個嵌入式Linux裝置驅動的大致流程如下。

(1)檢視原理圖,理解裝置的工作原理。一般嵌入式處理器的生產商提供參考電路,也可以根據需要自行設計。

(2)定義裝置號。裝置由一個主裝置號和一個次裝置號來標識。主裝置號惟一標識了裝置型別,即裝置驅動程式型別,它是塊裝置表或字元裝置表中裝置表項的索引。次裝置號僅由裝置驅動程式解釋,區分被一個裝置驅動控制下的某個獨立的裝置。

(3)實現初始化函式。在驅動程式中實現驅動的註冊和解除安裝。

(4)設計所要實現的檔案操作,定義file_operations結構。

(5)實現所需的檔案操作呼叫,如read、write等。

(6)實現中斷服務,並用request_irq向核心註冊,中斷並不是每個裝置驅動所必需的。

(7)編譯該驅動程式到核心中,或者用insmod命令載入模組。

(8)測試該裝置,編寫應用程式,對驅動程式進行測試。

7.裝置驅動開發的基本函式

7.1.I/O口函式

無論驅動程式多麼複雜,歸根結底,無非還是向某個埠或者某個暫存器位賦值,這個值只能是0或1。接收值的就是I/O口。與中斷和記憶體不同,使用一個沒有申請的I/O埠不會使處理器產生異常,也就不會導致諸如“segmentationfault”一類的錯誤發生。由於任何程序都可以訪問任何一個I/O埠,此時系統無法保證對I/O埠的操作不會發生衝突,甚至因此而使系統崩潰。因此,在使用I/O埠前,也應該檢查此I/O埠是否已有別的程式在使用,若沒有,再把此埠標記為正在使用,在使用完以後釋放它。

這樣需要用到如下幾個函式:

int check_region(unsigned int from,unsigned int extent);

void request_region(unsigned int from,unsigned int extent,const char *name);

void release_region(unsigned int from, unsignedint extent);

呼叫這些函式時的引數為:

— from表示所申請的I/O埠的起始地址;

— extent為所要申請的從from開始的埠數;

— name為裝置名,將會出現在/proc/ioports檔案裡;

— check_region返回0表示I/O埠空閒,否則為正在被使用。

在申請了I/O埠之後,可以藉助asm/io.h中的如下幾個函式來訪問I/O埠:

inline unsigned int inb(unsigned shortport);

inline unsigned int inb_p(unsigned shortport);

inline void outb(char value, unsigned shortport);

inline void outb_p(char value,unsigned short port);

其中inb_p和outb_p插入了一定的延時以適應某些低速的I/O埠。

7.2.時鐘函式

在裝置驅動程式中,一般都需要用到計時機制。在Linux系統中,時鐘是由系統接管的,裝置驅動程式可以向系統申請時鐘。與時鐘有關的系統呼叫有:

#include <asm/param.h>

#include <linux/timer.h>

void add_timer(struct timer_list * timer);

int del_timer(struct timer_list * timer);

inline void init_timer(struct timer_list *timer);

struct timer_list的定義為:

struct timer_list {

struct timer_list *next;

struct timer_list *prev;

unsigned long expires;

unsigned long data;

void (*function)(unsigned long d);

};

其中,expires是要執行function的時間。系統核心有一個全域性變數jiffies表示當前時間,一般在呼叫add_timer時jiffies=JIFFIES+num,表示在num個系統最小時間間隔後執行function函式。系統最小時間間隔與所用的硬體平臺有關,在核心裡定義了常數HZ表示一秒內最小時間間隔的數目,則num*HZ表示num秒。系統計時到預定時間就呼叫function,並把此子程式從定時佇列裡刪除,可見,如果想要每隔一定時間間隔執行一次的話,就必須在function裡再一次呼叫add_timer。function的引數d即為timer裡面的data項。

7.3.記憶體操作函式

作為系統核心的一部分,裝置驅動程式在申請和釋放記憶體時不是呼叫malloc和free,而代之以呼叫kmalloc和kfree,它們在linux/kernel.h中被定義為:

void * kmalloc(unsigned int len, intpriority);

void kfree(void * obj);

引數len為希望申請的位元組數,obj為要釋放的記憶體指標。priority為分配記憶體操作的優先順序,即在沒有足夠空閒記憶體時如何操作,一般由取值GFP_KERNEL解決即可。

7.4.複製函式

在使用者程式呼叫read、write時,因為程序的執行狀態由使用者態變為核心態,地址空間也變為核心地址空間。由於read、write中引數buf是指向使用者程式的私有地址空間的,所以不能直接訪問,必須通過下面兩個系統函式來訪問使用者程式的私有地址空間。

#include <asm/segment.h>

void memcpy_fromfs(void * to,const void *from,unsigned long n);

void memcpy_tofs(void * to,const void *from,unsigned long n);

memcpy_fromfs由使用者程式地址空間往核心地址空間複製,memcpy_tofs則反之。引數to為複製的目的指標,from為源指標,n為要複製的位元組數。

在裝置驅動程式裡,可以呼叫printk來列印一些除錯資訊,printk的用法與printf類似。printk列印的資訊不僅出現在螢幕上,同時還記錄在檔案syslog裡。

8.模組載入與解除安裝

雖然模組作為核心的一部分,但並未被編譯到核心中,它們被分別編譯和連結成目標檔案。Linux中模組可以用C語言編寫,用gcc命令編譯成模組*.o,在命令列里加上-c的引數和“-D__KERNEL__-DMODULE”引數。然後用depmod -a 使此模組成為可載入模組。模組用insmod命令載入,用rmmod命令來解除安裝,這兩個命令分別呼叫init_module()和cleanup_ module()函式,還可以用lsmod命令來檢視所有已載入的模組的狀態。

insmod命令可將編譯好的模組調入記憶體。核心模組與系統中其他程式一樣是已連結的目標檔案,但不同的是它們被連結成可重定位映像。insmod將執行一個特權級系統呼叫get_kernel_sysms()函式以找到核心的輸出內容,insmod修改模組對核心符號的引用後,將再次使用特權級系統呼叫create_module()函式來申請足夠的實體記憶體空間,以儲存新的模組。核心將為其分配一個新的module結構,以及足夠的核心記憶體,並將新模組新增在核心模組連結串列的尾部,然後將新模組標記為uninitialized。

利用rmmod命令可以解除安裝模組。如果核心中還在使用此模組,這個模組就不能被解除安裝。原因是如果裝置檔案正被一個程序開啟就解除安裝還在使用的核心模組,並導致對核心模組的讀/寫函式所在記憶體區域的呼叫。如果幸運,沒有其他程式碼被載入到那個記憶體區域,將得到一個錯誤提示;否則,另一個核心模組被載入到同一區域,這就意味著程式跳到核心中另一個函式的中間,結果是不可預見的。

9.例項剖析

9.1例項一

9.1.1驅動程式

/******************************

 *   LED_Driver    2007/09/20        

 *****************************/

#include<linux/config.h>

#include<linux/kernel.h>

#include<linux/sched.h>

#include<linux/timer.h>

#include<linux/init.h>

#include<linux/module.h>

#include<asm/hardware.h>

#defineGPIO_LED_MAJOR   97

#defineARM_GPIO_LED_DEBUG

#defineARM_GPIO_LED (GPIO96)

#defineLED_ON  0

#defineLED_OFF 1

#definectl_GPIO_LED1 1

#defineVERSION         "ARM_GPIO_LED_2007/09/20"

voidshowversion(void)

{

        printk("*********************************************\n");

        printk("\t %s \t\n",VERSION);

        printk("********************************************\n\n");

}

//------------------- READ ------------------------

ssize_tGPIO_LED_read (struct file * file ,char * buf, size_t count, loff_t * f_ops)

{

    #ifdef ARM_GPIO_LED_DEBUG

       printk ("GPIO_LED_read [--kernel--]\n");

    #endif             

    return count;

}  

//------------------- WRITE -----------------------

ssize_tGPIO_LED_write (struct file * file ,const char * buf, size_t count, loff_t *f_ops)

{

    #ifdef ARM_GPIO_LED_DEBUG

           printk("GPIO_LED_write [ --kernel--]\n");

        #endif

    return count;

}  

//------------------- IOCTL -----------------------

ssize_tGPIO_LED_ioctl (struct inode * inode ,struct file * file, unsigned int cmd,long data)

{

    #ifdef ARM_GPIO_LED_DEBUG

           printk("GPIO_LED_ioctl [ --kernel--]\n");

        #endif

    switch (cmd)

        {

       case LED_ON : {GPCR3 |= 0x1;break;}

       case LED_OFF: {GPSR3 |= 0x1;break;}

                default :

                        {printk ("lcdcontrol : no cmd run  [ --kernel--]\n"); return (-EINVAL);}

        }

    return 0;

    }

//------------------- OPEN ------------------------

ssize_tGPIO_LED_open (struct inode * inode ,struct file * file)

{

    #ifdef ARM_GPIO_LED_DEBUG

           printk("GPIO_LED_open [ --kernel--]\n");

        #endif

    MOD_INC_USE_COUNT;

    return 0;

}  

//------------------- RELEASE/CLOSE ---------------

ssize_tGPIO_LED_release (struct inode  * inode,struct file * file)

{

    #ifdef ARM_GPIO_LED_DEBUG

           printk ("GPIO_LED_release [ --kernel--]\n");

        #endif

    MOD_DEC_USE_COUNT;

    return 0;

}

//-------------------------------------------------

structfile_operations GPIO_LED_ctl_ops ={

    open:      GPIO_LED_open,

    read:      GPIO_LED_read,

    write:     GPIO_LED_write,

    ioctl:     GPIO_LED_ioctl,

    release:           GPIO_LED_release,

};

//------------------- INIT ------------------------

staticint __init HW_GPIO_LED_CTL_init(void)

{

    int ret = -ENODEV;

    printk("Driver Loding.....................\n\n");

    showversion();

    // init GPIO

    GPDR3 |= 0x00000001; // SET GPIO96 OUTPUTMODE

    GPSR3 |= 0x00000001; // OFF THE LED

    #ifdef ARM_GPIO_LED_DEBUG

        printk (" GPLR3=%x\n",GPLR3);

       printk (" GPDR3=%x \n",GPDR3);

        #endif

    ret = devfs_register_chrdev(GPIO_LED_MAJOR,"led_drv", &GPIO_LED_ctl_ops);

    if( ret < 0 )

    {

       printk (" ARM: init_module failedwith %d\n [ --kernel--]", ret); 

       return ret;

    }

    else

    {

       printk(" ARM gpio_led_driverregister success!!! [ --kernel--]\n");

    }

    return ret;

}

staticint __init ARM_GPIO_LED_CTL_init(void)

{

    int ret = -ENODEV;

    #ifdef ARM_GPIO_LED_DEBUG

                printk("ARM_GPIO_LED_CTL_init [ --kernel--]\n");

        #endif

    ret = HW_GPIO_LED_CTL_init();

    if (ret)

      return ret;

    return 0;

}

staticvoid __exit cleanup_GPIO_LED_ctl(void)

{

    #ifdef ARM_GPIO_LED_DEBUG

           printk("cleanup_GPIO_LED_ctl [ --kernel--]\n");

        #endif

    devfs_unregister_chrdev (GPIO_LED_MAJOR,"gpio_led_ctl" );

}

MODULE_DESCRIPTION("GPIO_led driver module");

MODULE_AUTHOR("zsm");

MODULE_LICENSE("GPL");//GPL協議證書資訊

module_init(ARM_GPIO_LED_CTL_init);

module_exit(cleanup_GPIO_LED_ctl);

9.1.2使用者程式

在編寫使用者應用程式過程中,考慮通過介面open()函式開啟裝置,再通過介面ioctl()函式來實現對LED的控制功能。

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <fcntl.h>      // open() close()

#include <unistd.h>     // read() write()

#define DEVICE_NAME "/dev/led_drv"

#define LED_ON 0

#define LED_OFF 1

int main(void)

{

        intfd;

    int ret;

    char *i;

       printf("\nstart GPIO_led_driver test\n\n");

        fd= open(DEVICE_NAME, O_RDWR);

    printf("fd= %d\n",fd);

        if(fd == -1)

        {

               printf("open device %s error\n",DEVICE_NAME);

        }

        else

        {

       while(1)

       {   ioctl(fd,LED_OFF);

           sleep(1);

           ioctl(fd,LED_ON);

           sleep(1);

       }

       ret =close(fd);

       printf("ret=%d\n",ret);

       printf("close led_driver test\n");

        }

       return 0;

}

9.1.3執行效果

(1)Create:

[[email protected] usb]# mknod /dev/led_drv c 97 0

(2)view:

[[email protected] usb]# ls -l /dev/led_drv

crw-r--r--   1 root     root      97,  0 Jan  1 01:29 /dev/led_drv

(3)insmod:

[[email protected] usb]# insmod led_drv.o

Using led_drv.o

ARM_GPIO_LED_CTL_init [ --kernel--]

Driver Loding .....................                                                                               

*********************************************

        ARM_GPIO_LED_2007/09/20

*********************************************

 GPLR3=73e7fd

 GPDR3=1efffc3

 ARMgpio_led_driver register success!!! [ --kernel--]

(4)./test_led:

[[email protected] usb]# ./test_led

                                                                             startGPIO_led_driver test

                                                                                 GPIO_LED_open[ --kernel--]

fd = 3

GPIO_LED_ioctl [ --kernel--]

GPIO_LED_ioctl [ --kernel--]

GPIO_LED_ioctl [ --kernel--]

GPIO_LED_ioctl [ --kernel--]

GPIO_LED_ioctl [ --kernel--]

GPIO_LED_ioctl [ --kernel--]

GPIO_LED_ioctl [ --kernel--]

GPIO_LED_ioctl [ --kernel--]

GPIO_LED_ioctl [ --kernel--]

GPIO_LED_ioctl [ --kernel--]

GPIO_LED_ioctl [ --kernel--]

GPIO_LED_release [ --kernel--]                                                                                 

(5) remove led_drv mode:

[[email protected] usb]# rmmod led_drv

cleanup_GPIO_LED_ctl [ --kernel--]

9.2例項二

/*************************************

NAME:gt2440_leds.c

COPYRIGHT:www.e-online.cc

*************************************/

#include <linux/miscdevice.h>

#include <linux/delay.h>

#include <asm/irq.h>

#include <mach/regs-gpio.h>

#include <mach/hardware.h>

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/init.h>

#include <linux/mm.h>

#include <linux/fs.h>

#include <linux/types.h>

#include <linux/delay.h>

#include <linux/moduleparam.h>

#include <linux/slab.h>

#include <linux/errno.h>

#include <linux/ioctl.h>

#include <linux/cdev.h>

#include <linux/string.h>

#include <linux/list.h>

#include <linux/pci.h>

#include <asm/uaccess.h>

#include <asm/atomic.h>

#include <asm/unistd.h>

#define DEVICE_NAME "leds"

/* 應用程式執行ioctl(fd, cmd, arg)時的第2個引數 */

#define IOCTL_LED_ON     1

#define IOCTL_LED_OFF    0

/* 用來指定LED所用的GPIO引腳 */

static unsigned long led_table [] =

{

       S3C2410_GPB5,

       S3C2410_GPB6,

       S3C2410_GPB7,

       S3C2410_GPB8,

};

/* 用來指定GPIO引腳的功能:輸出 */

static unsigned int led_cfg_table [] =

{

       S3C2410_GPB5_OUTP,

       S3C2410_GPB6_OUTP,

       S3C2410_GPB7_OUTP,

       S3C2410_GPB8_OUTP,

};

static int gt2440_leds_ioctl(

       structinode *inode,

       structfile *file,

       unsignedint cmd,

       unsignedlong arg)

{

       if(arg > 4)

       {

              return-EINVAL;

       }

       switch(cmd)

       {

              caseIOCTL_LED_ON:

                     //設定指定引腳的輸出電平為0

                     s3c2410_gpio_setpin(led_table[arg], 0);

                     return0;

              caseIOCTL_LED_OFF:

                     //設定指定引腳的輸出電平為1

                     s3c2410_gpio_setpin(led_table[arg], 1);

                     return0;

              default:

                     return-EINVAL;

       }

}

static struct file_operations dev_fops = {

       .owner    =     THIS_MODULE,

       .ioctl       =     gt2440_leds_ioctl,

};

static struct miscdevice misc = {

       .minor= MISC_DYNAMIC_MINOR,

       .name= DEVICE_NAME,

       .fops= &dev_fops,

};

static int __init dev_init(void)

{

       intret;

       inti;

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

       {

              s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]);

              s3c2410_gpio_setpin(led_table[i], 0);

       }

       ret= misc_register(&misc);

       printk(DEVICE_NAME" initialized\n");

       returnret;

}

static void __exit dev_exit(void)

{

       misc_deregister(&misc);

}

module_init(dev_init);

module_exit(dev_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("www.e-online.cc");

MODULE_DESCRIPTION("LEDS control forGT2440 Board");

10塊裝置驅動程式的編寫

塊裝置檔案通常指一些需要以塊(如512位元組)的方式寫入的裝置,如IDE硬碟、SCSI硬碟、光碟機等。它的驅動程式的編寫過程與字元型裝置驅動程式的編寫有很大的區別。

為了把各種塊裝置的操作請求佇列有效地組織起來,核心中設定了一個結構陣列blk_dev,該陣列中的元素型別是blk_dev_struct結構。這個結構由三個成分組成,其主體是執行操作的請求佇列request_queue,還有一個函式指標queue。當這個指標不為0時,就呼叫這個函式來找到具體裝置的請求佇列。塊裝置驅動程式描述符是一個包含在<linux/blkdev.h>中的blk_dev_struct型別的資料結構,其定義如下所示:

struct blk_dev_struct {

request_queue_t request_queue;

queue_proc *queue;

void *date;

};

在這個結構中,請求佇列request_queue是主體,包含了初始化之後的I/O 請求佇列。

所有塊裝置的描述符都存放在blk_dev表struct blk_dev_structblk_dev[MAX_BLKDEV]中;每個塊裝置都對應著陣列中的一項,可以使用主裝置號進行檢索。每當使用者程序對一個塊裝置發出一個讀寫請求時,首先呼叫塊裝置所公用的函式generic_file_read(),generic_file_write()。如果資料存在在緩衝區中或緩衝區還可以存放資料,那麼就同緩衝區進行資料交換。否則,系統會將相應的請求佇列結構新增到其對應項的blk_dev_struct中,如下圖所示:

10.1塊裝置驅動編寫流程

塊裝置驅動程式的編寫流程同字元裝置驅動程式的編寫流程很類似,也包括了註冊和使

用兩部分。但與字元驅動裝置所不同的是,塊裝置驅動程式包括一個request請求佇列。它是

當核心安排一次資料傳輸時在列表中的一個請求佇列,用以最大化系統性能為原則進行排序。

塊裝置驅動程式流程圖

10.2重要資料結構

Linux系統中有一個名為blkdevs的結構陣列,它描述了一系列在系統中登記的塊裝置。陣列blkdevs也使用裝置的主裝置號作為索引,其元素型別是device_struct結構。該結構中包括指向已登記的裝置驅動程式名的指標和指向block_device_operations結構的指標。在block_device_operations結構中包含指向有關操作的函式指標。所以,該結構就是連線抽象的塊裝置操作與具體塊裝置型別的操作之間的樞紐。

10.2.1 struct bio

一個bio結構體是在通用塊層或更底層對塊裝置i/o操作的的表示單位。通常1個bio對應1個I/O請求.

struct bio {

     sector_t        bi_sector;      /* device address in 512 byte

                                    sectors */

      structbio      *bi_next; /*request queue link */

     structblock_device *bi_bdev;

     unsignedlong         bi_flags;   /* status, command, etc */

     unsignedlong         bi_rw;      /* bottom bits READ/WRITE,

                                 * top bits priority

                                  */

     unsignedshort        bi_vcnt;   /*how many bio_vec's */

     unsignedshort        bi_idx;          /* current index into bvl_vec */

      /* Numberofsegments in this BIO after

       *physical address coalescing is performed.

       */

     unsignedint       bi_phys_segments;

     unsignedint       bi_size;    /*residual I/O count */

      /*

       * Tokeep track of the max segment size, weaccount for the

       *sizes of the first and last mergeablesegments in this bio.

       */

     unsignedint       bi_seg_front_size;

     unsignedint       bi_seg_back_size;

      unsignedint      bi_max_vecs;     /* max bvl_vecs we can hold */

      unsignedint      bi_comp_cpu;    /* completion CPU */

     atomic_t      bi_cnt;           /*pin count*/

     structbio_vec         *bi_io_vec;   /* the actual vec list */

     bio_end_io_t          *bi_end_io;

     void              *bi_private;

#if defined(CONFIG_BLK_DEV_INTEGRITY)

     structbio_integrity_payload *bi_integrity;  /*data integrity */

#endif

     bio_destructor_t     *bi_destructor;  /* destructor */

      /*

       * Wecan inline a number of vecs at the end ofthe bio, to avoid

       *double allocations for a small number ofbio_vecs. This member

       * MUSTobviously be kept at the very end ofthe bio.

       */

     structbio_vec          bi_inline_vecs[0];

};

10.2.2 struct gendisk


struct gendisk {   //表示一個獨立的磁碟裝置或分割槽

  intmajor;                /* major number of driver */

  intfirst_minor;       /*starting minor number*/

  intminors;       /* maximumnumber ofminors, =1 for

                 *disks that can't be partitioned. 每一個分割槽都有一個minor號*/

  chardisk_name[DISK_NAME_LEN];  /* name ofmajor driver */

  structdisk_part_tbl *part_tbl;

  structhd_struct part0;

  structblock_device_operations *fops;

  structrequest_queue*queue;

  void*private_data;

  int flags;

  struct device*driverfs_dev;  // FIXME: remove

  struct kobject*slave_dir;

  structtimer_rand_state *random;

 atomic_tsync_io;         /* RAID */

  structwork_struct async_notify;

#ifdef CONFIG_BLK_DEV_INTEGRITY

  structblk_integrity *integrity;

#endif

  int node_id;

};

struct device_struct {

const char *name;

struct file_operations *chops;

};

static struct device_structblkdevs[MAX_BLKDEV];

struct sbull_dev {

void **data;

int quantum;// thecurrent quantum size

int qset;// the current array size

unsigned long size;

unsigned int access_key;// used by sbulluid and sbullpriv

unsigned int usage;// lock the device while using it

unsigned int new_msg;

struct sbull_dev *next;// next listitem

};

與字元裝置驅動程式一樣,塊裝置驅動程式也包含一個file_operation結構,其結構定義一般如下所示:

struct file_operation blk_fops = {

NULL,//seek

block_read,//核心函式

block_write,//核心函式

NULL,//readdir

NULL,//poll

sbull_ioctl,// ioctl

NULL,//mmap

sbull_open,//open

NULL,//flush

sbull_release,//release

block_fsync,//核心函式

NULL,//fasync

sbull_check_media_change,//check media change

NULL,//revalidate

NULL,//lock

};

所有的塊驅動程式都呼叫核心函式block_read()、block_write(),block_fsync()函式,所以在塊裝置驅動程式入口中不包含這些函式,只需包括ioctl()、open()

和release()函式即可。

(1)裝置初始化

塊裝置的初始化過程要比字元裝置複雜,它既需要像字元裝置一樣在引導核心時完成一定的

工作,還需要在核心編譯時增加一些內容。塊裝置驅動程式初始化時,由驅動程式的init()完成。

塊裝置驅動程式初始化的工作主要包括:

· 檢查硬體是否存在;

· 登記主裝置號;

· 將fops結構的指標傳遞給核心;

· 利用register_blkdev()函式對裝置進行註冊:

if(register_blkdev(sbull_MAJOR,“sbull”,&sbull_fops)) {

printk(“Registering block device major:%d failed\n”,sbull_MAJOR);

return-EIO;

};

· 將request()函式的地址傳遞給核心:

blk_dev[sbull_MAJOR].request_fn= DEVICE_REQUEST;

· 將塊裝置驅動程式的資料容量傳遞給緩衝區:

#define sbull_HARDS_SIZE 512

#define sbull_BLOCK_SIZE 1024

static int sbull_hard = sbull_HARDS_SIZE;

static int sbull_soft = sbull_BLOCK_SIZE;

hardsect_size[sbull_MAJOR] = &sbull_hard;

blksize_size[sbull_MAJOR] = &sbull_soft;

在塊裝置驅動程式核心編譯時,應把下列巨集加到blk.h檔案中:

#define MAJOR_NR sbull_MAJOR

#define DEVICE_NAME “sbull”

#define DEVICE_REQUEST sbull_request

#define DEVICE_NR(device) (MINOR(device))

#define DEVICE_ON(device)

#define DEVICE_OFF(device)

(2)request操作

Request操作涉及一個重要的資料結構如下。

struct request {

kdev_t rq_dev;

int cmd; // 讀或寫

int errors;

unsigned long sector;

char *buffer;

struct request *next;

};

對於具體的塊裝置,函式指標request_fn當然是不同的。塊裝置的讀寫操作都是由request()函式完成。所有的讀寫請求都儲存在request結構的連結串列中。request()函式利用CURRENT巨集

檢查當前的請求。request()函式從INIT_REQUEST巨集命令開始(它也在blk.h中定義),它對請求佇列進行檢查,保證請求佇列中至少有一個請求在等待處理。如果沒有請求(即CURRENT = 0),則INIT_REQUEST巨集命令將使request()函式返回,任務結束。

    假定佇列中至少有一個請求,request()函式現在應處理佇列中的第一個請求,當處理完

請求後,request()函式將呼叫end_request()函式。如果成功地完成了讀寫操作,那麼應該用引數值1 呼叫end_request()函式;如果讀寫操作不成功,那麼以引數值0 呼叫end_request()函式。如果佇列中還有其他請求,那麼將CURRENT 指標設為指向下一個請求。執行end_request()函式後,request()函式回到迴圈的起點,對下一個請求重複上面的處理過程。

(3)開啟操作

(4)釋放裝置操作

(5)ioctl操作

10.3中斷程式設計

很多Linux 的驅動都是通過中斷的方式來進行核心和硬體的互動。

這是驅動程式申請中斷和釋放中斷的呼叫。在include/linux/sched.h裡宣告。

request_irq()呼叫的定義:

int request_irq(unsigned int irq,

void (*handler)(int irq, void*dev_id, struct pt_regs *regs),

unsigned long irqflags,const char* devname,oid *dev_id);

irq 是要申請的硬體中斷號。在Intel平臺,範圍是0~15。handler 是向系統登記的中斷處理函式。這是一個回撥函式,中斷髮生時,系統呼叫這個函式,傳入的引數包括硬體中斷號,device id,暫存器值。dev_id就是下面的request_irq時傳遞給系統的引數dev_id。irqflags是中斷處理的一些屬性。比較重要的有SA_INTERRUPT,標明中斷處理程式是快速處理程式(設定SA_INTERRUPT)還是慢速處理程式(不設定SA_INTERRUPT)。快速處理程式

被呼叫時遮蔽所有中斷。慢速處理程式不遮蔽。還有一個SA_SHIRQ 屬性,設定了以後執行多個裝置共享中斷。dev_id在中斷共享時會用到。一般設定為這個裝置的device結構本身或者NULL。中斷處理程式可以用dev_id找到相應的控制這個中斷的裝置,或者用irq2dev_map

找到中斷對應的裝置。void free_irq(unsigned int irq,void *dev_id);

10.4一個簡單的塊裝置驅動

通過寫一個建立在記憶體中的塊裝置驅動,來學習linux核心和相關裝置驅動知識

#defineSIMP_BLKDEV_DISKNAME        "simp_blkdev"

#defineSIMP_BLKDEV_BYTES        (16*1024*1024)// 使用巨集定義了塊裝置的大小,定為16M

#defineSIMP_BLKDEV_DEVICEMAJOR        COMPAQ_SMART2_MAJOR

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

structblock_device_operations simp_blkdev_fops = {
          .owner               = THIS_MODULE,
  };// gendisk結構需要設定fops指標,雖然我們用不到,但該設還是要設的

static structgendisk *simp_blkdev_disk;

static structrequest_queue *simp_blkdev_queue;// 指向塊裝置需要的請求佇列

unsigned charsimp_blkdev_data[SIMP_BLKDEV_BYTES];

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

module_init(simp_blkdev_init);//然後申明模組的入口和出口

 module_exit(simp_blkdev_exit);

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

static int __initsimp_blkdev_init(void) //在入口處新增這個裝置、出口處刪除這個裝置
  {

simp_blkdev_disk = alloc_disk(1); //在新增裝置之前我們需要申請這個裝置的資源,這用到了alloc_disk()函式

strcpy(simp_blkdev_disk->disk_name,SIMP_BLKDEV_DISKNAME);  //裝置有關的屬性也是需要設定
simp_blkdev_disk->major = SIMP_BLKDEV_DEVICEMAJOR;
simp_blkdev_disk->first_minor = 0;

simp_blkdev_disk->fops =&simp_blkdev_fops;
simp_blkdev_disk->queue = simp_blkdev_queue;
set_capacity(simp_blkdev_disk, SIMP_BLKDEV_BYTES>>9);

 add_disk(simp_blkdev_disk);

if (!simp_blkdev_disk) {
ret = -ENOMEM;

goto err_alloc_disk;
        }

 simp_blkdev_queue =blk_init_queue(simp_blkdev_do_request, NULL);//初始化請求佇列
 if (!simp_blkdev_queue) {
             ret = -ENOMEM;
             goto err_init_queue;
  }//在載入模組時用simp_blkdev_do_request()函式的地址作引數

呼叫blk_init_queue()初始化一個請求佇列

//用來從一個請求佇列中拿出一條請求(其實嚴格來說,拿出的可能是請求中的一段)。
隨後的處理請求本質上是根據rq_data_dir(req)返回的該請求的方向(讀/寫),把塊裝置中的資料裝入req->buffer、或是把req->buffer中的資料寫入塊裝置。

static voidsimp_blkdev_do_request(struct request_queue *q) //請求佇列的處理函式。
{
        struct request *req;
        while ((req = elv_next_request(q)) != NULL) {
                if ((req->sector +req->current_nr_sectors) << 9
                       > SIMP_BLKDEV_BYTES) {
                       printk(KERN_ERR SIMP_BLKDEV_DISKNAME
                               ": bad request: block=%llu,count=%u\n",
                               (unsigned long long)req->sector,
                               req->current_nr_sectors);
                       end_request(req, 0);
                       continue;
                }

                switch (rq_data_dir(req)){
                case READ:
                       memcpy(req->buffer,
                               simp_blkdev_data + (req->sector <<9),
                               req->current_nr_sectors << 9);
                       end_request(req, 1);
                       break;
                case WRITE:
                       memcpy(simp_blkdev_data + (req->sector << 9),
                               req->buffer, req->current_nr_sectors<< 9);
                       end_request(req, 1);
                       break;
                default:
                       /* No default because rq_data_dir(req) is 1 bit */
                       break;
                }
        }
}
 return 0;

err_alloc_disk:
        blk_cleanup_queue(simp_blkdev_queue);
err_init_queue:
        return ret;}

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

static void __exit simp_blkdev_exit(void)

{
      del_gendisk(simp_blkdev_disk);

put_disk(simp_blkdev_disk);  

blk_cleanup_queue(simp_blkdev_queue);

}

相關推薦

嵌入式Linux驅動程式開發

嵌入式Linux驅動程式開發系統呼叫是作業系統核心和應用程式之間的介面,裝置驅動程式是作業系統核心和機器硬體之間的介面。裝置驅動程式為應用程式遮蔽了硬體的細節,這樣在應用程式看來,硬體裝置只是一個裝置檔案,應用程式可以象操作普通檔案一樣對硬體裝置進行操作。裝置驅動程式是核心的

嵌入式Linux驅動程式-基礎篇】- 驅動與硬體層間的通訊

驅動與硬體層間的通訊 1 IO埠和IO記憶體 目前大多數處理器外設都是通過讀寫暫存器操作晶片外設,這些暫存器處於記憶體地址或者I/O地址上。從硬體角度考慮,記憶體和IO區域沒有概念上的區別,均是通過地址匯流排、資料匯流排和控制匯流排(讀寫訊號)來進行讀寫操作。 並非所有

嵌入式Linux應用程式開發——多執行緒4(執行緒的同步——訊號量)

#include <pthread.h> #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <semaphore.h> int

嵌入式初學者學習嵌入式必看必看書籍列表,有電子檔的同學可以共享出來,謝謝 Linux基礎 1、《Linux與Unix Shell 程式設計指南》 2、《嵌入式Linux應用程式開發詳解》

嵌入式初學者參考書目 無論學習哪方面的程式設計,都需要掌握基礎知識和程式語言,其中《深入理解計算機作業系統》是比較重要的。下面是一些計算機關於嵌入式方面的推薦,有些是借鑑他人的歸納。 Linux基礎 1、《Linux與Unix Shell 程式設計指南》 2、《嵌入式Linux應用程式開發詳

嵌入式linux應用程式開發完全手冊》NAND FLASH硬體程式設計學習筆記

1.先看一下Flash的引腳圖,它與S3C2440連線比較少(相對nor flash),地址資料和命令都是在如圖示的一些使能訊號的配合下,通過8個I/O引腳傳輸。寫地址,資料,命令時,nCE,nWE訊號

linux驅動程式開發的學習步驟

1. 學會寫簡單的makefile  2. 編一應用程式,可以用makefile跑起來  3. 學會寫驅動的makefile 4. 寫一簡單char驅動,makefile編譯通過,可以insmod, lsmod, rmmod. 在驅動的init函式裡列印hello world, insmod後應該能夠通過d

基於ARM的嵌入式Linux應用程式開發

0 引 言   當今社會,嵌入式系統已經滲透到人們工作、生活中的各個領域,嵌入式處理器已佔分散處理器市場份額的94%。而嵌入式Linux系統也蓬勃發展,不僅繼承了Linux原始碼開放、核心穩定高效、軟體豐富等優勢,還具備支援廣泛處理器結構和硬體平臺、佔有空間小、成本低廉、結構緊湊等特點。 1 ARM處理

《精通Linux 裝置驅動程式開發》下載

2018年11月02日 14:24:20 茹粿鰅莧丨你 閱讀數:1 標籤: 程式設計 資料 區

Eclipse 搭建 Linux 核心驅動程式開發環境

1、開發工具 eclipse 、arm-linux-gcc交叉工具鏈、對應開發板的Linux 核心原始碼。2、安裝開發工具,並將核心原始碼包解壓到指定路徑中,並編譯。 eg:/usr/local/arm/linux_E9_3.0.35_for_Linux3、利用eclipse

Linux USB 驅動開發(五)—— USB驅動程式開發過程簡單總結

       裝置驅動程式是作業系統核心和機器硬體之間的介面,由一組函式和一些私有資料組成,是應用程式和硬體裝置之間的橋樑。在應用程式看來,硬體裝置只是一個裝置檔案,應用程式可以像操作普通檔案一樣對硬體裝置進行操作。       裝置驅動程式是核心的一部分,主要完成以下功能

Linux下PCI裝置驅動程式開發基本框架

PCI是一種廣泛採用的匯流排標準,它提供了許多優於其它匯流排標準(如EISA)的新特性,目前已經成為計算機系統中應用最為廣泛,並且最為通用的匯流排標準。Linux的核心能較好地支援PCI匯流排,本文以Intel 386體系結構為主,探討了在Linux下開發PCI裝置驅動程式的基本框架。    一、PCI匯流排

[嵌入式Linux驅動]S5PV210的步進電機Linux驅動程式

#include <asm/io.h> #include <asm/uaccess.h> #include <linux/fs.h> #include <linux/delay.h> #include <linux/module.h> #inclu

Exynos 4412 Cortex-A9嵌入式Linux驅動開發學習筆記-第一期

一、Linux 體系結構 如下圖所示,Linux 體系結構,從大的方面可以分為使用者空間(User Space)和核心空間 (Kernel Space)。 使用者空間中包含了 C 庫,使用者的應用程式。在某些體系結構圖中還包含了 shell,當然 shell指令碼

嵌入式linux驅動開發環境搭建(親測成功)

一 開發環境: (1)主機:vmware8+fedora14,核心版本為2.6.35,主機自帶GCC版本4.5。(這兩點很重要,所有的問題都與這兩個因素有關)。 (2)目標機:S3C6410(arm11) (3)目標機核心:linux2.6.24 (4)交叉編譯工具:arm

嵌入式Linux驅動開發案例流程--LED驅動

本文主要是以一個最簡單的LED驅動開發流程,來窺探一下Linux驅動開發為何物。 基本流程: 1.編寫驅動檔案xxxx.c 這個檔案的主要作用是對裝置硬體初始化,主要是xxx_init(),其中也包括

嵌入式Linux驅動開發(四)——字元裝置驅動之中斷方式以及中斷方式獲取按鍵值

之前我們完成了關於通過查詢的方式獲取按鍵鍵值的驅動程式,可以參考:嵌入式Linux開發——裸板程式之中斷控制器。 雖然讀取鍵值沒有什麼問題,但是測試程式佔用CPU過高,一直在不斷的查詢,資源消耗過大,這個問題非常嚴重,我們必須要來解決一下。但是在解決這個問題之前,我們先來思考一個問題,除

Linux網路驅動程式開發例項分析

一.Linux系統裝置驅動程式概述   1.1 Linux裝置驅動程式分類   1.2 編寫驅動程式的一些基本概念 二.Linux系統網路裝置驅動程式 2.1 網路驅動程式的結構 2.2 網路驅動程式的基本方法 2.3 網路驅動程式中用到的資料結構 2.4 常用的系統支援 

Linux裝置驅動程式開發詳解》中的virtualBox下的ubuntu在vmware下使用

最近在看宋寶華老師的《Linux裝置驅動程式開發詳解》第三版,在看到1.5小節Linux裝置驅動的開發環境構建時,我也 打算在自己電腦使用宋老師的系統(裡面有書配套的原始碼)。由於我之前電腦上已經安裝過了vmware workstation,就決定在 此環境下安裝系統。按照

【 專欄 】- 嵌入式linux驅動/linux驅動測試/嵌入式測試驅動開發(TDD)例項

嵌入式linux驅動/linux驅動測試/嵌入式測試驅動開發(TDD)例項 介紹AST2500處理器下,對應的LINUX驅動,包括AST2500的暫存器以及對應的通用驅動。 涉及到的模組有ADC,網口(NIC),Video等。

嵌入式linux驅動開發【獨家+原創視訊教學】

【創科之龍】嵌入式裸機開發系列【原創+獨家】 http://pan.baidu.com/s/1bn4Hpmv 【創科之龍】嵌入式linux驅動開發系列【原創+獨家】 http://pan.baidu.com/s/1ESCIM 第一套: 第一期【嵌入式入門及專案實戰專案】 百度網盤下載地址:http://pan