日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

驱动相关的内核函数分析

發布時間:2025/5/22 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 驱动相关的内核函数分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄:

字符設備驅動程序對一些高級特性的實現 nonseekable_open;?scull_p_poll

Linux內核中獲取當前時間?do_gettimeofday

用戶空間和內核空間傳遞數據:get_user;put_user;copy_to_user;copy_from_user

__raw_readl和__raw_writel

GPIO端口控制及宏定義

?

?


?

字符設備驅動程序對一些高級特性的實現?nonseekable_open;?scull_p_poll

詳見:http://blog.sina.com.cn/s/blog_6e5b342e0100m87o.html

?

但像串口或鍵盤一類設備,使用的是數據流,所以使用lseek定位這些設備沒有意義;在 open 方法中調用 nonseekable_open() 時,它會通知內核設備不支持 llseek,nonseekable_open() 函數的實現定義在 fs/open.c 中。

int nonseekable_open(struct inode *inode, struct file *filp) {filp->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);return 0; }

為了完整起見,如果不希望設備被 seek,還應該將 file_operations 結構中的 llseek 方法設置為特殊的輔助函數 no_llseek 。即" .llseek = no_llseek, "。

而no_llseek函數無需手動定義,內核中已經定義好了。

?


Linux內核中獲取當前時間?do_gettimeofday

#include <linux/time.h> void do_gettimeofday(struct timeval *tv);

獲取當前距離系統啟動的時間差,并將時間拆分為秒和微秒存入struct timeval結構體。

?


get_user;put_user;copy_to_user;copy_from_user

這些函數均是內核態用于內核空間與用戶空間的數據傳輸的函數

get_user和put_user復制的是簡單的數據類型,如char,int ,long等;

copy_to_user和copy_from_user則是以數據塊的形式在內核空間與用戶空間傳輸的。

//函數與宏定義 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 ? *)指針強制轉換為unsigned int型。

其中volatile關鍵字有以下用途:

(1)用來同步,因為同一個東西可能在不同的存儲介質中有多個副本,有些情況下會使得這些副本中的值不同,這是不允許的,所以干脆用volatile,讓它只有一個,沒有其他的副本,這樣就不會發生不同步的問題。

如下所示:

volatile的意思是告訴編譯器,在編程源代碼時,對這個變量不要使用優化。
在一般的程序設計中,如:
int *a; int b;
b = (*a) * (*a);這種情況。
通常編譯器為了減少存儲器的讀寫時間,會把代碼優化為:
int *a; int b; int c;
c = *a;
b = c * c;
如果把int *a改為volatile int* a編譯器就不會自動把它優化掉了。在整個運算過程中,對變量*a的值又讀取了一次。防止因變量*a的值在這一期間發生了改變,而導致程序結果的錯誤。

(2)防止編譯器優化去掉某些語句,像我在arm中見到個寄存器非常奇怪,當中斷來的時候,相對應的位置1,而清0又不能向這位寫0,向這位寫1才是1才是清中斷(清0),

// 假設0x560012300 為寄存器地址
#define INTPAND *(volatile unsigned int *)0x560012300

INTPAND = INTPAND; // 清中斷?

像編譯器如果看到有INTPAND = INTPAND;這種看似無用的操作,如果沒有volatile說明,編譯器就很有可能會去掉INTPAND = INTPAND;實際上有用的東西,卻被編譯器當沒用的東西優化掉了。

(3)當地址是io端口的時候,讀寫這個地址是不能對它進行緩存的,這是相對于某些嵌入式中有cache才有這個。比如寫這個io端口的時候,如果沒有這個volatile,很可能由于編譯器的優化,會先把值先寫到一個緩沖區,到一定時候再寫到io端口,這樣就不能使數據及時的寫到io端口,有了volatile說明以后,就不會再經過cache,write buffer這種,而是直接寫到io端口,從而避免了讀寫io端口的延時。

?

在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)展開是:((void)0, *(volatile unsigned int _force *)(a))。在定義了__CHECKER__的時候先調用__chk_io_ptr檢查該地址,否則__chk_io_ptr什么也不做,* (volatile unsigned int _force *)(a)就是返回地址為a處的值。(void)xx的做法有時候是有用的,例如編譯器打開了檢查未使用的參數的時候需要將沒有用到的參數這么弄一下才能 編譯通過。

注:語句表達式 x= (y=(a+b), z=10); 的執行過程:順序執行括號內的語句,注意各語句分隔使用的是逗號,把最后一個語句z的值賦給x。

?

CPU對I/O的物理地址的編程方式有兩種:一種是I/O映射,一種是內存映射。 __raw_readl和__raw_writel等是原始的操作I/O的方法,由此派生出來的操作方法有:inb、outb、 _memcpy_fromio、readb、writeb、ioread8、iowrite8等。

?


S3C2410GPIO端口的宏定義?arch/arm/mach-s3c2410/include/mach/regs-gpio.h

1.S3C2410_GPB5是端口編號,定義在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個GPIO,分為9組(GPA~GPJ),每組最多可以有 32個,每個GPIO有2~4個可選功能,每組的控制寄存器空間有4個,例如對于GPB,有GPBCON、GPBDAT、GPBUP和Reserved, 分別是功能配置、數據緩存、上拉使能和保留。

上面的S3C2410_GPB5就是GPIO的編號,也就是在號碼空間(0~32*9-1)中的位置,bank是分組的基號碼,offset是組內偏移量。(也就是說把所有的IO口從0開始進行統一的編號如: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的操作函數

在hardware.h文件中有:

s3c2410_gpio_cfgpin???? //配置端口的GPIO的功能
s3c2410_gpio_getcfg???? //讀取功能配置
s3c2410_gpio_pullup???? //配置上拉電阻
s3c2410_modify_misccr //雜項配置

s3c2410_gpio_getirq????? //給定端口,轉換出IRQ號
s3c2410_gpio_irqfilter??? //配置IRQ過濾使能與否

s3c2410_gpio_setpin???? //寫數據到端口
s3c2410_gpio_getpin???? //從端口讀數據

這些函數的實現在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等 //讀出當前GP*DAT寄存器的值 dat &= ~(1 << offs); //根據offs偏移量對該寄存器中選中的 位 清零,其他位保持不變dat |= to << offs; //根據形參對要求的位進行位操作,來實現對具體某個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對應的bit mask相與以后的值,0表示該GPIO對應的bit為0, 非0表示該bit為1,所以s3c2410_gpio_getpin(S3C2410_GPG(9))如果GPG9為低電平則返回的是0,如果是高電平則返回的是GPxDAT中的GPG9對應位的值為0x0100而不是0x0001,查處問題后修改也很簡單了。

?

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作用是:根 據端口編號pin,算出端口所在組的虛擬基址。((pin) & ~31)是去掉pin當中小于等于31的零頭(清0低5位),>>1的原因是每組GPIO中最多可以有32個端口,控制這些端口需要4個寄存 器空間,4個寄存器空間就需要4*4=16個字節進行編址,32/16=2,左移一位剛好滿足。也就是說,上一組端口和下一組端口的編號相差32,而控制 寄存器的地址相差16。

S3C2410_GPIO_OFFSET作用是:根據端口編號pin,算出端口所在組的偏移量。((pin) & 31)即去掉比31大的數(清0第6位以上的位)。 ?

?


?

轉載于:https://www.cnblogs.com/kwseeker-bolgs/p/4415968.html

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的驱动相关的内核函数分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。