linux驅動開發(一)

1:驅動開發環境html

要進行linux驅動開發咱們首先要有linux內核的源碼樹,而且這個linux內核的源碼樹要和開發板中的內核源碼樹要一直;node

好比說咱們開發板中用的是linux kernel內核版本爲2.6.35.7,在咱們ubuntu虛擬機上必需要有一樣版本的源碼樹,linux

咱們再編譯好驅動的的時候,使用modinfo XXX命令會打印出一個版本號,這個版本號是與使用的源碼樹版本有關,若是開發板中源碼樹中版本與shell

modinfo的版本信息不一導致沒法安裝驅動的;編程

咱們開發板必須設置好nfs掛載;這些在根文件系統一章有詳細的介紹;ubuntu

2:開發驅動經常使用的幾個命令api

lsmod :list moduel 把咱們機器上全部的驅動打印出來,數組

insmod:安裝驅動cookie

rmmod:刪除驅動app

modinfo:打印驅動信息

3:寫linux驅動文件和裸機程序有很大的不一樣,雖然都是操做硬件設備,可是因爲寫裸機程序的時候是咱們直接寫代碼操做硬件設備,這隻有一個層次;

而咱們寫驅動程序首先要讓linux內核經過必定的接口對接,而且要在linux內核註冊,應用程序還要經過內核跟應用程序的接口相關api來對接;

 4:驅動的編譯模式是固定的,之後編譯驅動的就是就按照這個模式來套便可,下面咱們來分下一下驅動的編譯規則:

#ubuntu的內核源碼樹,若是要編譯在ubuntu中安裝的模塊就打開這2個 #KERN_VER = $(shell uname -r) #KERN_DIR = /lib/modules/$(KERN_VER)/build # 開發板的linux內核的源碼樹目錄 KERN_DIR = /root/driver/kernel obj-m    += module_test.o all: make -C $(KERN_DIR) M=`pwd` modules cp: cp *.ko /root/porting_x210/rootfs/rootfs/driver_test .PHONY: clean clean: make -C $(KERN_DIR) M=`pwd` modules clean

make -C $(KERN_DIR) M=`PWD` modules

這句話代碼的做用就是到 KERN_DIR這個文件夾中 make modules

把當前目錄賦值給M,M做爲參數傳到主目錄的Makefile中,其實是主目錄的makefile中有目標modules,下面有必定的規則來編譯驅動;

#KERN_VER = $(shell uname -r)

#KERN_DIR = /lib/modules/$(KERN_VER)/build

咱們在ubuntu中編譯內核的時候用這兩句代碼,由於在ubuntu中爲咱們保留了一份linux內核的源碼樹,咱們編譯的時候直接調用那個源碼樹的主Makefile以及一些頭文件、內核函數等;

瞭解規則之後,咱們設置好KERN_DIR、obj-m這兩個變量之後直接make就能夠了;

通過編譯會獲得下面一些文件:

下面咱們可使用

lsmod命令來看一下咱們ubuntu機器現有的一些驅動

能夠看到有不少的驅動,

下面咱們使用

insmod XXX命令來安裝驅動,在使用lsmod命令看一下實驗現象

能夠看到咱們剛纔安裝的驅動放在了第一個位置;

使用modinfo來打印一下驅動信息

 modinfo xxx.ko

這裏注意vermagic 這個的1.8.0-41是你用的linux內核源碼樹的版本號,只有這個編譯的版本號與運行的linux內核版本一致的時候,驅動程序纔會被安裝

 注意license:GPL linux內核開元項目的許可證通常都是GPL這裏儘可能設置爲GPL,不然有些狀況下會出現錯誤;

下面使用

rmmod xxx刪除驅動;

-------------------------------------------------------------------------------------

5:下面咱們分析一下驅動。C文件

#include <linux/module.h>        // module_init module_exit
#include <linux/init.h>            // __init __exit // 模塊安裝函數
static int __init chrdev_init(void) { printk(KERN_INFO "chrdev_init helloworld init\n"); //printk("<7>" "chrdev_init helloworld init\n"); //printk("<7> chrdev_init helloworld init\n");

    return 0; } // 模塊下載函數
static void __exit chrdev_exit(void) { printk(KERN_INFO "chrdev_exit helloworld exit\n"); } module_init(chrdev_init); module_exit(chrdev_exit); // MODULE_xxx這種宏做用是用來添加模塊描述信息
MODULE_LICENSE("GPL");                // 描述模塊的許可證
MODULE_AUTHOR("aston");                // 描述模塊的做者
MODULE_DESCRIPTION("module test");    // 描述模塊的介紹信息
MODULE_ALIAS("alias xxx");            // 描述模塊的別名信息

module_init宏的做用就是把insmod與module_init(XXX)中的XXX綁定起來,insmod命令其實是執行的chrdev_init這個函數;

咱們寫的這個chrdev_init函數只有一條打印信息;

由於內核的printk函數是設置打印級別的,咱們能夠是用dmesg命令來查看打印信息

如上面圖,可見咱們insmod的時候打印了 chrdev_init helloworld init這條信息,這裏正是chrdev_init 這個函數中打印的信息;

由於這裏用到了不少內核函數、宏等,因此要包含這些函數、宏的頭文件

咱們能夠創建內核的man手冊來查詢這些函數;

http://blog.sina.com.cn/s/blog_6642cd020101gtin.html

 下載的版本爲linux-2.6.35.7創建內核man手冊

如今就能夠man printk來查找linux內核函數了,可是這裏注意到,好像仍是沒有相關的頭文件包含

能夠參考,經常使用的內核函數的頭文件包含,

http://blog.csdn.net/guowenyan001/article/details/43342301

最後辦法就是把內核文件在SI中創建鏈接來查找;

下面來看一下__init

#define __init __section(.init.text) __cold notrace

用來定義段屬性的,把Chrdev_init函數定義爲.init.text段,這個段在啓動內核之後會內核會自動釋放掉,以節省內存空間;

採用2.6.35.7源碼樹編譯在開發板上運行;

KERN_DIR = /usr/bhc/kernel/linux-2.6.35.7/

把KERN_DIR目錄變量更改成咱們安裝的內核源碼樹目錄便可

make

注意:必定要把源碼樹目錄中主Makefile中ARCH、cross_compile變量的值更改了;

 開發板使用在zImage 也要是用這個內核來編譯的zImage

在開發板中一樣使用

lsmod

insmod

rmmod

----------------------------------------------------------------------------------------------------------

6:應用層是如何調用驅動的

在應用層進行應用編程的時候咱們對底層設備的操做包括:open close write read 等操做;

如fd = open("/dev/mouse", O_RDWR);

這些操做都是經過內核給定的api接口來實現的,這些接口是經過file_operations這個結構體來實現的

struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, int datasync); int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); int (*setlease)(struct file *, long, struct file_lock **); };

這個結構體中包含了對硬件的全部操做;

結構體中大多都是函數指針,這些函數指針指向真正的設備的操做函數;

寫好硬件真正的讀寫函數之後,在創建一個struct file_operations類型的結構體,把相應的操做對應真正的讀寫函數初始化之後,

咱們還須要把這個結構體向內核初始化,告訴內核,讓內核知道咱們創建了一個驅動,

咱們用register_chrdev這個函數來向內核註冊:

static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
{
return __register_chrdev(major, 0, 256, name, fops);

}

static inline void unregister_chrdev(unsigned int major, const char *name)
{
__unregister_chrdev(major, 0, 256, name);
}

這個函數來註銷驅動

註冊的時候須要一個註冊號major、name、以及創建好的struct file_operations結構體;

內核中有一個結構體數組,這個數組中一共255個元素,咱們寫好的驅動註冊的時候須要一個major主設備號,這個主設備號對應數組的下標,註冊的時候設備命令以及file_operations的指針就放入了這個結構體數組對應的major下標的那個元素中;

註冊之後,內核知道有這個設備了,應用程序才能夠經過內核來調用api來操做這個設備;

應用是如何調用驅動呢?

應用調用驅動是經過驅動設備文件來調用驅動的,咱們首先要用mknod /dev/xxx c 主設備號 次設備號 命令來建立驅動設備文件,

這樣的話應用程序就能夠通/dev/xxx這個設備驅動文件,獲取對應的主設備號,內核在經過這個主設備號找到設備名稱和file_operations這個結構體;

能夠看一下上面這個圖:

應用程序:經過/dev/xxx設備文件來找到這個主設備號,在經過系統api(open、close、read、write),執行對設備的操做;

而在內核中linux內核經過主設備號找到file_operation這個結構圖,在經過這個結構體找到真正的操做設備的函數;

因此咱們寫驅動程序在應用層作的事情就是:

使用mknod命令來創建設備驅動文件; 

找到設備文件,調用api操做設備便可;

在linux內核中要作的事情有:

寫好真正的設備操做函數,創建file_operation結構體,用register_chrdev函數來向內核註冊;

下面咱們對每個步驟作詳細的操做分析:

真正操做硬件設備的函數(以led爲例)

#include <linux/module.h> // module_init module_exit
#include <linux/init.h>    // __init __exit
#include <linux/fs.h>

#define MYMAJOR 200
#define MYNAME    "LED_DEVICE"

 //int (*open) (struct inode *, struct file *); //open函數的格式是上面的格式:

static int led_dev_open(struct inode *inode, struct file *file) {   printk(KERN_INFO "led_dev_open open\n"); } //release函數的原型是:int (*release) (struct inode *, struct file *);

static int led_dev_close(struct inode *inode, struct file *file) {   printk(KERN_INFO "led_dev_close close\n"); } static const struct file_operations led_dev_fops{   .opne = led_dev_open,   .release = led_dev_close, } static int __init leddev_init(void) {   int ret = -1;   printk(KERN_INFO "leddev_init");      ret = register_chrdev(MYMAJOR, MYNAME, &led_dev_fops);   if(ret) {     printk(KERN_ERR "led devices rigister failed");     retunt -EINVAL;   }   printk(KERN_INFO "led regist sucess");   return 0; } static int __exit leddev_exit(void) {   printfk(KERN_INFO "led device exit");   unregister_chrdev(MYMAJOR, NAME); }


module_init(leddev_init);
module_exit(leddev_exit);

 
 

// MODULE_xxx這種宏做用是用來添加模塊描述信息
MODULE_LICENSE("GPL"); // 描述模塊的許可證
MODULE_AUTHOR("bh

c"); // 描述模塊的做者
MODULE_DESCRIPTION("led test"); // 描述模塊的介紹信息
MODULE_ALIAS("alias xxx"); // 描述模塊的別名信息

 
 

 

 

 

這樣咱們就能夠去編譯之後在開發板中使用insmod rmmod等命令來安裝刪除驅動了;

在這裏補充一問題,安裝好驅動之後,主設備號能夠在/proc/devices文件中查看,可是因爲不一樣的設備主設備號佔用的不同,有時候須要系統來自動分配

主設備號,這個如何實現呢:

咱們能夠在register_chrdev函數的major變量傳參0進去,由於這個函數的返回值爲主設備號,因此咱們定義一個全局變量來接受這個值便可

static int mymajor;

//註冊的時候

mymajor = register_chrdev(0, MYNAME, &ded_dev_fops);

//釋放的時候

unregister_chrdev(mymajor, MYNAME);

這樣便可;

-----------------------------------------------------------------------------------------------------------------

 下面介紹api的write read函數:

read函數的函數原型是下面:

ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)

write函數的函數原型:

static ssize_t ab3550_bank_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)

write函數的函數原型是這樣的

這裏注意下:應用層面的內存buf與內核中的buf數據不能直接交換的,不能用memcpy函數複製;

要使用

copy_form_user

copy_to_user兩個函數;

static inline long copy_to_user(void __user *to, const void *from, unsigned long n);

static inline long copy_from_user(void *to, const void __user * from, unsigned long n)

兩個函數的原型爲上:

app程序

#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>

#define FILE "/dev/led_device"

char ubuf[100]; int main(void) { int fd = -1; int ret = -1; //打開led設備
    fd = open(FILE, O_RDWR); if(fd < 0) { printf("/dev/led_device open failed\n"); return -1; } printf("/dev/led_device open success\n"); //寫led設備
    ret = write(fd, "led_blink_bbb", 13); if(ret < 0) { printf("write error\n"); } printf("write success...\n"); //讀led設備
    ret =  read(fd, ubuf, 100); printf("read is %s\n", ubuf); //關閉led設備
 close(fd); }

驅動程序相關代碼

#include <linux/module.h>        // module_init module_exit
#include <linux/init.h>            // __init __exit
#include <linux/fs.h> #include <asm/uaccess.h>

#define MYMAJOR        200
#define MYNAME        "LED_DEVICE"

static char kbuf[100]; static int mymojor; static int led_dev_open(struct inode *inode, struct file *file) { printk(KERN_INFO "led_dev open\n"); return 0; } static int led_dev_release(struct inode *inode, struct file *file) { printk(KERN_INFO "led_dev close\n"); return 0; } ssize_t led_dev_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { int ret = -1; ret = copy_to_user(buf, kbuf, sizeof(kbuf)); if(ret) { printk(KERN_ERR "kernel led read error\n"); } printk(KERN_INFO "led device read success\n"); } static ssize_t led_dev_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { int ret = -1; ret = copy_from_user(kbuf, user_buf, count); if(ret) { printk(KERN_ERR "kernel led write error\n"); return -EINVAL; } printk(KERN_INFO "led device write success\n"); return 0; } static const struct file_operations led_dev_fops = { .open = led_dev_open, .write = led_dev_write, .read = led_dev_read, .release = led_dev_release, .owner = THIS_MODULE, }; // 模塊安裝函數
static int __init leddev_init(void) { printk(KERN_INFO "led_device init\n"); //printk("<7>" "chrdev_init helloworld init\n"); //printk("<7> chrdev_init helloworld init\n"); //在這裏進行註冊驅動,由於安裝驅動實際上執行的就是這個函數;
    mymojor = register_chrdev(0, MYNAME, &led_dev_fops); if(!mymojor) { printk(KERN_ERR "led_device failed\n"); return -EINVAL; } printk(KERN_INFO "leddev_dev regist success\n"); return 0; } // 模塊下載函數
static void __exit leddev_exit(void) { printk(KERN_INFO "leddev_dev exit\n"); //註銷led設備驅動
 unregister_chrdev(mymojor, MYNAME); printk(KERN_INFO "leddev_dev unregist success\n"); } module_init(leddev_init); module_exit(leddev_exit); // MODULE_xxx這種宏做用是用來添加模塊描述信息
MODULE_LICENSE("GPL");                // 描述模塊的許可證
MODULE_AUTHOR("bhc");                // 描述模塊的做者
MODULE_DESCRIPTION("led test");    // 描述模塊的介紹信息
MODULE_ALIAS("alias xxx");            // 描述模塊的別名信息

編譯之後在開發板上測試。。。。。

-----------------------------------------------------------------------------------------------------

下面咱們的低層驅動開始真正的操做硬件了:

在操做硬件的時候,咱們會用到硬件的香瓜寄存器,由於咱們以前在邏輯程序中使用的物理地址來直接寫的,而咱們在開發板上移植好內核之後

咱們的內核程序就是運行在虛擬地址上了,因此咱們要看一下咱們的linux內核中虛擬地址跟物理地址是如何映射的,三星在移植的內核的時候,把硬件相關

的寄存器,創建了三個映射表文件,讓咱們來查找這些物理地址對應的虛擬地址:

首先來看一下靜態虛擬地址映射:

分別爲

arch/arm/plat-samsung/plat/map-base.h

看一下這個文件中的內容:

#define S3C_VA_IRQ    S3C_ADDR(0x00000000)    /* irq controller(s) */
#define S3C_VA_SYS    S3C_ADDR(0x00100000)    /* system control */
#define S3C_VA_MEM    S3C_ADDR(0x00200000)    /* memory control */
#define S3C_VA_TIMER    S3C_ADDR(0x00300000)    /* timer block */
#define S3C_VA_WATCHDOG    S3C_ADDR(0x00400000)    /* watchdog */
#define S3C_VA_OTG    S3C_ADDR(0x00E00000)    /* OTG */
#define S3C_VA_OTGSFR    S3C_ADDR(0x00F00000)    /* OTG PHY */
#define S3C_VA_UART    S3C_ADDR(0x01000000)    /* UART */

由於cpu的相關模塊的寄存器地址都是分塊的,如終端相關寄存器,被分配在一塊兒,如mem內存相關寄存器,map-base.h中把各個模塊先關的虛擬基地址羅列了出來;

arch/arm/plat-s5p/include/plat/map-s5p.h

#define S5P_VA_CHIPID        S3C_ADDR(0x00700000)
#define S5P_VA_GPIO        S3C_ADDR(0x00500000)
#define S5P_VA_SYSTIMER        S3C_ADDR(0x01200000)
#define S5P_VA_SROMC        S3C_ADDR(0x01100000)
#define S5P_VA_AUDSS        S3C_ADDR(0X01600000)

#define S5P_VA_UART0        (S3C_VA_UART + 0x0)
#define S5P_VA_UART1        (S3C_VA_UART + 0x400)
#define S5P_VA_UART2        (S3C_VA_UART + 0x800)
#define S5P_VA_UART3        (S3C_VA_UART + 0xC00)

#define S3C_UART_OFFSET        (0x400)

#define VA_VIC(x)        (S3C_VA_IRQ + ((x) * 0x10000))
#define VA_VIC0            VA_VIC(0)
#define VA_VIC1            VA_VIC(1)
#define VA_VIC2            VA_VIC(2)
#define VA_VIC3            VA_VIC(3)

這個文件中三星工程師把要用獲得的模塊的基地址又細化了,如GPIO UART0-3 SROM VIC中斷,如咱們要添加新的模塊的話,能夠在這個文件中,定義相關模塊寄存器

的基地址;

arch/arm/mach-s5pv210/include/mach/regs-gpio.h

arch/arm/mach-s5pv210/include/mach/gpio-bank.h

能夠看到在gpio-bank.h文件中,定義了每一個寄存器的虛擬地址;咱們在操做相關寄存器的時候直接用這裏定義好的宏就能夠;

如何遇到一個新的開發板如何找虛擬內存映射表呢,通常都是在arch/arm/plat或者 arch/arm/mach 等目錄,通常是文件名都是map-文件;

 下面開始咱們真正的應用程序經過驅動來控制led硬件;

驅動模塊

#include <linux/module.h>        // module_init module_exit
#include <linux/init.h>            // __init __exit
#include <linux/fs.h> #include <asm/uaccess.h> #include <plat/map-base.h> #include <plat/map-s5p.h> #include <mach/regs-gpio.h> #include <mach/gpio-bank.h> #include <linux/string.h>

#define MYMAJOR        200
#define MYNAME        "LED_DEVICE"

#define GPJ0CON        S5PV210_GPJ0CON
#define GPJ0DAT        S5PV210_GPJ0DAT

#define rGPJ0CON    *((volatile unsigned int *)GPJ0CON)
#define rGPJ0DAT    *((volatile unsigned int *)GPJ0DAT)

static char kbuf[100]; static int mymojor; static int led_dev_open(struct inode *inode, struct file *file) { printk(KERN_INFO "led_dev open\n"); return 0; } static int led_dev_release(struct inode *inode, struct file *file) { printk(KERN_INFO "led_dev close\n"); return 0; } ssize_t led_dev_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { int ret = -1; ret = copy_to_user(buf, kbuf, sizeof(kbuf)); if(ret) { printk(KERN_ERR "kernel led read error\n"); } printk(KERN_INFO "led device read success\n"); } static ssize_t led_dev_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { int ret = -1; //首先把kbuf清零
    memset(kbuf, 0, sizeof(kbuf)); ret = copy_from_user(kbuf, user_buf, count); if(ret) { printk(KERN_ERR "kernel led write error\n"); return -EINVAL; } printk(KERN_INFO "led device write success\n"); if (kbuf[0] == '1') { rGPJ0CON = 0x11111111; rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5)); } if (kbuf[0] == '0') { rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5)); } return 0; } static const struct file_operations led_dev_fops = { .open = led_dev_open, .write = led_dev_write, .read = led_dev_read, .release = led_dev_release, .owner = THIS_MODULE, }; // 模塊安裝函數
static int __init leddev_init(void) { printk(KERN_INFO "led_device init\n"); //printk("<7>" "chrdev_init helloworld init\n"); //printk("<7> chrdev_init helloworld init\n"); //在這裏進行註冊驅動,由於安裝驅動實際上執行的就是這個函數;
    mymojor = register_chrdev(0, MYNAME, &led_dev_fops); if(!mymojor) { printk(KERN_ERR "led_device failed\n"); return -EINVAL; } printk(KERN_INFO "leddev_dev regist success\n"); return 0; } // 模塊下載函數
static void __exit leddev_exit(void) { printk(KERN_INFO "leddev_dev exit\n"); //註銷led設備驅動
 unregister_chrdev(mymojor, MYNAME); printk(KERN_INFO "leddev_dev unregist success\n"); } module_init(leddev_init); module_exit(leddev_exit); // MODULE_xxx這種宏做用是用來添加模塊描述信息
MODULE_LICENSE("GPL");                // 描述模塊的許可證
MODULE_AUTHOR("bhc");                // 描述模塊的做者
MODULE_DESCRIPTION("led test");    // 描述模塊的介紹信息
MODULE_ALIAS("alias xxx");            // 描述模塊的別名信息

app文件

#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h>


#define FILE "/dev/led_device"

char ubuf[100]; int main(void) { int fd = -1; int ret = -1; //打開led設備
    fd = open(FILE, O_RDWR); if(fd < 0) { printf("/dev/led_device open failed\n"); return -1; } printf("please input on | off | quit.\n"); while (1) { memset(ubuf, 0, sizeof(ubuf)); scanf("%s", ubuf); if (!strcmp(ubuf, "on")) { write(fd , "1", 1); } if (!strcmp(ubuf, "off")) { write(fd, "0", 1); } if (!strcmp(ubuf, "quit")) { break; } } //讀led設備 //ret = read(fd, ubuf, 100); //printf("read is %s\n", ubuf); //關閉led設備
 close(fd); }

使用動態虛擬地址

動態虛擬地址:當咱們要使用這個寄存器的物理地址的時候,不用事先創建好的頁表,而是給物理地址動態的分配一個虛擬地址,操做的時候直接使用這個動態分配的虛擬地址

操做物理地址便可,使用完之後取消映射便可;

使用動態虛擬地址映射首先:

1:創建映射

使用request_mem_region向內核申請虛擬地址空間;

#define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name), 0)

request_mem_region其實是一個宏,真正調用的是 __request_region這個函數;

request_mem_region宏須要三個參數:start:啓示的物理地址,n長度,name 

申請成功則返回0;

ioremap

 #define ioremap(cookie,size) __arm_ioremap(cookie, size, MT_DEVICE)

 也是一個宏,調用的是內核函數__arm_ioremap

 這個宏須要兩個參數起始物理地址以及 長度;

2:使用完之後咱們首先要消除映射

取消映射iounmap宏

#define iounmap(cookie) __iounmap(cookie)

 接受一個參數,起始物理地址;

而後在消除分配的虛擬地址

使用release_mem_region

 #define release_mem_region(start,n) __release_region(&iomem_resource, (start), (n))

 這個宏只須要兩個參數便可一個是起始物理地址,一個長度;

下面看具體代碼:咱們只修改驅動代碼,應用層代碼不進行修改了。。。

#include <linux/module.h>        // module_init module_exit
#include <linux/init.h>            // __init __exit
#include <linux/fs.h> #include <asm/uaccess.h> #include <plat/map-base.h> #include <plat/map-s5p.h> #include <mach/regs-gpio.h> #include <mach/gpio-bank.h> #include <linux/ioport.h> #include <linux/string.h> #include <asm/io.h>

#define MYMAJOR            200
#define MYNAME            "LED_DEVICE"

#define GPJ0_PA_base        0xE0200240        
#define GPJ0CON_PA_OFFSET    0x0 unsigned int *pGPJ0CON; unsigned int *pGPJ0DAT; static char kbuf[100]; static int mymojor; static int led_dev_open(struct inode *inode, struct file *file) { printk(KERN_INFO "led_dev open\n"); return 0; } static int led_dev_release(struct inode *inode, struct file *file) { printk(KERN_INFO "led_dev close\n"); return 0; } ssize_t led_dev_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { int ret = -1; ret = copy_to_user(buf, kbuf, sizeof(kbuf)); if(ret) { printk(KERN_ERR "kernel led read error\n"); } printk(KERN_INFO "led device read success\n"); } static ssize_t led_dev_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { int ret = -1; //首先把kbuf清零
    memset(kbuf, 0, sizeof(kbuf)); ret = copy_from_user(kbuf, user_buf, count); if(ret) { printk(KERN_ERR "kernel led write error\n"); return -EINVAL; } printk(KERN_INFO "led device write success\n"); if (kbuf[0] == '1') { *pGPJ0CON = 0x11111111; *(pGPJ0CON + 1) = ((0<<3) | (0<<4) | (0<<5)); } if (kbuf[0] == '0') { *(pGPJ0CON + 1) = ((1<<3) | (1<<4) | (1<<5)); } return 0; } static const struct file_operations led_dev_fops = { .open = led_dev_open, .write = led_dev_write, .read = led_dev_read, .release = led_dev_release, .owner = THIS_MODULE, }; // 模塊安裝函數
static int __init leddev_init(void) { printk(KERN_INFO "led_device init\n"); //printk("<7>" "chrdev_init helloworld init\n"); //printk("<7> chrdev_init helloworld init\n"); //在這裏進行註冊驅動,由於安裝驅動實際上執行的就是這個函數;
    mymojor = register_chrdev(0, MYNAME, &led_dev_fops); if(!mymojor) { printk(KERN_ERR "led_device failed\n"); return -EINVAL; } printk(KERN_INFO "leddev_dev regist success\n"); if(!request_mem_region(GPJ0_PA_base + GPJ0CON_PA_OFFSET, 8, "GPJ0PABAST")) { return -EINVAL; } pGPJ0CON = ioremap(GPJ0_PA_base + GPJ0CON_PA_OFFSET, 4); return 0; } // 模塊下載函數
static void __exit leddev_exit(void) { printk(KERN_INFO "leddev_dev exit\n"); //註銷led設備驅動
 unregister_chrdev(mymojor, MYNAME); iounmap(GPJ0_PA_base + GPJ0CON_PA_OFFSET); release_mem_region(GPJ0_PA_base + GPJ0CON_PA_OFFSET, 8); printk(KERN_INFO "leddev_dev unregist success\n"); } module_init(leddev_init); module_exit(leddev_exit); // MODULE_xxx這種宏做用是用來添加模塊描述信息
MODULE_LICENSE("GPL");                // 描述模塊的許可證
MODULE_AUTHOR("bhc");                // 描述模塊的做者
MODULE_DESCRIPTION("led test");    // 描述模塊的介紹信息
MODULE_ALIAS("alias xxx");            // 描述模塊的別名信息
相關文章
相關標籤/搜索