考慮一千次,不如去做一次;猶豫一萬次,不如實踐一次。華麗的跌倒,勝過無謂的徘徊,邁出第一步,你就成功了一半。
一、完善地址映射
在上一節,做IO地址映射時,爲求代碼精簡易於理解,沒有加入取消映射,這一節就來繼續完善LED驅動程序。
修改led_opr.h
結構體,單獨加入地址映射和取消映射函數。
#ifndef _LED_OPR_H
#define _LED_OPR_H
struct led_operations {
int num; /* num-LED數量 */
int (*remap) (void); /* LED寄存器地址映射 */
int (*init) (int which); /* 初始化LED, which-哪個LED */
int (*ctl) (int which, char status); /* 控制LED, which-哪個LED, status:1-亮,0-滅 */
int (*unmap) (void); /* LED寄存器地址取消映射 */
};
struct led_operations *get_board_led_opr(void);
#endif
接着在board_qemu.c
中,初始化映射和取消映射函數,別忘了在board_qemu_led_init
刪除IO映射那一部分內容!
...
static int board_qemu_led_remap(void)
{
CCM_CCGR1 = ioremap(0x20C406C, 4);
IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = ioremap(0x2290014, 4);
iomux = ioremap(0x20E0000, sizeof(struct iomux));
gpio1 = ioremap(0x209C000, sizeof(struct imx6ull_gpio));
gpio5 = ioremap(0x20AC000, sizeof(struct imx6ull_gpio));
return 0;
}
...
static int board_qemu_led_unmap(void)
{
iounmap(CCM_CCGR1);
iounmap(IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3);
iounmap(iomux);
iounmap(gpio1);
iounmap(gpio5);
return 0;
}
static struct led_operations board_qemu_led_opr = {
.num = 4,
.remap = board_qemu_led_remap,
.init = board_qemu_led_init,
.ctl = board_qemu_led_ctl,
.unmap = board_qemu_led_unmap,
};
修改LED驅動leddrv.c
,在open和close時單獨進行調用映射和取消映射!
static int led_drv_open (struct inode *node, struct file *file)
{
int minor = iminor(node);
/* LED寄存器映射 */
p_led_opr->remap();
/* 根據次設備號初始化LED */
p_led_opr->init(minor);
return 0;
}
static int led_drv_close (struct inode *node, struct file *file)
{
/* LED取消寄存器映射 */
p_led_opr->unmap();
return 0;
}
其他內容基本一致。
二、完善虛擬地址讀寫
還有要說的,使用 ioremap 函數將寄存器的物理地址映射到虛擬地址以後,我們就可以直接通過指針訪問這些地址,但是 Linux 內核不建議這麼做,而是推薦使用一組操作函數來對映射後的內存進行讀寫操作。而上面的程序我們也並沒有這樣做,原因還是這樣做了之後程序顯得更加臃腫了,不易於理解掌握,此處僅作介紹。
- 讀操作函數
- u8 readb(const volatile void __iomem *addr)
- u16 readw(const volatile void __iomem *addr)
- u32 readl(const volatile void __iomem *addr)
- readb、 readw 和 readl 這三個函數分別對應 8bit、 16bit 和 32bit 讀操作,參數 addr 就是要讀取寫內存地址,返回值就是讀取到的數據。
- 寫操作函數
- void writeb(u8 value, volatile void __iomem *addr)
- void writew(u16 value, volatile void __iomem *addr)
- void writel(u32 value, volatile void __iomem *addr)
- writeb、 writew 和 writel 這三個函數分別對應 8bit、 16bit 和 32bit 寫操作,參數 value 是要寫入的數值, addr 是要寫入的地址。