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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux wc -l 对io,linux设备驱动归纳总结(五):2.操作硬件——IO内存

發布時間:2025/4/5 linux 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux wc -l 对io,linux设备驱动归纳总结(五):2.操作硬件——IO内存 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

linux設備驅動歸納總結(五):2.操作硬件——IO內存

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

在之前章節的驅動,都沒有對硬件進行操作,接寫來將從我之前學的裸板驅動開始,講解在linux系統下如何訪問硬件。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

一、IO端口與IO內存

x86體系和ARM體系的尋址方式是有差別的:

在x86下,為了能夠滿足CPU高速地運行,內存與CPU之間通過北橋相連并通過地址方式訪問,而外設通過南橋與CPU相連并通過端口訪問。

在ARM下也實現了類似的操作,通過兩條不同的總線(AHB

BUS和APB

BUS)來連接不同訪問速度的外設。但是它與x86不同,無論是內存還是外設,ARM都是通過地址訪問。

因為這兩種訪問方式的不同,linux分出了兩種不同的訪問操作:

以地址方式訪問硬件——使用IO內存操作。

以端口方式訪問硬件——使用IO端口操作。

在ARM下,訪問寄存器就像訪問內存一樣——從指定的寄存器地址獲取數據,修改。所以,ARM下一般是使用IO內存的操作。但這并不是說IO端口的操作在ARM下不能用,它們的代碼差不多,只是沒有使用的必要,下面也將介紹IO內存操作。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

二、如何使用IO內存獲得硬件的地址

之前已經說過,不能在linux使用實際的物理地址,要對指定的物理地址進行操作,必須要先將物理地址與虛擬地址對應,通過虛擬地址訪問。于是有了以下的物理地址映射函數:

#include

void

*ioremap(unsigned long phys_addr, unsigned long size);

其實這也是上一節介紹的內存分配的一種方式,它同樣會建立新頁表來管理虛擬地址。函數傳入兩個參數,需要訪問的物理內存(寄存器)的首地址phys_addr和這段內存區域的大小size,返回與該段物理地址對應的虛擬地址。這段地址可以多次被映射,當然,每次映射的虛擬地址也不一樣。

對應的也有撤銷映射關系的函數:

void

ioumap(void *addr);

接下來,我將會從一個裸板的ARMled驅動開始,講解linux下的操作和裸板有什么不一樣。

我的ARM裸板程序是在linux下編寫的,我不知道這跟win下使用ADS有什么區別,在裸板驅動中,一般我是通過這樣的辦法來操作寄存器的:

首先,先給個地址定義個容易記的名字:

#define

GPECON*(volatile unsigned long *) 0x56000040

接著,我就要操作這個GPECON寄存器了:

*GPECON

&= ~(3 << 24);//將24和25位清零

*GPECON

|= (1 << 24);//將24和25位分別賦值為1、0

可以看到,操作寄存器其實就是拿個地址出來進行操作。其實在linux下也是一樣,只是操作的時候不能使用物理地址,需要用映射出來的虛擬地址。

上個函數,這個程序我將要點亮連在我開發板上的led燈,這個燈接在我開發板的GPE12上,如果需要下載程序運行,需要改一下接口。

/*5th_mm_2/1st/test.c*/

1

#include

2

#include

3

4

#include //上面介紹的函數需要包含該頭文件

5

6

volatile unsigned long virt, phys;

//用于存放虛擬地址和物理地址

7

volatile unsigned long *GPECON, *GPEDAT, *GPEUP;

//用與存放三個寄存器的地址

8

9

void led_device_init(void)

10

{

11

phys = 0x56000000;

//1、指定物理地址

12

virt = (unsigned long)ioremap(phys,

0x0c);

//2、通過ioremap獲得對應的虛擬地址

13

//0x0c表示只要12字節的大小

14

GPECON = (unsigned long *)(virt + 0x40);

//3、指定需要操作的三個寄存器的地址

15

GPEDAT = (unsigned long *)(virt + 0x44);

16

GPEUP = (unsigned long *)(virt + 0x48);

17

}

18

19

void led_configure(void)

//led配置函數

20

{

21

*GPECON &= ~(3 << 24);

//配置GPE12為輸出端口

22

*GPECON |= (1 << 24);

//先清零再賦值

23

24

*GPEUP |= (1 << 12);

//禁止上拉電阻

25

}

26

27

void led_on(void)

//點亮led

28

{

29

*GPEDAT &= ~(1 << 12);

30

}

31

32

void led_off(void)

//滅掉led

33

{

34

*GPEDAT |= (1 << 12);

35

}

36

37

static int __init test_init(void) //模塊初始化函數

38

{

39

led_device_init();

40

led_configure();

41

led_on();

42

printk("hello led!\n");

43

return 0;

44

}

45

46

static void __exit test_exit(void) //模塊卸載函數

47

{

48

led_off();

49

iounmap((void *)virt);

//注意,即使取消了映射,通過之前的虛擬地址還能訪問硬件,

50

printk("bye\n");//但不是肯定可以,只要該虛擬地址被內核改動后就不行了。

51

}

52

53

module_init(test_init);

54

module_exit(test_exit);

55

56

MODULE_LICENSE("GPL");

57

MODULE_AUTHOR("xoao bai");

58

MODULE_VERSION("v0.1");

從上面的程序可以看到,除了獲得地址有點和裸板驅動不一樣外,寄存器的操作還是一樣的。

接下來驗證一下:

[root:

1st]# insmod test.ko

hello

led!

//這時候燈亮了

[root:

1st]# rmmod test

bye

//燈滅了

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

三、改進函數,使用更好的內存訪問接口

為了實現更好的移植性,上面的程序就有缺陷了。內核建議,盡量使用內核提供的內存訪問接口:

#include

//從內存讀取數據,返回值是指定內存地址中的值

unsigned

int ioread8(void *addr)

unsigned

int ioread16(void *addr)

unsigned

int ioread32(void *addr)

//往指定內存地址寫入數據

void

iowrite8(u8 value, void *addr)

void

iowrite16(u16 value, void *addr)

void

iowrite32(u32 value, void *addr)

一般常用的是32位內存存取接口。

接下來就改進一下函數,其實實質沒有改變,上面的函數是根據對應的平臺體系結構編寫的,這樣可以提高驅動的移植性。

/*5th_mm_2/1st/test.c*/

1

#include

2

#include

3

4

#include

5

#include

6

7

volatile unsigned long virt, phys;

8

volatile unsigned long *GPECON, *GPEDAT, *GPEUP;

9

unsigned long reg;

10

11

void led_device_init(void)

12

{

13

phys = 0x56000000;

14

virt = (unsigned long)ioremap(phys,SZ_16);

//這里只是想介紹一下,在asm/sizes.h中有一下

15

//定義好用來表示內存大小的宏,這里其實我只

16

GPECON = (unsigned long *)(virt + 0x40);

//需要12個字節,并不需要16個字節。

17

GPEDAT = (unsigned long *)(virt + 0x44);

18

GPEUP = (unsigned long *)(virt + 0x48);

19

}

20

21

void led_configure(void)

22

{

23

//*GPECON &= ~(3 << 24);

24

//*GPECON |= (1 << 24);

25

reg = ioread32(GPECON);

26

reg &= ~(3 << 24);

27

reg |= (1 << 24);

28

iowrite32(reg, GPECON);

29

30

//*GPEUP |= (1 << 12);

31

reg = ioread32(GPEUP);

32

reg &= ~(3 << 12);

33

iowrite32(reg, GPEUP);

34

}

35

36

void led_on(void)

37

{

38

//*GPEDAT &= ~(1 << 12);

39

reg = ioread32(GPEDAT);

40

reg &= ~(1 << 12);

41

iowrite32(reg, GPEDAT);

42

}

43

44

void led_off(void)

45

{

46

//*GPEDAT |= (1 << 12);

47

reg = ioread32(GPEDAT);

48

reg |= (1 << 12);

49

iowrite32(reg, GPEDAT);

50

}

51

52

static int __init test_init(void) //模塊初始化函數

53

{

54

led_device_init();

55

led_configure();

56

led_on();

57

printk("hello led!\n");

58

return 0;

59

}

60

61

static void __exit test_exit(void) //模塊卸載函數

62

{

63

led_off();

64

iounmap((void *)virt);

65

printk("bye\n");

66

}

67

68

module_init(test_init);

69

module_exit(test_exit);

70

71

MODULE_LICENSE("GPL");

72

MODULE_AUTHOR("xoao bai");

73

MODULE_VERSION("v0.1");

會發現發現,程序將原來直接訪問內存的一句話變成了3句話,其他都沒有改變。

我就不驗證了,效果其實是一樣的。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

四、再改進一下程序:

在使用IO內存映射操作之前,其實還可以添加一個步驟:分配內存區域。

#include

struct

resource *request_mem_region(unsigned long start, unsinged long len,

char *name)

該函數從start開始分配len字節長的內存空間。如果成功,返回一個結構體指針,但這結構體我們沒必要用,如果失敗返回NULL。成功后,可以在.proc/iomem查看到name的信息。

其實調用request_mem_region()不是必須的,但是建議使用。該函數的任務是檢查申請的資源是否可用,如果可用則申請成功,并標志為已經使用,其他驅動想再申請該資源時就會失敗。

如果不再使用,需要調用釋放函數:

void

release_mem_region(unsigned long start, unsigned long len)

現在把這兩個函數加上去:

/*5th_mm_2/3rd/test.c*/

1

#include

2

#include

3

4

#include

5

#include

6

7

volatile unsigned long virt, phys;

8

volatile unsigned long *GPECON, *GPEDAT, *GPEUP;

9

unsigned long reg;

10

struct resource *led_resource;

11

12

void led_device_init(void)

13

{

14

phys = 0x56000000;

15

virt = (unsigned long)ioremap(phys, 0x0c);

16

17

GPECON = (unsigned long *)(virt + 0x40);

18

GPEDAT = (unsigned long *)(virt + 0x44);

19

GPEUP = (unsigned long *)(virt + 0x48);

20

}

21

22

void led_configure(void)

23

{

24

reg = ioread32(GPECON);

25

reg &= ~(3 << 24);

26

reg |= (1 << 24);

27

iowrite32(reg, GPECON);

28

29

reg = ioread32(GPEUP);

30

reg &= ~(3 << 12);

31

iowrite32(reg, GPEUP);

32

}

33

34

void led_on(void)

35

{

36

reg = ioread32(GPEDAT);

37

reg &= ~(1 << 12);

38

iowrite32(reg, GPEDAT);

39

}

40

41

void led_off(void)

42

{

43

reg = ioread32(GPEDAT);

44

reg |= (1 << 12);

45

iowrite32(reg, GPEDAT);

46

}

47

48

static int __init test_init(void) //模塊初始化函數

49

{

50

led_device_init();

51

52

led_resource = request_mem_region(phys, 0x0c, "LED_MEM");

53

if(NULL == led_resource){

54

printk("request mem error!\n");

55

return - ENOMEM;

56

}

57

58

led_configure();

59

led_on();

60

printk("hello led!\n");

61

return 0;

62

}

63

64

static void __exit test_exit(void) //模塊卸載函數

65

{

66if(NULL

!= led_resource){

67

led_off();

68

iounmap((void *)virt);

69release_mem_region(phys, 0x0c);

70

}

71

printk("bye\n");

72

}

73

74

module_init(test_init);

75

module_exit(test_exit);

76

77

MODULE_LICENSE("GPL");

78

MODULE_AUTHOR("xoao bai");

79

MODULE_VERSION("v0.1");

寫完就得驗證一下:

[root:

3rd]# insmod test.ko

hello

led!

//燈亮了

[root:

3rd]# cat /proc/iomem

19000300-19000310

: cs8900

19000300-19000310

: cs8900

。。。。

56000000-5600000b

: LED_MEM

//看到了

57000000-570000ff

: s3c2410-rtc

57000000-570000ff

: s3c2410-rtc

5a000000-5a0fffff

: s3c2440-sdi

[root:

3rd]# rmmod test

bye

//燈滅了

[root:

3rd]# cat /proc/iomem

//LED_MEM不見了

19000300-19000310

: cs8900

19000300-19000310

: cs8900

。。。。。。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

五、總結

今天介紹的內容不多,其實就幾個函數,下面重溫一下使用IO內存的步驟:

其中第一步和最后一步可以不做。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

總結

以上是生活随笔為你收集整理的linux wc -l 对io,linux设备驱动归纳总结(五):2.操作硬件——IO内存的全部內容,希望文章能夠幫你解決所遇到的問題。

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