【嵌入式Linux驅動開發】四、LED驅動完善 - 加入iounmap釋放虛擬地址

  考慮一千次,不如去做一次;猶豫一萬次,不如實踐一次。華麗的跌倒,勝過無謂的徘徊,邁出第一步,你就成功了一半。


一、完善地址映射

在上一節,做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 是要寫入的地址。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章