1. 程式人生 > >Linux ARM 驅動筆記之一 : 準備

Linux ARM 驅動筆記之一 : 準備

Linux ARM 驅動筆記之一 : 準備

轉載請註明:http://blog.csdn.net/zkwsr/article/details/12510439

本文以Linux ARMSPI裝置驅動為例記錄Linux驅動的開發中的一些套路和準備.本文內容拋開具體程式碼,描述相關的準備知識,描述書本中很少提及的內容.

1 驅動編寫的套路

Linux 驅動編寫歸根揭底,涉及到底部,就是設定暫存器, 從設定暫存器的角度來看,Linux驅動編寫分為兩種套路:

1)     直接操控暫存器: 這種方式寫驅動程式碼結構簡單,直接設定控制暫存器即可,但是對程式設計師的硬體知識要求比較高, 一個Arm的Datasheet看上3-5遍,才有可能寫出個正確的驅動.(從微控制器寫過程式的程式設計師轉到linux驅動,開始容易寫這種程式碼.)

注意事項:

volatile 這個關鍵字, 有些時候必須加上.否則變數不會及時更新,導致程式出錯.

管腳地址示例:

 a) 使用核心設定  

 定義:S3C2410_GPB(0)//S3C2410 CPU GPIO_B 0個管腳,linux核心提供了這個對應的巨集.

 設定/賦 : s3c2410_gpio_setpin(S3C2410_GPB(0),0);

 b) 直接使用暫存器地址


 #define  PMC_PCER  0xFFFFFC10  //sam9G15 PMC暫存器.

#definev_uint32_t volatile uint32_t
 //根據暫存器的位數設定位元組長度.uint32_t, uint16_t  
 v_uint32_t *pmc_pcer; 
 //ioremap地址對映
 pmc_pcer =(uint32_t *)ioremap(PMC_PCER, 4);
  * pmc_pcer = (1u<< 5); //設定暫存器值第的值


通過直接操控暫存器就可完成裝置的驅動, 但是不建議使用這種方法.

2)     不直接操控暫存器: 通過Linux核心提供的驅動框架寫程式,速度快,相對於直接控制暫存器,對硬體知識要求不那麼高了.在編寫個規範和可靠性方面,按linux驅動框架編寫會更好.驅動架構分為:控制器驅動層/Linux驅動核心層/裝置驅動層. 下面以SPI驅動為例介紹.(其他型別裝置類似)

 SPI驅動架構圖:

SPI控制器驅動層:每種處理器平臺都有自己的控制器驅動, 屬於平臺移植相關,一般廠商都會提供到linux核心中. (編譯核心時將之編譯進去,或者選擇為module,編譯後將之insmod)

//這樣程式設計師就不用去過多瞭解硬體控制器部分的細節了.

SPI核心層:Linux SPI的核心部分,裡面定義了相關的資料結構, 用於向上(SPI裝置)提供統一的介面.提供了spi驅動相關資料結構和操作函式.

// linux-2.6.39/drivers/spi/spi.c

SPI裝置驅動(裝置協議驅動):針對不同裝置的特定驅動.利用SPI核心層提供的介面編寫協議驅動. //這個驅動是程式設計師需要開發的驅動.

2 控制器驅動模組的載入:

載入了控制器驅動後, spicore的功能才會正常使用,spidevice 才可以正常執行.

Linux核心編譯時有 [*] [M] [ ] 三個選項:

[*] : 選中編入核心,啟動時會載入.

[ M ] : Module 編譯成模組,手動載入.

[ ] : 不選.

#make menuconfig //進行選擇

#make zImage/uImage/…. 各種形式的核心映象

#make modules //編譯[M]選中的模組,然後在相關路徑下找到, copy出來使用.

// 下圖編譯後module為 linux-2.6.39/drivers/spi/atmel_spi.ko

// 和自己寫的module檔案一樣使用 #insmod.


3 設備註冊形式:

  根據裝置的不同存在形式進行程式碼的對應編寫.裝置的存在形式分為板載裝置和動態載入裝置.

1)      板載裝置:裝置啟動時載入的裝置, 固定在板子/機器上的裝置,不進行熱插拔.

linux-2.6.39/arch/arm/mach-at91/board-sam9x5cm.c


//  在spi board_info中新增裝置資訊, 啟動時通過spi core 的__init函式spi_register_board_info()函式進行初始化.

api_register_board_info()  <-- at91_add_device_spi()

//載入spi devide 時會呼叫. //在深挖就找不到了,暫時放在這.

  2) 動態載入裝置: 載入裝置驅動時載入上去的裝置.

動態裝置新增例子:

152     spi_master =spi_busnum_to_master(SPI_BUS);

153     spi_device =spi_alloc_device(spi_master);

154     pdev =bus_find_device_by_name(spi_device->dev.bus, NULL, buff);

155     if (pdev) {

156        spi_dev_put(spi_device);

157     } else {

158         status =spi_add_device(spi_device);       

159     }                                                                                                                                                                         

160    

161    put_device(&spi_master->dev);

其他的驅動也有類似的方法.

4 裝置驅動編寫方法:

關於裝置驅動的寫法,沒有捷徑.多讀程式碼,多寫程式碼.裝置驅動起初最好的方法就是—看核心原始碼,裝置驅動最好的例子. 以spi驅動為例:

~/linux-2.6.39/drivers/spi/spidev.c就是第一個需要看的例子.參照這個例子就能寫出spi裝置驅動. 當然,spidev.c提供的僅僅是一種方式.還有其他的方式—internet資源模仿. 例如:git,github,…. 最終寫出漂亮的驅動.

5 驅動的makefile

1 ifneq ($(KERNELRELEASE),)                                                                                                                                                     

  2     #module name not same any file

 3     MODULE_NAME := EEPROM

  4     #module with init and exit

 5     RESMAIN_CORE_OBJS :=eeprom.o

 6     RESMAIN_GLUE_OBJS := twi.o

 7     $(MODULE_NAME)-objs :=  $(RESMAIN_CORE_OBJS) $(RESMAIN_GLUE_OBJS)

 8     obj-m := $(MODULE_NAME).o

  9else

 10    PWD=$(shell pwd)

 11    KERNEL_SRC=~/linux-2.6.39

 12default:

 13     # -C input kernel src -M reback thismakefile

 14     #$(MAKE) -C $(KERNEL_SRC) M=$(PWD)LDDINC=$(PWD) modules

 15    $(MAKE) -C $(KERNEL_SRC) M=$(PWD) modules

 16clean:

 17    rm *.o *.ko #and other generated code

 18endif

6 關於中斷號

寫中斷函式的時候,需要這樣一個函式:

 intrequest_irq(unsigned int irq, irq_handler_t handler,

 unsigned long flags, const char *devname, void*dev_id);

其中 unsignedint irq, 是中斷號,這個中斷號如何選取,怎麼能和板子上的某個GPIO引腳對應上呢?下面介紹一下查詢中斷號的方法.

首先看CPUdatasheet,有類似下圖的一張表格,列出了0-31個號碼(具體CPU不同,可能編號數目不同 16 32 64…).   

內部中斷: 這32是CPU的內部中斷 (外設中斷), 這32箇中斷是由各外設產生的—外設—這裡應該理解成CPU的外設控制器(這樣和外部裝置就能區別開來了).這32箇中斷對應的中斷編號是(0-31),對外是沒有引腳的.(記住,這些中斷是沒辦法和真實的外部裝置用線連起來的, 所以長說為內部中斷, 所謂內部是CPU內部,相對於外部而言的).這些內部中斷普通的驅動程式設計師是不需要去處理的,核心控制器驅動提供者已經做好了相關的工作,僅需要呼叫相關的處理函式即可.

外部中斷:所謂的外部中斷,是在裝置程式設計時長用到的中斷, GPIO_X_Y //GPIO的X控制器第Y個引腳.這個編號從32-255,根據GPIO的引腳數量而定.那GPIO的引腳的中斷線時怎麼編號的呢? 是以32為基數,向上累加的.具體使用可使用相關的標頭檔案對應的定義即可.


下面舉例說明:

   例如SAM9G15晶片的 GPIO_D_21引腳對應的中斷號,在linux-2.6.39/arch/arm/mach-at91/include/mach/gpio.h中定義

#define AT91_PIN_PD21  (PIN_BASE + 0x60 + 21)  //32+96+21=149

//GPIO_D_21號引腳對應的中斷編號是 149.

//使用時多使用核心提供的定義,不要使用純數字.

//那麼149顯然不在其上圖0-31箇中斷號中, linux核心為了後續編寫程式方便提供了對映.

//149對應到了PIOD的暫存器上的第21.然後進行處理.

//對於上層的程式設計師只需關心GPIO引腳對應的中斷編號即可.