<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > IO端口映射和IO內(nèi)存映射(詳解S3C24XX_GPIO驅(qū)動(dòng))

          IO端口映射和IO內(nèi)存映射(詳解S3C24XX_GPIO驅(qū)動(dòng))

          作者: 時(shí)間:2016-11-09 來(lái)源:網(wǎng)絡(luò) 收藏
          剛剛我們實(shí)現(xiàn)了linux系統(tǒng)內(nèi)存的分配,讀寫(xiě),釋放功能,下面,我們一鼓作氣將IO端口映射IO內(nèi)存映射搞定,加油!


          (一)地址的概念

          本文引用地址:http://www.ex-cimer.com/article/201611/318060.htm

          1)物理地址:CPU地址總線傳來(lái)的地址,由硬件電路控制其具體含義。物理地址中很大一部分是留給內(nèi)存條中的內(nèi)存的,但也常被映射到其他存儲(chǔ)器上(如顯存、BIOS等)。在程序指令中的虛擬地址經(jīng)過(guò)段映射和頁(yè)面映射后,就生成了物理地址,這個(gè)物理地址被放到CPU的地址線上。
          物理地址空間,一部分給物理RAM(內(nèi)存)用,一部分給總線用,這是由硬件設(shè)計(jì)來(lái)決定的,因此在32 bits地址線的x86處理器中,物理地址空間是2的32次方,即4GB,但物理RAM一般不能上到4GB,因?yàn)檫€有一部分要給總線用(總線上還掛著別的許多設(shè)備)。在PC機(jī)中,一般是把低端物理地址給RAM用,高端物理地址給總線用。
          2)總線地址:總線的地址線或在地址周期上產(chǎn)生的信號(hào)。外設(shè)使用的是總線地址,CPU使用的是物理地址。
          物理地址與總線地址之間的關(guān)系由系統(tǒng)的設(shè)計(jì)決定的。在x86平臺(tái)上,物理地址就是總線地址,這是因?yàn)樗鼈児蚕硐嗤牡刂房臻g——這句話有點(diǎn)難理解,詳見(jiàn)下面的"獨(dú)立編址"。在其他平臺(tái)上,可能需要轉(zhuǎn)換/映射。比如:CPU需要訪問(wèn)物理地址是0xfa的單元,那么在x86平臺(tái)上,會(huì)產(chǎn)生一個(gè)PCI總線上對(duì)0xfa地址的訪問(wèn)。因?yàn)槲锢淼刂泛涂偩€地址相同,所以憑眼睛看是不能確定這個(gè)地址是用在哪兒的,它或者在內(nèi)存中,或者是某個(gè)卡上的存儲(chǔ)單元,甚至可能這個(gè)地址上沒(méi)有對(duì)應(yīng)的存儲(chǔ)器。

          3)虛擬地址:現(xiàn)代操作系統(tǒng)普遍采用虛擬內(nèi)存管理(Virtual Memory Management)機(jī)制,這需要MMU(Memory Management Unit)的支持。MMU通常是CPU的一部分,如果處理器沒(méi)有MMU,或者有MMU但沒(méi)有啟用,CPU執(zhí)行單元發(fā)出的內(nèi)存地址將直接傳到芯片引腳上,被內(nèi)存芯片(物理內(nèi)存)接收,這稱為物理地址(Physical Address),如果處理器啟用了MMU,CPU執(zhí)行單元發(fā)出的內(nèi)存地址將被MMU截獲,從CPU到MMU的地址稱為虛擬地址(Virtual Address),而MMU將這個(gè)地址翻譯成另一個(gè)地址發(fā)到CPU芯片的外部地址引腳上,也就是將虛擬地址映射成物理地址。
          Linux中,進(jìn)程的4GB(虛擬)內(nèi)存分為用戶空間、內(nèi)核空間。用戶空間分布為0~3GB(即PAGE_OFFSET,在0X86中它等于0xC0),剩下的1G為內(nèi)核空間。程序員只能使用虛擬地址。系統(tǒng)中每個(gè)進(jìn)程有各自的私有用戶空間(0~3G),這個(gè)空間對(duì)系統(tǒng)中的其他進(jìn)程是不可見(jiàn)的。
          CPU發(fā)出取指令請(qǐng)求時(shí)的地址是當(dāng)前上下文的虛擬地址,MMU再?gòu)捻?yè)表中找到這個(gè)虛擬地址的物理地址,完成取指。同樣讀取數(shù)據(jù)的也是虛擬地址,比如mov ax, var.編譯時(shí)var就是一個(gè)虛擬地址,也是通過(guò)MMU從也表中來(lái)找到物理地址,再產(chǎn)生總線時(shí)序,完成取數(shù)據(jù)的。


          (二)編址方式
          1)外設(shè)都是通過(guò)讀寫(xiě)設(shè)備上的寄存器來(lái)進(jìn)行的,外設(shè)寄存器也稱為"I/O端口",而IO端口有兩種編址方式:獨(dú)立編址和統(tǒng)一編制。

          統(tǒng)一編址:外設(shè)接口中的IO寄存器(即IO端口)與主存單元一樣看待,每個(gè)端口占用一個(gè)存儲(chǔ)單元的地址,將主存的一部分劃出來(lái)用作IO地址空間,如,在PDP-11中,把最高的4K主存作為IO設(shè)備寄存器地址。端口占用了存儲(chǔ)器的地址空間,使存儲(chǔ)量容量減小。
          統(tǒng)一編址也稱為"I/O內(nèi)存"方式,外設(shè)寄存器位于"內(nèi)存空間"(很多外設(shè)有自己的內(nèi)存、緩沖區(qū),外設(shè)的寄存器和內(nèi)存統(tǒng)稱"I/O空間")。
          如,Samsung的S3C2440,是32位ARM處理器,它的4GB地址空間被外設(shè)、RAM等瓜分:
          0x8 1 LED 8*8點(diǎn)陣的地址
          0x4800 0 ~ 0x6 0 SFR(特殊暫存器)地址空間
          0x3800 1002鍵盤(pán)地址
          0x3 0 ~ 0x3400 0 SDRAM空間
          0x2 0020 ~ 0x2 002e IDE
          0x1900 0300 CS8900

          獨(dú)立編址(單獨(dú)編址):IO地址與存儲(chǔ)地址分開(kāi)獨(dú)立編址,I/0端口地址不占用存儲(chǔ)空間的地址范圍,這樣,在系統(tǒng)中就存在了另一種與存儲(chǔ)地址無(wú)關(guān)的IO地址,CPU也必須具有專用與輸入輸出操作的IO指令(IN、OUT等)和控制邏輯。獨(dú)立編址下,地址總線上過(guò)來(lái)一個(gè)地址,設(shè)備不知道是給IO端口的、還是給存儲(chǔ)器的,于是處理器通過(guò)MEMR/MEMW和IOR/IOW兩組控制信號(hào)來(lái)實(shí)現(xiàn)對(duì)I/O端口和存儲(chǔ)器的不同尋址。如,intel 80x86就采用單獨(dú)編址,CPU內(nèi)存和I/O是一起編址的,就是說(shuō)內(nèi)存一部分的地址和I/O地址是重疊的。
          獨(dú)立編址也稱為"I/O端口"方式,外設(shè)寄存器位于"I/O(地址)空間"。
          對(duì)于x86架構(gòu)來(lái)說(shuō),通過(guò)IN/OUT指令訪問(wèn)。PC架構(gòu)一共有65536個(gè)8bit的I/O端口,組成64K個(gè)I/O地址空間,編號(hào)從0~0xFFFF,有16位,80x86用低16位地址線A0-A15來(lái)尋址。連續(xù)兩個(gè)8bit的端口可以組成一個(gè)16bit的端口,連續(xù)4個(gè)組成一個(gè)32bit的端口。I/O地址空間和CPU的物理地址空間是兩個(gè)不同的概念,例如I/O地址空間為64K,一個(gè)32bit的CPU物理地址空間是4G。如,在Intel 8086+Redhat9.0下用"more /proc/ioports"可看到:

          0-001f : dma1
          0020-003f : pic1
          0040-005f : timer
          0060-006f : keyboard
          0070-007f : rtc
          0080-008f : dma page reg
          00a0-00bf : pic2
          00c0-00df : dma2
          00f0-00ff : fpu
          0170-0177 : ide1
          ……

          不過(guò)Intel x86平臺(tái)普通使用了名為內(nèi)存映射(MMIO)的技術(shù),該技術(shù)是PCI規(guī)范的一部分,IO設(shè)備端口被映射到內(nèi)存空間,映射后,CPU訪問(wèn)IO端口就如同訪問(wèn)內(nèi)存一樣??碔ntel TA 719文檔給出的x86/x64系統(tǒng)典型內(nèi)存地址分配表:
          系統(tǒng)資源占用

          BIOS 1M

          本地APIC 4K

          芯片組保留 2M

          IO APIC 4K

          PCI設(shè)備 256M

          PCI Express設(shè)備 256M

          PCI設(shè)備(可選) 256M

          顯示幀緩存 16M

          TSEG 1M

          對(duì)于某一既定的系統(tǒng),它要么是獨(dú)立編址、要么是統(tǒng)一編址,具體采用哪一種則取決于CPU的體系結(jié)構(gòu)。 如,PowerPC、m68k等采用統(tǒng)一編址,而X86等則采用獨(dú)立編址,存在IO空間的概念。目前,大多數(shù)嵌入式微控制器如ARM、PowerPC等并 不提供I/O空間,僅有內(nèi)存空間,可直接用地址、指針訪問(wèn)。但對(duì)于Linux內(nèi)核而言,它可能用于不同的CPU,所以它必須都要考慮這兩種方式,于是它采 用一種新的方法,將基于I/O映射方式的或內(nèi)存映射方式的I/O端口通稱為"I/O區(qū)域"(I/O region),不論你采用哪種方式,都要先申請(qǐng)IO區(qū)域:request_resource(),結(jié)束時(shí)釋放 它:release_resource()。

          2)對(duì)外設(shè)的訪問(wèn)

          1、訪問(wèn)I/O內(nèi)存的流程是:request_mem_region()->ioremap()->ioread8()/iowrite8()->iounmap()->release_mem_region()。
          前面說(shuō)過(guò),IO內(nèi)存是統(tǒng)一編址下的概念,對(duì)于統(tǒng)一編址,IO地址空間是物理主存的一部分,對(duì)于編程而言,我們只能操作虛擬內(nèi)存,所以,訪問(wèn)的第一步就是要把設(shè)備所處的物理地址映射到虛擬地址,Linux2.6下用ioremap():
          void *ioremap(unsigned long offset, unsigned long size);
          然后,我們可以直接通過(guò)指針來(lái)訪問(wèn)這些地址,但是也可以用Linux內(nèi)核的一組函數(shù)來(lái)讀寫(xiě):
          ioread8(), iowrite16(), ioread8_rep(), iowrite8_rep()......

          2、訪問(wèn)I/O端口
          訪問(wèn)IO端口有2種途徑:I/O映射方式(I/O-mapped)、內(nèi)存映射方式(Memory-mapped)。前一種途徑不映射到內(nèi)存空間,直接使用intb()/outb()之類的函數(shù)來(lái)讀寫(xiě)IO端口;后一種MMIO是先把IO端口映射到IO內(nèi)存("內(nèi)存空間"),再使用訪問(wèn)IO內(nèi)存的函數(shù)來(lái)訪問(wèn)IO端口。
          void ioport_map(unsigned long port, unsigned int count);
          通過(guò)這個(gè)函數(shù),可以把port開(kāi)始的count個(gè)連續(xù)的IO端口映射為一段"內(nèi)存空間",然后就可以在其返回的地址是像訪問(wèn)IO內(nèi)存一樣訪問(wèn)這些IO端口。

          (三)Linux下的IO端口和IO內(nèi)存
          CPU對(duì)外設(shè)端口物理地址的編址方式有兩種:一種是IO映射方式,另一種是內(nèi)存映射方式。
           Linux將基于IO映射方式的和內(nèi)存映射方式的IO端口統(tǒng)稱為IO區(qū)域(IO region)。
            IO region仍然是一種IO資源,因此它仍然可以用resource結(jié)構(gòu)類型來(lái)描述。

            Linux管理IO region:

            1) request_region()

            把一個(gè)給定區(qū)間的IO端口分配給一個(gè)IO設(shè)備。

            2) check_region()

            檢查一個(gè)給定區(qū)間的IO端口是否空閑,或者其中一些是否已經(jīng)分配給某個(gè)IO設(shè)備。

            3) release_region()

            釋放以前分配給一個(gè)IO設(shè)備的給定區(qū)間的IO端口。

            Linux中可以通過(guò)以下輔助函數(shù)來(lái)訪問(wèn)IO端口:

            inb(),inw(),inl(),outb(),outw(),outl()

            "b""w""l"分別代表8位,16位,32位。

           對(duì)IO內(nèi)存資源的訪問(wèn)

            1) request_mem_region()

            請(qǐng)求分配指定的IO內(nèi)存資源。

            2) check_mem_region()

            檢查指定的IO內(nèi)存資源是否已被占用。

            3) release_mem_region()

            釋放指定的IO內(nèi)存資源。

            其中傳給函數(shù)的start address參數(shù)是內(nèi)存區(qū)的物理地址(以上函數(shù)參數(shù)表已省略)。

            驅(qū)動(dòng)開(kāi)發(fā)人員可以將內(nèi)存映射方式的IO端口和外設(shè)內(nèi)存統(tǒng)一看作是IO內(nèi)存資源。

            ioremap()用來(lái)將IO資源的物理地址映射到內(nèi)核虛地址空間(3GB - 4GB)中,參數(shù)addr是指向內(nèi)核虛地址的指針。

            Linux中可以通過(guò)以下輔助函數(shù)來(lái)訪問(wèn)IO內(nèi)存資源:

            readb(),readw(),readl(),writeb(),writew(),writel()。

            Linux在kernel/resource.c文件中定義了全局變量ioport_resource和iomem_resource,來(lái)分別描述基于IO映射方式的整個(gè)IO端口空間和基于內(nèi)存映射方式的IO內(nèi)存資源空間(包括IO端口和外設(shè)內(nèi)存)。

          1)關(guān)于IO與內(nèi)存空間:
          在X86處理器中存在著I/O空間的概念,I/O空間是相對(duì)于內(nèi)存空間而言的,它通過(guò)特定的指令in、out來(lái)訪問(wèn)。端口號(hào)標(biāo)識(shí)了外設(shè)的寄存器地址。Intel語(yǔ)法的in、out指令格式為:
          IN累加器, {端口號(hào)│DX}
          OUT {端口號(hào)│DX},累加器
          目前,大多數(shù)嵌入式微控制器如ARM、PowerPC等中并不提供I/O空間,而僅存在內(nèi)存空間。內(nèi)存空間可以直接通過(guò)地址、指針來(lái)訪問(wèn),程序和程序運(yùn)行中使用的變量和其他數(shù)據(jù)都存在于內(nèi)存空間中。
          即便是在X86處理器中,雖然提供了I/O空間,如果由我們自己設(shè)計(jì)電路板,外設(shè)仍然可以只掛接在內(nèi)存空間。此時(shí),CPU可以像訪問(wèn)一個(gè)內(nèi)存單元那樣訪問(wèn)外設(shè)I/O端口,而不需要設(shè)立專門(mén)的I/O指令。因此,內(nèi)存空間是必須的,而I/O空間是可選的。

          (2)inb和outb:

          在Linux設(shè)備驅(qū)動(dòng)中,宜使用Linux內(nèi)核提供的函數(shù)來(lái)訪問(wèn)定位于I/O空間的端口,這些函數(shù)包括:
          ·讀寫(xiě)字節(jié)端口(8位寬)
          unsigned inb(unsigned port);
          void outb(unsigned char byte, unsigned port);
          ·讀寫(xiě)字端口(16位寬)
          unsigned inw(unsigned port);
          void outw(unsigned short word, unsigned port);
          ·讀寫(xiě)長(zhǎng)字端口(32位寬)
          unsigned inl(unsigned port);
          void outl(unsigned longword, unsigned port);
          ·讀寫(xiě)一串字節(jié)
          void insb(unsigned port, void *addr, unsigned long count);
          void outsb(unsigned port, void *addr, unsigned long count);
          · insb()從端口port開(kāi)始讀count個(gè)字節(jié)端口,并將讀取結(jié)果寫(xiě)入addr指向的內(nèi)存;outsb()將addr指向的內(nèi)存的count個(gè)字節(jié)連續(xù)地寫(xiě)入port開(kāi)始的端口。
          ·讀寫(xiě)一串字
          void insw(unsigned port, void *addr, unsigned long count);
          void outsw(unsigned port, void *addr, unsigned long count);
          ·讀寫(xiě)一串長(zhǎng)字
          void insl(unsigned port, void *addr, unsigned long count);
          void outsl(unsigned port, void *addr, unsigned long count);
          上述各函數(shù)中I/O端口號(hào)port的類型高度依賴于具體的硬件平臺(tái),因此,只是寫(xiě)出了unsigned。

          以上知識(shí)點(diǎn)摘自:http://www.embeddedlinux.org.cn/html/yingjianqudong/201304/20-2556.html

          這兒我不獻(xiàn)丑了,因?yàn)樯厦娴囊呀?jīng)寫(xiě)的很好了。

          (四)Linux下的IO端口和IO內(nèi)存

          由于我本人對(duì)IMX257的IO內(nèi)存分配不太了解,曾經(jīng)詢問(wèn)過(guò)很多周立功公司那些所謂的技術(shù)人員,每個(gè)人回答的答案都是,"你好,我們提供的資料就只是官網(wǎng)上的光盤(pán)資料了哦",每次碰到這種話,我真的很無(wú)語(yǔ),我只能說(shuō),好吧,只能說(shuō)是我智商太低了,那些資料太高深了。

          好了,廢話也不多說(shuō)了,這次,我就針對(duì)S3C24XX的gpio驅(qū)動(dòng)程序來(lái)講解io端口的分配。

          1. 定義一些寄存器(數(shù)據(jù)寄存器,配置寄存器)

          上圖中,gpio_va就是我們GPIO的基址,是用來(lái)確定GPFCON配置寄存器和GPFDAT數(shù)據(jù)寄存器的指針地址的

          ,其初始化我們?cè)趇nit函數(shù)中會(huì)講解。

          2.在init函數(shù)中確定gpio基址

          前面我們寄存器的正常作用的前提就是gpio_va這個(gè)基址,所以我們?cè)趇nit函數(shù)中分配IO端口

          如圖所示,將gpio的地址0x56這個(gè)io端口分配為gpio_va,這樣也就確定了,前面的配置寄存器和數(shù)據(jù)寄存器。

          3.在open函數(shù)中配置GPIO

          單片機(jī)程序一樣,使用GPIO都要先配置GPIO,原理一樣:

          給GPIO引腳賦初值,以后對(duì)GPIO引腳的操作,直接用這個(gè)寄存器賦值就可以了

          4.在exit函數(shù)中釋放IO端口

          使用完GPIO引腳后,要將我們申請(qǐng)的IO端口釋放掉。

          總結(jié)一下,很簡(jiǎn)單,申請(qǐng),配置,釋放

          申請(qǐng)IO端口à配置GPIO端口為輸出(入)à給數(shù)據(jù)寄存器賦值(讀?。?agrave;釋放IO端口

          附上驅(qū)動(dòng)程序:



          1 #include 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 #include 10 #include 11 12 #define DEVICE_NAME     "leds"  13 /* 加載模式后,執(zhí)行”cat /proc/devices”命令看到的設(shè)備名稱 */14 #define LED_MAJOR       231     /* 主設(shè)備號(hào) */15 16 static struct class *leds_class;17 static struct class_device    *leds_class_devs[4];18 19 /* bit0<=>D10, 0:亮, 1:滅 20  *  bit1<=>D11, 0:亮, 1:滅 21  *  bit2<=>D12, 0:亮, 1:滅 22  */ 23 static char leds_status = 0x0;  24 static DECLARE_MUTEX(leds_lock); // 定義賦值25 26 //static int minor;27 static unsigned long gpio_va;  //gpio基址28 #define GPIO_OFT(x) ((x) - 0x56)29 //GPIO 配置寄存器30 #define GPFCON  (*(volatile unsigned long *)(gpio_va + GPIO_OFT(0x56050)))31 //GPIO 數(shù)據(jù)寄存器32 #define GPFDAT  (*(volatile unsigned long *)(gpio_va + GPIO_OFT(0x56054)))33 /* 應(yīng)用程序?qū)υO(shè)備文件/dev/leds執(zhí)行open(...)時(shí),34  * 就會(huì)調(diào)用s3c24xx_leds_open函數(shù)35  */36 static int s3c24xx_leds_open(struct inode *inode, struct file *file)37 {38     int minor = MINOR(inode->i_rdev); //MINOR(inode->i_cdev);39     switch(minor)40     {41         case 0: /* /dev/leds */42         {43             // 配置3引腳為輸出44             //s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP);45             GPFCON &= ~(0x3<<(4*2));46             GPFCON = (1<<(4*2));47             48             //s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);49             GPFCON &= ~(0x3<<(5*2));50             GPFCON = (1<<(5*2));51 52             //s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP);53             GPFCON &= ~(0x3<<(6*2));54             GPFCON = (1<<(6*2));55 56             // 都輸出057             //s3c2410_gpio_setpin(S3C2410_GPF4, 0);58             GPFDAT &= ~(1<<4);59             60             //s3c2410_gpio_setpin(S3C2410_GPF5, 0);61             GPFDAT &= ~(1<<5);62             //s3c2410_gpio_setpin(S3C2410_GPF6, 0);63             GPFDAT &= ~(1<<6);64 65             down(&leds_lock);66             leds_status = 0x0;67             up(&leds_lock);68                 69             break;70         }71 72         case 1: /* /dev/led1 */73         {74             s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP);75             s3c2410_gpio_setpin(S3C2410_GPF4, 0);76             77             down(&leds_lock);78             leds_status &= ~(1<<0);79             up(&leds_lock);80             81             break;82         }83 84         case 2: /* /dev/led2 */85         {86             s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);87             s3c2410_gpio_setpin(S3C2410_GPF5, 0);88             leds_status &= ~(1<<1);89             break;90         }91 92         case 3: /* /dev/led3 */93         {94             s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP);95             s3c2410_gpio_setpin(S3C2410_GPF6, 0);96 97             down(&leds_lock);98             leds_status &= ~(1<<2);99             up(&leds_lock);100             101             break;102         }103     }    104     return 0;105 }106 static int s3c24xx_leds_read(struct file *filp, char __user *buff, 107                                          size_t count, loff_t *offp)108 {109     int minor = MINOR(filp->f_dentry->d_inode->i_rdev);110     char val;112     switch (minor)113     {114         case 0: /* /dev/leds */115         {116             copy_to_user(buff, (const void *)&leds_status, 1);117             break;118         }119         case 1: /* /dev/led1 */120         {121             down(&leds_lock);122             val = leds_status & 0x1;123             up(&leds_lock);124             copy_to_user(buff, (const void *)&val, 1);125             break;126         }127         case 2: /* /dev/led2 */128         {129             down(&leds_lock);130             val = (leds_status>>1) & 0x1;131             up(&leds_lock);132             copy_to_user(buff, (const void *)&val, 1);133             break;134         }135         case 3: /* /dev/led3 */136         {137             down(&leds_lock);138             val = (leds_status>>2) & 0x1;139             up(&leds_lock);140             copy_to_user(buff, (const void *)&val, 1);141             break;142         }143     }144     return 1;145 }146 static ssize_t s3c24xx_leds_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)147 {148     //int minor = MINOR(inode->i_rdev); //MINOR(inode->i_cdev);149     int minor = MINOR(file->f_dentry->d_inode->i_rdev);150     char val;151     copy_from_user(&val, buf, 1);152     switch (minor)153     {154         case 0: /* /dev/leds */155         {            156             s3c2410_gpio_setpin(S3C2410_GPF4, (val & 0x1));157             s3c2410_gpio_setpin(S3C2410_GPF5, (val & 0x1));158             s3c2410_gpio_setpin(S3C2410_GPF6, (val & 0x1));159             down(&leds_lock);160             leds_status = val;161             up(&leds_lock);162             break;163         }164         case 1: /* /dev/led1 */165         {166             s3c2410_gpio_setpin(S3C2410_GPF4, val);167             if (val == 0)168             {169                 down(&leds_lock);170                 leds_status &= ~(1<<0);171                 up(&leds_lock);172             }173             else174             {175                 down(&leds_lock);176                 leds_status = (1<<0);                177                 up(&leds_lock);178             }179             break;180         }181         case 2: /* /dev/led2 */182         {183             s3c2410_gpio_setpin(S3C2410_GPF5, val);184             if (val == 0)185             {186                 down(&leds_lock);187                 leds_status &= ~(1<<1);188                 up(&leds_lock);189             }190             else191             {192                 down(&leds_lock);193                 leds_status = (1<<1);                194                 up(&leds_lock);195             }196             break;197         }198         case 3: /* /dev/led3 */199         {200             s3c2410_gpio_setpin(S3C2410_GPF6, val);201             if (val == 0)202             {203                 down(&leds_lock);204                 leds_status &= ~(1<<2);205                 up(&leds_lock);206             }207             else208             {209                 down(&leds_lock);210                 leds_status = (1<<2);                211                 up(&leds_lock);212             }213             break;214         }215     }216     return 1;217 }218 /* 這個(gè)結(jié)構(gòu)是字符設(shè)備驅(qū)動(dòng)程序的核心219  * 當(dāng)應(yīng)用程序操作設(shè)備文件時(shí)所調(diào)用的open、read、write等函數(shù),220  * 最終會(huì)調(diào)用這個(gè)結(jié)構(gòu)中指定的對(duì)應(yīng)函數(shù)221  */static struct file_operations s3c24xx_leds_fops = {223     .owner  =   THIS_MODULE,    /* 這是一個(gè)宏,推向編譯模塊時(shí)自動(dòng)創(chuàng)建的__this_module變量 */224     .open   =   s3c24xx_leds_open,     225     .read    =    s3c24xx_leds_read,       226     .write    =    s3c24xx_leds_write,       227 };228 229 /*230  * 執(zhí)行insmod命令時(shí)就會(huì)調(diào)用這個(gè)函數(shù) 231  */232 static int __init s3c24xx_leds_init(void)233 {234     int ret;235     int minor = 0;236 237     gpio_va = ioremap(0x56, 0x100);238     if (!gpio_va) {239         return -EIO;240     }241     /* 注冊(cè)字符設(shè)備242      * 參數(shù)為主設(shè)備號(hào)、設(shè)備名字、file_operations結(jié)構(gòu);243      * 這樣,主設(shè)備號(hào)就和具體的file_operations結(jié)構(gòu)聯(lián)系起來(lái)了,244      * 操作主設(shè)備為L(zhǎng)ED_MAJOR的設(shè)備文件時(shí),就會(huì)調(diào)用s3c24xx_leds_fops中的相關(guān)成員函數(shù)245      * LED_MAJOR可以設(shè)為0,表示由內(nèi)核自動(dòng)分配主設(shè)備號(hào)246      */247     ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s3c24xx_leds_fops);248     if (ret < 0) {249       printk(DEVICE_NAME " cant register major numbern");250       return ret;251     }252     leds_class = class_create(THIS_MODULE, "leds");253     if (IS_ERR(leds_class))254         return PTR_ERR(leds_class);255     256     leds_class_devs[0] = class_device_create(leds_class, NULL, MKDEV(LED_MAJOR, 0), NULL, "leds");257     258     for (minor = 1; minor < 4; minor++)259     {260         leds_class_devs[minor] = class_device_create(leds_class, NULL, MKDEV(LED_MAJOR, minor), NULL, "led%d", minor);261         if (unlikely(IS_ERR(leds_class_devs[minor])))262             return PTR_ERR(leds_class_devs[minor]);263     }264         265     printk(DEVICE_NAME " initializedn");266     return 0;267 }268 269 /*270  * 執(zhí)行rmmod命令時(shí)就會(huì)調(diào)用這個(gè)函數(shù) 271  */272 static void __exit s3c24xx_leds_exit(void)273 {274     int minor;275     /* 卸載驅(qū)動(dòng)程序 */276     unregister_chrdev(LED_MAJOR, DEVICE_NAME);277 278     for (minor = 0; minor < 4; minor++)279     {280         class_device_unregister(leds_class_devs[minor]);281     }282     class_destroy(leds_class);283     //釋放IO端口284     iounmap(gpio_va);285 }286 287 /* 這兩行指定驅(qū)動(dòng)程序的初始化函數(shù)和卸載函數(shù) */288 module_init(s3c24xx_leds_init);289 module_exit(s3c24xx_leds_exit);290 291 /* 描述驅(qū)動(dòng)程序的一些信息,不是必須的 */292 MODULE_LICENSE("GPL");
          View Code



          評(píng)論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();