電子羅盤Kernel層分析

電子羅盤的流程順序涉及到第三方庫的調用,以及g-sensor數據的獲取,因此需要externel中庫文件的支撐。

         目前以akm8975c數據爲例子,首先modify的文件位置大致在3個文件夾中

Hal層

External層

Kernel層

Kernel層驅動

1、  Kernel層驅動分析

文件位置:kernel\drivers\misc\xx_compass_akm8975c.c

該函數採用模塊化函數方法進行註冊,通過module_init調用該模塊化初始化函數akm8975_init(void)函數。

在akm8975_init(void)函數中調用系統函數i2c_add_driver將i2c_driver類型結構體akm8975_dirver添加到系統內核中。當然,退出模塊時需要在akm8975_exit中調用i2c_del_driver將akm8975_driver刪除。

關於akm8975_driver結構體包含以下幾個函數,也就是對i2c_driver定義結構體的填充。

Static struct i2c_driver akm8975_driver = {

         .probe= akm8975_probe,

         .remove= akm8975_remove,

         .id_table= akm8975_id,

         .driver= {

                   .name= AKM8975_I2C_NAME,

},

};

接下來分析該結構體中分析入口函數akm8975_probe函數,該函數實現了設備啓動配電後模塊註冊到系統內核的過程,對相關的寄存器及要使用的工作隊列進行初始化。

目前疑問是akm8975_probe函數中有兩個參數是如何引入系統的?

第一個參數是i2c_client,第二個參數是i2c_device_id

3.1 這裏應當注意的是i2c_client參數最後賦值給該文件的全局變量,用於封裝i2c讀寫函數(AKI2C_RxData, AKI2C_TxData).

Probe函數中首先調用i2c_check_functionality(client->adapter,I2C_FUNC_I2C)驗證i2c適配器功能。然後對akm8975_data(自定義,可以按照需要繼承各種數據類型,例如input等)類型局部數據分配空間,然後調用i2c_set_clientdata---à>>dev_set_driverdata,在dev_set_driverdata中實現賦值dev->drivedata= data。

以上就完成了i2c_client數據的初始化。

3.2 接着,利用平臺設備實現akm8975_data數據的初始化(這裏不需要初始化,初始化部分已經被刪除掉)。

平臺數據的實現步驟主要如下

         If(client->dev.platform_data==NULL){

                  Return ….

}

Pdata =client->dev.platform_data;(pdata爲全局akm8975_platform_data自定義結構體)

This->client= client;(this->client爲全局i2c_client)

         //Pdata->init;

3.3 聲明input設備

Input_dev封裝在akm8975_data變量當中,調用input_allocate_device對其需要分配空間,成功後調用set_bit(EV_ABS,akm->input->dev)對其聲明上報數據類型

調用input_set_abs_params(akm->input_dev,ABS_RX,0,23040,0,0)設置上報事件的數據範圍。

給輸入設備名字賦值。Akm->input_dev->name= “compass”

調用input_register_device註冊input設備。

3.4 註冊混雜設備,實現用戶層與內核層數據的控制。

這裏註冊了兩個misc設備,具體作用還沒有搞明白,一個通過akm_aot_ioctl,主要是獲取和設置sensor狀態(打開和關閉)和延時,另外一個通過akmd_ioctl實現數據與內核層和externel層的寫入和讀取。

閱讀akm_aot_ioctl代碼,ECS_IOCTL_APP_SET_MVFLAG,表示設置原始的磁場矢量標誌符。調用copy_from_user將參數argp拷貝到flag中.

設置完成後,在下一個開關語句中,調用atomic_set(&mv_flag,flag)實現設置標識位爲flag。

(注意:static atomic_t mv_flag)

該ioctl操作中共有3個開關語句,主要實現的是執行原子操作,實現對各種sensor狀態的控制。

然後閱讀akmd_ioctl代碼,

這裏首先分析ECS_IOCTL_READ,分別在以下三個開關函數中實現read功能。

首先,判斷參數有效後,調用copy_from_user將參數argp拷貝到rwbuf(char數組類型)

然後,判斷rwbuf區域沒有越界後,調用AKI2C_RxData(&rwbuf[1],rwbuf[0])實現i2c讀數據。

最後,調用copy_to_user將rwbuf拷貝到argp參數,注意rwbuf大小爲rwbuf[0]+1

ECS_IOCTL_READ,ECS_IOCTL_WRITE主要是數據的傳輸過程,在extenel層中獲取數據的方式包括從akm8975的寄存器或者是eprom上,在AK8975Driver.c文件中調用。

另外注意的應該是ECS_IOCTL_GET_TEST, ECS_IOCTL_SET_TEST, ECS_IOCTL_GET_TEST_RESULT, ECS_IOCTL_SET_TEST_RESULT,控制命令,這些命令對應的是externel空間中的操作,主要是原廠提供的算法庫。

需要注意的是新的linux內核中刪去了ioctl,使用unlocked_ioctl代替原來的功能

Misc設備中的操作函數除了上面介紹的ioctl,還應包括akm_aot_open,akm_aot_release函數。

分析akm_aot_open時,

atomic_cmpxchg((v),c, c + (a)); //如果v的值等於c就加a,old=c。如果v的值不等於c,old=v的值;返回值爲v的值

需要思考的是open_count,open_flag與reserve_open_flag分別代表的邏輯意義是什麼,open_count和open_flag只要一個代表1就會退出喚醒工作隊列open_wq

akm_aot_release函數中將以上需要思考的三個標示符置爲0後頁喚醒工作隊列open_wq,因此在下一個段落中將重點分析下open_wq的意義

3.5 open_wq

從字面上理解是打開工作隊列。

在該文件中實現open_wq的步驟可以總結如下:

         1聲明

Static DECLEAR_WAIT_QUEUE_HEAD(open_wq)

         2初始化

Init_workqueue_head(&open_wq);

         3睡眠

調用wait_event_interruptible(open_wq,(atomic_read(&open_flag)<=0));

         4喚醒

調用wake_up(&open_wq)

注意:在probe函數中實現第二步,完成初始化,然後在讀取狀態時將open_wq睡眠下去,遇到原子操作時喚醒該隊列。

本文件中除了該隊列還定義了data_ready_wq,該隊列從字面上理解應該是準備好相關的數據。實現步驟同上,其中睡眠在AKECS_GetData中調用,從而實現文件的阻塞。

Wake_up在akm8975_func_work函數中調用,應該注意的是採用輪訓方式後,文件沒有調用該函數,具體如何實現喚醒數據,待具體分析

仔細觀察了data_ready_wq調用的是

wait_event_interruptible_timeout(data_ready_wq,atomic_read(&data_ready),1000)

該函數喚醒有兩種方式,1.其他人在等待隊列上調用了wake_up 2.超時到期(實際爲本文采用方式)。schedule_timeout的進程始終會在超時到期時被喚醒,在本文件中可以替代該函數。

3.6 關於early_suspend函數

Akm->early_suspend.level

Akm->early_suspend.suspend

Akm->early_suspend.resume

調用register_early_suspend(&akm->early_suspend)

這裏爲什麼要用到early_suspend?

發佈了9 篇原創文章 · 獲贊 2 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章