Linux-Kernel
不使用 mmap 寫入 /dev/mem
不使用 mmap 可以在 /dev/mem 上寫入嗎?
我在 LKM 內的 Raspberry Pi 上啟用了上拉電阻,但該功能
void *mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset)
不存在。我嘗試使用
open
(稍後將其轉換為filp_open
),但它什麼也沒做:#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> #include <errno.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 #define BASE_READ 0x1000 #define BASE_SIZE (BASE_READ/sizeof(uint32_t)) 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); exit(EXIT_FAILURE); } else { if (fread(revision, 1, sizeof(revision), fd) == 4) cpu = (revision[2] >> 4) & 0xf; else { printf("Revision data too short\n"); exit(EXIT_FAILURE); } 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] //chip = &gpio_chip_2835; return 0x20000000 + GPIO_BASE_OFFSET; case 1: // BCM2836 [Pi 2 B] case 2: // BCM2837 [Pi 3 B; Pi 3 B+; Pi 3 A+] //chip = &gpio_chip_2835; return 0x3f000000 + GPIO_BASE_OFFSET; case 3: // BCM2711 [Pi 4 B] //chip = &gpio_chip_2711; return 0xfe000000 + GPIO_BASE_OFFSET; default: printf("Unrecognised revision code\n"); exit(1); } } int writeBase(uint32_t reg_base, uint32_t offset, uint32_t data) { int fd; if ((fd = open("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC) ) < 0) return -1; if (lseek(fd, reg_base+offset, SEEK_SET) == -1) return -2; if (write(fd, (void*)&data, sizeof(uint32_t)) != sizeof(uint32_t)) return -3; if (close(fd) == -1) return -4; return 0; } int setPull(unsigned int gpio, int pull) { int r; int clkreg = GPPUDCLK0 + (gpio / 32); int clkbit = 1 << (gpio % 32); uint32_t reg_base = getGpioRegBase(); r = writeBase(reg_base, GPPUD, pull); // base[GPPUD] = pull if (r < 0) return r; usleep(10); r = writeBase(reg_base, clkreg, clkbit); // base[clkreg] = clkbit if (r < 0) return r; usleep(10); r = writeBase(reg_base, GPPUD, 0); // base[GPPUD] = 0 if (r < 0) return r; usleep(10); r = writeBase(reg_base, clkreg, 0); // base[clkreg] = 0 usleep(10); return r; } int main(int argc, char *argv[]) { int gpio, r; if (argc!=2) { printf("GPIO pin needed!\n"); return 1; } gpio = atoi(argv[1]); printf("Enabling pull-up on GPIO%d...\n", gpio); r = setPull(gpio, PULL_UP); printf("Return value: %d\n", r); if (r != 0) printf("%s\n", strerror(errno)); return r; }
raspi-gpio
這是我想要的一個片段:#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(int argc, char *argv[]) { if (argc!=2) { printf("GPIO pin needed!\n"); return 1; } 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, atoi(argv[1]), PULL_UP); return 0; }
這是啟用上拉的 KML 片段(我需要刪除該
mmap
部分):#include <linux/types.h> // uint_32 #include <linux/fs.h> // filp_open/filp_close #include <linux/delay.h> // udelay #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; }```
/proc
和設備節點/dev
用於使用者空間;核心不需要它們,核心模組也不應該使用它們。相反,要訪問 GPIO,您應該使用
ioremap
各種ioread...``iowrite...
功能:獲取與ioremap
您所追求的物理地址相對應的地址,以及執行 IO 的其他功能。我不知道如何立即檢索您從中獲得的設備樹資訊
/proc
,但應該有核心函式可以這樣做。
也許另一種方式:
在我的 RPi 3B+/bullseye 上:
$ ls -l /dev | grep mem crw-rw---- 1 root gpio 247, 0 Jan 26 00:06 gpiomem crw-r----- 1 root kmem 1, 1 Jan 26 00:06 mem
該組的成員
gpio
可以訪問gpiomem
,並且RPi OS (née raspbian) 中的預設(通常是唯一的)使用者是該組的成員。pi