驱动相关的内核函数分析
目錄:
字符設(shè)備驅(qū)動(dòng)程序?qū)σ恍└呒?jí)特性的實(shí)現(xiàn) nonseekable_open;?scull_p_poll
Linux內(nèi)核中獲取當(dāng)前時(shí)間?do_gettimeofday
用戶空間和內(nèi)核空間傳遞數(shù)據(jù):get_user;put_user;copy_to_user;copy_from_user
__raw_readl和__raw_writel
GPIO端口控制及宏定義
?
?
?
字符設(shè)備驅(qū)動(dòng)程序?qū)σ恍└呒?jí)特性的實(shí)現(xiàn)?nonseekable_open;?scull_p_poll
詳見(jiàn):http://blog.sina.com.cn/s/blog_6e5b342e0100m87o.html
?
但像串口或鍵盤一類設(shè)備,使用的是數(shù)據(jù)流,所以使用lseek定位這些設(shè)備沒(méi)有意義;在 open 方法中調(diào)用 nonseekable_open() 時(shí),它會(huì)通知內(nèi)核設(shè)備不支持 llseek,nonseekable_open() 函數(shù)的實(shí)現(xiàn)定義在 fs/open.c 中。
int nonseekable_open(struct inode *inode, struct file *filp) {filp->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);return 0; }為了完整起見(jiàn),如果不希望設(shè)備被 seek,還應(yīng)該將 file_operations 結(jié)構(gòu)中的 llseek 方法設(shè)置為特殊的輔助函數(shù) no_llseek 。即" .llseek = no_llseek, "。
而no_llseek函數(shù)無(wú)需手動(dòng)定義,內(nèi)核中已經(jīng)定義好了。
?
Linux內(nèi)核中獲取當(dāng)前時(shí)間?do_gettimeofday
#include <linux/time.h> void do_gettimeofday(struct timeval *tv);獲取當(dāng)前距離系統(tǒng)啟動(dòng)的時(shí)間差,并將時(shí)間拆分為秒和微秒存入struct timeval結(jié)構(gòu)體。
?
get_user;put_user;copy_to_user;copy_from_user
這些函數(shù)均是內(nèi)核態(tài)用于內(nèi)核空間與用戶空間的數(shù)據(jù)傳輸?shù)暮瘮?shù)
get_user和put_user復(fù)制的是簡(jiǎn)單的數(shù)據(jù)類型,如char,int ,long等;
copy_to_user和copy_from_user則是以數(shù)據(jù)塊的形式在內(nèi)核空間與用戶空間傳輸?shù)摹?/span>
//函數(shù)與宏定義 unsigned long copy_to_user(void __user *to, const void *from, unsigned long n){...} unsigned long copy_from_user(void *to, const void __user *from, unsigned long n){...} #define put_user(x,ptr) (...) #define put_user(x,ptr) (...)
?arch\arm\include\asm\Io.h
#define __raw_readl(a) (__chk_io_ptr(a), *(volatile unsigned int __force ? *)(a))??
#define __raw_writel(v,a) (__chk_io_ptr(a), *(volatile unsigned int __force ? *)(a) = (v))
注:(volatile unsigned int __force ? *)指針強(qiáng)制轉(zhuǎn)換為unsigned int型。
其中volatile關(guān)鍵字有以下用途:
(1)用來(lái)同步,因?yàn)橥粋€(gè)東西可能在不同的存儲(chǔ)介質(zhì)中有多個(gè)副本,有些情況下會(huì)使得這些副本中的值不同,這是不允許的,所以干脆用volatile,讓它只有一個(gè),沒(méi)有其他的副本,這樣就不會(huì)發(fā)生不同步的問(wèn)題。
如下所示:
volatile的意思是告訴編譯器,在編程源代碼時(shí),對(duì)這個(gè)變量不要使用優(yōu)化。
在一般的程序設(shè)計(jì)中,如:
int *a; int b;
b = (*a) * (*a);這種情況。
通常編譯器為了減少存儲(chǔ)器的讀寫時(shí)間,會(huì)把代碼優(yōu)化為:
int *a; int b; int c;
c = *a;
b = c * c;
如果把int *a改為volatile int* a編譯器就不會(huì)自動(dòng)把它優(yōu)化掉了。在整個(gè)運(yùn)算過(guò)程中,對(duì)變量*a的值又讀取了一次。防止因變量*a的值在這一期間發(fā)生了改變,而導(dǎo)致程序結(jié)果的錯(cuò)誤。
(2)防止編譯器優(yōu)化去掉某些語(yǔ)句,像我在arm中見(jiàn)到個(gè)寄存器非常奇怪,當(dāng)中斷來(lái)的時(shí)候,相對(duì)應(yīng)的位置1,而清0又不能向這位寫0,向這位寫1才是1才是清中斷(清0),
// 假設(shè)0x560012300 為寄存器地址
#define INTPAND *(volatile unsigned int *)0x560012300
INTPAND = INTPAND; // 清中斷?
像編譯器如果看到有INTPAND = INTPAND;這種看似無(wú)用的操作,如果沒(méi)有volatile說(shuō)明,編譯器就很有可能會(huì)去掉INTPAND = INTPAND;實(shí)際上有用的東西,卻被編譯器當(dāng)沒(méi)用的東西優(yōu)化掉了。
(3)當(dāng)?shù)刂肥莍o端口的時(shí)候,讀寫這個(gè)地址是不能對(duì)它進(jìn)行緩存的,這是相對(duì)于某些嵌入式中有cache才有這個(gè)。比如寫這個(gè)io端口的時(shí)候,如果沒(méi)有這個(gè)volatile,很可能由于編譯器的優(yōu)化,會(huì)先把值先寫到一個(gè)緩沖區(qū),到一定時(shí)候再寫到io端口,這樣就不能使數(shù)據(jù)及時(shí)的寫到io端口,有了volatile說(shuō)明以后,就不會(huì)再經(jīng)過(guò)cache,write buffer這種,而是直接寫到io端口,從而避免了讀寫io端口的延時(shí)。
?
在include\linux\compiler.h中:
#ifdef __CHECKER__
……
extern void __chk_io_ptr(void __iomem *);
#else
……
# define __chk_io_ptr(x) (void)0
……
#endif
__raw_readl(a)展開(kāi)是:((void)0, *(volatile unsigned int _force *)(a))。在定義了__CHECKER__的時(shí)候先調(diào)用__chk_io_ptr檢查該地址,否則__chk_io_ptr什么也不做,* (volatile unsigned int _force *)(a)就是返回地址為a處的值。(void)xx的做法有時(shí)候是有用的,例如編譯器打開(kāi)了檢查未使用的參數(shù)的時(shí)候需要將沒(méi)有用到的參數(shù)這么弄一下才能 編譯通過(guò)。
注:語(yǔ)句表達(dá)式 x= (y=(a+b), z=10); 的執(zhí)行過(guò)程:順序執(zhí)行括號(hào)內(nèi)的語(yǔ)句,注意各語(yǔ)句分隔使用的是逗號(hào),把最后一個(gè)語(yǔ)句z的值賦給x。
?
CPU對(duì)I/O的物理地址的編程方式有兩種:一種是I/O映射,一種是內(nèi)存映射。 __raw_readl和__raw_writel等是原始的操作I/O的方法,由此派生出來(lái)的操作方法有:inb、outb、 _memcpy_fromio、readb、writeb、ioread8、iowrite8等。
?
S3C2410GPIO端口的宏定義?arch/arm/mach-s3c2410/include/mach/regs-gpio.h
1.S3C2410_GPB5是端口編號(hào),定義在regs-gpio.h中,
#define S3C2410_GPIO_BANKB?? (32*1)
#define S3C2410_GPIONO(bank,offset)?? ((bank) + (offset))
#define S3C2410_GPB5???????? S3C2410_GPIONO(S3C2410_GPIO_BANKB, 5)
S3C2410共有130個(gè)GPIO,分為9組(GPA~GPJ),每組最多可以有 32個(gè),每個(gè)GPIO有2~4個(gè)可選功能,每組的控制寄存器空間有4個(gè),例如對(duì)于GPB,有GPBCON、GPBDAT、GPBUP和Reserved, 分別是功能配置、數(shù)據(jù)緩存、上拉使能和保留。
上面的S3C2410_GPB5就是GPIO的編號(hào),也就是在號(hào)碼空間(0~32*9-1)中的位置,bank是分組的基號(hào)碼,offset是組內(nèi)偏移量。(也就是說(shuō)把所有的IO口從0開(kāi)始進(jìn)行統(tǒng)一的編號(hào)如:S3C2410_GPA0=0,S3C2410_GPA1=1,S3C2410_GPB0=32,S3C2410_GPC0=48等)
2.S3C2410_GPB5_OUTP是端口功能,定義在regs-gpio.h中,
#define S3C2410_GPB5_INP???? (0x00 << 10)
#define S3C2410_GPB5_OUTP??? (0x01 << 10)
GPBCON的第10、11兩位用于配置GPB5的功能,00 = Input ,01 = Output
3.S3C2410 GPIO的操作函數(shù)
在hardware.h文件中有:
s3c2410_gpio_cfgpin???? //配置端口的GPIO的功能
s3c2410_gpio_getcfg???? //讀取功能配置
s3c2410_gpio_pullup???? //配置上拉電阻
s3c2410_modify_misccr //雜項(xiàng)配置
s3c2410_gpio_getirq????? //給定端口,轉(zhuǎn)換出IRQ號(hào)
s3c2410_gpio_irqfilter??? //配置IRQ過(guò)濾使能與否
s3c2410_gpio_setpin???? //寫數(shù)據(jù)到端口
s3c2410_gpio_getpin???? //從端口讀數(shù)據(jù)
這些函數(shù)的實(shí)現(xiàn)在gpio.h中
void s3c2410_gpio_setpin(unsigned int pin, unsigned int to) {void __iomem *base = S3C2410_GPIO_BASE(pin);//算出端口所在組虛擬基址如://GPA=0xF0E00000 //GPB=0XF0E00010unsigned long offs = S3C2410_GPIO_OFFSET(pin); //算出端口所在組的偏移量(0~31)unsigned long flags;unsigned long dat;local_irq_save(flags);dat = __raw_readl(base + 0x04); //虛擬基址加0x04為 GP*DAT寄存器,加0x00為GP*ON等 //讀出當(dāng)前GP*DAT寄存器的值 dat &= ~(1 << offs); //根據(jù)offs偏移量對(duì)該寄存器中選中的 位 清零,其他位保持不變dat |= to << offs; //根據(jù)形參對(duì)要求的位進(jìn)行位操作,來(lái)實(shí)現(xiàn)對(duì)具體某個(gè)IO口的配置__raw_writel(dat, base + 0x04); //將配置寫入到寄存器(這里是虛擬地址)local_irq_restore(flags); }?
unsigned int s3c2410_gpio_getpin(unsigned int pin){void __iomem *base = S3C24XX_GPIO_BASE(pin);unsigned long offs = S3C2410_GPIO_OFFSET(pin);return __raw_readl(base + 0x04) & (1<< offs);}s3c2410_gpio_getpin()的返回值是GPxDAT寄存器的值與所要讀取的GPIO對(duì)應(yīng)的bit mask相與以后的值,0表示該GPIO對(duì)應(yīng)的bit為0, 非0表示該bit為1,所以s3c2410_gpio_getpin(S3C2410_GPG(9))如果GPG9為低電平則返回的是0,如果是高電平則返回的是GPxDAT中的GPG9對(duì)應(yīng)位的值為0x0100而不是0x0001,查處問(wèn)題后修改也很簡(jiǎn)單了。
?
4.S3C2410_GPIO_BASE和S3C2410_GPIO_OFFSET也是在regs-gpio.h文件中定義,
#define S3C2410_GPIO_BASE(pin)?????? ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)
#define S3C2410_GPIO_OFFSET(pin)?? ((pin) & 31)
而在map.h中有:
/* GPIO ports */
#define S3C24XX_VA_GPIO??? S3C2410_ADDR(0x00E00000) //虛擬地址S3C24XX_VA_GPIO= 0xF0E00000
#define S3C2400_PA_GPIO??? (0x15600000)
#define S3C2410_PA_GPIO??? (0x56000000) //GPACON 物理地址
#define S3C24XX_SZ_GPIO??? SZ_1M //0x100000 = 1024 *1024
S3C2410_GPIO_BASE作用是:根 據(jù)端口編號(hào)pin,算出端口所在組的虛擬基址。((pin) & ~31)是去掉pin當(dāng)中小于等于31的零頭(清0低5位),>>1的原因是每組GPIO中最多可以有32個(gè)端口,控制這些端口需要4個(gè)寄存 器空間,4個(gè)寄存器空間就需要4*4=16個(gè)字節(jié)進(jìn)行編址,32/16=2,左移一位剛好滿足。也就是說(shuō),上一組端口和下一組端口的編號(hào)相差32,而控制 寄存器的地址相差16。
S3C2410_GPIO_OFFSET作用是:根據(jù)端口編號(hào)pin,算出端口所在組的偏移量。((pin) & 31)即去掉比31大的數(shù)(清0第6位以上的位)。 ?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/kwseeker-bolgs/p/4415968.html
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的驱动相关的内核函数分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: {转} 如何做学问
- 下一篇: 怎样修改SSIS包中 Connectio