├── .gitignore
├── License.txt
├── README.md
├── build
├── bulletin.xml
├── component
│ └── VMW-esx-7.0.1-thgpio-1.0-0.00001.zip
├── sverify
│ └── thgpio.sva
├── thgpio
├── thgpio-nostrip
├── thgpio.map
└── vib
│ ├── thgpio-0.0.1-1OEM.701.1.0.40650718.aarch64.vib
│ └── thgpio-0.1.0-1OEM.701.1.0.40650718.aarch64.vib
├── gpio.h
├── gpio_charDev.c
├── gpio_charDev.h
├── gpio_drv.c
├── gpio_drv.h
├── gpio_os.c
├── gpio_types.h
└── pyUtil
├── gpioLib
└── __init__.py
├── gpio_fanShim.py
└── gpio_util.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.sh
2 | *.sc
3 | gpio_acpi_device.py
4 | bump*
5 | #build
6 | .build
7 | .vscode
8 | Makefile
9 |
--------------------------------------------------------------------------------
/License.txt:
--------------------------------------------------------------------------------
1 | Native ESXi GPIO Driver
2 | Copyright (c) 2020 Tom Hebel
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is
9 | furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Note: all the source code contained in this repository is for educational use only, and is provided with no support, endorsement, warranty, guarantee or implied fitness for a particular purpose. It does not constitute sample driver code. Do not reach out to VMware to support this code. Do not copy and reuse in any commercial or product setting.
2 |
3 | # thgpio
4 | Native ESXi driver for RPi 4's GPIO interface.
5 |
6 | Allows controlling the pins of the Raspberry Pi 4's GPIO interface.
7 |
8 | Still very much work-in-progress!
9 |
10 | Branch main contains:
11 | - the base GPIO hardware driver
12 | - a Python library for interacting with the GPIO MMIO memory directly
13 | - a Python utility for interacting with the GPIO interface
14 | - a Python utility for interacting with the Pimoroni FanShim
15 |
16 | ### Videos
17 |
18 | These demos use the Pimoroni FanShim as an example. Any GPIO accessory should work analogously.
19 |
20 | Branch main: https://youtu.be/Mw8L1XAi8qA?t=170
21 |
22 | Branch fanShimButton: https://youtu.be/YjSxgKr7gZg
23 |
24 | Branch charDev: https://youtu.be/Ri1py3AzIsU?t=107
25 |
26 | ### Installation
27 |
28 | To install the driver, download the VIB file in the build directory and copy it to your RPi.
29 |
30 | Install it as described here: https://kb.vmware.com/s/article/2008939
31 |
32 | Note: you will need to reboot the RPi after installing the VIB.
33 |
34 | I would recommend downloading the Python library in ./pyUtil/gpioLib/ as well so you don't have to worry about all the bit twiddling.
35 |
36 | ### Post-Installation Setup
37 |
38 | You may need to manually change the `GPIO_DEVICE_PATH` variable in `./pyUtil/gpioLib/__init__.py` to reflect the correct name for the character device. This is due to a limitation in the VMKernel character device API.
39 |
40 | 1. Open the `__init__.py` file ( https://github.com/thebel1/thgpio/blob/main/pyUtil/gpioLib/__init__.py )
41 | 2. Locate the line `GPIO_DEVICE_PATH = '/dev/vmgfx32'`
42 | 3. Run the command `ls /dev/` in the ESXi shell and locate the vmgfx* device(s)
43 | 4. Note the device number corresponding to the GPIO device
44 | 4.1. If you installed the GPIO driver first, the file path should be `/dev/vmgfx32`
45 | 4.2. If you installed the GPIO driver second, the file should be `/dev/vmgfx33`
46 | 4.3. It just keeps counting up from there as far as I can tell
47 | 5. Go back to the `__init__.py` file and set `GPIO_DEVICE_PATH` to the correct device path
48 | 6. Save the file and you're set
49 |
--------------------------------------------------------------------------------
/build/bulletin.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | thgpio_1.0-0.00001
6 |
7 |
8 |
9 | Tom Hebel
10 |
11 |
12 |
13 | thgpio: Native GPIO Driver
14 |
15 |
16 | general
17 |
18 |
19 | enhancement
20 |
21 |
22 | important
23 |
24 |
25 | extension
26 |
27 |
28 |
29 | Native GPIO Driver module for vmkernel
30 |
31 |
32 |
33 | http://kb.example.com/kb/example.html
34 |
35 |
36 |
37 | thebel@vmware.com
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | 2020-10-30T10:42:17+00:00
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | THX_bootbank_thgpio_0.1.0-1OEM.701.1.0.40650718
--------------------------------------------------------------------------------
/build/component/VMW-esx-7.0.1-thgpio-1.0-0.00001.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thebel1/thgpio/81084b3f965f3eefa0bbed7e676e53d37ab0bef6/build/component/VMW-esx-7.0.1-thgpio-1.0-0.00001.zip
--------------------------------------------------------------------------------
/build/sverify/thgpio.sva:
--------------------------------------------------------------------------------
1 | vmware-esx-nativeddk-devtools-7.0.0-1.0.15843807.x86_64
2 | vmware-esx-nativeddkarm64-devtools-7.0.1-1.0.40650718.x86_64
3 | vmware-esx-nativeddk-devtools-7.0.0-1.0.16966451.x86_64
4 | vmware-esx-nativeddkarm64-toolchain-2.0.0-1.x86_64
5 | vmware-esx-nativeddkarm64-devtools-sockets-7.0.1-1.0.40650718.x86_64
6 | aarch64-vmk-linux-gnu-gcc (GCC) 6.4.0
7 | Copyright (C) 2017 Free Software Foundation, Inc.
8 | This is free software; see the source for copying conditions. There is NO
9 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 |
11 | aarch64-vmk-linux-gnu-g++ (GCC) 6.4.0
12 | Copyright (C) 2017 Free Software Foundation, Inc.
13 | This is free software; see the source for copying conditions. There is NO
14 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 |
16 | LLD 8.0.1 (compatible with GNU linkers)
17 | /opt/vmware/toolchain/cayman_esx_toolchain_gcc6-978272870a6446e021754e84f5d75e0c6c53a9b7/usr/bin/aarch64-vmk-linux-gnu-gcc -march="armv8-a" --sysroot="/opt/vmware/toolchain/cayman_esx_glibc_2_17-8ef7e61952433909958dd15ee027ca4e03896da7/sysroot-aarch64" -fwrapv -pipe -fno-strict-aliasing -Wno-unused-but-set-variable -fno-working-directory -g -O2 -ffreestanding -nostdinc -fno-common -funwind-tables -fasynchronous-unwind-tables -Wall -Werror -Wno-error="inline" -Wformat-nonliteral -Wstrict-prototypes -fno-strict-aliasing -freg-struct-return -falign-jumps="1" -falign-functions="4" -falign-loops="1" -march="armv8-a" -mgeneral-regs-only -DVMK_ARM_BRINGUP -finline-limit="2000" -fomit-frame-pointer -Wlogical-op -Wno-pointer-sign -Wno-strict-prototypes -Wno-enum-compare -Wno-declaration-after-statement -std="gnu90" -DNO_FLOATING_POINT -DUSE_32_BIT -DVMKERNEL -DVMKERNEL_MODULE -DVMK_DEVKIT_HAS_API_VMKAPI_BASE -DVMK_DEVKIT_HAS_API_VMKAPI_ENS -DVMK_DEVKIT_HAS_API_VMKAPI_GPU -DVMK_DEVKIT_HAS_API_VMKAPI_ISCSI -DVMK_DEVKIT_HAS_API_VMKAPI_NET -DVMK_DEVKIT_HAS_API_VMKAPI_NPIV -DVMK_DEVKIT_HAS_API_VMKAPI_NVME_DRV -DVMK_DEVKIT_HAS_API_VMKAPI_RDMA -DVMK_DEVKIT_HAS_API_VMKAPI_SCSI -DVMK_DEVKIT_HAS_API_VMKAPI_SOCKETS -DVMK_DEVKIT_HAS_NATIVE_DDK -DVMK_DEVKIT_USES_BINARY_COMPATIBLE_APIS -DVMK_DEVKIT_USES_PUBLIC_APIS -DVMX86_RELEASE -DVMX86_SERVER -D_GLIBCXX_USE_CXX11_ABI="0" -D__KERNEL__ -D__KMDK__ -D__MODULE_LICENSE__="ThirdParty:MIT" -D__MODULE_VERSION__="0.1.0-1OEM.701.1.0.40650718" -Ipartners/samples/public/thgpio -Ipartners/samples/public/thgpio/.build/build/version -Ipartners/samples/public/thgpio/.build/build/HEADERS/vmkapi-current-all-public-bincomp/generic/release -isystem /opt/vmware/toolchain/cayman_esx_toolchain_gcc6-978272870a6446e021754e84f5d75e0c6c53a9b7/usr/lib/gcc/aarch64-vmk-linux-gnu/6.4.0/include -E bora/modules/vmkapi/module_info/module_info.c -U__TIME__ -U__DATE__
18 | WD: /opt/vmware/nativeddkarm64-7.0.1-40650718/src
19 | BD: /opt/vmware/nativeddkarm64-7.0.1-40650718/src/partners/samples/public/thgpio/.build
20 | ff5edde8b8fc17827f6a83cb952b9832 partners/samples/public/thgpio/.build/build/vmkdriver-thgpio/release/vmkernel64-arm64/SUBDIRS/bora/modules/vmkapi/module_info/module_info.i
21 | /opt/vmware/toolchain/cayman_esx_toolchain_gcc6-978272870a6446e021754e84f5d75e0c6c53a9b7/usr/bin/aarch64-vmk-linux-gnu-gcc -march="armv8-a" --sysroot="/opt/vmware/toolchain/cayman_esx_glibc_2_17-8ef7e61952433909958dd15ee027ca4e03896da7/sysroot-aarch64" -fwrapv -pipe -fno-strict-aliasing -Wno-unused-but-set-variable -fno-working-directory -g -O2 -ffreestanding -nostdinc -fno-common -funwind-tables -fasynchronous-unwind-tables -Wall -Werror -Wno-error="inline" -Wformat-nonliteral -Wstrict-prototypes -fno-strict-aliasing -freg-struct-return -falign-jumps="1" -falign-functions="4" -falign-loops="1" -march="armv8-a" -mgeneral-regs-only -DVMK_ARM_BRINGUP -finline-limit="2000" -fomit-frame-pointer -Wlogical-op -Wno-pointer-sign -Wno-strict-prototypes -Wno-enum-compare -Wno-declaration-after-statement -std="gnu90" -DNO_FLOATING_POINT -DUSE_32_BIT -DVMKERNEL -DVMKERNEL_MODULE -DVMK_DEVKIT_HAS_API_VMKAPI_BASE -DVMK_DEVKIT_HAS_API_VMKAPI_ENS -DVMK_DEVKIT_HAS_API_VMKAPI_GPU -DVMK_DEVKIT_HAS_API_VMKAPI_ISCSI -DVMK_DEVKIT_HAS_API_VMKAPI_NET -DVMK_DEVKIT_HAS_API_VMKAPI_NPIV -DVMK_DEVKIT_HAS_API_VMKAPI_NVME_DRV -DVMK_DEVKIT_HAS_API_VMKAPI_RDMA -DVMK_DEVKIT_HAS_API_VMKAPI_SCSI -DVMK_DEVKIT_HAS_API_VMKAPI_SOCKETS -DVMK_DEVKIT_HAS_NATIVE_DDK -DVMK_DEVKIT_USES_BINARY_COMPATIBLE_APIS -DVMK_DEVKIT_USES_PUBLIC_APIS -DVMX86_RELEASE -DVMX86_SERVER -D_GLIBCXX_USE_CXX11_ABI="0" -D__KERNEL__ -D__KMDK__ -Ipartners/samples/public/thgpio -Ipartners/samples/public/thgpio/.build/build/version -Ipartners/samples/public/thgpio/.build/build/HEADERS/vmkapi-current-all-public-bincomp/generic/release -isystem /opt/vmware/toolchain/cayman_esx_toolchain_gcc6-978272870a6446e021754e84f5d75e0c6c53a9b7/usr/lib/gcc/aarch64-vmk-linux-gnu/6.4.0/include -E partners/samples/public/thgpio/gpio_os.c -U__TIME__ -U__DATE__
22 | WD: /opt/vmware/nativeddkarm64-7.0.1-40650718/src
23 | BD: /opt/vmware/nativeddkarm64-7.0.1-40650718/src/partners/samples/public/thgpio/.build
24 | edc13860af4638ff15e6eefe7a05860e partners/samples/public/thgpio/.build/build/vmkdriver-thgpio/release/vmkernel64-arm64/SUBDIRS/opt/vmware/nativeddkarm64-7.0.1-40650718/src/partners/samples/public/thgpio/gpio_os.i
25 | /opt/vmware/toolchain/cayman_esx_toolchain_gcc6-978272870a6446e021754e84f5d75e0c6c53a9b7/usr/bin/aarch64-vmk-linux-gnu-gcc -march="armv8-a" --sysroot="/opt/vmware/toolchain/cayman_esx_glibc_2_17-8ef7e61952433909958dd15ee027ca4e03896da7/sysroot-aarch64" -fwrapv -pipe -fno-strict-aliasing -Wno-unused-but-set-variable -fno-working-directory -g -O2 -ffreestanding -nostdinc -fno-common -funwind-tables -fasynchronous-unwind-tables -Wall -Werror -Wno-error="inline" -Wformat-nonliteral -Wstrict-prototypes -fno-strict-aliasing -freg-struct-return -falign-jumps="1" -falign-functions="4" -falign-loops="1" -march="armv8-a" -mgeneral-regs-only -DVMK_ARM_BRINGUP -finline-limit="2000" -fomit-frame-pointer -Wlogical-op -Wno-pointer-sign -Wno-strict-prototypes -Wno-enum-compare -Wno-declaration-after-statement -std="gnu90" -DNO_FLOATING_POINT -DUSE_32_BIT -DVMKERNEL -DVMKERNEL_MODULE -DVMK_DEVKIT_HAS_API_VMKAPI_BASE -DVMK_DEVKIT_HAS_API_VMKAPI_ENS -DVMK_DEVKIT_HAS_API_VMKAPI_GPU -DVMK_DEVKIT_HAS_API_VMKAPI_ISCSI -DVMK_DEVKIT_HAS_API_VMKAPI_NET -DVMK_DEVKIT_HAS_API_VMKAPI_NPIV -DVMK_DEVKIT_HAS_API_VMKAPI_NVME_DRV -DVMK_DEVKIT_HAS_API_VMKAPI_RDMA -DVMK_DEVKIT_HAS_API_VMKAPI_SCSI -DVMK_DEVKIT_HAS_API_VMKAPI_SOCKETS -DVMK_DEVKIT_HAS_NATIVE_DDK -DVMK_DEVKIT_USES_BINARY_COMPATIBLE_APIS -DVMK_DEVKIT_USES_PUBLIC_APIS -DVMX86_RELEASE -DVMX86_SERVER -D_GLIBCXX_USE_CXX11_ABI="0" -D__KERNEL__ -D__KMDK__ -Ipartners/samples/public/thgpio -Ipartners/samples/public/thgpio/.build/build/version -Ipartners/samples/public/thgpio/.build/build/HEADERS/vmkapi-current-all-public-bincomp/generic/release -isystem /opt/vmware/toolchain/cayman_esx_toolchain_gcc6-978272870a6446e021754e84f5d75e0c6c53a9b7/usr/lib/gcc/aarch64-vmk-linux-gnu/6.4.0/include -E partners/samples/public/thgpio/gpio_drv.c -U__TIME__ -U__DATE__
26 | WD: /opt/vmware/nativeddkarm64-7.0.1-40650718/src
27 | BD: /opt/vmware/nativeddkarm64-7.0.1-40650718/src/partners/samples/public/thgpio/.build
28 | 2add78a669e05aa7bf19599bebf7f652 partners/samples/public/thgpio/.build/build/vmkdriver-thgpio/release/vmkernel64-arm64/SUBDIRS/opt/vmware/nativeddkarm64-7.0.1-40650718/src/partners/samples/public/thgpio/gpio_drv.i
29 | /opt/vmware/toolchain/cayman_esx_toolchain_gcc6-978272870a6446e021754e84f5d75e0c6c53a9b7/usr/bin/aarch64-vmk-linux-gnu-gcc -march="armv8-a" --sysroot="/opt/vmware/toolchain/cayman_esx_glibc_2_17-8ef7e61952433909958dd15ee027ca4e03896da7/sysroot-aarch64" -fwrapv -pipe -fno-strict-aliasing -Wno-unused-but-set-variable -fno-working-directory -g -O2 -ffreestanding -nostdinc -fno-common -funwind-tables -fasynchronous-unwind-tables -Wall -Werror -Wno-error="inline" -Wformat-nonliteral -Wstrict-prototypes -fno-strict-aliasing -freg-struct-return -falign-jumps="1" -falign-functions="4" -falign-loops="1" -march="armv8-a" -mgeneral-regs-only -DVMK_ARM_BRINGUP -finline-limit="2000" -fomit-frame-pointer -Wlogical-op -Wno-pointer-sign -Wno-strict-prototypes -Wno-enum-compare -Wno-declaration-after-statement -std="gnu90" -DNO_FLOATING_POINT -DUSE_32_BIT -DVMKERNEL -DVMKERNEL_MODULE -DVMK_DEVKIT_HAS_API_VMKAPI_BASE -DVMK_DEVKIT_HAS_API_VMKAPI_ENS -DVMK_DEVKIT_HAS_API_VMKAPI_GPU -DVMK_DEVKIT_HAS_API_VMKAPI_ISCSI -DVMK_DEVKIT_HAS_API_VMKAPI_NET -DVMK_DEVKIT_HAS_API_VMKAPI_NPIV -DVMK_DEVKIT_HAS_API_VMKAPI_NVME_DRV -DVMK_DEVKIT_HAS_API_VMKAPI_RDMA -DVMK_DEVKIT_HAS_API_VMKAPI_SCSI -DVMK_DEVKIT_HAS_API_VMKAPI_SOCKETS -DVMK_DEVKIT_HAS_NATIVE_DDK -DVMK_DEVKIT_USES_BINARY_COMPATIBLE_APIS -DVMK_DEVKIT_USES_PUBLIC_APIS -DVMX86_RELEASE -DVMX86_SERVER -D_GLIBCXX_USE_CXX11_ABI="0" -D__KERNEL__ -D__KMDK__ -Ipartners/samples/public/thgpio -Ipartners/samples/public/thgpio/.build/build/version -Ipartners/samples/public/thgpio/.build/build/HEADERS/vmkapi-current-all-public-bincomp/generic/release -isystem /opt/vmware/toolchain/cayman_esx_toolchain_gcc6-978272870a6446e021754e84f5d75e0c6c53a9b7/usr/lib/gcc/aarch64-vmk-linux-gnu/6.4.0/include -E partners/samples/public/thgpio/gpio_charDev.c -U__TIME__ -U__DATE__
30 | WD: /opt/vmware/nativeddkarm64-7.0.1-40650718/src
31 | BD: /opt/vmware/nativeddkarm64-7.0.1-40650718/src/partners/samples/public/thgpio/.build
32 | 2d618e20acbff6a763d513f934b42628 partners/samples/public/thgpio/.build/build/vmkdriver-thgpio/release/vmkernel64-arm64/SUBDIRS/opt/vmware/nativeddkarm64-7.0.1-40650718/src/partners/samples/public/thgpio/gpio_charDev.i
33 | /opt/vmware/toolchain/cayman_llvm-e51314a6e421afc0fc8856057af8b195bb9805f3/lin64/usr/bin+glibc212/ld.lld -r -o partners/samples/public/thgpio/.build/build/vmkdriver-thgpio/release/vmkernel64-arm64/thgpio.sv --whole-archive partners/samples/public/thgpio/.build/build/vmkdriver-thgpio/release/vmkernel64-arm64/thgpio
34 | WD: /opt/vmware/nativeddkarm64-7.0.1-40650718/src
35 | BD: /opt/vmware/nativeddkarm64-7.0.1-40650718/src/partners/samples/public/thgpio/.build
36 | 1e4f08ebc9b7a2ec4bf0755e5c5f913b partners/samples/public/thgpio/.build/build/vmkdriver-thgpio/release/vmkernel64-arm64/thgpio
37 | 564261baf4b168246307fa34142d2e4f partners/samples/public/thgpio/.build/build/vmkdriver-thgpio/release/vmkernel64-arm64/signed/thgpio
38 |
--------------------------------------------------------------------------------
/build/thgpio:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thebel1/thgpio/81084b3f965f3eefa0bbed7e676e53d37ab0bef6/build/thgpio
--------------------------------------------------------------------------------
/build/thgpio-nostrip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thebel1/thgpio/81084b3f965f3eefa0bbed7e676e53d37ab0bef6/build/thgpio-nostrip
--------------------------------------------------------------------------------
/build/thgpio.map:
--------------------------------------------------------------------------------
1 | regtype=native,bus=acpi,id=BCMGPIO,driver=thgpio
2 |
--------------------------------------------------------------------------------
/build/vib/thgpio-0.0.1-1OEM.701.1.0.40650718.aarch64.vib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thebel1/thgpio/81084b3f965f3eefa0bbed7e676e53d37ab0bef6/build/vib/thgpio-0.0.1-1OEM.701.1.0.40650718.aarch64.vib
--------------------------------------------------------------------------------
/build/vib/thgpio-0.1.0-1OEM.701.1.0.40650718.aarch64.vib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thebel1/thgpio/81084b3f965f3eefa0bbed7e676e53d37ab0bef6/build/vib/thgpio-0.1.0-1OEM.701.1.0.40650718.aarch64.vib
--------------------------------------------------------------------------------
/gpio.h:
--------------------------------------------------------------------------------
1 | /******************************************************************************\
2 | * Native ESXi driver for the RPi's GPIO interface.
3 | *
4 | * Tom Hebel, 2020
5 | \******************************************************************************/
6 |
7 | /*
8 | * gpio.h --
9 | */
10 |
11 | #ifndef GPIO_H
12 | #define GPIO_H
13 |
14 | #define VMKAPI_ONLY
15 | #include "vmkapi.h"
16 |
17 | /***********************************************************************/
18 |
19 | #define GPIO_DEBUG
20 |
21 | #define GPIO_DRIVER_NAME "thgpio"
22 |
23 | // Probably overkill
24 | #define GPIO_HEAP_INITIAL_SIZE (1024 * 1024)
25 | #define GPIO_HEAP_MAX_SIZE (2 * 1024 * 1024)
26 |
27 | #define GPIO_MIN(a, b) (a > b ? b : a)
28 | #define GPIO_MAX(a, b) (a > b ? a : b)
29 |
30 | #define GPIO_INT_MAX ((vmk_uint32)~0)
31 |
32 | #define GPIO_LOG_ERR 1
33 | #define GPIO_LOG_WARN 2
34 | #define GPIO_LOG_NOTE 3
35 | #define GPIO_LOG_INIT 4
36 | #define GPIO_LOG_DISC 5
37 | #define GPIO_LOG_INFO 6
38 | #define GPIO_LOG_FUNC 7
39 | #define GPIO_LOG_TRACEIO 8
40 |
41 | /***********************************************************************/
42 |
43 | #endif /* GPIO_H */
--------------------------------------------------------------------------------
/gpio_charDev.c:
--------------------------------------------------------------------------------
1 | /******************************************************************************\
2 | * Native ESXi driver for the RPi's GPIO interface.
3 | *
4 | * Tom Hebel, 2020
5 | \******************************************************************************/
6 |
7 | /*
8 | * gpio_charDev.c --
9 | */
10 |
11 | #include "gpio_charDev.h"
12 |
13 | /***********************************************************************/
14 |
15 | static vmk_CharDevOps gpio_charDevFileOps = {
16 | .open = gpio_charDevOpen,
17 | .close = gpio_charDevClose,
18 | .ioctl = gpio_charDevIoctl,
19 | .poll = gpio_charDevPoll,
20 | .read = gpio_charDevRead,
21 | .write = gpio_charDevWrite,
22 | };
23 |
24 | static vmk_CharDevRegOps gpio_CharDevOps = {
25 | .associate = gpio_charDevAssoc,
26 | .disassociate = gpio_charDevDisassoc,
27 | .fileOps = &gpio_charDevFileOps,
28 | };
29 |
30 | /*
31 | * Dev ops for the new vmk_Device associated with the char dev
32 | */
33 | static vmk_DeviceOps gpio_CharVmkDevOps = {
34 | .removeDevice = gpio_charVmkDevRemove,
35 | };
36 |
37 | /*
38 | * Call backs that glue the char dev to the GPIO driver
39 | */
40 | static gpio_CharDevCallbacks_t *gpio_CharDevCBs;
41 |
42 | /***********************************************************************/
43 |
44 | /*
45 | * Char dev has its own pointer to gpio_os's driver struct, so we don't
46 | * have a global shared by several files.
47 | */
48 | static gpio_Driver_t *gpio_Driver;
49 |
50 | static vmk_TimerQueue gpio_charDevTimerQueue;
51 |
52 | /*
53 | ***********************************************************************
54 | * gpio_charDevRegister --
55 | *
56 | * Register a character device with the vmkernel chardev layer.
57 | *
58 | * Results:
59 | * VMK_OK on success, error code otherwise
60 | *
61 | * Side Effects:
62 | * None.
63 | ***********************************************************************
64 | */
65 | VMK_ReturnStatus
66 | gpio_charDevRegister(gpio_Driver_t *gpioDriver,
67 | vmk_BusType logicalBusType,
68 | vmk_Device parentDevice,
69 | gpio_CharDev_t *charDev,
70 | vmk_uint32 logicalPort,
71 | gpio_CharDevCallbacks_t *devCBs)
72 | {
73 | VMK_ReturnStatus status = VMK_OK;
74 | vmk_Device newDevice;
75 | vmk_DeviceID devID;
76 | vmk_AddrCookie registeringDriverData;
77 | vmk_AddrCookie registrationData;
78 | vmk_DeviceProps deviceProps;
79 | vmk_AddrCookie charDevPriv;
80 |
81 | /* Make available globally */
82 | gpio_Driver = gpioDriver;
83 |
84 | devID.busType = logicalBusType;
85 | status = vmk_LogicalCreateBusAddress(gpioDriver->driverHandle,
86 | parentDevice, logicalPort,
87 | &devID.busAddress,
88 | &devID.busAddressLen);
89 | if (status != VMK_OK) {
90 | goto logical_bus_failed;
91 | }
92 |
93 | /*
94 | * As per vmkapi_char.h it can only be graphics or a test device
95 | */
96 | devID.busIdentifier = VMK_CHARDEV_IDENTIFIER_GRAPHICS;
97 | devID.busIdentifierLen = vmk_Strnlen(devID.busIdentifier, VMK_MISC_NAME_MAX);
98 |
99 | charDevPriv.ptr = charDev;
100 |
101 | /*
102 | * Set up char dev registration
103 | */
104 |
105 | charDev->regData.moduleID = gpioDriver->moduleID;
106 | charDev->regData.deviceOps = &gpio_CharDevOps;
107 | charDev->regData.devicePrivate = charDevPriv;
108 |
109 | registrationData.ptr = &charDev->regData;
110 | registeringDriverData.ptr = charDev;
111 |
112 | deviceProps.registeringDriver = gpioDriver->driverHandle;
113 | deviceProps.deviceID = &devID;
114 | deviceProps.deviceOps = &gpio_CharVmkDevOps;
115 | deviceProps.registeringDriverData = registeringDriverData;
116 | deviceProps.registrationData = registrationData;
117 |
118 | status = vmk_DeviceRegister(&deviceProps, parentDevice, &newDevice);
119 | if (status != VMK_OK) {
120 | vmk_Warning(gpio_Driver->logger,
121 | "failed to register device: %s",
122 | vmk_StatusToString(status));
123 | goto register_device_failed;
124 | }
125 |
126 | status = vmk_LogicalFreeBusAddress(gpioDriver->driverHandle,
127 | devID.busAddress);
128 | if (status != VMK_OK) {
129 | vmk_Warning(gpio_Driver->logger,
130 | "failed to free logical bus: %s",
131 | vmk_StatusToString(status));
132 | goto free_bus_failed;
133 | }
134 |
135 | /*
136 | * Set CBs
137 | */
138 | gpio_CharDevCBs = devCBs;
139 |
140 | return VMK_OK;
141 |
142 | free_bus_failed:
143 | vmk_LogicalFreeBusAddress(gpioDriver->driverHandle,
144 | devID.busAddress);
145 | register_device_failed:
146 | logical_bus_failed:
147 | return status;
148 | }
149 |
150 | /*
151 | ***********************************************************************
152 | * gpio_charVmkDevRemove --
153 | *
154 | * Remove character device.
155 | *
156 | * Results:
157 | * VMK_OK on success, error code otherwise
158 | *
159 | * Side Effects:
160 | * None.
161 | ***********************************************************************
162 | */
163 | VMK_ReturnStatus
164 | gpio_charVmkDevRemove(vmk_Device logicalDev)
165 | {
166 | VMK_ReturnStatus status = VMK_OK;
167 |
168 | status = vmk_DeviceUnregister(logicalDev);
169 | if (status != VMK_OK)
170 | {
171 | vmk_Warning(gpio_Driver->logger,
172 | "failed to unregister device: %s",
173 | vmk_StatusToString(status));
174 | }
175 |
176 | return status;
177 | }
178 |
179 | /*
180 | ***********************************************************************
181 | * gpio_charDevAssoc --
182 | *
183 | * Associate a char device with a device.
184 | *
185 | * Results:
186 | * VMK_OK on success, error code otherwise
187 | *
188 | * Side Effects:
189 | * None.
190 | ***********************************************************************
191 | */
192 | VMK_ReturnStatus
193 | gpio_charDevAssoc(vmk_AddrCookie charDevPriv,
194 | vmk_CharDevHandle charDevHandle)
195 | {
196 | VMK_ReturnStatus status = VMK_OK;
197 | vmk_Name charDevAlias;
198 |
199 | status = vmk_CharDeviceGetAlias(charDevHandle, &charDevAlias);
200 | if (status != VMK_OK) {
201 | vmk_Warning(gpio_Driver->logger,
202 | "failed to obtain logical device alias: %s",
203 | vmk_StatusToString(status));
204 | goto get_alias_failed;
205 | }
206 |
207 | #ifdef GPIO_DEBUG
208 | {
209 | vmk_Log(gpio_Driver->logger,
210 | "obtained logical device alias %s",
211 | charDevAlias.string);
212 | }
213 | #endif /* GPIO_DEBUG */
214 |
215 | return VMK_OK;
216 |
217 | get_alias_failed:
218 | return status;
219 | }
220 |
221 | /*
222 | ***********************************************************************
223 | * gpio_charDevDisassoc --
224 | *
225 | * Disassociate a char device with a device.
226 | *
227 | * Results:
228 | * VMK_OK on success, error code otherwise
229 | *
230 | * Side Effects:
231 | * None.
232 | ***********************************************************************
233 | */
234 | VMK_ReturnStatus
235 | gpio_charDevDisassoc(vmk_AddrCookie charDevPriv)
236 | {
237 | VMK_ReturnStatus status = VMK_OK;
238 |
239 | return status;
240 | }
241 |
242 | /*
243 | ***********************************************************************
244 | * gpio_charDevOpen --
245 | *
246 | * Opens a file.
247 | *
248 | * Results:
249 | * VMK_OK on success, error code otherwise
250 | *
251 | * Side Effects:
252 | * None.
253 | ***********************************************************************
254 | */
255 | VMK_ReturnStatus
256 | gpio_charDevOpen(vmk_CharDevFdAttr *attr)
257 | {
258 | VMK_ReturnStatus status = VMK_OK;
259 | gpio_CharFileData_t *fileData;
260 | vmk_SpinlockCreateProps lockProps;
261 |
262 | /*
263 | * Init private file data
264 | */
265 |
266 | fileData = vmk_HeapAlloc(gpio_Driver->heapID, sizeof(*fileData));
267 | if (fileData == NULL) {
268 | status = VMK_NO_MEMORY;
269 | vmk_Warning(gpio_Driver->logger,
270 | "failed to create file private data: %s",
271 | vmk_StatusToString(status));
272 | goto file_priv_alloc_failed;
273 | }
274 | vmk_Memset(fileData, 0, sizeof(*fileData));
275 |
276 | /*
277 | * Init lock
278 | */
279 |
280 | lockProps.moduleID = gpio_Driver->moduleID;
281 | lockProps.heapID = gpio_Driver->heapID;
282 | status = vmk_NameInitialize(&lockProps.name, GPIO_DRIVER_NAME);
283 | if (status != VMK_OK) {
284 | vmk_Warning(gpio_Driver->logger,
285 | "failed to init lock name: %s",
286 | vmk_StatusToString(status));
287 | goto lock_init_failed;
288 | }
289 |
290 | lockProps.type = VMK_SPINLOCK;
291 | lockProps.domain = VMK_LOCKDOMAIN_INVALID;
292 | lockProps.rank = VMK_SPINLOCK_UNRANKED;
293 | status = vmk_SpinlockCreate(&lockProps, &fileData->lock);
294 | if (status != VMK_OK) {
295 | vmk_Warning(gpio_Driver->logger,
296 | "failed to create spinlock: %s",
297 | vmk_StatusToString(status));
298 | goto lock_init_failed;
299 | }
300 |
301 | /*
302 | * We don't use this buffer, so set it to NULL
303 | */
304 | fileData->data = NULL;
305 |
306 | /*
307 | * Prep file for I/O
308 | */
309 | fileData->timerPending = VMK_FALSE;
310 | fileData->deathPending = VMK_FALSE;
311 | fileData->pollMask = VMKAPI_POLL_WRITE;
312 | fileData->timeoutUS = GPIO_CHARDEV_POLL_TIMEOUT_US;
313 | attr->clientInstanceData.ptr = fileData;
314 |
315 | #ifdef GPIO_DEBUG
316 | {
317 | vmk_Log(gpio_Driver->logger,
318 | "opened file; priv %p lock %p",
319 | fileData,
320 | fileData->lock);
321 | }
322 | #endif /* GPIO_DEBUG */
323 |
324 | /* Call CB */
325 | status = gpio_CharDevCBs->open(attr);
326 |
327 | return status;
328 |
329 | lock_init_failed:
330 | vmk_HeapFree(gpio_Driver->heapID, fileData);
331 |
332 | file_priv_alloc_failed:
333 | return status;
334 | }
335 |
336 | /*
337 | ***********************************************************************
338 | * gpio_charDevClose --
339 | *
340 | * Closes a file.
341 | *
342 | * Results:
343 | * VMK_OK on success, error code otherwise
344 | *
345 | * Side Effects:
346 | * None.
347 | ***********************************************************************
348 | */
349 | VMK_ReturnStatus
350 | gpio_charDevClose(vmk_CharDevFdAttr *attr)
351 | {
352 | VMK_ReturnStatus status = VMK_OK;
353 | gpio_CharFileData_t *fileData = attr->clientInstanceData.ptr;
354 |
355 | if (fileData == NULL) {
356 | status = VMK_BAD_PARAM;
357 | vmk_Warning(gpio_Driver->logger, "file data null");
358 | goto file_data_null;
359 | }
360 |
361 | vmk_SpinlockLock(fileData->lock);
362 |
363 | fileData->deathPending = VMK_TRUE;
364 |
365 | if (fileData->timerPending == VMK_FALSE) {
366 | vmk_SpinlockUnlock(fileData->lock);
367 | gpio_charDevFileDestroy(fileData);
368 | }
369 |
370 | /* Call CB */
371 | status = gpio_CharDevCBs->close(attr);
372 |
373 | file_data_null:
374 | return status;
375 | }
376 |
377 | /*
378 | ***********************************************************************
379 | * gpio_charDevIoctl --
380 | *
381 | * GPIO chardev-specific I/O ops. Used for programming reads to input
382 | * pins.
383 | *
384 | * Results:
385 | * VMK_OK on success, error code otherwise
386 | *
387 | * Side Effects:
388 | * None.
389 | ***********************************************************************
390 | */
391 | VMK_ReturnStatus
392 | gpio_charDevIoctl(vmk_CharDevFdAttr *attr,
393 | unsigned int cmd,
394 | vmk_uintptr_t userData,
395 | vmk_IoctlCallerSize callerSize,
396 | vmk_int32 *result)
397 | {
398 | VMK_ReturnStatus status = VMK_OK;
399 | gpio_IoctlCookie_t ioctlData;
400 | gpio_CharFileData_t *fileData = attr->clientInstanceData.ptr;
401 |
402 | if (fileData == NULL) {
403 | status = VMK_BAD_PARAM;
404 | vmk_Warning(gpio_Driver->logger, "file data null");
405 | goto file_data_null;
406 | }
407 |
408 | /* Copy ioctl data from UW */
409 | status = vmk_CopyFromUser((vmk_VA)&ioctlData,
410 | (vmk_VA)userData,
411 | sizeof(ioctlData));
412 | if (status != VMK_OK) {
413 | vmk_Warning(gpio_Driver->logger,
414 | "unable to copy ioctl data from UW ptr %p: %s",
415 | userData,
416 | vmk_StatusToString(status));
417 | goto ioctl_uw2vmk_failed;
418 | }
419 |
420 | #ifdef GPIO_DEBUG
421 | {
422 | vmk_Log(gpio_Driver->logger,
423 | "executing ioctl cmd %d with data %p",
424 | cmd,
425 | userData);
426 | }
427 | #endif
428 |
429 | /* Call CB */
430 | vmk_SpinlockLock(fileData->lock);
431 | status = gpio_CharDevCBs->ioctl(cmd, &ioctlData);
432 | vmk_SpinlockUnlock(fileData->lock);
433 | if (status != VMK_OK) {
434 | vmk_Warning(gpio_Driver->logger,
435 | "ioctl cmd %d with data %p failed: %s",
436 | cmd,
437 | userData,
438 | vmk_StatusToString(status));
439 | goto ioctl_cmd_failed;
440 | }
441 |
442 | /* Copy iotl data back to UW */
443 | status = vmk_CopyToUser((vmk_VA)userData,
444 | (vmk_VA)&ioctlData,
445 | sizeof(ioctlData));
446 | if (status != VMK_OK) {
447 | vmk_Warning(gpio_Driver->logger,
448 | "unable to copy ioctl data back to UW: %s",
449 | vmk_StatusToString(status));
450 | goto ioctl_vmk2uw_failed;
451 | }
452 |
453 | return VMK_OK;
454 |
455 | ioctl_vmk2uw_failed:
456 | ioctl_cmd_failed:
457 | ioctl_uw2vmk_failed:
458 | file_data_null:
459 | return status;
460 | }
461 |
462 | /*
463 | ***********************************************************************
464 | * gpio_charDevRead --
465 | *
466 | * Reads from a file. Not supported for now, as we only allow writing
467 | * commands of sorts to the chardev file.
468 | *
469 | * Results:
470 | * VMK_OK on success, error code otherwise
471 | *
472 | * Side Effects:
473 | * None.
474 | ***********************************************************************
475 | */
476 | VMK_ReturnStatus
477 | gpio_charDevRead(vmk_CharDevFdAttr *attr,
478 | char *buffer,
479 | vmk_ByteCount nbytes,
480 | vmk_loff_t *ppos,
481 | vmk_ByteCountSigned *nread)
482 | {
483 | return gpio_charDevIO(attr, buffer, nbytes, ppos, nread, VMK_FALSE);
484 | }
485 |
486 | /*
487 | ***********************************************************************
488 | * gpio_charDevWrite --
489 | *
490 | * Writes to a file.
491 | *
492 | * Results:
493 | * VMK_OK on success, error code otherwise
494 | *
495 | * Side Effects:
496 | * None.
497 | ***********************************************************************
498 | */
499 | VMK_ReturnStatus
500 | gpio_charDevWrite(vmk_CharDevFdAttr *attr,
501 | char *buffer,
502 | vmk_ByteCount nbytes,
503 | vmk_loff_t *ppos,
504 | vmk_ByteCountSigned *nwritten)
505 | {
506 | return gpio_charDevIO(attr, buffer, nbytes, ppos, nwritten, VMK_TRUE);
507 | }
508 |
509 | /*
510 | ***********************************************************************
511 | * gpio_charDevIO --
512 | *
513 | * Read/write to file.
514 | *
515 | * Results:
516 | * VMK_OK on success, error code otherwise
517 | *
518 | * Side Effects:
519 | * None.
520 | ***********************************************************************
521 | */
522 | VMK_ReturnStatus
523 | gpio_charDevIO(vmk_CharDevFdAttr *attr,
524 | char *buffer,
525 | vmk_ByteCount nbytes,
526 | vmk_loff_t *ppos,
527 | vmk_ByteCountSigned *ndone,
528 | vmk_Bool isWrite)
529 | {
530 | VMK_ReturnStatus status = VMK_OK;
531 | gpio_CharFileData_t *fileData;
532 | vmk_ByteCount curByte;
533 | vmk_ByteCountSigned doneBytes = 0;
534 | vmk_loff_t offset;
535 | char *localBuffer;
536 |
537 | if (attr == NULL || ppos == NULL || ndone == NULL) {
538 | status = VMK_BAD_PARAM;
539 | vmk_Warning(gpio_Driver->logger,
540 | "invalid write command received;"
541 | " attr %p ppos %p ndone %p",
542 | attr, ppos, ndone);
543 | goto invalid_params;
544 | }
545 |
546 | #ifdef GPIO_DEBUG
547 | {
548 | vmk_Log(gpio_Driver->logger,
549 | "nbytes %lu offset %ld buffer %p",
550 | nbytes,
551 | *ppos,
552 | buffer);
553 | }
554 | #endif /* GPIO_DEBUG */
555 |
556 | localBuffer = vmk_HeapAlloc(gpio_Driver->heapID, GPIO_CHARDEV_BUFFER_SIZE);
557 | if (localBuffer == NULL) {
558 | status = VMK_NO_MEMORY;
559 | vmk_Warning(gpio_Driver->logger,
560 | "unable to allocate local buffer: %s",
561 | vmk_StatusToString(status));
562 | goto localBuffer_alloc_failed;
563 | }
564 |
565 | offset = *ppos;
566 | fileData = (gpio_CharFileData_t *)attr->clientInstanceData.ptr;
567 |
568 | /*
569 | * Perform I/O and copy data to/from user space
570 | */
571 | if (isWrite) { /* Write */
572 | for (curByte = 0; curByte < nbytes; ++curByte) {
573 | status = vmk_CopyFromUser(
574 | (vmk_VA)&localBuffer[curByte % GPIO_CHARDEV_BUFFER_SIZE],
575 | (vmk_VA)&buffer[curByte % GPIO_CHARDEV_BUFFER_SIZE],
576 | sizeof(char)
577 | );
578 |
579 | if (status != VMK_OK) {
580 | break;
581 | }
582 | ++doneBytes;
583 | }
584 | vmk_SpinlockLock(fileData->lock);
585 | status = gpio_CharDevCBs->write(localBuffer, doneBytes, ppos, &doneBytes);
586 | vmk_SpinlockUnlock(fileData->lock);
587 | }
588 | else { /* Read */
589 | status = gpio_CharDevCBs->read(localBuffer, nbytes, ppos, &doneBytes);
590 | for (curByte = 0; curByte < doneBytes; ++curByte) {
591 | status = vmk_CopyToUser(
592 | (vmk_VA)&buffer[curByte % GPIO_CHARDEV_BUFFER_SIZE],
593 | (vmk_VA)&localBuffer[curByte % GPIO_CHARDEV_BUFFER_SIZE],
594 | sizeof(char)
595 | );
596 | if (status != VMK_OK) {
597 | break;
598 | }
599 | }
600 | doneBytes = curByte + 1;
601 | }
602 |
603 | vmk_HeapFree(gpio_Driver->heapID, localBuffer);
604 |
605 | *ndone = doneBytes;
606 |
607 | if (status != VMK_OK) {
608 | vmk_Warning(gpio_Driver->logger,
609 | "I/O failed to file %p: %s",
610 | vmk_StatusToString(status));
611 | goto io_failed;
612 | }
613 |
614 | return VMK_OK;
615 |
616 | io_failed:
617 | localBuffer_alloc_failed:
618 | invalid_params:
619 | return status;
620 | }
621 |
622 | /*
623 | ***********************************************************************
624 | * gpio_charDevPoll --
625 | *
626 | * Polls a file.
627 | *
628 | * Results:
629 | * VMK_OK on success, error code otherwise
630 | *
631 | * Side Effects:
632 | * None.
633 | ***********************************************************************
634 | */
635 | VMK_ReturnStatus
636 | gpio_charDevPoll(vmk_CharDevFdAttr *attr,
637 | vmk_PollContext pollCtx,
638 | vmk_uint32 *pollMask)
639 | {
640 | VMK_ReturnStatus status = VMK_OK;
641 | gpio_CharFileData_t *fileData = attr->clientInstanceData.ptr;
642 | vmk_TimerCookie tmrCookie;
643 |
644 | if (fileData == NULL) {
645 | status = VMK_BAD_PARAM;
646 | vmk_Warning(gpio_Driver->logger, "file data null");
647 | goto file_data_null;
648 | }
649 |
650 | vmk_SpinlockLock(fileData->lock);
651 |
652 | if (fileData->pollMask == VMK_FALSE
653 | && fileData->timerPending == VMK_FALSE
654 | && fileData->deathPending == VMK_FALSE) {
655 | tmrCookie.ptr = fileData;
656 | status = vmk_TimerSchedule(gpio_charDevTimerQueue,
657 | gpio_charDevTimerCB,
658 | tmrCookie,
659 | GPIO_CHARDEV_POLL_TIMEOUT_US,
660 | VMK_TIMER_DEFAULT_TOLERANCE,
661 | VMK_TIMER_ATTR_NONE,
662 | VMK_LOCKDOMAIN_INVALID,
663 | VMK_SPINLOCK_UNRANKED,
664 | &fileData->timer);
665 | if (status == VMK_OK) {
666 | fileData->timerPending = VMK_TRUE;
667 | }
668 | else {
669 | vmk_Warning(gpio_Driver->logger,
670 | "failed to create poll timer: %s",
671 | vmk_StatusToString(status));
672 | goto create_poll_timer_failed;
673 | }
674 | }
675 |
676 | vmk_CharDevSetPollContext(pollCtx, (void *)fileData);
677 | *pollMask = fileData->pollMask;
678 |
679 | vmk_SpinlockUnlock(fileData->lock);
680 |
681 | return VMK_OK;
682 |
683 | create_poll_timer_failed:
684 | vmk_SpinlockUnlock(fileData->lock);
685 |
686 | file_data_null:
687 | return status;
688 | }
689 |
690 | /*
691 | ***********************************************************************
692 | * gpio_charDevTimerCB --
693 | *
694 | * Callback for char device poll timer.
695 | *
696 | * Results:
697 | * VMK_OK on success, error code otherwise
698 | *
699 | * Side Effects:
700 | * None.
701 | ***********************************************************************
702 | */
703 | void
704 | gpio_charDevTimerCB(vmk_TimerCookie data)
705 | {
706 | gpio_CharFileData_t *fileData = (gpio_CharFileData_t *)data.ptr;
707 |
708 | if (fileData == NULL) {
709 | vmk_Warning(gpio_Driver->logger, "file data null");
710 | goto file_data_null;
711 | }
712 |
713 | vmk_SpinlockLock(fileData->lock);
714 |
715 | fileData->timerPending = VMK_FALSE;
716 | fileData->pollMask = VMKAPI_POLL_WRITE;
717 |
718 | if (fileData->deathPending == VMK_FALSE) {
719 | /* Wake up pollers in UW */
720 | vmk_CharDevWakePollers(fileData);
721 | }
722 | else {
723 | vmk_SpinlockUnlock(fileData->lock);
724 | gpio_charDevFileDestroy(fileData);
725 | goto death_pending;
726 | }
727 |
728 | vmk_SpinlockUnlock(fileData->lock);
729 |
730 | death_pending:
731 | file_data_null:
732 | return;
733 | }
734 |
735 | /*
736 | ***********************************************************************
737 | * gpio_charDevFileDestroy --
738 | *
739 | * Destroy a file.
740 | *
741 | * Results:
742 | * VMK_OK on success, error code otherwise
743 | *
744 | * Side Effects:
745 | * None.
746 | ***********************************************************************
747 | */
748 | void
749 | gpio_charDevFileDestroy(gpio_CharFileData_t *fileData)
750 | {
751 | #ifdef GPIO_DEBUG
752 | {
753 | vmk_Log(gpio_Driver->logger,
754 | "destroying file %p",
755 | fileData);
756 | }
757 | #endif /* GPIO_DEBUG */
758 |
759 | vmk_SpinlockDestroy(fileData->lock);
760 | vmk_HeapFree(gpio_Driver->heapID, fileData);
761 | }
--------------------------------------------------------------------------------
/gpio_charDev.h:
--------------------------------------------------------------------------------
1 | /******************************************************************************\
2 | * Native ESXi driver for the RPi's GPIO interface.
3 | *
4 | * Tom Hebel, 2020
5 | \******************************************************************************/
6 |
7 | /*
8 | * gpio_charDev.h --
9 | */
10 |
11 | #ifndef GPIO_CHARDEV_H
12 | #define GPIO_CHARDEV_H
13 |
14 | #include "gpio.h"
15 | #include "gpio_types.h"
16 | #include "gpio_drv.h"
17 |
18 | /***********************************************************************/
19 |
20 | #define GPIO_CHARDEV_BUFFER_SIZE 4096 /* Probably overkill */
21 | #define GPIO_CHARDEV_POLL_TIMEOUT_US 1000000
22 |
23 | /***********************************************************************/
24 |
25 | /*
26 | * Callbacks for gluing the char dev driver to the GPIO driver
27 | */
28 | typedef VMK_ReturnStatus (*gpio_CharDevOpenCB_t)(vmk_CharDevFdAttr *attr);
29 | typedef VMK_ReturnStatus (*gpio_CharDevCloseCB_t)(vmk_CharDevFdAttr *attr);
30 |
31 | typedef VMK_ReturnStatus (*gpio_CharDevIoctlCB_t)(unsigned int cmd,
32 | gpio_IoctlCookie_t *ioctlData);
33 | typedef VMK_ReturnStatus (*gpio_CharDevReadCB_t)(char *buffer,
34 | vmk_ByteCount nbytes,
35 | vmk_loff_t *ppos,
36 | vmk_ByteCountSigned *nread);
37 | typedef VMK_ReturnStatus (*gpio_CharDevWriteCB_t)(char *buffer,
38 | vmk_ByteCount nbytes,
39 | vmk_loff_t *ppos,
40 | vmk_ByteCountSigned *nread);
41 | typedef struct gpio_CharDevCallbacks_t {
42 | gpio_CharDevOpenCB_t open;
43 | gpio_CharDevCloseCB_t close;
44 | gpio_CharDevIoctlCB_t ioctl;
45 | gpio_CharDevReadCB_t read;
46 | gpio_CharDevWriteCB_t write;
47 | } gpio_CharDevCallbacks_t;
48 |
49 | typedef struct gpio_CharDev_t {
50 | vmk_Device vmkDevice;
51 | vmk_CharDevRegData regData;
52 | gpio_CharDevCallbacks_t devCBs;
53 | } gpio_CharDev_t;
54 |
55 | typedef struct gpio_CharFileData_t {
56 | vmk_Lock lock;
57 | char *data;
58 | vmk_Bool timerPending;
59 | vmk_Bool deathPending;
60 | vmk_Timer timer;
61 | vmk_int32 timeoutUS;
62 | vmk_uint32 pollMask;
63 | } gpio_CharFileData_t;
64 |
65 | /***********************************************************************/
66 |
67 | VMK_ReturnStatus gpio_charDevRegister(gpio_Driver_t *gpioDriver,
68 | vmk_BusType logicalBusType,
69 | vmk_Device parentDevice,
70 | gpio_CharDev_t *charDev,
71 | vmk_uint32 logicalPort,
72 | gpio_CharDevCallbacks_t *devCBs);
73 |
74 | VMK_ReturnStatus gpio_charVmkDevRemove(vmk_Device logicalDev);
75 |
76 | VMK_ReturnStatus gpio_charDevAssoc(vmk_AddrCookie charDevPriv,
77 | vmk_CharDevHandle charDevHandle);
78 |
79 | VMK_ReturnStatus gpio_charDevDisassoc(vmk_AddrCookie charDevPriv);
80 |
81 | VMK_ReturnStatus gpio_charDevOpen(vmk_CharDevFdAttr *attr);
82 |
83 | VMK_ReturnStatus gpio_charDevClose(vmk_CharDevFdAttr *attr);
84 |
85 | VMK_ReturnStatus gpio_charDevIoctl(vmk_CharDevFdAttr *attr,
86 | vmk_uint32 cmd,
87 | vmk_uintptr_t userData,
88 | vmk_IoctlCallerSize callerSize,
89 | vmk_int32 *result);
90 |
91 | VMK_ReturnStatus gpio_charDevRead(vmk_CharDevFdAttr *attr,
92 | char *buffer,
93 | vmk_ByteCount nbytes,
94 | vmk_loff_t *ppos,
95 | vmk_ByteCountSigned *nread);
96 |
97 | VMK_ReturnStatus gpio_charDevWrite(vmk_CharDevFdAttr *attr,
98 | char *buffer,
99 | vmk_ByteCount nbytes,
100 | vmk_loff_t *ppos,
101 | vmk_ByteCountSigned *nwritten);
102 |
103 | VMK_ReturnStatus gpio_charDevIO(vmk_CharDevFdAttr *attr,
104 | char *buffer,
105 | vmk_ByteCount nbytes,
106 | vmk_loff_t *ppos,
107 | vmk_ByteCountSigned *ndone,
108 | vmk_Bool isWrite);
109 |
110 | VMK_ReturnStatus gpio_charDevPoll(vmk_CharDevFdAttr *attr,
111 | vmk_PollContext pollCtx,
112 | vmk_uint32 *pollMask);
113 |
114 | void gpio_charDevTimerCB(vmk_TimerCookie data);
115 |
116 | void gpio_charDevFileDestroy(gpio_CharFileData_t *fileData);
117 |
118 | /***********************************************************************/
119 |
120 | #endif /* GPIO_CHARDEV_H */
--------------------------------------------------------------------------------
/gpio_drv.c:
--------------------------------------------------------------------------------
1 | /******************************************************************************\
2 | * Native ESXi driver for the RPi's GPIO interface.
3 | *
4 | * Tom Hebel, 2020
5 | \******************************************************************************/
6 |
7 | /*
8 | * gpio_drv.c --
9 | *
10 | * Implementation of gpio hardware interface.
11 | *
12 | * TODO:
13 | * - fix up interrupts to work as described here:
14 | * https://www.raspberrypi.org/documentation/hardware/raspberrypi/gpio/README.md
15 | */
16 |
17 | #include "gpio_drv.h"
18 |
19 | /***********************************************************************/
20 |
21 | static gpio_Driver_t *gpio_Driver;
22 | static gpio_Device_t *gpio_Device;
23 |
24 | /*
25 | ***********************************************************************
26 | * gpio_drvInit --
27 | *
28 | * Initializes the GPIO driver.
29 | *
30 | * Results:
31 | * VMK_OK on success, error code otherwise
32 | *
33 | * Side Effects:
34 | * None.
35 | ***********************************************************************
36 | */
37 | VMK_ReturnStatus
38 | gpio_drvInit(gpio_Driver_t *driver,
39 | gpio_Device_t *adapter)
40 | {
41 | VMK_ReturnStatus status = VMK_OK;
42 |
43 | gpio_Driver = driver;
44 | gpio_Device = adapter;
45 |
46 | return status;
47 | }
48 |
49 | /*
50 | ***********************************************************************
51 | * gpio_mmioPoll --
52 | *
53 | * Polls a GPIO register and returns once the maked bits have changed.
54 | *
55 | * Results:
56 | * VMK_OK on success, error code otherwise
57 | *
58 | * Side Effects:
59 | * None.
60 | ***********************************************************************
61 | */
62 | VMK_ReturnStatus
63 | gpio_mmioPoll(vmk_uint32 offset, // IN
64 | vmk_uint32 mask, // IN
65 | vmk_uint32 *data) // OUT
66 | {
67 | VMK_ReturnStatus status = VMK_OK;
68 | vmk_uint32 prevData, curData;
69 | vmk_uint32 prevMasked, curMasked;
70 | vmk_TimerCycles monoCounterFreq = vmk_GetMonotonicCounterFrequency();
71 | unsigned long startTime, curTime;
72 |
73 | #ifdef GPIO_DEBUG
74 | {
75 | vmk_Log(gpio_Driver->logger,
76 | "polling GPIO MMIO offset 0x%x with mask 0x%x",
77 | offset,
78 | mask);
79 | }
80 | #endif /* GPIO_DEBUG */
81 |
82 | status = vmk_MappedResourceRead32(&gpio_Device->mmioMappedAddr,
83 | offset,
84 | &prevData);
85 | if (status != VMK_OK) {
86 | goto mmio_read_failed;
87 | }
88 |
89 | /* Mask on only the bits we want to poll */
90 | prevMasked = prevData & mask;
91 |
92 | /*
93 | * Loop until the read value changes or we hit the timeout
94 | */
95 | startTime = vmk_TimerMonotonicCounter;
96 | do {
97 | status = vmk_MappedResourceRead32(&gpio_Device->mmioMappedAddr,
98 | offset,
99 | &curData);
100 | if (status != VMK_OK) {
101 | goto mmio_read_failed;
102 | }
103 |
104 | curMasked = curData & mask;
105 |
106 | curTime = vmk_TimerMonotonicCounter;
107 | if ((curTime - startTime) / monoCounterFreq >= GPIO_POLL_TIMEOUT_SEC) {
108 | goto poll_timed_out;
109 | }
110 | } while (curMasked == prevMasked);
111 |
112 | *data = curData;
113 |
114 | return VMK_OK;
115 |
116 | poll_timed_out:
117 | return VMK_TIMEOUT;
118 |
119 | mmio_read_failed:
120 | vmk_Warning(gpio_Driver->logger,
121 | "read from GPIO MMIO offset 0x%x failed: %s",
122 | offset,
123 | vmk_StatusToString(status));
124 | return status;
125 | }
126 |
127 | /*
128 | ***********************************************************************
129 | * gpio_mmioDirectRead --
130 | *
131 | * Read directly from the GPIO MMIO mapped area.
132 | *
133 | * Results:
134 | * VMK_OK on success, error code otherwise
135 | *
136 | * Side Effects:
137 | * None.
138 | ***********************************************************************
139 | */
140 | VMK_INLINE
141 | VMK_ReturnStatus
142 | gpio_mmioDirectRead(vmk_uint32 offset,
143 | vmk_uint32 *value)
144 | {
145 | VMK_ReturnStatus status = VMK_OK;
146 |
147 | #ifdef GPIO_DEBUG
148 | {
149 | vmk_Log(gpio_Driver->logger,
150 | "reading value from GPIO MMIO offset 0x%x",
151 | offset);
152 | }
153 | #endif /* GPIO_DEBUG */
154 |
155 | status = vmk_MappedResourceRead32(&gpio_Device->mmioMappedAddr,
156 | offset,
157 | value);
158 |
159 | return status;
160 | }
161 |
162 | /*
163 | ***********************************************************************
164 | * gpio_mmioDirectWrite --
165 | *
166 | * Write directly to the GPIO MMIO mapped area.
167 | *
168 | * Results:
169 | * VMK_OK on success, error code otherwise
170 | *
171 | * Side Effects:
172 | * None.
173 | ***********************************************************************
174 | */
175 | VMK_INLINE
176 | VMK_ReturnStatus
177 | gpio_mmioDirectWrite(vmk_uint32 offset,
178 | vmk_uint32 value)
179 | {
180 | VMK_ReturnStatus status = VMK_OK;
181 |
182 | #ifdef GPIO_DEBUG
183 | {
184 | vmk_Log(gpio_Driver->logger,
185 | "writing value 0x%x to GPIO MMIO offset 0x%x",
186 | value,
187 | offset);
188 | }
189 | #endif /* GPIO_DEBUG */
190 |
191 | status = vmk_MappedResourceWrite32(&gpio_Device->mmioMappedAddr,
192 | offset,
193 | value);
194 |
195 | return status;
196 | }
197 |
198 | /*
199 | ***********************************************************************
200 | * gpio_mmioOpenCB --
201 | *
202 | * Callback used by char dev driver when the /dev/vmgfx32 file is
203 | * opened.
204 | *
205 | * Results:
206 | * VMK_OK on success, error code otherwise
207 | *
208 | * Side Effects:
209 | * None.
210 | ***********************************************************************
211 | */
212 | VMK_ReturnStatus
213 | gpio_mmioOpenCB(vmk_CharDevFdAttr *attr)
214 | {
215 | VMK_ReturnStatus status = VMK_OK;
216 |
217 | return status;
218 | }
219 |
220 | /*
221 | ***********************************************************************
222 | * gpio_mmioCloseCB --
223 | *
224 | * Callback used by char dev driver when the /dev/vmgfx32 file is
225 | * closed.
226 | *
227 | * Results:
228 | * VMK_OK on success, error code otherwise
229 | *
230 | * Side Effects:
231 | * None.
232 | ***********************************************************************
233 | */
234 | VMK_ReturnStatus
235 | gpio_mmioCloseCB(vmk_CharDevFdAttr *attr)
236 | {
237 | VMK_ReturnStatus status = VMK_OK;
238 |
239 | return status;
240 | }
241 |
242 | /*
243 | ***********************************************************************
244 | * gpio_mmioIoctlCB --
245 | *
246 | * Callback used by char dev driver for I/O control. I/O control is
247 | * used to allow to perform alternate reads/writes that aren't
248 | * supported by the corresponding syscalls. For example, for polling
249 | * a pin value until it changes.
250 | *
251 | * Results:
252 | * VMK_OK on success, error code otherwise
253 | *
254 | * Side Effects:
255 | * None.
256 | ***********************************************************************
257 | */
258 | VMK_ReturnStatus
259 | gpio_mmioIoctlCB(unsigned int cmd, // IN
260 | gpio_IoctlCookie_t *data) // IN/OUT
261 | {
262 | VMK_ReturnStatus status = VMK_OK;
263 | gpio_IoctlData_t ioctlData;
264 | vmk_uint32 outData;
265 |
266 | vmk_Memcpy(&ioctlData, data, sizeof(ioctlData));
267 |
268 | /* Sanity checks */
269 | if (ioctlData.offset % GPIO_REG_SIZE != 0
270 | || ioctlData.offset > GPIO_MMIO_SIZE
271 | || cmd <= GPIO_IOCTL_INVALID
272 | || cmd > GPIO_IOCTL_MAX) {
273 | status = VMK_BAD_PARAM;
274 | vmk_Warning(gpio_Driver->logger,
275 | "invalid ioctl: cmd %d data %p"
276 | " (0x%x, 0x%x)",
277 | cmd,
278 | *data,
279 | ioctlData.offset,
280 | ioctlData.mask);
281 | goto ioctl_invalid;
282 | }
283 |
284 | /* Run ioctl command */
285 | switch (cmd) {
286 | case GPIO_IOCTL_POLL:
287 | status = gpio_mmioPoll(ioctlData.offset, ioctlData.mask, &outData);
288 | break;
289 | }
290 | if (status != VMK_OK) {
291 | goto ioctl_cmd_failed;
292 | }
293 |
294 | /* Prep data for output */
295 | ioctlData.data = outData;
296 | vmk_Memcpy(data, &ioctlData, sizeof(ioctlData));
297 |
298 | return VMK_OK;
299 |
300 | ioctl_cmd_failed:
301 | ioctl_invalid:
302 | return status;
303 | }
304 |
305 | /*
306 | ***********************************************************************
307 | * gpio_mmioReadCB --
308 | *
309 | * Callback used by char dev driver when the /dev/vmgfx32 file is
310 | * read.
311 | *
312 | * Results:
313 | * VMK_OK on success, error code otherwise
314 | *
315 | * Side Effects:
316 | * None.
317 | ***********************************************************************
318 | */
319 | VMK_ReturnStatus
320 | gpio_mmioReadCB(char *buffer,
321 | vmk_ByteCount nbytes,
322 | vmk_loff_t *ppos,
323 | vmk_ByteCountSigned *nread)
324 | {
325 | VMK_ReturnStatus status = VMK_OK;
326 | vmk_loff_t offset = *ppos;
327 | vmk_uint32 value = 0;
328 |
329 | /*
330 | * Offset sanity checks
331 | */
332 | if (offset % GPIO_REG_SIZE != 0
333 | || offset > GPIO_MMIO_SIZE - GPIO_REG_SIZE) {
334 | status = VMK_FAILURE;
335 | vmk_Warning(gpio_Driver->logger,
336 | "attempted read from invalid MMIO offset 0x%x",
337 | offset);
338 | goto invalid_mmio_offset;
339 | }
340 |
341 | /*
342 | * Read from GPIO MMIO space
343 | */
344 | status = gpio_mmioDirectRead(offset, &value);
345 | if (status != VMK_OK) {
346 | vmk_Warning(gpio_Driver->logger,
347 | "failed to read from GPIO MMIO region: %s",
348 | vmk_StatusToString(status));
349 | *nread = 0;
350 | goto mmio_read_failed;
351 | }
352 |
353 | /* Write register value to UW buffer */
354 | *(vmk_uint32 *)buffer = value;
355 |
356 | #ifdef GPIO_DEBUG
357 | {
358 | vmk_Log(gpio_Driver->logger,
359 | "read 0x%x from offset 0x%x",
360 | value,
361 | offset);
362 | }
363 | #endif /* GPIO_DEBUG */
364 |
365 | /* Each read is the size of a register */
366 | *nread = GPIO_REG_SIZE;
367 |
368 | return VMK_OK;
369 |
370 | mmio_read_failed:
371 | invalid_mmio_offset:
372 | return status;
373 | }
374 |
375 | /*
376 | ***********************************************************************
377 | * gpio_mmioWrite --
378 | *
379 | * Callback used by char dev driver when the /dev/vmgfx32 file is
380 | * written to. We can write without locking, since locking is handled
381 | * by the char dev driver.
382 | *
383 | * Results:
384 | * VMK_OK on success, error code otherwise
385 | *
386 | * Side Effects:
387 | * None.
388 | ***********************************************************************
389 | */
390 | VMK_ReturnStatus
391 | gpio_mmioWriteCB(char *buffer,
392 | vmk_ByteCount nbytes,
393 | vmk_loff_t *ppos,
394 | vmk_ByteCountSigned *nwritten)
395 | {
396 | VMK_ReturnStatus status = VMK_OK;
397 | vmk_loff_t offset = *ppos;
398 | long value;
399 |
400 | /*
401 | * Offset sanity checks
402 | */
403 | if (offset % GPIO_REG_SIZE != 0
404 | || offset + nbytes > GPIO_MMIO_SIZE) {
405 | status = VMK_FAILURE;
406 | vmk_Warning(gpio_Driver->logger,
407 | "attempted write to invalid MMIO offset 0x%x",
408 | offset);
409 | goto invalid_mmio_offset;
410 | }
411 |
412 | value = *(vmk_uint32*)buffer;
413 | status = gpio_mmioDirectWrite(offset, (vmk_uint32)value);
414 | if (status != VMK_OK) {
415 | vmk_Warning(gpio_Driver->logger,
416 | "failed to write to GPIO MMIO region: %s",
417 | vmk_StatusToString(status));
418 | *nwritten = 0;
419 | goto mmio_write_failed;
420 | }
421 |
422 | /* Each write is the size of a register */
423 | *nwritten = GPIO_REG_SIZE;
424 |
425 | mmio_write_failed:
426 | invalid_mmio_offset:
427 | return status;
428 | }
--------------------------------------------------------------------------------
/gpio_drv.h:
--------------------------------------------------------------------------------
1 | /******************************************************************************\
2 | * Native ESXi driver for the RPi's GPIO interface.
3 | *
4 | * Tom Hebel, 2020
5 | \******************************************************************************/
6 |
7 | /*
8 | * gpio_drv.h --
9 | *
10 | * Definition for gpio device layer interface
11 | */
12 |
13 | #ifndef GPIO_DRV_H
14 | #define GPIO_DRV_H
15 |
16 | #include "gpio.h"
17 | #include "gpio_types.h"
18 |
19 | /***********************************************************************/
20 |
21 | /*
22 | * As documented in:
23 | * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2711/rpi_DATA_2711_1p0.pdf
24 | */
25 | #define GPIO_MMIO_SIZE 0xf4
26 |
27 | /* Each register is 4 bytes in size */
28 | #define GPIO_REG_SIZE 4
29 |
30 | #define GPIO_NUM_REGS (GPIO_MMIO_SIZE / GPIO_REG_SIZE)
31 |
32 | #define GPIO_POLL_TIMEOUT_SEC 1
33 |
34 | /***********************************************************************/
35 |
36 | /*
37 | * Packing the struct to avoid alignment issues when passing between UW and
38 | * driver. Must be exactly 64 bits in size!
39 | */
40 | #pragma pack(push, 1)
41 | typedef struct gpio_IoctlData_t {
42 | vmk_uint16 offset;
43 | union {
44 | vmk_uint32 mask;
45 | vmk_uint32 data;
46 | };
47 | /* We might use this for something at some point */
48 | vmk_uint16 __padding;
49 | } gpio_IoctlData_t;
50 | #pragma pack(pop)
51 |
52 | /*
53 | * An opaque version of gpio_IoctlData_t, to be used outside of gpio_drv.c.
54 | */
55 | typedef vmk_uint64 gpio_IoctlCookie_t;
56 |
57 | typedef enum gpio_IoctlCommands_t {
58 | GPIO_IOCTL_INVALID = 0,
59 | /* Poll a GPIO register until masked bits change */
60 | GPIO_IOCTL_POLL = 1,
61 | /* Used for sanity */
62 | GPIO_IOCTL_MAX = GPIO_IOCTL_POLL,
63 | } gpio_IoctlCommands_t;
64 |
65 | /***********************************************************************/
66 |
67 | VMK_ReturnStatus gpio_drvInit(gpio_Driver_t *driver,
68 | gpio_Device_t *adapter);
69 |
70 | VMK_ReturnStatus gpio_mmioPoll(vmk_uint32 offset,
71 | vmk_uint32 mask,
72 | vmk_uint32 *data);
73 |
74 | VMK_ReturnStatus gpio_mmioDirectRead(vmk_uint32 offset,
75 | vmk_uint32 *value);
76 |
77 | VMK_ReturnStatus gpio_mmioDirectWrite(vmk_uint32 offset,
78 | vmk_uint32 value);
79 |
80 | VMK_ReturnStatus gpio_mmioOpenCB(vmk_CharDevFdAttr *attr);
81 |
82 | VMK_ReturnStatus gpio_mmioCloseCB(vmk_CharDevFdAttr *attr);
83 |
84 | VMK_ReturnStatus gpio_mmioIoctlCB(unsigned int cmd,
85 | gpio_IoctlCookie_t *data);
86 |
87 | VMK_ReturnStatus gpio_mmioReadCB(char *buffer,
88 | vmk_ByteCount nbytes,
89 | vmk_loff_t *ppos,
90 | vmk_ByteCountSigned *nread);
91 |
92 | VMK_ReturnStatus gpio_mmioWriteCB(char *buffer,
93 | vmk_ByteCount nbytes,
94 | vmk_loff_t *ppos,
95 | vmk_ByteCountSigned *nwritten);
96 |
97 | /***********************************************************************/
98 |
99 | #endif /* GPIO_DRV_H */
--------------------------------------------------------------------------------
/gpio_os.c:
--------------------------------------------------------------------------------
1 | /******************************************************************************\
2 | * Native ESXi driver for the RPi's GPIO interface.
3 | *
4 | * Tom Hebel, 2020
5 | \******************************************************************************/
6 |
7 | /*
8 | * gpio_os.c --
9 | *
10 | * Module initialization and other OS stuff.
11 | */
12 |
13 | #include "gpio.h"
14 | #include "gpio_types.h"
15 | #include "gpio_drv.h"
16 | #include "gpio_charDev.h"
17 |
18 | /***********************************************************************/
19 |
20 | static gpio_Driver_t gpio_Driver;
21 | static gpio_Device_t gpio_Device;
22 |
23 | /* For chardev */
24 | static gpio_CharDev_t gpio_CharDev;
25 | static vmk_BusType gpio_logicalBusType;
26 | static vmk_uint32 gpio_logicalPort = 0;
27 |
28 | VMK_ReturnStatus gpio_attachDevice(vmk_Device device);
29 | VMK_ReturnStatus gpio_scanDevice(vmk_Device device);
30 | VMK_ReturnStatus gpio_detachDevice(vmk_Device device);
31 | VMK_ReturnStatus gpio_quiesceDevice(vmk_Device device);
32 | VMK_ReturnStatus gpio_startDevice(vmk_Device device);
33 | void gpio_forgetDevice(vmk_Device device);
34 |
35 | static vmk_DriverOps gpio_DriverOps = {
36 | .attachDevice = gpio_attachDevice,
37 | .scanDevice = gpio_scanDevice,
38 | .detachDevice = gpio_detachDevice,
39 | .quiesceDevice = gpio_quiesceDevice,
40 | .startDevice = gpio_startDevice,
41 | .forgetDevice = gpio_forgetDevice,
42 | };
43 |
44 | /*
45 | * Callbacks gluing the chardev driver to the GPIO driver "back-end".
46 | */
47 | static gpio_CharDevCallbacks_t gpio_CharDevCBs = {
48 | .open = gpio_mmioOpenCB,
49 | .close = gpio_mmioCloseCB,
50 | .ioctl = gpio_mmioIoctlCB,
51 | .read = gpio_mmioReadCB,
52 | .write = gpio_mmioWriteCB,
53 | };
54 |
55 | /*
56 | ***********************************************************************
57 | * init_module --
58 | *
59 | * Entry point for driver.
60 | *
61 | * Results:
62 | * VMK_OK on success, error code otherwise
63 | *
64 | * Side Effects:
65 | * None
66 | ***********************************************************************
67 | */
68 | VMK_ReturnStatus
69 | init_module(void)
70 | {
71 | VMK_ReturnStatus status;
72 | vmk_HeapID heapID;
73 | vmk_HeapCreateProps heapProps;
74 | vmk_DriverProps driverProps;
75 | vmk_LogProperties logProps;
76 | vmk_Name logicalBusName; /* For the chardev */
77 |
78 | status = vmk_NameInitialize(&gpio_Driver.driverName, GPIO_DRIVER_NAME);
79 | VMK_ASSERT(status == VMK_OK);
80 |
81 | gpio_Driver.moduleID = vmk_ModuleCurrentID;
82 |
83 | gpio_Device.initialized = VMK_FALSE;
84 |
85 | /*
86 | * Create heap
87 | */
88 |
89 | heapProps.type = VMK_HEAP_TYPE_SIMPLE;
90 | status = vmk_NameInitialize(&heapProps.name, GPIO_DRIVER_NAME);
91 | VMK_ASSERT(status == VMK_OK);
92 | heapProps.module = gpio_Driver.moduleID;
93 | heapProps.initial = GPIO_HEAP_INITIAL_SIZE;
94 | heapProps.creationTimeoutMS = VMK_TIMEOUT_NONBLOCKING;
95 | heapProps.max = GPIO_HEAP_MAX_SIZE;
96 |
97 | status = vmk_HeapCreate(&heapProps, &heapID);
98 | if (status != VMK_OK) {
99 | vmk_WarningMessage("vmk_HeapCreate failed: %s",
100 | vmk_StatusToString(status));
101 | goto heap_create_failed;
102 | }
103 |
104 | vmk_ModuleSetHeapID(gpio_Driver.moduleID, heapID);
105 | VMK_ASSERT(vmk_ModuleGetHeapID(gpio_Driver.moduleID) == heapID);
106 | gpio_Driver.heapID = heapID;
107 |
108 | /*
109 | * Set up logger
110 | */
111 |
112 | status = vmk_NameInitialize(&logProps.name, GPIO_DRIVER_NAME);
113 | VMK_ASSERT(status == VMK_OK);
114 | logProps.module = gpio_Driver.moduleID;
115 | logProps.heap = gpio_Driver.heapID;
116 | logProps.defaultLevel = 0;
117 | logProps.throttle = NULL;
118 |
119 | status = vmk_LogRegister(&logProps, &gpio_Driver.logger);
120 | if (status != VMK_OK) {
121 | vmk_WarningMessage("failed to register logger: %s",
122 | vmk_StatusToString(status));
123 | goto logger_reg_failed;
124 | }
125 | vmk_LogSetCurrentLogLevel(gpio_Driver.logger, GPIO_LOG_INIT);
126 |
127 | /*
128 | * Init logical bus
129 | */
130 |
131 | status = vmk_NameInitialize(&logicalBusName, VMK_LOGICAL_BUS_NAME);
132 | status = vmk_BusTypeFind(&logicalBusName, &gpio_logicalBusType);
133 | if (status != VMK_OK) {
134 | vmk_Warning(gpio_Driver.logger,
135 | "vmk_BusTypeFind for logical bus failed: %s",
136 | vmk_StatusToString(status));
137 | goto logical_bus_failed;
138 | }
139 |
140 | /*
141 | * Register driver
142 | */
143 |
144 | vmk_Memset(&driverProps, 0, sizeof(vmk_DriverProps));
145 | status = vmk_NameInitialize(&driverProps.name, GPIO_DRIVER_NAME);
146 | VMK_ASSERT(status == VMK_OK);
147 | driverProps.moduleID = gpio_Driver.moduleID;
148 | driverProps.ops = &gpio_DriverOps;
149 |
150 | status = vmk_DriverRegister(&driverProps,
151 | &(gpio_Driver.driverHandle));
152 | if (status == VMK_OK) {
153 | vmk_Log(gpio_Driver.logger,
154 | "vmk_DriverRegister successful",
155 | GPIO_DRIVER_NAME,
156 | __FUNCTION__);
157 | }
158 | else {
159 | vmk_Warning(gpio_Driver.logger,
160 | "vmk_DriverRegister failed: %s",
161 | vmk_StatusToString(status));
162 | goto driver_register_failed;
163 | }
164 |
165 | /*
166 | * Init GPIO driver
167 | */
168 | gpio_drvInit(&gpio_Driver, &gpio_Device);
169 |
170 | return VMK_OK;
171 |
172 | driver_register_failed:
173 | logical_bus_failed:
174 | vmk_LogUnregister(gpio_Driver.logger);
175 |
176 | logger_reg_failed:
177 | vmk_HeapDestroy(gpio_Driver.heapID);
178 |
179 | heap_create_failed:
180 | vmk_Warning(gpio_Driver.logger,
181 | "%s: %s failed",
182 | GPIO_DRIVER_NAME,
183 | __FUNCTION__);
184 |
185 | return status;
186 | }
187 |
188 | /*
189 | ***********************************************************************
190 | * cleanup_module --
191 | *
192 | * Cleans up the module.
193 | *
194 | * Results:
195 | * None.
196 | *
197 | * Side Effects:
198 | * None
199 | ***********************************************************************
200 | */
201 | void cleanup_module(void)
202 | {
203 | vmk_BusTypeRelease(gpio_logicalBusType);
204 | vmk_HeapDestroy(gpio_Driver.heapID);
205 | vmk_ACPIUnmapIOResource(gpio_Driver.moduleID, gpio_Device.acpiDevice, 0);
206 | vmk_DriverUnregister(gpio_Driver.driverHandle);
207 | }
208 |
209 | /*
210 | ***********************************************************************
211 | * gpio_attachDevice --
212 | *
213 | * Driver callback that attaches gpio logical device to the driver.
214 | *
215 | * Results:
216 | * VMK_OK on success, error code otherwise
217 | *
218 | * Side Effects:
219 | * None
220 | ***********************************************************************
221 | */
222 | VMK_ReturnStatus
223 | gpio_attachDevice(vmk_Device device)
224 | {
225 | VMK_ReturnStatus status = VMK_OK;
226 | vmk_ACPIDevice acpiDev;
227 | vmk_ACPIInfo acpiInfo;
228 | vmk_MappedResourceAddress mappedAddr;
229 | gpio_Device_t *adapter = &gpio_Device;
230 |
231 | /*
232 | * Since there is only one gpio device on the system board, we're making
233 | * sure we're not initializing more.
234 | */
235 | if (adapter->initialized != VMK_FALSE) {
236 | vmk_Warning(gpio_Driver.logger,
237 | "gpio device %p already initialized as %p",
238 | device,
239 | adapter->vmkDevice);
240 | status = VMK_EXISTS;
241 | goto device_already_exists;
242 | }
243 |
244 | /*
245 | * Get vmk acpi dev
246 | */
247 | status = vmk_DeviceGetRegistrationData(device, (vmk_AddrCookie *)&acpiDev);
248 | if (status != VMK_OK) {
249 | vmk_Warning(gpio_Driver.logger,
250 | "failed to get acpi dev for vmk dev %p: %s",
251 | device,
252 | vmk_StatusToString(status));
253 | goto get_acpi_dev_failed;
254 | }
255 |
256 | /*
257 | * Find out if this is an acpi dev
258 | */
259 | status = vmk_ACPIQueryInfo(acpiDev, &acpiInfo);
260 | if (status != VMK_OK) {
261 | goto not_an_acpi_dev;
262 | }
263 |
264 | vmk_Log(gpio_Driver.logger,
265 | "retrieved acpi dev %p for vmk dev %p",
266 | acpiDev,
267 | device);
268 |
269 | /*
270 | * Map io resources
271 | */
272 |
273 | status = vmk_ACPIMapIOResource(gpio_Driver.moduleID,
274 | acpiDev,
275 | 0,
276 | &mappedAddr);
277 | if (status != VMK_OK) {
278 | vmk_Warning(gpio_Driver.logger,
279 | "unable to acquire io resources"
280 | " for device %p: %s",
281 | device,
282 | vmk_StatusToString(status));
283 | goto iores_map_failed;
284 | }
285 | else if (mappedAddr.type != VMK_IORESOURCE_MEM) {
286 | vmk_Warning(gpio_Driver.logger,
287 | "mapped io space %p of type %d, expecting %d",
288 | mappedAddr,
289 | mappedAddr.type,
290 | VMK_IORESOURCE_MEM);
291 | goto iores_map_failed;
292 | }
293 |
294 | vmk_Log(gpio_Driver.logger,
295 | "mapped io space at %p of length %d for device %p",
296 | (void*)mappedAddr.address.vaddr,
297 | mappedAddr.len,
298 | device);
299 |
300 | /*
301 | * Init adapter
302 | */
303 |
304 | vmk_Memset(adapter, 0, sizeof(*adapter));
305 | adapter->vmkDevice = device;
306 | adapter->acpiDevice = acpiDev;
307 | adapter->acpiInfo = acpiInfo;
308 | vmk_AtomicWrite64(&adapter->refCount, 0);
309 | adapter->wakeWorld = VMK_INVALID_WORLD_ID;
310 | vmk_Memcpy(&adapter->mmioMappedAddr,
311 | &mappedAddr,
312 | sizeof(adapter->mmioMappedAddr));
313 | adapter->mmioBase = (void *)mappedAddr.address.vaddr;
314 | adapter->mmioLen = mappedAddr.len;
315 |
316 | /* Currently unused */
317 | vmk_AtomicWrite64(&adapter->intCount, 0);
318 | vmk_AtomicWrite64(&adapter->intState, 0);
319 |
320 | adapter->initialized = VMK_TRUE;
321 |
322 | /*
323 | * Attach device
324 | */
325 | status = vmk_DeviceSetAttachedDriverData(adapter->vmkDevice, adapter);
326 | if (status != VMK_OK) {
327 | vmk_Warning(gpio_Driver.logger,
328 | "failed to attach device %p: %s",
329 | adapter->vmkDevice,
330 | vmk_StatusToString(status));
331 | goto device_attach_failed;
332 | }
333 |
334 | return status;
335 |
336 | device_attach_failed:
337 | iores_map_failed:
338 | not_an_acpi_dev:
339 | get_acpi_dev_failed:
340 | device_already_exists:
341 | vmk_Warning(gpio_Driver.logger,
342 | "no device attached",
343 | GPIO_DRIVER_NAME,
344 | __FUNCTION__);
345 | return status;
346 | }
347 |
348 | /*
349 | ***********************************************************************
350 | * gpio_scanDevice --
351 | *
352 | * Register char dev.
353 | *
354 | * Results:
355 | * VMK_OK on success, error code otherwise
356 | *
357 | * Side Effects:
358 | * None
359 | ***********************************************************************
360 | */
361 | VMK_ReturnStatus
362 | gpio_scanDevice(vmk_Device device)
363 | {
364 | VMK_ReturnStatus status = VMK_OK;
365 | gpio_CharDev_t *charDev = &gpio_CharDev;
366 |
367 | /*
368 | * Register char device
369 | */
370 | status = gpio_charDevRegister(&gpio_Driver,
371 | gpio_logicalBusType,
372 | device,
373 | charDev,
374 | gpio_logicalPort,
375 | &gpio_CharDevCBs);
376 |
377 | return status;
378 | }
379 |
380 | /*
381 | ***********************************************************************
382 | * gpio_detachDevice --
383 | *
384 | * Detaches the device from the driver.
385 | *
386 | * Results:
387 | * VMK_OK on success, error code otherwise
388 | *
389 | * Side Effects:
390 | * None
391 | ***********************************************************************
392 | */
393 | VMK_ReturnStatus
394 | gpio_detachDevice(vmk_Device device)
395 | {
396 | VMK_ReturnStatus status = VMK_OK;
397 |
398 | return status;
399 | }
400 |
401 | /*
402 | ***********************************************************************
403 | * gpio_quiesceDevice --
404 | *
405 | * Do nothing.
406 | *
407 | * Results:
408 | * VMK_OK on success, error code otherwise
409 | *
410 | * Side Effects:
411 | * None
412 | ***********************************************************************
413 | */
414 | VMK_ReturnStatus
415 | gpio_quiesceDevice(vmk_Device device)
416 | {
417 | VMK_ReturnStatus status = VMK_OK;
418 |
419 | return status;
420 | }
421 |
422 | /*
423 | ***********************************************************************
424 | * gpio_startDevice --
425 | *
426 | * Start the GPIO device.
427 | *
428 | * Results:
429 | * VMK_OK on success, error code otherwise
430 | *
431 | * Side Effects:
432 | * None
433 | ***********************************************************************
434 | */
435 | VMK_ReturnStatus
436 | gpio_startDevice(vmk_Device device)
437 | {
438 | VMK_ReturnStatus status = VMK_OK;
439 |
440 | return status;
441 | }
442 |
443 | /*
444 | ***********************************************************************
445 | * gpio_forgetDevice --
446 | *
447 | * Do nothing.
448 | *
449 | * Results:
450 | * None.
451 | *
452 | * Side Effects:
453 | * None
454 | ***********************************************************************
455 | */
456 | void
457 | gpio_forgetDevice(vmk_Device device)
458 | {
459 | return;
460 | }
--------------------------------------------------------------------------------
/gpio_types.h:
--------------------------------------------------------------------------------
1 | /******************************************************************************\
2 | * Native ESXi driver for the RPi's GPIO interface.
3 | *
4 | * Tom Hebel, 2020
5 | \******************************************************************************/
6 |
7 | /*
8 | * gpio_types.h --
9 | *
10 | * Type definitions
11 | */
12 |
13 | #ifndef GPIO_TYPES_H
14 | #define GPIO_TYPES_H
15 |
16 | /***********************************************************************/
17 |
18 | typedef struct gpio_Driver_t {
19 | vmk_Name driverName;
20 | vmk_ModuleID moduleID;
21 | vmk_HeapID heapID;
22 | vmk_Driver driverHandle;
23 | vmk_IOResource resHandle;
24 | vmk_LogComponent logger;
25 | } gpio_Driver_t;
26 |
27 | /*
28 | * The gpio adapter. It should only ever be allocated once since there is only
29 | * one gpio interface on a Raspberry Pi.
30 | */
31 | typedef struct gpio_Device_t {
32 | /* Object */
33 | vmk_Bool initialized;
34 | vmk_atomic64 refCount;
35 | /* Device */
36 | vmk_Device vmkDevice;
37 | vmk_ACPIDevice acpiDevice;
38 | vmk_ACPIInfo acpiInfo;
39 | /* Interrupts */
40 | vmk_IntrCookie intrCookie;
41 | vmk_atomic64 intCount;
42 | vmk_atomic64 intState;
43 | vmk_WorldID wakeWorld;
44 | /* MMIO */
45 | vmk_MappedResourceAddress mmioMappedAddr;
46 | char *mmioBase;
47 | vmk_ByteCount mmioLen;
48 | } gpio_Device_t;
49 |
50 | /*
51 | * Struct holding information about a world to launch while starting the GPIO
52 | * device in gpio_startDevice().
53 | */
54 | typedef struct gpio_StartUpWorld_t {
55 | char name[32];
56 | vmk_WorldID worldID;
57 | vmk_WorldStartFunc startFunc;
58 | } gpio_StartUpWorld_t;
59 |
60 | /***********************************************************************/
61 |
62 | #endif /* GPIO_TYPES_H */
--------------------------------------------------------------------------------
/pyUtil/gpioLib/__init__.py:
--------------------------------------------------------------------------------
1 | ################################################################################
2 | # UW library interfacing with the GPIO MMIO area.
3 | #
4 | # Tom Hebel, 2020
5 | ################################################################################
6 |
7 | import math
8 | import struct
9 | import fcntl
10 |
11 | #########################################################################
12 |
13 | GPIO_DEVICE_PATH = '/dev/vmgfx32'
14 | GPIO_REG_SIZE = 4
15 | GPIO_MAX_PIN = 57
16 | GPIO_MMIO_SIZE = 248
17 | GPIO_INT_MAX = pow(2, 32) - 1
18 | GPIO_PIN_MASK = 31
19 | GPIO_RPI_BYTE_ORDER = 'little'
20 | GPIO_IOCTL_COOKIE_SIZE = 8
21 |
22 | #########################################################################
23 |
24 | #
25 | # GPIO ioctl commands
26 | #
27 | GPIO_IOCTL_POLL = 1
28 |
29 | #########################################################################
30 |
31 | #
32 | # GPIO function selectors
33 | #
34 | gpioFuncSelectors = {
35 | 'IN': 0b000,
36 | 'OUT': 0b001,
37 | 'F0': 0b100,
38 | 'F1': 0b101,
39 | 'F2': 0b110,
40 | 'F3': 0b111,
41 | 'F4': 0b011,
42 | 'F5': 0b010,
43 | }
44 | gpioPinToSelReg= [
45 | 'GPFSEL0', 'GPFSEL1', 'GPFSEL2', 'GPFSEL3', 'GPFSEL4', 'GPFSEL5',
46 | ]
47 |
48 | #
49 | # Pull-up/down states
50 | #
51 | gpioPUDs = {
52 | 'OFF': 0b00,
53 | 'DN': 0b10,
54 | 'UP': 0b01,
55 | }
56 | gpioPinToPudReg = [
57 | 'GPIO_PUP_PDN_CNTRL_REG0',
58 | 'GPIO_PUP_PDN_CNTRL_REG1',
59 | 'GPIO_PUP_PDN_CNTRL_REG2',
60 | 'GPIO_PUP_PDN_CNTRL_REG3',
61 | ]
62 |
63 | #
64 | # GPIO registers
65 | #
66 | gpioRegisters = {
67 | #
68 | # Function selector registers which control the operation of the 54 gpio pins.
69 | #
70 | 'GPFSEL0': 0x00, # gpio function select 0
71 | 'GPFSEL1': 0x04, # gpio function select 1
72 | 'GPFSEL2': 0x08, # gpio function select 2
73 | 'GPFSEL3': 0x0c, # gpio function select 3
74 | 'GPFSEL4': 0x10, # gpio function select 4
75 | 'GPFSEL5': 0x14, # gpio function select 5
76 |
77 | #
78 | # Used to set a specific gpio pin. Setting to 0 has no effect.
79 | #
80 | 'GPSET0': 0x1c, # gpio pin output set 0
81 | 'GPSET1': 0x20, # gpio pin output set 1
82 |
83 | #
84 | # Used to clear gpio pin. Setting to 0 has no effect.
85 | #
86 | 'GPCLR0': 0x28, # gpio pin output clear 0
87 | 'GPCLR1': 0x2c, # gpio pin output clear 1
88 |
89 | #
90 | # Return value of a pin.
91 | #
92 | 'GPLEV0': 0x34, # gpio pin level 0
93 | 'GPLEV1': 0x38, # gpio pin level 1
94 |
95 | #
96 | # Used to retrieve signal edge detection results.
97 | #
98 | 'GPEDS0': 0x40, # gpio pin event detect status 0
99 | 'GPEDS1': 0x44, # gpio pin event detect status 1
100 | 'GPREN0': 0x4c, # gpio pin rising edge detect enable 0
101 | 'GPREN1': 0x50, # gpio pin rising edge detect enable 1
102 | 'GPFEN0': 0x58, # gpio pin falling edge detect enable 0
103 | 'GPFEN1': 0x5c, # gpio pin falling edge detect enable 1
104 | 'GPHEN0': 0x64, # gpio pin high detect enable 0
105 | 'GPHEN1': 0x68, # gpio pin high detect enable 1
106 | 'GPLEN0': 0x70, # gpio pin low detect enable 0
107 | 'GPLEN1': 0x74, # gpio pin low detect enable 1
108 | 'GPAREN0': 0x7c, # gpio pin async rising edge detect 0
109 | 'GPAREN1': 0x80, # gpio pin async rising edge detect 1
110 | 'GPAFEN0': 0x88, # gpio pin async falling edge detect 0
111 | 'GPAFEN1': 0x8c, # gpio ping async falling edge detect 1
112 |
113 | #
114 | # Pull-up/down config.
115 | #
116 | 'GPIO_PUP_PDN_CNTRL_REG0': 0xe4,
117 | 'GPIO_PUP_PDN_CNTRL_REG1': 0xe8,
118 | 'GPIO_PUP_PDN_CNTRL_REG2': 0xec,
119 | 'GPIO_PUP_PDN_CNTRL_REG3': 0xf0,
120 | }
121 |
122 | #########################################################################
123 | # GPIO --
124 | #
125 | # GPIO char dev interface class.
126 | #########################################################################
127 | class GPIO:
128 | gpioDev = open(GPIO_DEVICE_PATH, 'r+b')
129 | def __del__(self):
130 | self.gpioDev.close()
131 |
132 | #########################################################################
133 | # pollReg --
134 | #
135 | # Polls a GPIO register until the masked bits have changed. Then,
136 | # returns the registers current value.
137 | #########################################################################
138 | def pollReg(self, reg, mask):
139 | try:
140 | #
141 | # The GPIO driver's ioctl struct looks like this:
142 | #
143 | # +--------------------+
144 | # | uint16_t offset |
145 | # +--------------------+
146 | # | uint32_t mask/data |
147 | # +--------------------+
148 | # | uint16_t padding |
149 | # +--------------------+
150 | #
151 | ioctlData = bytearray(struct.pack('> shift
280 | return list(gpioPUDs.keys())[list(gpioPUDs.values()).index(key)]
281 |
282 | #########################################################################
283 | # setPull --
284 | #
285 | # Set pull-up/down state of a pin.
286 | #########################################################################
287 | def setPull(self, pin, pull):
288 | pud = gpioPUDs[pull]
289 | shift = (pin % 16) * 2
290 | reg = gpioPinToPudReg[int(math.floor(pin / 16))]
291 | oldPud = self.readReg(reg)
292 | newPud = oldPud | (0b11 << shift)
293 | newPud ^= (0b11 << shift)
294 | newPud |= (pud << shift)
295 | self.writeReg(reg, newPud)
--------------------------------------------------------------------------------
/pyUtil/gpio_fanShim.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | ################################################################################
3 | # Script to test the Pimoroni FanShim LED.
4 | #
5 | # Tom Hebel, 2020
6 | ################################################################################
7 |
8 | import sys
9 | import time
10 | import argparse
11 | from gpioLib import *
12 |
13 | #########################################################################
14 |
15 | GPIO_FANSHIM_PIN_CLOCK = 14
16 | GPIO_FANSHIM_PIN_DATA = 15
17 | GPIO_FANSHIM_PIN_BUTTON = 17
18 | GPIO_FANSHIM_PIN_FAN = 18
19 |
20 | #########################################################################
21 |
22 | class FanShim:
23 | gpio = GPIO()
24 |
25 | #########################################################################
26 | # toggleFan --
27 | #
28 | # Turn on/off the FanShim fan.
29 | #########################################################################
30 | def toggleFan(self):
31 | self.gpio.funcSel(GPIO_FANSHIM_PIN_FAN, 'OUT')
32 | val = self.gpio.levPin(GPIO_FANSHIM_PIN_FAN)
33 | if val:
34 | self.gpio.clrPin(GPIO_FANSHIM_PIN_FAN)
35 | else:
36 | self.gpio.setPin(GPIO_FANSHIM_PIN_FAN)
37 |
38 | #########################################################################
39 | # setLED --
40 | #
41 | # Turn on/off the FanShim LED.
42 | #########################################################################
43 | def setLED(self, red, green, blue):
44 | buf = [0, 0, 0, 0,
45 | 255, blue, green, red,
46 | ~0, ~0, ~0, ~0]
47 | self.gpio.funcSel(GPIO_FANSHIM_PIN_CLOCK, 'OUT')
48 | self.gpio.funcSel(GPIO_FANSHIM_PIN_DATA, 'OUT')
49 | self.gpio.clrPin(GPIO_FANSHIM_PIN_CLOCK)
50 | self.gpio.clrPin(GPIO_FANSHIM_PIN_DATA)
51 | for i in range(len(buf)):
52 | byte = buf[i]
53 | for j in range(8):
54 | bit = (byte & 0x80) > 0
55 | if bit:
56 | self.gpio.setPin(GPIO_FANSHIM_PIN_DATA)
57 | else:
58 | self.gpio.clrPin(GPIO_FANSHIM_PIN_DATA)
59 | self.gpio.setPin(GPIO_FANSHIM_PIN_CLOCK)
60 | time.sleep(0)
61 | self.gpio.clrPin(GPIO_FANSHIM_PIN_CLOCK)
62 | time.sleep(0)
63 | byte <<= 1
64 |
65 | #########################################################################
66 | # flashLED --
67 | #
68 | # Flash the FanShim LED.
69 | #########################################################################
70 | def flashLED(self, red, green, blue, durationSec, intervalMs):
71 | bufOn = [0, 0, 0, 0,
72 | 255, blue, green, red,
73 | ~0, ~0, ~0, ~0]
74 | bufOff = [0, 0, 0, 0,
75 | 255, 0, 0, 0,
76 | ~0, ~0, ~0, ~0]
77 | curBuf = bufOff
78 | intervalUs = intervalMs * 1000
79 | self.gpio.funcSel(GPIO_FANSHIM_PIN_CLOCK, 'OUT')
80 | self.gpio.funcSel(GPIO_FANSHIM_PIN_DATA, 'OUT')
81 | self.gpio.clrPin(GPIO_FANSHIM_PIN_CLOCK)
82 | self.gpio.clrPin(GPIO_FANSHIM_PIN_DATA)
83 | startTime = time.monotonic()
84 | while True:
85 | curTime = time.monotonic()
86 | if curTime - startTime > durationSec:
87 | return
88 | if curBuf == bufOff:
89 | curBuf = bufOn
90 | else:
91 | curBuf = bufOff
92 | for i in range(len(curBuf)):
93 | byte = curBuf[i]
94 | for j in range(8):
95 | bit = (byte & 0x80) > 0
96 | if bit:
97 | self.gpio.setPin(GPIO_FANSHIM_PIN_DATA)
98 | else:
99 | self.gpio.clrPin(GPIO_FANSHIM_PIN_DATA)
100 | self.gpio.setPin(GPIO_FANSHIM_PIN_CLOCK)
101 | time.sleep(0)
102 | self.gpio.clrPin(GPIO_FANSHIM_PIN_CLOCK)
103 | time.sleep(0)
104 | byte <<= 1
105 | time.sleep(intervalMs / 1000)
106 |
107 | #########################################################################
108 | # pulseLED --
109 | #
110 | # Pulse the FanShim LED.
111 | #########################################################################
112 | def pulseLED(self, col, durationSec, periodMs):
113 | buf = [0, 0, 0, 0,
114 | 255, 0, 0, 0,
115 | ~0, ~0, ~0, ~0]
116 | curCol = [0, 0, 0]
117 | intervalSec = (periodMs / 1000) / 256
118 | self.gpio.funcSel(GPIO_FANSHIM_PIN_CLOCK, 'OUT')
119 | self.gpio.funcSel(GPIO_FANSHIM_PIN_DATA, 'OUT')
120 | self.gpio.clrPin(GPIO_FANSHIM_PIN_CLOCK)
121 | self.gpio.clrPin(GPIO_FANSHIM_PIN_DATA)
122 | sign = 1
123 | startTime = time.monotonic()
124 | while True:
125 | curTime = time.monotonic()
126 | if curTime - startTime > durationSec:
127 | return
128 | for i in range(len(buf)):
129 | byte = buf[i]
130 | for j in range(8):
131 | bit = (byte & 0x80) > 0
132 | if bit:
133 | self.gpio.setPin(GPIO_FANSHIM_PIN_DATA)
134 | else:
135 | self.gpio.clrPin(GPIO_FANSHIM_PIN_DATA)
136 | self.gpio.setPin(GPIO_FANSHIM_PIN_CLOCK)
137 | time.sleep(0)
138 | self.gpio.clrPin(GPIO_FANSHIM_PIN_CLOCK)
139 | time.sleep(0)
140 | byte <<= 1
141 | if curCol[col] == 255:
142 | sign = -1
143 | elif curCol[col] == 0:
144 | sign = 1
145 | curCol[col] += sign
146 | buf[5] = curCol[0]
147 | buf[6] = curCol[1]
148 | buf[7] = curCol[2]
149 | time.sleep(0)
150 |
151 | #########################################################################
152 | # gradientLED --
153 | #
154 | # Smoothly transition from blue to green to red to blue.
155 | #########################################################################
156 | def gradientLED(self, durationSec, periodMs):
157 | buf = [0, 0, 0, 0,
158 | 255, 0, 0, 0,
159 | ~0, ~0, ~0, ~0]
160 | intervalSec = (periodMs / 1000) / 256
161 | col = [255, 0, 0]
162 | self.gpio.funcSel(GPIO_FANSHIM_PIN_CLOCK, 'OUT')
163 | self.gpio.funcSel(GPIO_FANSHIM_PIN_DATA, 'OUT')
164 | self.gpio.clrPin(GPIO_FANSHIM_PIN_CLOCK)
165 | self.gpio.clrPin(GPIO_FANSHIM_PIN_DATA)
166 | k = 1
167 | startTime = time.monotonic()
168 | while True:
169 | curTime = time.monotonic()
170 | if curTime - startTime > durationSec:
171 | return
172 | for i in range(len(buf)):
173 | byte = buf[i]
174 | for j in range(8):
175 | bit = (byte & 0x80) > 0
176 | if bit:
177 | self.gpio.setPin(GPIO_FANSHIM_PIN_DATA)
178 | else:
179 | self.gpio.clrPin(GPIO_FANSHIM_PIN_DATA)
180 | self.gpio.setPin(GPIO_FANSHIM_PIN_CLOCK)
181 | time.sleep(0)
182 | self.gpio.clrPin(GPIO_FANSHIM_PIN_CLOCK)
183 | time.sleep(0)
184 | byte <<= 1
185 | if col[k] == 255:
186 | k = (k + 1) % 3
187 | if k == 0:
188 | col[2] -= 1
189 | col[0] += 1
190 | elif k == 1:
191 | col[0] -= 1
192 | col[1] += 1
193 | elif k == 2:
194 | col[1] -= 1
195 | col[2] += 1
196 | buf[5] = col[0]
197 | buf[6] = col[1]
198 | buf[7] = col[2]
199 | time.sleep(0)
200 |
201 | #########################################################################
202 | # printBtnChng --
203 | #
204 | # Prints changes to the button state (pressed or released).
205 | #########################################################################
206 | def printBtnChng(self):
207 | self.gpio.funcSel(GPIO_FANSHIM_PIN_BUTTON, 'IN')
208 | self.gpio.setPull(GPIO_FANSHIM_PIN_BUTTON, 'UP')
209 | time.sleep(0.1)
210 | prev = self.gpio.levPin(GPIO_FANSHIM_PIN_BUTTON)
211 | while True:
212 | cur = self.gpio.pollPin(GPIO_FANSHIM_PIN_BUTTON, prev)
213 | if cur != prev:
214 | # Don't use an if/elif block here; Python doesn't handle changes
215 | # to the button state properly then.
216 | sys.stdout.write('Button pressed\n' if cur < prev \
217 | else 'Button released\n')
218 | sys.stdout.flush()
219 | prev = cur
220 | time.sleep(0)
221 |
222 | #########################################################################
223 | # main --
224 | #
225 | # Main entry point.
226 | #########################################################################
227 | def main(argv):
228 | fanShim = FanShim()
229 | progDesc = ('Utility for controlling the Pimoroni FanShim.'
230 | '\n\nCommands:'
231 | '\nfan'
232 | '\nbutton'
233 | '\nset\t '
234 | '\nflash\t '
235 | '\npulse\t<[red|green|blue]>'
236 | '\ngradient'
237 | '\n\nAll colors 0-255.')
238 | parser = argparse.ArgumentParser(prog='gpio',
239 | description=progDesc,
240 | formatter_class=argparse.RawTextHelpFormatter)
241 | parser.add_argument('command', help='interact with the FanShim accessory')
242 | parser.add_argument('args',
243 | nargs='*',
244 | help='arguments for the command')
245 | args = parser.parse_args()
246 | if args.command == 'fan':
247 | fanShim.toggleFan()
248 | elif args.command == 'set':
249 | try:
250 | red = int(args.args[0])
251 | green = int(args.args[1])
252 | blue = int(args.args[2])
253 | except Exception as e:
254 | print('Invalid color: {}'.format(str(e)))
255 | exit(1)
256 | fanShim.setLED(red, green, blue)
257 | elif args.command == 'flash':
258 | try:
259 | red = int(args.args[0])
260 | green = int(args.args[1])
261 | blue = int(args.args[2])
262 | except Exception as e:
263 | print('Invalid color: {}'.format(str(e)))
264 | exit(1)
265 | fanShim.flashLED(red, green, blue, 5, 500)
266 | fanShim.setLED(0, 0, 0)
267 | elif args.command == 'pulse':
268 | arg = args.args[0]
269 | cols = ['blue', 'green', 'red']
270 | if arg not in cols:
271 | print('Invalid color: {}'.format(col))
272 | print('Must be one of: red, green, blue')
273 | exit(1)
274 | col = cols.index(arg)
275 | fanShim.pulseLED(col, 5, 500)
276 | fanShim.setLED(0, 0, 0)
277 | elif args.command == 'gradient':
278 | fanShim.gradientLED(8, 2000)
279 | fanShim.setLED(0, 0, 0)
280 | elif args.command == 'button':
281 | fanShim.printBtnChng()
282 |
283 | if __name__ == '__main__':
284 | main(sys.argv)
--------------------------------------------------------------------------------
/pyUtil/gpio_util.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | ################################################################################
3 | # UW interface to the GPIO MMIO area.
4 | #
5 | # Tom Hebel, 2020
6 | ################################################################################
7 |
8 | import sys
9 | import argparse
10 | import time
11 | from gpioLib import *
12 |
13 | #########################################################################
14 |
15 | class GPIOCommands:
16 | gpio = GPIO()
17 |
18 | #########################################################################
19 | # infoCmd --
20 | #
21 | # Get information about the GPIO interface, such as the MMiO area size.
22 | #########################################################################
23 | def infoCmd(self, args):
24 | print('\nAll registers are 4 bytes long.')
25 | print('All pins are either set (1) or cleared (0).')
26 | print(('\nRegisters:\n'
27 | + '\n'.join([x for x in gpioRegisters.keys()])))
28 | print(('\nFunction Selectors:\n'
29 | + '\n'.join([x for x in gpioFuncSelectors.keys()])))
30 | print(('\nPull-up/down States:\n'
31 | + '\n'.join([x for x in gpioPUDs.keys()])))
32 |
33 | #########################################################################
34 | # readRegCmd --
35 | #
36 | # Command func for reading register value.
37 | #########################################################################
38 | def readRegCmd(self, subParsers, argv):
39 | parser = subParsers.add_parser('gpio read',
40 | help=('Read the value of a GPIO'
41 | ' register'))
42 | parser.add_argument('register',
43 | nargs=1,
44 | type=str,
45 | help='the GPIO register to read')
46 | args = parser.parse_args(argv)
47 | reg = str(args.register[0])
48 | if reg not in gpioRegisters.keys():
49 | print('Invalid register: {}'.format(reg))
50 | exit(1)
51 | print(self.gpio.readReg(reg))
52 |
53 | #########################################################################
54 | # writeRegCmd --
55 | #
56 | # Command func for writing register value.
57 | #########################################################################
58 | def writeRegCmd(self, subParsers, argv):
59 | parser = subParsers.add_parser('gpio write',
60 | help=('Write 4 bytes to a GPIO'
61 | ' register'))
62 | parser.add_argument('register',
63 | nargs=1,
64 | type=str,
65 | help='the GPIO register to write to')
66 | parser.add_argument('value',
67 | nargs=1,
68 | type=int,
69 | help='the 4-byte value to write')
70 | args = parser.parse_args(argv)
71 | reg = str(args.register[0])
72 | val = int(args.value[0])
73 | if val > GPIO_INT_MAX:
74 | print('Value doesn\'t fit into 4 bytes: {}'.format(val))
75 | exit(1)
76 | if reg not in gpioRegisters.keys():
77 | print('Invalid register: {}'.format(reg))
78 | exit(1)
79 | self.gpio.writeReg(reg, val)
80 |
81 | #########################################################################
82 | # funcSelCmd --
83 | #
84 | # Command func for setting the function of a GPIO pin.
85 | #########################################################################
86 | def funcSelCmd(self, subParsers, argv):
87 | parser = subParsers.add_parser('gpio fsel',
88 | help=('sets the function of a GPIO'
89 | ' pin'))
90 | parser.add_argument('pin',
91 | nargs=1,
92 | type=int,
93 | help='the GPIO pin')
94 | parser.add_argument('func',
95 | nargs=1,
96 | type=str,
97 | help='the function to select')
98 | args = parser.parse_args(argv)
99 | pin = int(args.pin[0])
100 | func = str(args.func[0])
101 | if pin > GPIO_MAX_PIN:
102 | print('Invalid pin: {}'.format(pin))
103 | exit(1)
104 | if func not in gpioFuncSelectors.keys():
105 | print('Invalid function: {}'.format(func))
106 | exit(1)
107 | self.gpio.funcSel(pin, func)
108 |
109 | #########################################################################
110 | # setPinCmd --
111 | #
112 | # Command func for setting a GPIO pin to 1.
113 | #########################################################################
114 | def setPinCmd(self, subParsers, argv):
115 | parser = subParsers.add_parser('gpio set',
116 | help=('sets a GPIO pin to 1'))
117 | parser.add_argument('pin',
118 | nargs=1,
119 | type=int,
120 | help='the GPIO pin')
121 | args = parser.parse_args(argv)
122 | pin = int(args.pin[0])
123 | if pin > GPIO_MAX_PIN:
124 | print('Invalid pin: {}'.format(pin))
125 | exit(1)
126 | self.gpio.setPin(pin)
127 |
128 | #########################################################################
129 | # clrPinCmd --
130 | #
131 | # Command func for clearing a GPIO pin.
132 | #########################################################################
133 | def clrPinCmd(self, subParsers, argv):
134 | parser = subParsers.add_parser('clear',
135 | help=('clears a GPIO pin'))
136 | parser.add_argument('pin',
137 | nargs=1,
138 | type=int,
139 | help='the GPIO pin')
140 | args = parser.parse_args(argv)
141 | pin = int(args.pin[0])
142 | if pin > GPIO_MAX_PIN:
143 | print('Invalid pin: {}'.format(pin))
144 | exit(1)
145 | self.gpio.clrPin(pin)
146 |
147 | #########################################################################
148 | # levPinCmd --
149 | #
150 | # Command func for getting the level of a GPIO pin.
151 | #########################################################################
152 | def levPinCmd(self, subParsers, argv):
153 | parser = subParsers.add_parser('level',
154 | help=('gets the level of a GPIO'
155 | ' pin'))
156 | parser.add_argument('pin',
157 | nargs=1,
158 | type=int,
159 | help='the GPIO pin')
160 | args = parser.parse_args(argv)
161 | pin = int(args.pin[0])
162 | if pin > GPIO_MAX_PIN:
163 | print('Invalid pin: {}'.format(pin))
164 | exit(1)
165 | print(self.gpio.levPin(pin))
166 |
167 | #########################################################################
168 | # pollPinCmd --
169 | #
170 | # Command func for getting the level of a GPIO pin.
171 | #########################################################################
172 | def pollPinCmd(self, subParsers, argv):
173 | parser = subParsers.add_parser('poll',
174 | help=('polls the level of a GPIO'
175 | ' pin until it changes'))
176 | parser.add_argument('pin',
177 | nargs=1,
178 | type=int,
179 | help='the GPIO pin')
180 | args = parser.parse_args(argv)
181 | pin = int(args.pin[0])
182 | if pin > GPIO_MAX_PIN:
183 | print('Invalid pin: {}'.format(pin))
184 | exit(1)
185 | prev = self.gpio.levPin(pin)
186 | while True:
187 | cur = self.gpio.pollPin(pin, prev)
188 | if cur != prev:
189 | print(cur)
190 | break
191 | prev = cur
192 | time.sleep(0)
193 |
194 | #########################################################################
195 | # getPullCmd --
196 | #
197 | # Command func for getting the pull of a GPIO pin.
198 | #########################################################################
199 | def getPullCmd(self, subParsers, argv):
200 | parser = subParsers.add_parser('getpull',
201 | help=('gets the pull of a GPIO'
202 | ' pin'))
203 | parser.add_argument('pin',
204 | nargs=1,
205 | type=int,
206 | help='the GPIO pin')
207 | args = parser.parse_args(argv)
208 | pin = int(args.pin[0])
209 | if pin > GPIO_MAX_PIN:
210 | print('Invalid pin: {}'.format(pin))
211 | exit(1)
212 | print(self.gpio.getPull(pin))
213 |
214 | #########################################################################
215 | # setPullCmd --
216 | #
217 | # Command func for setting the pull of a GPIO pin.
218 | #########################################################################
219 | def setPullCmd(self, subParsers, argv):
220 | parser = subParsers.add_parser('setpull',
221 | help=('sets the pull of a'
222 | 'GPIO pin'))
223 | parser.add_argument('pin',
224 | nargs=1,
225 | type=int,
226 | help='the GPIO pin')
227 | parser.add_argument('pull',
228 | nargs=1,
229 | type=str,
230 | help='the pull to apply (UP, DN, OFF)')
231 | args = parser.parse_args(argv)
232 | pin = int(args.pin[0])
233 | pull = str(args.pull[0])
234 | if pin > GPIO_MAX_PIN:
235 | print('Invalid pin: {}'.format(pin))
236 | exit(1)
237 | if pull not in gpioPUDs.keys():
238 | print('Invalid pull value: {}'.format(pull))
239 | exit(1)
240 | self.gpio.setPull(pin, pull)
241 |
242 | #########################################################################
243 | # main --
244 | #
245 | # Main entry point.
246 | #########################################################################
247 | def main(argv):
248 | cmdsObj = GPIOCommands()
249 | commands = {
250 | 'info': cmdsObj.infoCmd,
251 | 'read': cmdsObj.readRegCmd,
252 | 'write': cmdsObj.writeRegCmd,
253 | 'fsel': cmdsObj.funcSelCmd,
254 | 'set': cmdsObj.setPinCmd,
255 | 'clear': cmdsObj.clrPinCmd,
256 | 'level': cmdsObj.levPinCmd,
257 | 'poll': cmdsObj.pollPinCmd,
258 | 'getpull': cmdsObj.getPullCmd,
259 | 'setpull': cmdsObj.setPullCmd,
260 | }
261 | progDesc = ('Wrapper for the GPIO device driver.'
262 | '\nFor more information, check out the documenation for the'
263 | ' RPi 4B\'s system on a chip, BCM2711:'
264 | '\nhttps://www.raspberrypi.org/documentation/hardware/'
265 | 'raspberrypi/bcm2711/rpi_DATA_2711_1p0.pdf'
266 | '\n\nRun `./self.gpio.py info` to get information about the GPIO'
267 | ' interface.'
268 | '\n\nAll registers are 4 bytes long.'
269 | '\n\nCommands:'
270 | '\nread\t'
271 | '\nwrite\t '
272 | '\nfsel\t '
273 | '\nset\t'
274 | '\nclear\t'
275 | '\nlevel\t'
276 | '\npoll\t '
277 | '\ngetpull\t'
278 | '\nsetpull\t ')
279 | parser = argparse.ArgumentParser(prog='gpio',
280 | description=progDesc,
281 | formatter_class=argparse.RawTextHelpFormatter)
282 | parser.add_argument('command', help='interact with the GPIO interface')
283 | parser.add_argument('args',
284 | nargs='*',
285 | help='arguments for the command')
286 | args = parser.parse_args()
287 | if args.command not in commands.keys():
288 | print('Invalid command: {}'.format(args.command))
289 | exit(1)
290 | subParsers = parser.add_subparsers()
291 | # Call the command func
292 | commands[args.command](subParsers, args.args)
293 |
294 | if __name__ == '__main__':
295 | main(sys.argv)
--------------------------------------------------------------------------------