首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在Linux内核上执行mmap

在Linux内核上执行mmap
EN

Stack Overflow用户
提问于 2021-10-03 13:59:40
回答 1查看 141关注 0票数 1

我试着在树莓pi上启用pull-up,更简单的方法是执行raspi-gpio set <gpio> <pu/pd>,问题是出于某种原因,我不能用call_usermodehelper来做这件事(它不会抛出任何错误,但它什么也不做)。

作为另一种选择,我一直在查看raspi-gpio源代码,我有一个支持下拉的函数式C代码(此代码打印GPIO CPU并启用GPIO26的下拉):

代码语言:javascript
运行
复制
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <time.h>

// From https://github.com/RPi-Distro/raspi-gpio/blob/master/raspi-gpio.c

#define PULL_UNSET  -1
#define PULL_NONE    0
#define PULL_DOWN    1
#define PULL_UP      2

#define GPIO_BASE_OFFSET 0x00200000
#define GPPUD        37
#define GPPUDCLK0    38

uint32_t getGpioRegBase(void) {
    const char *revision_file = "/proc/device-tree/system/linux,revision";
    uint8_t revision[4] = { 0 };
    uint32_t cpu = 0;
    FILE *fd;

    if ((fd = fopen(revision_file, "rb")) == NULL)
    {
        printf("Can't open '%s'\n", revision_file);
    }
    else
    {
        if (fread(revision, 1, sizeof(revision), fd) == 4)
            cpu = (revision[2] >> 4) & 0xf;
        else
            printf("Revision data too short\n");

        fclose(fd);
    }

    printf("CPU: %d\n", cpu);
    switch (cpu) {
        case 0: // BCM2835 [Pi 1 A; Pi 1 B; Pi 1 B+; Pi Zero; Pi Zero W]
            return 0x20000000 + GPIO_BASE_OFFSET;
        case 1: // BCM2836 [Pi 2 B]
        case 2: // BCM2837 [Pi 3 B; Pi 3 B+; Pi 3 A+]
            return 0x3f000000 + GPIO_BASE_OFFSET;
        case 3: // BCM2711 [Pi 4 B]
            return 0xfe000000 + GPIO_BASE_OFFSET;
        default:
            printf("Unrecognised revision code\n");
            exit(1);
    }
}

volatile uint32_t *getBase(uint32_t reg_base) {
    int fd;
    if ((fd = open ("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC) ) < 0) return NULL;
    return (uint32_t *)mmap(0, /*chip->reg_size*/ 0x1000,
                                  PROT_READ|PROT_WRITE, MAP_SHARED,
                                  fd, reg_base);
}

void setPull(volatile uint32_t *base, unsigned int gpio, int pull) {
    int clkreg = GPPUDCLK0 + (gpio / 32);
    int clkbit = 1 << (gpio % 32);

    base[GPPUD] = pull;
    usleep(10);
    base[clkreg] = clkbit;
    usleep(10);
    base[GPPUD] = 0;
    usleep(10);
    base[clkreg] = 0;
    usleep(10);
}

int main() {
    uint32_t reg_base = getGpioRegBase();
    volatile uint32_t *base = getBase(reg_base);
    if (base == NULL || base == (uint32_t *)-1) {
    printf("Base error");
        return 1;
    }
    printf("Base: %p\n", base);
    setPull(base, 26, PULL_UP);
    return 0;
}

现在,很明显,我需要将该代码转换为内核代码。我在延迟和文件方面做得很好,但我不知道如何处理mmap (我以前从未见过它,我也不知道它到底是做什么的,我只知道它映射内存)。

代码语言:javascript
运行
复制
#include <linux/types.h>    // uint_32
#include <linux/fs.h>       // filp_open/filp_close
#include <linux/delay.h>    // delay

#define PULL_DOWN    1
#define PULL_UP      2

#define GPIO_BASE_OFFSET 0x00200000
#define GPPUD        37
#define GPPUDCLK0    38

static uint32_t getGpioRegBase(bool *error) {
    uint8_t revision[4] = { 0 };
    uint32_t cpu = 0;
    struct file *fd;
    ssize_t rc = 0;

    if (IS_ERR(( fd = filp_open("/proc/device-tree/system/linux,revision", O_RDONLY | O_SYNC | O_CLOEXEC, 0) ))) {
        *error = true;
        return 0;
    }
    
    if ((rc = kernel_read(fd, revision, sizeof(revision), 0)) == 4) cpu = (revision[2] >> 4) & 0xf;
    else {
        *error = true;
        return 0;
    }

    filp_close(fd, NULL);

    *error = false;
    switch (cpu) {
        case 0: // BCM2835 [Pi 1 A; Pi 1 B; Pi 1 B+; Pi Zero; Pi Zero W]
            return 0x20000000 + GPIO_BASE_OFFSET;
        case 1: // BCM2836 [Pi 2 B]
        case 2: // BCM2837 [Pi 3 B; Pi 3 B+; Pi 3 A+]
            return 0x3f000000 + GPIO_BASE_OFFSET;
        case 3: // BCM2711 [Pi 4 B]
            return 0xfe000000 + GPIO_BASE_OFFSET;
        default:
            *error = true;
            return 0;
    }
}

static volatile uint32_t *getBase(uint32_t reg_base) {
    struct file *fd;
    volatile uint32_t *r;
    
    if (IS_ERR(( fd = filp_open("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC, 0) ))) return NULL;
    r = (uint32_t*)mmap(0, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, reg_base);
    filp_close(fd, NULL); // TODO the original didn't have this
    
    return r;
}

static void setPull(volatile uint32_t *base, uint32_t gpio, int pull) {
    int clkreg = GPPUDCLK0 + (gpio / 32);
    int clkbit = 1 << (gpio % 32);

    base[GPPUD] = pull;
    udelay(10);
    base[clkreg] = clkbit;
    udelay(10);
    base[GPPUD] = 0;
    udelay(10);
    base[clkreg] = 0;
    udelay(10);
}

/**
 * Equivalent to 'raspi-gpio set <gpio> <pu/pd>'
 * @param gpio Valid GPIO pin
 * @param pull PULL_DOWN/PULL_UP
 */
static int setGpioPull(uint32_t gpio, int pull) {
    bool error;
    uint32_t reg_base;
    volatile uint32_t *base;
    
    reg_base = getGpioRegBase(&error);
    if (error) return -1;
    base = getBase(reg_base);
    if (base == NULL || base == (uint32_t*)-1) return -1;
    setPull(base, gpio, pull);
    
    return 0;
}

我发现它只是一个函数声明(int (*mmap) (struct file *filp, struct vm_area_struct *vma)),但是我不知道如何发送mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset)使用的任何参数和返回值。

提前感谢!

EN

Stack Overflow用户

回答已采纳

发布于 2021-10-03 22:51:21

Mmap访问"/dev/mem",在some more questions之后,我发现这个文件连接了用户空间和内核空间(并且LKM它在内核空间上,所以不需要访问它)。在本例中,ioremap完成了这项工作。

下面是最终的代码:

代码语言:javascript
运行
复制
#include <linux/types.h>    // uint_32
#include <linux/fs.h>       // filp_open/filp_close
#include <linux/delay.h>    // udelay
#include <linux/io.h>       // ioremap?

#define PULL_DOWN    1
#define PULL_UP      2


/**
 * Equivalent to 'raspi-gpio set <gpio> <pu/pd>'
 * @param gpio Valid GPIO pin
 * @param pull PULL_DOWN/PULL_UP
 */
static int setGpioPull(uint32_t gpio, int pull);

/****************************
 ***   PRIVATE FUNCTIONS  ***
 ****************************/

#define GPIO_BASE_OFFSET 0x00200000
#define GPPUD        37
#define GPPUDCLK0    38

static uint32_t getGpioRegBase(bool *error) {
    uint8_t revision[4] = { 0 };
    uint32_t cpu = 0;
    struct file *fd;
    ssize_t rc = 0;

    if (IS_ERR(( fd = filp_open("/proc/device-tree/system/linux,revision", O_RDONLY | O_SYNC | O_CLOEXEC, 0) ))) {
        *error = true;
        return 0;
    }
    
    if ((rc = kernel_read(fd, revision, sizeof(revision), 0)) == 4) cpu = (revision[2] >> 4) & 0xf;
    else {
        *error = true;
        return 0;
    }

    filp_close(fd, NULL);

    *error = false;
    switch (cpu) {
        case 0: // BCM2835 [Pi 1 A; Pi 1 B; Pi 1 B+; Pi Zero; Pi Zero W]
            return 0x20000000 + GPIO_BASE_OFFSET;
        case 1: // BCM2836 [Pi 2 B]
        case 2: // BCM2837 [Pi 3 B; Pi 3 B+; Pi 3 A+]
            return 0x3f000000 + GPIO_BASE_OFFSET;
        case 3: // BCM2711 [Pi 4 B]
            return 0xfe000000 + GPIO_BASE_OFFSET;
        default:
            *error = true;
            return 0;
    }
}

static void setPull(volatile uint32_t *base, uint32_t gpio, int pull) {
    int clkreg = GPPUDCLK0 + (gpio / 32);
    int clkbit = 1 << (gpio % 32);

    base[GPPUD] = pull;
    udelay(10);
    base[clkreg] = clkbit;
    udelay(10);
    base[GPPUD] = 0;
    udelay(10);
    base[clkreg] = 0;
    udelay(10);
}

static int setGpioPull(uint32_t gpio, int pull) {
    bool error;
    uint32_t reg_base;
    volatile uint32_t *base;
    
    reg_base = getGpioRegBase(&error);
    if (error) return -1;
    base = (uint32_t*)ioremap(reg_base, 0x1000);
    if (base == NULL || base == (uint32_t*)-1) return -1;
    setPull(base, gpio, pull);
    iounmap(base);
    
    return 0;
}
票数 0
EN
查看全部 1 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69425540

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档