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

时间:2022-04-21 17:51:07

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;
}