在 Linux 内核上执行 mmap
Execute mmap on Linux kernel
我正在尝试在 Raspberry pi 上启用上拉,更简单的方法是执行 raspi-gpio set <gpio> <pu/pd>
,问题是由于某些原因我无法使用 call_usermodehelper
(它不会抛出任何错误,但它什么也不做)。
作为替代方案,我一直在查看 raspi-gpio 源代码,并且我有一个启用上拉的功能 C 代码(此代码打印 GPIO CPU 并启用 GPIO26 的上拉):
#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(我以前从未见过它,我也不知道它到底做了什么,我只知道它映射内存)。
#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)
使用的 return 值.
提前致谢!
Mmap 在 some more questions 之后访问“/dev/mem”,我发现该文件将 user-space 与 kernel-space 连接起来(而 LKM 是在内核 space 上,因此无需访问它)。在这种情况下,ioremap
就可以了。
这是最终代码:
#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;
}
我正在尝试在 Raspberry pi 上启用上拉,更简单的方法是执行 raspi-gpio set <gpio> <pu/pd>
,问题是由于某些原因我无法使用 call_usermodehelper
(它不会抛出任何错误,但它什么也不做)。
作为替代方案,我一直在查看 raspi-gpio 源代码,并且我有一个启用上拉的功能 C 代码(此代码打印 GPIO CPU 并启用 GPIO26 的上拉):
#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(我以前从未见过它,我也不知道它到底做了什么,我只知道它映射内存)。
#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)
使用的 return 值.
提前致谢!
Mmap 在 some more questions 之后访问“/dev/mem”,我发现该文件将 user-space 与 kernel-space 连接起来(而 LKM 是在内核 space 上,因此无需访问它)。在这种情况下,ioremap
就可以了。
这是最终代码:
#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;
}