Linux kernel 如何从内核空间驱动程序访问--i/o端口

Linux kernel 如何从内核空间驱动程序访问--i/o端口,linux-kernel,linux-device-driver,Linux Kernel,Linux Device Driver,我可以使用mmap系统调用/dev/mem #define BCM2708_PERI_BASE 0x20000000 #define GPIO_PADS (BCM2708_PERI_BASE + 0x100000) #define CLOCK_BASE (BCM2708_PERI_BASE + 0x101000) #define GPIO_BASE (BCM2708_PERI_BASE + 0x2000

我可以使用
mmap
系统调用
/dev/mem

#define BCM2708_PERI_BASE       0x20000000
#define GPIO_PADS               (BCM2708_PERI_BASE + 0x100000)
#define CLOCK_BASE              (BCM2708_PERI_BASE + 0x101000)
#define GPIO_BASE               (BCM2708_PERI_BASE + 0x200000)
#define GPIO_PWM                (BCM2708_PERI_BASE + 0x20C000)


#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <stdint.h>

static volatile uint32_t *gpio;

int main(int argc, char **argv)
{
        int fd ;

        //Obtain handle to physical memory
        if ((fd = open ("/dev/mem", O_RDWR | O_SYNC) ) < 0) {
                printf("Unable to open /dev/mem: %s\n", strerror(errno));
                return -1;
        }


        //map a page of memory to gpio at offset 0x20200000 which is where GPIO goodnessstarts
        gpio = (uint32_t *)mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0x20200000);

        if ((int32_t)gpio < 0){
                printf("Mmap failed: %s\n", strerror(errno));
                return -1;
        }

        //set gpio17 as an output
        //increment the pointer to 0x20200004
        //set the value through a little bit twiddling where we only modify the bits 21-23 in the register
        *(gpio + 1) = (*(gpio + 1) & ~(7 << 21)) | (1 << 21);

        //toggle gpio17 every second
        while(1){
                //set the pin high
                //increment the pointer to 0x2020001C
                *(gpio + 7) = 1 << 17;

                //sleep
                sleep(1);

                //set the pin to low
                //increment the pointer to 0x20200028
                *(gpio + 10) = 1 << 17;

                sleep(1);
        }
}
#定义BCM2708_PERI_基础0x20000000
#定义GPIO_焊盘(BCM2708_PERI_基座+0x100000)
#定义时钟基准(BCM2708时钟基准+0x101000)
#定义GPIO_基地(BCM2708_PERI_基地+0x200000)
#定义GPIO_PWM(BCM2708_PERI_BASE+0x20C000)
#包括
#包括
#包括
#包括
#包括
#包括
#包括
静态易失性uint32_t*gpio;
int main(int argc,字符**argv)
{
int-fd;
//获取物理内存的句柄
如果((fd=open(“/dev/mem”,O|RDWR | O|u SYNC))<0){
printf(“无法打开/dev/mem:%s\n”,strerror(errno));
返回-1;
}
//将内存页映射到偏移量0x20200000处的gpio,这是gpio开始的位置
gpio=(uint32_t*)mmap(0,getpagesize(),PROT_READ | PROT_WRITE,MAP_SHARED,fd,0x20200000);
如果((int32_t)gpio<0){
printf(“Mmap失败:%s\n”,strerror(errno));
返回-1;
}
//将gpio17设置为输出
//将指针递增到0x20200004
//通过稍微旋转一点设置值,我们只修改寄存器中的位21-23

*(gpio+1)=(*(gpio+1)和~(7ex:GPIO模块的每个模块都有自己的内存映射,即处理器技术参考手册中指定的物理地址。首先,您需要检查内存区域是否正在使用
检查内存区域
。如果可以使用
请求内存区域
自由请求访问此内存区域,则映射G使用
ioremap
ioremap\u nocache
的PIO模块(将总线内存映射到CPU空间),返回一个空指针。返回的地址不能保证直接用作虚拟地址;它只能由
ioread*| iowrite*| read*| write*
等函数使用。使用
ioread8 | 16 | 32/iowrite8 | 16 | 32
函数从/向i/o端口读取或写入。最后,需要
iounmap
来取消内存映射然后您需要使用
release\u mem\u region
释放内存区域。通常在内核空间中,大多数时候不需要检查和请求内存区域。您只需要使用
ioremap
ioremap\u nocache
将总线内存映射到CPU空间,并使用
iounmap
取消映射>ex:GPIO模块的每个模块都有自己的内存映射,即处理器的《技术参考手册》中指定的物理地址。首先,您需要检查内存区域是否正在使用
check_mem_region
。如果使用
request_mem_region
可以自由请求访问此内存区域,则映射GPIO m使用
ioremap
ioremap\u nocache
的模块(将总线内存映射到CPU空间),返回一个空指针。返回的地址不能保证直接用作虚拟地址;它只能由
ioread*| iowrite*| read*| write*
等函数使用。使用
ioread8 | 16 | 32/iowrite8 | 16 | 32
函数从/向i/o端口读取或写入。最后,需要
iounmap
来取消内存映射然后您需要使用
release\u mem\u region
释放内存区域。通常在内核空间中,大多数时间不需要检查和请求内存区域。您只需要使用
ioremap
ioremap\u nocache
将总线内存映射到CPU空间,并使用
iounmap
取消映射e这个问题:请看这个问题:……但是为什么在我的AT91RM9200板中,我只是使用宏访问控制器的GPIO端口。这些实现是否隐藏在头文件中?是的,例如“模拟设备”中的“Blackfin ADSPBF512”还可以使用宏,宏依次调用包装器函数,它在内部调用实际的i/o端口访问函数,如上所述。访问i/o端口函数的核心实现代码是隐藏的(可以到达实际的i/o端口访问函数,需要深入了解GPIO驱动程序)。虽然您不需要请求内存区域,但我坚信您的驱动程序应该请求内存区域。与SMP一样,先使用
check\u mem\u region
检查内存区域是不可取的,在您检查内存区域和请求内存区域之间,始终可能有另一个模块占用了内存。只需始终查看
request\u mem\u region
@Benjamin的返回值,谢谢。试图进行深入解释,所以谈到了check\u mem\u region。只需检查request\u mem\u region返回值,然后继续映射并访问i/o端口……但为什么我的AT91RM9200板来自ATmel,我只是使用宏访问GPIO端口控制器的。这些实现是否隐藏在头文件中?是的,例如,“模拟设备”中的“Blackfin ADSPBF512”也使用宏,宏反过来调用包装器函数,它在内部调用实际的i/o端口访问函数,如上所述。访问i/o端口函数的核心实现代码是隐藏的(可登陆至实际i/o端口访问功能,需要深入GPIO驱动程序)。虽然您不需要请求内存区域,但我坚信您的驱动程序应该请求内存区域。与SMP一样,先使用
check\u mem\u region
检查内存区域是不可取的,在您检查内存区域和请求内存区域之间,始终可能有另一个模块占用了内存。只需始终查看
request\u mem\u region
@Benjamin的返回值即可,谢谢。尝试进行深入解释,所以谈到了check\u mem\u region。只需检查request\u mem\u region返回值,然后继续映射并访问i/o端口。