2011年12月7日 星期三

我的CJSCOPE JS-131HR新NB

為什麼會選擇這個牌子ㄋ?當然是為了荷包和性能。就是C/P值啦。目前使用至今算是非常滿意,重量真的很輕(和ULTRA BOOK比也才多個300g)。加到8G的RAM後同時開了3個虛擬機還真是非常順暢,四核心加上HT技術果然和兩年前買的Core 2 Due有明顯的效能差異。編譯linux核心也不用等完喝一杯咖啡的時間。
目前VM都只安裝Oracle VirtualBox,為什麼用它呢?優點說不盡,缺點呢:None.。好比說你已在xp使用多年的軟體,偏偏安裝到64bit的win7或Vista就問題一堆。燒錄軟體、很多Embeded的軟體也都只有for 2k/Xp。這時候你就等於買了一部實體NB,但卻擁有N部虛擬computer,只要你的RAM夠大,同時開10個OS也不怕。所以我目前已經開始再觀察512*8的DRAM IC價格。當它來到了平民價,且品質也趨於穩定就是將我的寶貝增加到16GB的時候了。說真的,玩GAME也用不到那麼多記憶體;若是您有使用VM才會建議將DRAM擴充到那麼大。
總之,花了21k擁有4核的NB,真的只有這一部。若是您考慮的是這個品牌的存續或日後的維修等問題‧‧‧我想這些都是未知數。所以我願意冒這個險。總之我使用的評價就是"爽"。
為了荷包,我也沒有添購正版Win7,所以只用試用版!!!!!
--> 閱讀更多...

2011年11月29日 星期二

x64存儲管理機制初窺

x64存儲管理機制初窺
x64存儲管理機制,經proljmik指點,去找intel手冊和AMD手冊看,看完之後把我的理解寫下,
如有什麼理解偏差之處,還望各位不要笑話。
1.長模式(long mode
   AMD64架構的長模式中包括兩個子模式:64bit模式和compatibility模式。
   64bit模式:在此模式中虛擬位址空間使用平坦(flat)模式,段寄存器包括CS,DS,ES,SS全部清零(FS,GS除外,在線性位址計算時,提供額外base寄存器定址某些OS的系統資料結構),能為64bitOS和應用程式提供64位元定址支援。
   compatiblity模式:在此模式中使用與lagecy保護模式一樣的存儲管理模式,為的是平滑運行32bit16bit的應用程式,無需重新編譯,注意:在長模式中不支援真實模式和虛擬8086模式!!











由於在64bit模式下使用flat模式,所以無lagecy保護模式下的分段管理模式,所以虛擬位址直接對應線性位址。所以x64的分頁管理模式才是64bit下的重頭戲
  
   2.x64分頁管理模式
   如下圖,是x86-64下的所有頁大小和物理位址大小的匯總.










x64下,理論上可以通過使用PAE(實體位址擴充)分頁結構支援64bit的線性位址到52bit物理位址的映射的,但在對這一架構的首次實現中,只實現了48位元的線性位址到40位元物理位址的映射。現在你可以通過CPUID指令查看自己的cpu所支援的最大物理位址(MAXPHYDDR.
   大家一定還記得在lagecy保護模式下通過PAE功能來訪問超過32bit(36bit)的物理位址,但是在x64模式下的PAE與先前的PAE是不一樣的!!
   從線性位址到物理位址的轉換中用到了四級頁資料結構,一個新的頁表資料結構---PML4被引入用來指向頁目錄指標表。PML4只能在x64模式下使用。而且頁目錄表指標從原來的4個項增加到了512個,所以要從線性位址中分配9bit用來索引PDP表(原來是2bit)。
   PDEPTE的大小保持不變。所以我們可以看到PML4+PDP+PDE+PTE+page offset=48bit,高16bit保留(其實是用以符號擴展,下面將看到符號擴展的用途)。
   CR3指向了PML4的基底位址。
   需要注意的是在使用x64模式之前,必須使CR4.PAE=1PAE用來擴展PDEPTE64bit)。如果你在未使CR4.PAE=1之前,企圖開啟x64模式的話,會引發#GP異常。
   我們從上圖還可以看到頁大小的選擇完全取決於PDE.PS(但我並沒有在文檔中看到ps位,是直接在文檔中置10),無視CR4.PSE的存在。
   2.1x64分頁管理(4kb
   上圖再說




















sign extended--符號擴展位元
   PML4 entry--在線性位址3947bit用於索引PML4 entry,指向PDP
   PDP entry--在線性位址的3038bit用來索引PDP entry,指向PDE
   PDE entry--在線性位址的2129bit用來索引PDEentry,指向PTE
   PTE entry--在線性位址的1220bit用來索引PTE entry,指向page offset
   page offset--在線性位址的011bit提供在頁中的offset
   2.2x64分頁管理(2mb




















我們可以看到與4kb的頁相比少了PTE,page offset從原來的12bit增加到了21bit下面我們來看看各分頁表項目的內部結構.
























(4kB)上圖看到,用上述方法可以得到512×512×512×512=2^36個頁面,2^36*4kb=2^48個線性位址.



















上圖看到,用上述方法可以得到512×512×512=2^27個頁面,2^27*2mb=2^48個線性位址 0bitP位,即存在位,表示由項所指的頁面或頁表當前是否載入到了物理記憶體中,如不存在,可以通過引發#PF異常從磁片中載入到物理記憶體中
   1bitR/W,即讀寫位,為頁表指定讀寫特權,當置位時可讀可寫;清零是唯讀
   2bit:使用者/系統位元,即為頁表指定系統還是普通使用者特權
   3bit:頁面通寫,指定快取記憶體寫策略是回寫還是通寫
   4bit:快取記憶體禁止位,當置位元時禁止相關的頁面和頁表進行快取記憶體,反之,可以
   5bit:訪問位,當置位時表示已經被訪問,反之,沒有
   6bit(非PTEPDE2MB)):可用位
   6bitPTEPDE2MB)):D位,dirty,即髒位,置位元時表示頁面被寫過
   78bit(非PTEPDE2MB)):0
   avail:系統程式師可用
   G:全域標誌
   PAT:頁表屬性
   各種base address是用來得到下一級表的基底位址的,最後的物理位址=page base address*2^12(2^21)+2^12(2^21),page base address的大小取決於你的cpu的最大物理位址了。
   3.最後,我來看一看sign extended會形成什麼效果。
   由於sign extended的作用會將線性位址分成相同大小的兩段,從000007FFF`FFFFFFFF,以及從 FFFF8000`00000000FFFFFFFF`FFFFFFFF總計256TB的地址範圍,這非常符合作業系統的習慣。
   這種“古怪”的規則為日後擴展到真正的64位定址保留了一個重要的特性:很多的作業系統(包括但不限於Windows NT系列)將位址空間的高半部分(被稱作內核空間)留給自己,將低半部分(使用者空間)留給應用程式碼、用戶態棧、堆和其他數據區。這種設計保證了每一個符合AMD64的實現都擁有兩個記憶體片段:低半段從00000000`00000000開始,隨著更多的虛擬位址位元變得可用而“向上生長”;高半部分被“懸掛”在位址空間的頂部而“向下生長”。同樣,將未被使用的位址位元內容固定下來防止被作業系統用作標誌位元、特權級標號等其他用途,是為了避免當架構擴展至52,56, 60 64位的時候出現問題。















參考資料:
   1.http://zh.wikipedia.org/zh-cn/X86-64
   2.intel手冊
   3AMD手冊
--> 閱讀更多...

2011年10月6日 星期四

如何使用Bochs來Debug GRUB2 & Kernel

参考以下文章:
http://www.linuxsir.org/bbs/thread356982.html
http://man.he.net/man8/grub-setup
http://blog.csdn.net/zhouyelihua/article/details/6683437
http://www.gnu.org/software/grub/manual/grub.html#Installing-GRUB-using-grub_002dinstall
http://read.pudn.com/downloads153/ebook/675818/linux-0.11-040304/bochsrc-hd-new.bxrc__.htm

由於bochs內建調試功能, 且支援gdb, 用它調試內核會很方便.
假設你已經build好grub,這裡我舉的例子是使用grub1.99,若還沒有Build,現在正是時候:
1 ./configure CFLAGS=-g
2 make
3 sudo make install 
在ubuntu下預設是安裝到usr/local/lib/grub/i386-pc,別擔心會影響到您的開機MBR,因為make install只是將一些GRUB的各個executable Binary及mod模組存放到指定的prefix,除非你執行grub-install和update-grub 等指令才有可能去動到MBR。
以下指令請使用Root身份執行
1.1構建磁碟影像檔dd if=/dev/zero of=hd0.img count=$((63*16*100))
用這個命令可以構建一個50MB左右的磁片鏡像, 輸出結果如下:
100800+0 records in
100800+0 records out
51609600 bytes (52 MB) copied, 0.734578 s, 70.3 MB/s
注意count必須為63*16的倍數, 否則bochs識別硬碟會有問題.
1.2 掛載磁碟影像losetup /dev/loop0 hd0.img
這個命令可以將檔綁定到一個loop設備. 如果/dev/loop0不存在, 可以嘗試 modprobe loop.
然後進行設備初始化:cfdisk -s 63 -h 16 /dev/loop0
畫面如下橘色文字:
                         cfdisk (util-linux-ng 2.17.2)

                               硬碟: /dev/loop0
                      大小: 51609600 位元組(B), 51 MB
           磁頭: 16   每一磁軌的磁區數: 63   磁柱數: 100

    分割區名稱  屬性旗標   分割區型態 檔案系統型態     標記名稱       大小 (MB)
 ------------------------------------------------------------------------------
                            主要/邏輯 剩餘空間                            51.61

     [  說明  ]    [  新增  ]    [  列印  ]    [  離開  ]    [  單位  ]
     [  寫入  ]

                    由剩餘空間切一個新的分割區

只創建一個主要磁碟分割就可以,並設定 [使可開機] , 寫入後, 用命令fdisk檢查結果。
指令:fdisk -lu /dev/loop0 結果畫面如下:當然Linux版本不同,畫面難免大同小異
磁碟 /dev/loop0: 51 MB,51609600 位元組
16 磁頭,63 磁區/磁軌,100 磁柱,總計 100800 磁區
單位 = 磁區 之於 1 * 512 = 512 位元組
磁區大小 (邏輯/實體):512 位元組 / 512 位元組
I/O 大小 (最小/最佳化):512 位元組 / 512 位元組
磁碟識別碼:0x00000000

所用裝置 開機      開始         結束      區塊   識別號  系統
/dev/loop0p1   *          63      100799       50368+  83  Linux
接著將分區1掛載到/dev/loop1:losetup /dev/loop1 hd0.img -o $((63*512))
格式化/dev/loop1ext3格式.:mkfs.ext3 /dev/loop1
mnt下創建img目錄, 做以後維護用:mkdir -p /mnt/img
loop1掛載到/mnt/img:mount /dev/loop1 /mnt/img/
安裝引導程式. 因為我狂熱傾向於模組化架構, 所以選擇GRUB2.
mkdir /mnt/img/boot
cp -r /usr/lib/grub/i386-pc/ /mnt/img/boot/grub
這個/usr/lib/grub/i386-pc是你的GRUB2安裝路徑,像我的1.98及1.99版在ubuntu下是安裝在以下路徑:/usr/local/lib/grub/i386-pc
接著生成一個core.img, biosdisk模組負責讀取磁片, part_msdos模組負責處理MBR,ext2模組負責讀取ext3分區:
cd /mnt/img/boot/grub/
grub-mkimage -o core.img biosdisk part_msdos ext2   #1.98版使用這個指令
grub-mkimage -O i386-pc -o core.img biosdisk part_msdos ext2  #1.99版使用這個指令
注意,這個core.img還不是個可開機用的影像檔。它並未包含boot.img及diskboot.img。
接著安裝grub2(hd0), 根目錄在(hd0,1)
echo "(hd0)   /dev/loop0" > ./device.map
grub-setup -m ./device.map -d /mnt/img/boot/grub/ -r '(hd0,1)' '(hd0)'
其實上面這2行也可以用下面一行取代
grub-setup -d /mnt/img/boot/grub/ --force /dev/loop0
檢查一下/dev/loop0安裝成果:
hexdump -C /dev/loop0 | less
00000180  7d e8 2e 00 cd 18 eb fe  47 52 55 42 20 00 47 65  |}.......GRUB .Ge|
00000190  6f 6d 00 48 61 72 64 20  44 69 73 6b 00 52 65 61  |om.Hard Disk.Rea|
清理一下
離開終端,重新開一個終端,否則umount會失敗,若你不是以root執行的話
umount /mnt/img
losetup -d /dev/loop1
losetup -d /dev/loop0
2.1建立.bochsrc檔如下:說明一下,在linux下預設是讀取.bochsrc這個檔名,所以你若創建一個叫bochs.bxrc或其他檔名,則等一下開啟bochs時,指令如下:
bochs -q -f bochs.bxrc,否則只須下bochs -q
###############################################################
# bochsrc.bxrc file for kenos disk image.
###############################################################
megs: 32
romimage: file=/usr/share/bochs/BIOS-bochs-legacy, address=0xf0000
vgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latest
# hard disk
ata0-master: type=disk, mode=flat, path="/home/benson/working_benson/DBG_grub/hd0.img", cylinders=100, heads=16, spt=63
#ata0-slave: type=cdrom, path="/home/marc/setups/slax-6.0.9.iso", status=inserted
# choose the boot disk.
boot: c
# where do we send log messages?
log: bochsout.txt
# disable the mouse, since DLX is text only
mouse: enabled=0
keyboard_mapping: enabled=1, map=/usr/share/bochs/keymaps/x11-pc-us.map
#keyboard_mapping: enabled=1, map=$BXSHARE/keymaps/x11-pc-fr.map
#keyboard_mapping: enabled=1, map=$BXSHARE/keymaps/x11-pc-de.map
#keyboard_mapping: enabled=1, map=$BXSHARE/keymaps/x11-pc-es.map
#cpu: count=1, ips=10000000
# Make the PIT emulated by bochs tick at the correct interval. Note that
# enabling this feature sacrifices reproducibility, which may be a problem
# at times when tracking a specific bug.
#clock: sync=realtime, time0=utc
# For debugging with gdb.
# Keep this line when using bochs-gdb.
# Comment it out when using bochs-dbg
# See http://bochs.sourceforge.net/doc/docbook/user/bochsrc.html#BOCHSOPT-GDBSTUB
#gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0
使用bochs開啟hd0.img
bochs -f bochsrc.bxrc 雖然會顯示unknown filesystem
















解決辦法:
grub rescue>set prefix=(hd0,1)/boot/grub
grub rescue>root=(hd0,1)
grub rescue>insmod normal
grub rescue>normal
這樣就可以回到grub> 或選單狀態了。
此時也可以輸入help及其他command line指令了。
接著我們來試試如何使用gdb來debug GRUB2:
先在bochsrc.bxrc最後面加入幾行來使gdb connect to Bochs:

# Attach ne2000 to PCI bus, so that GRUB Legacy detects it automatically

i440fxsupport: enabled=1, slot1=ne2k
# Enable the ne2000 NIC and the builtin TFTP server
ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=vnet, ethdev="/"
# Enable the GDB remote stub
gdbstub: enabled=1

現在先開啟另一個終端輸入gdb,並重新啟動bochs -qf bochsrc.bxrc指令
gdb會出現如下畫面:藍色字為輸入指令
GNU gdb (Ubuntu/Linaro 7.2-1ubuntu11) 7.2
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
0x0000fff0 in ?? ()
(gdb)file kernel.exec
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from /home/benson/kernel.exec...done.

(gdb) b
No default breakpoint address now.
(gdb) b main
Breakpoint 1 at 0xc4fd: file kern/main.c, line 175.
(gdb) c
Continuing.
Program received signal SIGTRAP, Trace/breakpoint trap.
0x0000e05c in ?? ()
(gdb) c
Continuing.
Breakpoint 1, grub_main () at kern/main.c:175
warning: Source file is more recent than executable.
175
(gdb) list
170 void
171 grub_main (void)
172 {
173  /* First of all, initialize the machine.  */
174  grub_machine_init ();
175
176  /* Hello.  */
177  grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT);
178  grub_printf ("Welcome to GRUB!Benson Debug Here!\n\n");
179  grub_setcolorstate (GRUB_TERM_COLOR_STANDARD);
(gdb) 
執行過程如下圖所示:














這張圖是修改過source code,所以下一張圖會多出Benson Debug Here!字樣。















若是你的gdb無法connect到bochs,可能你須要重新build一次Bochs,動作如下:
註:--enable-gdb-stub和--enable-debugger是互斥的
1 ./configure --enable-gdb-stub
2 make
3 sudo make install
update.....

--> 閱讀更多...

2011年6月22日 星期三

mini2440-GPIO驅動相關筆記

GPIO驅動相關筆記

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/llxmedici/archive/2011/03/27/6282372.aspx

打算跟著友善之臂的《mini2440 linux移植開發指南》來做個LED驅動,雖然LED的原理簡單得不能再簡單了,但是要把kernel中針對於s3c24**的GPIO的一些資料結構,還有函數搞清楚也不是那麼輕鬆的事,所以本文主要簡單地說明下LED驅動中的相關資料結構以及函數/巨集的定義,並對驅動加以驗證

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

注意:在/arch/arm/mach-s3c2410/include/mach/gpio-fns.h源代碼中有如下說明:
16/* These functions are in the to-be-removed category and it is strongly
17 * encouraged not to use these in new code. They will be marked deprecated
18 * very soon.
19 *
20 * Most of the functionality can be either replaced by the gpiocfg calls
21 * for the s3c platform or by the generic GPIOlib API.
22 *
23 * As of 2.6.35-rc, these will be removed, with the few drivers using them
24 * either replaced or given a wrapper until the calls can be removed.
25*/

該頭檔包括:
static inline void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int cfg)

該函數直接使用
linux/arch/arm/plat-s3c/gpio-config.c中的
int s3c_gpio_cfgpin(unsigned int pin, unsigned int config)
即可

***************************************************************************
首先看一下設備初始化程式:
85 /*
86 * 設備初始化
87 */
88 static int __init dev_init(void)
89 {
90 int ret;
91 int i;
92 for (i = 0; i < 4; i++) {
93 //設置 LED 對應的埠寄存器為輸出(OUTPUT)
94 if (s3c_gpio_cfgpin(led_table[i], led_cfg_table[i])<0)
95 printk(KERN_INFO "config pin %d failed", i);
95 //設置 LED 對應的埠寄存器為低電平輸出,在模組載入> 結束後,四個 LED 應該是全部都是發光
96 狀態
97 s3c2410_gpio_setpin(led_table[i], 0);
98 }
99 ret = misc_register(&misc); //註冊設備
100 printk (DEVICE_NAME"\tinitialized\n"); //列印初始化資訊
101 return ret;
102 }
可以看到,這裏涉及到兩個函數,分別是s3c2410_gpio_cfgpin , s3c2410_gpio_setpin,這兩個函數分別對四個LED進行配置,從函數名來看,cfgpin對引腳寄存器狀態進行配置,而setpin應該是對寄存器資料值進行配置,我們在分析函數之前先弄清楚傳入的參數到底是什麼。

led_table[i]

28 //LED 對應的 GPIO 埠列表

29 static unsigned long led_table [] = {

30 S3C2410_GPB(5),

31 S3C2410_GPB(6),

32 S3C2410_GPB(7),

33 S3C2410_GPB(8),

34 };



這裏S3C2410_GPB巨集定義在mach/gpio-nrs.h中

/* GPIO bank sizes */

#define S3C2410_GPIO_A_NR (32)

#define S3C2410_GPIO_B_NR (32)

#define S3C2410_GPIO_C_NR (32)

#define S3C2410_GPIO_D_NR (32)

#define S3C2410_GPIO_E_NR (32)

#define S3C2410_GPIO_F_NR (32)

#define S3C2410_GPIO_G_NR (32)

#define S3C2410_GPIO_H_NR (32)

#define S3C2410_GPIO_J_NR (32) /* technically 16. */

#define S3C2410_GPIO_K_NR (32) /* technically 16. */

#define S3C2410_GPIO_L_NR (32) /* technically 15. */

#define S3C2410_GPIO_M_NR (32) /* technically 2. */



#if CONFIG_S3C_GPIO_SPACE != 0

#error CONFIG_S3C_GPIO_SPACE cannot be zero at the moment

#endif



#define S3C2410_GPIO_NEXT(__gpio) \

((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 0)

//這裏的CONFIG_S3C_GPIO_SPAC是內核配置選項,在.config中可以找到,我的配置為:

CONFIG_S3C_GPIO_SPACE = 0



enum s3c_gpio_number {

S3C2410_GPIO_A_START = 0,

S3C2410_GPIO_B_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_A),

S3C2410_GPIO_C_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_B),

S3C2410_GPIO_D_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_C),

S3C2410_GPIO_E_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_D),

S3C2410_GPIO_F_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_E),

S3C2410_GPIO_G_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_F),

S3C2410_GPIO_H_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_G),

S3C2410_GPIO_J_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_H),

S3C2410_GPIO_K_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_J),

S3C2410_GPIO_L_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_K),

S3C2410_GPIO_M_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_L),

};



#define S3C2410_GPB(_nr) (S3C2410_GPIO_B_START + (_nr))

因此,以S3C2410_GPB(5)為例,其宏展開為:

S3C2410_GPIO_NEXT(S3C2410_GPIO_A) +5 =>

(S3C2410_GPIO_A_START + S3C2410_GPIO_A_NR + CONFIG_S3C_GPIO_SPACE + 0) + 5 =>

很顯然, S3C2410_GPB(5)就是從GPA的首地址+GPA個數+GPB的offset就是當前GPB的IO偏移量,即

0+32+5=37, 同理

S3C2410_GPB(0) 相當於 32

30 S3C2410_GPB(5) 相當於 37

31 S3C2410_GPB(6) 相當於 38

32 S3C2410_GPB(7) 相當於 39

33 S3C2410_GPB(8) 相當於 40

***************************************************************************
led_cfg_table[i]
36 //LED 對應埠將要輸出的狀態列表
37 static unsigned int led_cfg_table [] = {
38 S3C2410_GPIO_OUTPUT,
39 S3C2410_GPIO_OUTPUT,
40 S3C2410_GPIO_OUTPUT,
41 S3C2410_GPIO_OUTPUT,
42 };
S3C2410_GPIO_OUTPUT定義在mach/regs-gpio.h
#define S3C2410_GPIO_LEAVE (0xFFFFFFFF) // 最後兩位是設置,11表示RESERVE
#define S3C2410_GPIO_INPUT (0xFFFFFFF0) /* not available on A */ // 最後兩位是設置,00表示INPUT
#define S3C2410_GPIO_OUTPUT (0xFFFFFFF1) // 最後兩位是設置,01表示OUTPUT
#define S3C2410_GPIO_IRQ (0xFFFFFFF2) /* not available for all */
#define S3C2410_GPIO_SFN2 (0xFFFFFFF2) /* bank A => addr/cs/nand */
#define S3C2410_GPIO_SFN3 (0xFFFFFFF3) /* not available on A */

***************************************************************************
根據前面的分析,s3c2410傳入了當前GPIO的偏移位址,以及OUTPUT狀態
現在我們深入前面的兩個函數:
定義在linux/arch/arm/plat-s3c/gpio-config.c
int s3c_gpio_cfgpin(unsigned int pin, unsigned int config)
{
struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin); //得到對應GPIO結構體首指標,裏面包含了該GPIO的各種參數
unsigned long flags;
int offset;
int ret;

if (!chip) return -EINVAL; // 沒找到的話,返回invalid
offset = pin - chip->chip.base; // 否則offset等於該GPIO引腳相對於GPX(0)的偏移量,每個偏移1
s3c_gpio_lock(chip, flags); // 自旋鎖鎖住該GPIO,通過chip指標指向lock,看下面的define和圖
ret = s3c_gpio_do_setcfg(chip, offset, config); //設置該GPIO狀態寄存器的數值為config
s3c_gpio_unlock(chip, flags); // 解鎖

// 自旋鎖操作
/* locking wrappers to deal with multiple access to the same gpio bank */
//#define s3c_gpio_lock(_oc, _fl) spin_lock_irqsave(&(_oc)->lock, _fl)
//#define s3c_gpio_unlock(_oc, _fl) spin_unlock_irqrestore(&(_oc)->lock, _fl)
//s3c_gpio_do_setcfg操作
static inline int s3c_gpio_do_setcfg(struct s3c_gpio_chip *chip,
unsigned int off, unsigned int config)
{
return (chip->config->set_config)(chip, off, config);
}
//這裏的set_config是一個函數指標,由後面的分析知道,如果針對GPA,該函數指標指向s3c_gpio_setcfg_s3c24xx_a , 如果針對GPX應該是指向s3c_gpio_setcfg_s3c24xx——但發現,如果是其他GPX,根本沒有定義set_config!!! (這個問題已經解決,見後文s3c24xx_gpiolib_init函數,事實上,其餘的config的確指向s3c_gpio_do_setcfg函數)

struct s3c_gpio_cfg s3c24xx_gpiocfg_default = {
.set_config = s3c_gpio_setcfg_s3c24xx,
.get_config = s3c_gpio_getcfg_s3c24xx,
};

int s3c_gpio_setcfg_s3c24xx_a(struct s3c_gpio_chip *chip, unsigned int off, unsigned int cfg)
{
void __iomem *reg = chip->base; // GPXCON的物理基底位址
unsigned int shift = off; // 每個GPA對應一位
u32 con;
if (s3c_gpio_is_cfg_special(cfg)) { //OUTPUT狀態是否為(0xfffffffX),是,返回1
cfg &= 0xf; // cfg = 0xX
/* Map output to 0, and SFN2 to 1 */ 本實驗不會運行到這
cfg -= 1;
if (cfg > 1) return -EINVAL;
cfg <<= shift;
}
con = __raw_readl(reg); // 先讀出該GPXCON的值,32位
con &= ~(0x1 << shift); //
con //
__raw_writel(con, reg); // 將新值寫入GPXCON

PS:
#define __raw_writeb(v,a) (__chk_io_ptr(a), *(volatile unsigned char __force *)(a) = (v))
#define __raw_writew(v,a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a) = (v))
#define __raw_writel(v,a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a) = (v))
#define __raw_readb(a) (__chk_io_ptr(a), *(volatile unsigned char __force *)(a))
#define __raw_readw(a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a))
#define __raw_readl(a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a))
return 0;
}

如果針對GPX情況
int s3c_gpio_setcfg_s3c24xx(struct s3c_gpio_chip *chip,
unsigned int off, unsigned int cfg)
{
void __iomem *reg = chip->base;
unsigned int shift = off * 2; // 每個GPX對應2位
u32 con;
if (s3c_gpio_is_cfg_special(cfg)) {
cfg &= 0xf;
if (cfg > 3)return -EINVAL;
cfg <<= shift; // 將cfg的0,1兩位左移offset
}
con = __raw_readl(reg); // 讀對應的GPXCON值
con &= ~(0x3 << shift); // 將GPXCON(pin)的兩bits請0
con = cfg; // 設置config值

__raw_writel(con, reg); // 寫入新的GPXCON

return 0;
}

return ret;
} // end s3c_gpio_cfgpin
這裏涉及到了一個重要的資料結構,s3c_gpio_chip,此資料結構比較複雜,我貼出這個資料結構的結構圖一:

這個重要的資料結構中可以記錄每個GPIO所需要的所有資料,後面會遇到的s3c24xx_gpios[]結構體就是該結構體的集合,描述了晶片中所有的GPIO埠,之後我們需要時時回頭看看這個結構。

我們先來看s3c_gpiolib_getchip ,它實現了返回對應pin值的GPIO結構體首指標的功能

#include

static inline struct s3c_gpio_chip *s3c_gpiolib_getchip(unsigned int pin)

{

struct s3c_gpio_chip *chip;



if (pin > S3C_GPIO_END) //如果超過GPJ(32)就return NULL

return NULL;

chip = &s3c24xx_gpios[pin/32]; //根據偏移,計算出對應pin的GPIO結構體指標

return ((pin - chip->chip.base) < chip->chip.ngpio) ? chip : NULL;// 這裏驗證,如果pin偏移超過了GPIO的個數,說明出錯了,否則就返回該GPIO的結構體指標

}

回想以下之前s3c2410_gpio_cfgpin中,我們傳入的參數是led_table[i]和 led_cfg_table[i],

/* GPIO sizes for various SoCs:

*

* 2442

* 2410 2412 2440 2443 2416

* ---- ---- ---- ---- ----

* A 23 22 25 16 25

* B 11 11 11 11 9

* C 16 15 16 16 16

* D 16 16 16 16 16

* E 16 16 16 16 16

* F 8   8   8   8   8

* G16 16 16 16 8

* H 11 11 9 15 15

* J -- -- 13 16 --

* K -- -- -- -- 16

* L -- -- -- 15 7

* M -- -- -- 2 2

*/

struct s3c_gpio_chip s3c24xx_gpios[] = {

[0] = {

.base = S3C2410_GPACON, // datasheet上地址為0x56000000

//#define S3C2410_GPACON S3C2410_GPIOREG(0x00)

#define S3C2410_GPIOREG(x) ((x) + S3C24XX_VA_GPIO)

#define S3C24XX_VA_GPIO ((S3C24XX_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART)

S3C24XX_PA_GPIO相當於(0x15600000)

S3C24XX_PA_UART相當於(0x15000000)

#define S3C_VA_UART S3C_ADDR(0x01000000) /* UART */

#define S3C_ADDR_BASE 0xF6000000



#ifndef __ASSEMBLY__

#define S3C_ADDR(x) ((void __iomem __force *)S3C_ADDR_BASE + (x))

#else

#define S3C_ADDR(x) (S3C_ADDR_BASE + (x))

#endif

0x15600000-15000000+F7000000 這裏的S3C2410_GPACON應該怎麼算?

.pm = __gpio_pm(&s3c_gpio_pm_1bit),

.config = &s3c24xx_gpiocfg_banka, // 設置GPIO的函數指標

static struct s3c_gpio_cfg s3c24xx_gpiocfg_banka = {

.set_config = s3c_gpio_setcfg_s3c24xx_a,

.get_config = s3c_gpio_getcfg_s3c24xx_a,

};

.chip = {

.base = S3C2410_GPA(0), //基底位址,也是偏移量

.owner = THIS_MODULE,

.label = "GPIOA",

.ngpio = 24,

.direction_input = s3c24xx_gpiolib_banka_input,

.direction_output = s3c24xx_gpiolib_banka_output,

},

},

[1] = {

.base = S3C2410_GPBCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPB(0),

.owner = THIS_MODULE,

.label = "GPIOB",

.ngpio = 16,

},

},

[2] = {

.base = S3C2410_GPCCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPC(0),

.owner = THIS_MODULE,

.label = "GPIOC",

.ngpio = 16,

},

},

[3] = {

.base = S3C2410_GPDCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPD(0),

.owner = THIS_MODULE,

.label = "GPIOD",

.ngpio = 16,

},

},

[4] = {

.base = S3C2410_GPECON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPE(0),

.label = "GPIOE",

.owner = THIS_MODULE,

.ngpio = 16,

},

},

[5] = {

.base = S3C2410_GPFCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPF(0),

.owner = THIS_MODULE,

.label = "GPIOF",

.ngpio = 8,

.to_irq = s3c24xx_gpiolib_bankf_toirq,

},

},

[6] = {

.base = S3C2410_GPGCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.irq_base = IRQ_EINT8,

.chip = {

.base = S3C2410_GPG(0),

.owner = THIS_MODULE,

.label = "GPIOG",

.ngpio = 16,

.to_irq = samsung_gpiolib_to_irq,

},

}, {

.base = S3C2410_GPHCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPH(0),

.owner = THIS_MODULE,

.label = "GPIOH",

.ngpio = 11,

},

},

/* GPIOS for the S3C2443 and later devices. */2440用不到

{

.base = S3C2440_GPJCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPJ(0),

.owner = THIS_MODULE,

.label = "GPIOJ",

.ngpio = 16,

},

}, {

.base = S3C2443_GPKCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPK(0),

.owner = THIS_MODULE,

.label = "GPIOK",

.ngpio = 16,

},

}, {

.base = S3C2443_GPLCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPL(0),

.owner = THIS_MODULE,

.label = "GPIOL",

.ngpio = 15,

},

}, {

.base = S3C2443_GPMCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPM(0),

.owner = THIS_MODULE,

.label = "GPIOM",

.ngpio = 2,

},

},

};

***************************************************************************
下面分析第二個函數,先看一下相關結構體

圖二:gpio_desc和gpio_chip結構圖

void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)

{

/* do this via gpiolib until all users removed */



gpio_request(pin, "temporary");

gpio_set_value(pin, to);

gpio_free(pin);

}

又出現了三個函數,我們一一說明:

1169/* These "optional" allocation calls help prevent drivers from stomping

1170 * on each other, and help provide better diagnostics in debugfs.

1171 * They're called even less than the "set direction" calls.

1172 */

PS:static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];

其中ARCH_NR_GPIOS在arch/arm/mach-s3c2410/include/mach/gpio.h中定義
#define ARCH_NR_GPIOS (32 * 9 + CONFIG_S3C24XX_GPIO_EXTRA)
因此,每個引腳都分配了一個gpio_desc資料結構
1173int gpio_request(unsigned gpio, const char *label) // 這個函數還不是很明白
1174{
1175 struct gpio_desc *desc;
1176 struct gpio_chip *chip;
1177 int status = -EINVAL;
1178 unsigned long flags;
1179
1180 spin_lock_irqsave(&gpio_lock, flags); // gpio_lock是自旋鎖,上鎖,保存FLAG在flags變數中
1181
1182 if (!gpio_is_valid(gpio)) // 不符合要求,跳轉到done
1183 goto done;
1184 desc = &gpio_desc[gpio]; // desc = &gpio_desc[pin]
1185 chip = desc->chip;
1186 if (chip == NULL) // gpio_desc.chip指向NULL,跳轉到done
1187 goto done;
1188
1189 if (!try_module_get(chip->owner)) // 該函數用於增加模組使用計數;若返回為0,表示調用失敗,希望使用的模組沒有被載入或正在被卸載中
1190 goto done;
1191
1192 /* NOTE: gpio_request() can be called in early boot,
1193 * before IRQs are enabled, for non-sleeping (SOC) GPIOs.
1194 */
1195
1196 if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) { // 原子操作,將flags的第FLAG_REQUESTED位置1,並返回其原值
1197 desc_set_label(desc, label ? : "?"); // 如果原來的值是0, 執行desc_set_label, 對desc->chip.label賦值,如果label有定義,直接用定義,比如上面的“temporary”,否則用“?”
static inline void desc_set_label(struct gpio_desc *d, const char *label)
{
#ifdef CONFIG_DEBUG_FS
d->label = label; // 為什麼不是d->chip.label = label; ?
#endif
}

1198 status = 0;
1199 } else { // 如果flags的第FLAG_REQUESTED位原來的值是1
1200 status = -EBUSY;
1201 module_put(chip->owner); // 該函數用於減少模組使用計數
1202 goto done;
1203 }
1204
1205 if (chip->request) { // chip->request在linux初始化時是沒有指向的,可以見後面s3c_gpiolib_add
1206 /* chip->request may sleep */
1207 spin_unlock_irqrestore(&gpio_lock, flags); // 如果chip->request不為0, 解鎖,因為後面調用的chip->request有可能睡眠
1208 status = chip->request(chip, gpio - chip->base);
1209 spin_lock_irqsave(&gpio_lock, flags); // 執行完後,繼續上鎖
1210
1211 if (status < 0) { // status返回負數,說明出錯
1212 desc_set_label(desc, NULL);
1213 module_put(chip->owner);
1214 clear_bit(FLAG_REQUESTED, &desc->flags);
1215 }
1216 }
1217
1218done:
1219 if (status)
1220 pr_debug("gpio_request: gpio-%d (%s) status %d\n", gpio, label ? : "?", status);
1221 // 如果狀態不為0, 列印gpio-pin"****"的狀態
1222 spin_unlock_irqrestore(&gpio_lock, flags); // 解鎖
1223 return status; // 返回狀態
1224}

***************************************************************************
下面先分析gpio_free函數
void gpio_free(unsigned gpio) // 待分析
{

unsigned long flags;
struct gpio_desc *desc;
struct gpio_chip *chip;
might_sleep();

if (!gpio_is_valid(gpio)) {
WARN_ON(extra_checks);
return;
}

gpio_unexport(gpio);
spin_lock_irqsave(&gpio_lock, flags);
desc = &gpio_desc[gpio];
chip = desc->chip;
if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) {
if (chip->free) {
spin_unlock_irqrestore(&gpio_lock, flags);
might_sleep_if(chip->can_sleep);
chip->free(chip, gpio - chip->base);
spin_lock_irqsave(&gpio_lock, flags);
}

desc_set_label(desc, NULL);
module_put(desc->chip->owner);
clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
clear_bit(FLAG_REQUESTED, &desc->flags);
} else
WARN_ON(extra_checks);

spin_unlock_irqrestore(&gpio_lock, flags);
}

EXPORT_SYMBOL_GPL(gpio_free);

***************************************************************************
arch/arm/mach-s3c2410/include/mach/gpio.h
#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)
{
struct gpio_chip *chip;
chip = gpio_to_chip(gpio); // 返回對應於pin的gpio_desc[pin].chip指針
WARN_ON(chip->can_sleep);
chip->set(chip, gpio - chip->base, value); // 這裏調用的是s3c_gpiolib_set函數!!!

}

/* caller holds gpio_lock *OR* gpio is marked as requested */
static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
{
return gpio_desc[gpio].chip;
}

看到這裏,一直有個問題讓我百思不得其解,這裏的chip按理說應該是s3c_gpio_chip中的chip成員,但是之前都沒有代碼交代他們是如何聯繫到一起的,s3c_gpio_chip與gpio_desc結構體如何聯繫在一起,也沒有函數交代,並且這裏的chip->set函數指標也沒有實現的代碼,但是,經實驗確認沒有問題後,我開始查找plat-s3c24xx/gpiolib.c中的函數希望能有些線索,果然,找到了這麼一個函數:

static __init int s3c24xx_gpiolib_init(void)
{
struct s3c_gpio_chip *chip = s3c24xx_gpios;
int gpn;
for (gpn = 0; gpn < ARRAY_SIZE(s3c24xx_gpios); gpn++, chip++) {
if (!chip->config)
chip->config = &s3c24xx_gpiocfg_default; // 原來chip->config默認函數也是在這裏!!!

s3c_gpiolib_add(chip); // 之前的疑惑都在這裏實現!!!
}
return 0;
}

core_initcall(s3c24xx_gpiolib_init);
但是,這個s3c24xx_gpiolib_init函數又是在什麼時候執行的呢?可以看到,在該函數的下面,有一句:core_initcall(s3c24xx_gpiolib_init); 查閱相關資料發現, 在linux初始化的過程中,內核採用了一種initcall的機制,它利用gcc的擴展功能以及ld的連接控制腳本實現了在內核初始化的過程中通過簡單的迴圈就實現了相關驅動的初始化
也就是說,在linux初始化期間,就已經執行了s3c24xx_gpiolib_init,現在我們可以分析下s3c_gpiolib_add(chip); 這個函數了,

__init void s3c_gpiolib_add(struct s3c_gpio_chip *chip)
{
struct gpio_chip *gc = &chip->chip;
int ret;
BUG_ON(!chip->base);
BUG_ON(!gc->label);
BUG_ON(!gc->ngpio);
spin_lock_init(&chip->lock); // 初始化s3c_gpio_chip的自旋鎖

if (!gc->direction_input)
gc->direction_input = s3c_gpiolib_input; // direction_input 函數指標

if (!gc->direction_output)

gc->direction_output = s3c_gpiolib_output; // direction_output 函數指標

if (!gc->set)

gc->set = s3c_gpiolib_set; // set函數指標

if (!gc->get)

gc->get = s3c_gpiolib_get; // get函數指標
#ifdef CONFIG_PM
if (chip->pm != NULL) {

if (!chip->pm->save

!chip->pm->resume)

printk(KERN_ERR "gpio: %s has missing PM functions\n", gc->label);

} else

printk(KERN_ERR "gpio: %s has no PM function\n", gc->label);

#endif

/* gpiochip_add() prints own failure message on error. */

ret = gpiochip_add(gc);

if (ret >= 0)

s3c_gpiolib_track(chip);

}

gpiochip_add函數分析:

/**

* gpiochip_add() - register a gpio_chip

* @chip: the chip to register, with chip->base initialized

* Context: potentially before irqs or kmalloc will work

*

* Returns a negative errno if the chip can't be registered, such as

* because the chip->base is invalid or already associated with a

* different chip. Otherwise it returns zero as a success code.

*

* When gpiochip_add() is called very early during boot, so that GPIOs

* can be freely used, the chip->dev device must be registered before

* the gpio framework's arch_initcall(). Otherwise sysfs initialization

* for GPIOs will fail rudely.

*

* If chip->base is negative, this requests dynamic assignment of

* a range of valid GPIOs.

*/

int gpiochip_add(struct gpio_chip *chip) // 在gpio_desc[]中分配空間,並鏈結chip結構

{

unsigned long flags;

int status = 0;

unsigned id;

int base = chip->base;

if ((!gpio_is_valid(base)

!gpio_is_valid(base + chip->ngpio - 1))

&& base >= 0) {

status = -EINVAL;

goto fail;

}

spin_lock_irqsave(&gpio_lock, flags); // 上鎖

if (base < 0) {

base = gpiochip_find_base(chip->ngpio); // 這個函數在gpiolib.c中,在gpio_desc[]中分配chip->ngpio個空間(從最後往前分配),返回第一個index

if (base < 0) { // 分配不到

status = base;

goto unlock; // 解鎖退出

}

chip->base = base; // gpio_chip *chip->base = i (i是gpio_desc[i]中的index)

}
/* these GPIO numbers must not be managed by another gpio_chip */

for (id = base; id < base + chip->ngpio; id++) {

if (gpio_desc[id].chip != NULL) {

status = -EBUSY;

break;

}

}

if (status == 0) { // 分配到空間,正常情況下

for (id = base; id < base + chip->ngpio; id++) {

gpio_desc[id].chip = chip; // 這裏將gpio_desc與s3c_gpio_chip聯繫起來,他們的chip成員指向的是同一個資料結構

/* REVISIT: most hardware initializes GPIOs as

* inputs (often with pullups enabled) so power

* usage is minimized. Linux code should set the

* gpio direction first thing; but until it does,

* we may expose the wrong direction in sysfs.

*/

gpio_desc[id].flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0; // 設置flags

}

} // end if

of_gpiochip_add(chip); // 沒操作

unlock:

spin_unlock_irqrestore(&gpio_lock, flags); // 解鎖

if (status)

goto fail;

status = gpiochip_export(chip); //×××××××××××××××待分析

if (status)

goto fail;

return 0;

fail:

/* failures here can mean systems won't boot... */

pr_err("gpiochip_add: gpios %d..%d (%s) failed to register\n",

chip->base, chip->base + chip->ngpio - 1,

chip->label ? : "generic");

return status; // 返回狀態

}

下面是s3c_gpiolib_track函數

#ifdef CONFIG_S3C_GPIO_TRACK

struct s3c_gpio_chip *s3c_gpios[S3C_GPIO_END];

static __init void s3c_gpiolib_track(struct s3c_gpio_chip *chip) // 沒完全理解,待分析

{

unsigned int gpn;

int i;

gpn = chip->chip.base;

for (i = 0; i < chip->chip.ngpio; i++, gpn++) {

BUG_ON(gpn >= ARRAY_SIZE(s3c_gpios));

s3c_gpios[gpn] = chip;

}

}

#endif /* CONFIG_S3C_GPIO_TRACK */

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

好,現在我們開始分析設備註冊與卸載函數,在初始化程式中,有如下語句:

ret = misc_register(&misc); //註冊設備

其中的misc_register就是雜項設備的註冊函數,首先關注下這裏的參數misc資料結構

75 /*

76 * 把 LED 驅動註冊為 MISC 設備

77 */

78 static struct miscdevice misc = {

79 .minor = MISC_DYNAMIC_MINOR, //動態設備號

80 .name = DEVICE_NAME,

81 .fops = &dev_fops,

82 };

miscdevice的資料結構如圖三所示:

/**

* misc_register - register a miscellaneous device

* @misc: device structure

*

* Register a miscellaneous device with the kernel. If the minor

* number is set to %MISC_DYNAMIC_MINOR a minor number is assigned

* and placed in the minor field of the structure. For other cases

* the minor number requested is used.

*

* The structure passed is linked into the kernel and may not be

* destroyed until it has been unregistered.

*

* A zero is returned on success and a negative errno code for

* failure.

*/

int misc_register(struct miscdevice * misc)
{
struct miscdevice *c;
dev_t dev;
int err = 0;

INIT_LIST_HEAD(&misc->list); // 初始化鏈表頭,將misc->list的next和pre都指向自己
mutex_lock(&misc_mtx); // 獲取互斥鎖, or睡眠
list_for_each_entry(c, &misc_list, list) { // 遍曆整個misc_list鏈表,所有的雜項驅動設備都有一個miscdevice資料結構,這些雜項驅動設備通過一個全局的misc_list鏈表連在一起, 相當一個記錄

if (c->minor == misc->minor) { // 如果misc_list中已經有了這個設備(minor相同),則解鎖返回,這裏c是遍歷時的tmp miscdevice,指向當前遍曆節點

mutex_unlock(&misc_mtx);

return -EBUSY;

}

}

if (misc->minor == MISC_DYNAMIC_MINOR) { // 如果misc_list中沒有該設備,判斷minor是否準備動態分配,實驗中如此設置

int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); // misc_minors是雜項設備點陣圖,總共有64個位DYNAMIC_MINORS=64,表示可以註冊64個雜項設備,這句代碼找到點陣圖中的空閒位置(表示還能加新設備)

if (i >= DYNAMIC_MINORS) { // 如果超過總設備數,則解鎖返回

mutex_unlock(&misc_mtx);

return -EBUSY;

}

misc->minor = DYNAMIC_MINORS - i - 1; // 計算子設備號,賦值到misc->minor

set_bit(i, misc_minors); // 對應的點陣圖也置位

}

dev = MKDEV(MISC_MAJOR, misc->minor); // 生成設備號
// 在sysfs中創建並註冊一個設備,可以在/dev下面看到misc->name
misc->this_device = device_create(misc_class, misc->parent, dev, misc, "%s", misc->name);
1480/**
1481 * device_create - creates a device and registers it with sysfs
1482 * @class: pointer to the struct class that this device should be registered to
1483 * @parent: pointer to the parent struct device of this new device, if any
1484 * @devt: the dev_t for the char device to be added
1485 * @drvdata: the data to be added to the device for callbacks
1486 * @fmt: string for the device's name
1487 *
1488 * This function can be used by char device classes. A struct device
1489 * will be created in sysfs, registered to the specified class.
1490 *
1491 * A "dev" file will be created, showing the dev_t for the device, if
1492 * the dev_t is not 0,0.
1493 * If a pointer to a parent struct device is passed in, the newly created
1494 * struct device will be a child of that device in sysfs.
1495 * The pointer to the struct device will be returned from the call.
1496 * Any further sysfs files that might be required can be created using this
1497 * pointer.
1498 *
1499 * Returns &struct device pointer on success, or ERR_PTR() on error.
1500 *
1501 * Note: the struct class passed to this function must have previously
1502 * been created with a call to class_create().
1503 */
1504struct device *device_create(struct class *class, struct device *parent, // 這個函數以後會詳細看
1505 dev_t devt, void *drvdata, const char *fmt, ...)
1506{
1507 va_list vargs;
1508 struct device *dev;
1509
1510 va_start(vargs, fmt);
1511 dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
1512 va_end(vargs);
1513 return dev;
1514}

// this_device是在創建設備節點時指向函數device_create()返回的設備結構
if (IS_ERR(misc->this_device)) { // 如果創建節點出錯,並且
int i = DYNAMIC_MINORS - misc->minor - 1; // 計算子設備號之前misc->minor的值
if (i < DYNAMIC_MINORS && i >= 0) // 計算點陣圖位i,如果在0-64之間,說明在set_bit中置位了,則清楚點陣圖,處理錯誤,準備返回

clear_bit(i, misc_minors);

err = PTR_ERR(misc->this_device);

goto out;

}

/*
* Add it to the front, so that later devices can "override"
* earlier defaults
*/
list_add(&misc->list, &misc_list); // 以上操作都沒有問題後,將新設備加入misc_list鏈表,解鎖返回

out:

mutex_unlock(&misc_mtx);

return err;

}

***************************************************************************
同樣,對應misc_register函數,在exit中會調用misc_deregister函數

/**

* misc_deregister - unregister a miscellaneous device

* @misc: device to unregister

*

* Unregister a miscellaneous device that was previously

* successfully registered with misc_register(). Success

* is indicated by a zero return, a negative errno code

* indicates an error.

*/

int misc_deregister(struct miscdevice *misc)

{

int i = DYNAMIC_MINORS - misc->minor - 1;
if (WARN_ON(list_empty(&misc->list))) // 如果該misc->list的next指向自己,則出錯返回

return -EINVAL;
mutex_lock(&misc_mtx); // 上鎖
list_del(&misc->list); // 將misc從misc_list鏈表中刪除
device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor)); // 對應device_create!
1524/**
1525 * device_destroy - removes a device that was created with device_create()
1526 * @class: pointer to the struct class that this device was registered with
1527 * @devt: the dev_t of the device that was previously registered
1528 *
1529 * This call unregisters and cleans up a device that was created with a
1530 * call to device_create().
1531 */
1532void device_destroy(struct class *class, dev_t devt)
1533{
1534 struct device *dev;
1535
1536 dev = class_find_device(class, NULL, &devt, __match_devt);
1537 if (dev) {
1538 put_device(dev);
1539 device_unregister(dev);
1540 }
1541}
1542EXPORT_SYMBOL_GPL(device_destroy);
if (i < DYNAMIC_MINORS && i >= 0)
clear_bit(i, misc_minors); // 計算點陣圖位i,如果在0-64之間,說明在set_bit中置位了,清楚點陣圖

mutex_unlock(&misc_mtx); // 解鎖返回
return 0;

}

***************************************************************************
總結雜項設備驅動的註冊與卸載流程:
misc_register:找到空閒設備點陣圖位置 -> 計算子設備號(如果動態的話),點陣圖位置位元 - > device_creat() -> miscdevice結構體加入misc_list鏈表中

misc_deregister: 將miscdevice結構體從misc_list鏈表中刪除 -> device_destory() -> 點陣圖位清零

***************************************************************************
與s3c24xx_gpiolib_init函數一樣,misc也有一個初始化函數會在linux初始化時運行,下面來分析這個函數
static int __init misc_init(void)
{
int err;
#ifdef CONFIG_PROC_FS //在proc檔系統下創建一個"misc"目錄。 misc_proc_fops是該檔系統下檔的操作函數集
proc_create("misc", 0, NULL, &misc_proc_fops);
#endif
misc_class = class_create(THIS_MODULE, "misc"); // 前面device_create()中的misc_class就是在這裏初始化的
err = PTR_ERR(misc_class);
if (IS_ERR(misc_class)) // 出錯處理
goto fail_remove;
err = -EIO;
if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) //註冊一個主設備號為MISC_MAJOR(10)的字元設備,設備操作函數集為misc_fops
goto fail_printk;
misc_class->devnode = misc_devnode;
return 0;
fail_printk: // 錯誤處理
printk("unable to get major %d for misc devices\n", MISC_MAJOR);
class_destroy(misc_class);
fail_remove:
remove_proc_entry("misc", NULL);
return err;
}

subsys_initcall(misc_init);

***************************************************************************
好,到這裏基本把一些GPIO相關的基本函數和結構體都簡單說明了,雖然還有不少不清楚的地方,但還是有些幫助,文中還有些不清楚的地方還有待以後能一一解決,我會不斷補充!
--> 閱讀更多...