Linux下/dev/mem和/dev/kmem及操作gpio實例

轉載地址:http://blog.csdn.net/williamwanglei/article/details/8636564

Linux下/dev/mem和/dev/kmem的區別:

/dev/mem: 物理內存的全鏡像。可以用來訪問物理內存。

/dev/kmem: kernel看到的虛擬內存的全鏡像。可以用來訪問kernel的內容。

作用:

前者用來訪問物理IO設備,比如X用來訪問顯卡的物理內存,或嵌入式中訪問GPIO。用法一般就是open,然後mmap,接着可以使用map之後的地址來訪問物理內存。這其實就是實現用戶空間驅動的一種方法。

後者一般可以用來查看kernel的變量,或者用作rootkit之類的。參考1和2描述了用來查看kernel變量這個問題。



 Linux 下操作gpio: 

   對於在不支持虛擬內存的操作系統和根本就沒有使用操作系統的系統裏操作GPIO直接讀寫對應的GPIO寄存器就可以啦,但是在linux這樣的操作系統下,內核層和應用層都是處於虛擬地址中,而GPIO的寄存器都是處於物理地址中,你必須編寫一個操作GPIO的驅動,或者是使用一些變通的技巧來操作GPIO.

   目前我所知道的在linux下操作GPIO有兩種方法: 

1.  編寫驅動,這當然要熟悉linux下驅動的編寫方法和技巧,在驅動裏都是虛擬地址,可以使用ioremap函數獲得GPIO物理基地址指針,然後使用這個指針根據ioctl命令進行GPIO寄存器的讀寫,並把結果回送到應用層。這裏提供一點程序片斷供大家參考:

    int  init_module(void){ 

        printk(KERN_ALERT "ioctl load.\r\n"); 

    register_chrdev(254,"ioreg",&fops); 

        stb_gpio = (STBX25XX_GPIO_REG *)ioremap(GPIO_BASE,GPIO_LEN); 

    if(stb_gpio == NULL){ 

            printk(KERN_ALERT "can''t get io base.\r\n"); 

        return -1; 

        } 

    return 0; 



int io_ioctl (struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg){

    unsigned long uGpio; 

    printk(KERN_ALERT "io_ioctl cmd=%04x,arg=%04x.\r\n",cmd,(int)arg); 

    switch(cmd){ 

        case SET_IO_DIR:{ 

             printk(KERN_ALERT "SET_IO_DIR\r\n"); 

             break; 

        } 

        case SET_IO_VALUE:{ 

             printk(KERN_ALERT "SET_IO_VALUE\r\n"); 

             break; 

        } 

        case GET_IO_VALUE:{ 

             printk(KERN_ALERT "GET_IO_VALUE\r\n"); 

             uGpio = stb_gpio->GPI; 

             printk(KERN_ALERT "GPIO = %08x",(int)uGpio); 

             copy_to_user((void *)arg,(const void *) &uGpio,sizeof(uGpio)); 

             break; 

        } 

        case GET_IO_DIR:{ 

             printk(KERN_ALERT "GET_IO_DIR\r\n"); 

             break; 

        } 

    } 

    return 0; 



  

2.  在應用層使用mmap函數在應用層獲得GPIO物理基地址對應的虛擬地址指針(應用層中),讓用戶程序直接訪問設備內存,然後使用這個指針來讀寫GPIO寄存器,這裏提供一點程序片斷供大家參考:

/dev/mem是物理內存的全映像,可以用來訪問物理內存,一般用法是open("/dev/mem",O_RDWR|O_SYNC),然後mmap,接着就可以用mmap的地址來訪問物理內存,這實際上就是實現用戶空間驅動的一種方法。

/dev/kmem:內核看到的虛擬內存的全映像,可以用來訪問kernel的內容。

 

內核空間用1G虛擬地址,用戶空間用3G虛擬地址
所以ioremap當然不能分出1G地址供你用(ioreamp的空間大小是有限制的)
一個物理地址,內核調用 ioremap得到一個1G內的虛擬地址,用來操作物理內存
             應用層調用 mmap 得到一個3G內的虛擬地址,用來操作物理內存


char dev_name[] = "/dev/mem"; 

        GPIO_REGISTER  *gpio_base; 

        fd  =  open(dev_name,O_RDWR); 

        if(fd<0){ 

               printf("open %s is error\n",dev_name); 

               return -1 ; 

        } 

        gpio_base = (GPIO_REGISTER *)mmap( 0, 0x32, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0x40060000 );

        if(gpio_base == NULL){ 

                printf("gpio base mmap is error\n"); 

                close(fd); 

                return -1; 

        } 

        gpio_base->or  = (gpio_base->or & 0x7fffffff); 

完整的程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <string.h>
#include <time.h>

#define TIME_OUT    5      /* the timeout time, in seconds */
/**
 * Define behaviour's when the button is pressed enough time.
 */
void longtu_timeout(){
    printf("****  Pressed 5 seconds, Call RECOVERY! ****\n");
    //system("/sbin/recover_longtu.sh");
    //system("/sbin/reboot");
}

/*
 * The main function.
 */
int main(int argc, char *argv[])
{
    int mfd;
    unsigned int val=0, last_val;
    void *base;
    char *sys_pinstaterd;
    time_t t_now, t_old;
    int flag_issued = 0;
#if 0
    // uncomment these to make the program a daemon.

    pid_t pid;
        int i;
        if ( (pid=fork())<0)
                return -1;
        else if (pid!=0)
                exit(0);
        setsid();
        chdir("/");
        umask(0);
        for (i=0;i<256;i++) 
                close(i);
#endif

    // open the memery mapped file.
    mfd=open("/dev/mem", O_RDWR);
    if (mfd < 0){
        printf("Cannot open /dev/mem.\n");
        exit(-1);
    }

    // Initialize the map
    base = mmap( NULL, 0x130,  PROT_READ | PROT_WRITE, MAP_SHARED, mfd, 0x1fe00000);
    if ( base < 0){
        exit(-1);
    }

    // Got the pointer to SYS_PINSTATERD register of GS32I CPU.
    
sys_pinstaterd = base + 0x011c;

    // init the temperay variables
    t_now=t_old=time(NULL);
    last_val = 0;
    while(1)
    {
        // Get status of GPIO7 pin.
        val = *( (volatile unsigned int*)sys_pinstaterd );
        val = (val&0x4) ? 1:0;
        printf("\tgpio 7 stat=%x.\n", val);
        if (val){
            // the button is pressed down !!
            if ( last_val==0 ){
                // starting time of press, log the time
                t_old = time(NULL);
                last_val=1;
                printf("Button Down\n");
            }else {
                // already pressed down! let's count the time!
                t_now = time(NULL);
                if (t_now-t_old>=TIME_OUT && flag_issued==0){
                    // Pressed LONG ENOUGH, issue the handler script!!
                    flag_issued = 1;
                    longtu_timeout();
                }
            }
        }else{
            // No button pressed.
            if (flag_issued){
                flag_issued = 0;
                printf("Button UP.\n");
            }
        }
        
        last_val = val;
        usleep(100);
    }

    munmap(base, 0x000);
    close(mfd);    
    return 0;
}



發佈了0 篇原創文章 · 獲贊 5 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章