mirror of
https://github.com/sipeed/NanoKVM.git
synced 2026-11-10 18:09:19 -06:00
Compare commits
130 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a9f5f9e6e | ||
|
|
cffa9a97f3 | ||
|
|
1bf986d41b | ||
|
|
eb343b0d31 | ||
|
|
628cb3b8c9 | ||
|
|
ebd391ec26 | ||
|
|
a3b577e563 | ||
|
|
f60adcc797 | ||
|
|
ee77f7bed1 | ||
|
|
6ef83cb22f | ||
|
|
1a3fd15fdf | ||
|
|
3e5a46570c | ||
|
|
2c4fbf7179 | ||
|
|
5409fb783c | ||
|
|
4cec610aba | ||
|
|
57d88d7889 | ||
|
|
7f305ae3cf | ||
|
|
eb4ac33fcd | ||
|
|
7a3c6c1690 | ||
|
|
ca63609545 | ||
|
|
bcaa8896ee | ||
|
|
13b19e3889 | ||
|
|
99d1fc0a64 | ||
|
|
86b7d676dd | ||
|
|
1adf6edffc | ||
|
|
a16af99648 | ||
|
|
58d5ab2d37 | ||
|
|
9aebabfd59 | ||
|
|
21b119294c | ||
|
|
e3a79daf89 | ||
|
|
4813e8bd50 | ||
|
|
93c0f3d353 | ||
|
|
be5a9f7515 | ||
|
|
36a1eb8eb8 | ||
|
|
20b452f3c0 | ||
|
|
b062569aab | ||
|
|
c91ce5ab62 | ||
|
|
602615e489 | ||
|
|
0e11855011 | ||
|
|
c5c768b143 | ||
|
|
5c0f098069 | ||
|
|
8bd7e02ec5 | ||
|
|
9233bd7c79 | ||
|
|
7d2664acb5 | ||
|
|
8c09cd7c44 | ||
|
|
aef8a7a851 | ||
|
|
8ed6e2eca3 | ||
|
|
a7b5091024 | ||
|
|
cd6da8b588 | ||
|
|
2d755fdb98 | ||
|
|
f231df4f65 | ||
|
|
95c8edcd3d | ||
|
|
d94eba0ab1 | ||
|
|
b48729c594 | ||
|
|
b9ba107587 | ||
|
|
c2d3224552 | ||
|
|
592939895b | ||
|
|
8d8c57bc48 | ||
|
|
8330b1b1fe | ||
|
|
9fdcb428c0 | ||
|
|
cee9bbb079 | ||
|
|
fcc09e2ef0 | ||
|
|
5d96a8060f | ||
|
|
023fb4b68b | ||
|
|
594c205a29 | ||
|
|
fc436e2743 | ||
|
|
125eddffe1 | ||
|
|
aad3d718e4 | ||
|
|
e3575e629c | ||
|
|
7bd7b77fa9 | ||
|
|
ba620db901 | ||
|
|
3259379345 | ||
|
|
08eb89dc5c | ||
|
|
1376929897 | ||
|
|
614ef4031c | ||
|
|
d9a19e132a | ||
|
|
7795191a46 | ||
|
|
5e3c4d98f2 | ||
|
|
de28273def | ||
|
|
148fb4e37b | ||
|
|
a920fcc58a | ||
|
|
5021ab8d2f | ||
|
|
0ae97e6dd7 | ||
|
|
8c2e744d42 | ||
|
|
1ea9dc0561 | ||
|
|
e4367f0b69 | ||
|
|
f973316f8f | ||
|
|
b5e48a07e8 | ||
|
|
2ec5393bd0 | ||
|
|
01c837f6ac | ||
|
|
899aa48875 | ||
|
|
29dd37852d | ||
|
|
9c0c50140b | ||
|
|
33d0eb3297 | ||
|
|
922a7772b8 | ||
|
|
38ddd89e56 | ||
|
|
b664411992 | ||
|
|
7a301f588a | ||
|
|
e5ebb36f78 | ||
|
|
01e4802247 | ||
|
|
66b1ea26c5 | ||
|
|
fbf2531dbb | ||
|
|
3debfc4cf1 | ||
|
|
b35542a446 | ||
|
|
dcf5bc99dd | ||
|
|
f984b9f870 | ||
|
|
8c547b8baa | ||
|
|
a265eaef15 | ||
|
|
f8eaa7f615 | ||
|
|
c47fc483a2 | ||
|
|
64254052c0 | ||
|
|
b99a8358a7 | ||
|
|
ed0a6f6681 | ||
|
|
055df124b3 | ||
|
|
06097068a2 | ||
|
|
ff5398ed67 | ||
|
|
c8dcdf7921 | ||
|
|
e09a0b14e0 | ||
|
|
0dbf8c007f | ||
|
|
a24c1b0cef | ||
|
|
42dc6ad079 | ||
|
|
d670cf19a1 | ||
|
|
7fe7c49e65 | ||
|
|
5c1e617862 | ||
|
|
9ff9d59aa5 | ||
|
|
7556d154d3 | ||
|
|
df808da5d6 | ||
|
|
06bd869fb1 | ||
|
|
7343ca3811 | ||
|
|
59d5944398 |
14
.gitignore
vendored
14
.gitignore
vendored
@@ -16,4 +16,16 @@ web/web
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
NanoKVM-Server
|
||||
NanoKVM-Server
|
||||
|
||||
support/sg2002/kvm_system/build
|
||||
support/sg2002/kvm_system/dist
|
||||
support/sg2002/kvm_system/CMakeLists.txt
|
||||
|
||||
support/sg2002/kvm_vision_test/build
|
||||
support/sg2002/kvm_vision_test/dist
|
||||
support/sg2002/kvm_vision_test/CMakeLists.txt
|
||||
|
||||
support/sg2002/additional/original
|
||||
kvmapp/server/dl_lib
|
||||
kvmapp/kvm_system/kvm_system
|
||||
|
||||
91
CHANGELOG.md
Normal file
91
CHANGELOG.md
Normal file
@@ -0,0 +1,91 @@
|
||||
## 2.2.2 [58d5ab2](https://github.com/sipeed/NanoKVM/commit/58d5ab2d37244b1e1a68b925a5c23c324c489ad3) (2025-03-11)
|
||||
|
||||
* feat: add watchdog for NanoKVM-Server
|
||||
* feat: add support for UE chip
|
||||
* feat: support system reboot
|
||||
* feat: support enable/disable mDNS
|
||||
* fix: resolve UE chip cannot start server
|
||||
* perf: refactor automatic resolution detection
|
||||
* perf: add output prompt for unsupported resolutions
|
||||
* perf: add lock to kvmv_read_img
|
||||
* perf: configurable VENC automatic recycling feature
|
||||
* perf: add maximum limit for vi
|
||||
* perf: add tooltips to menu bar (thanks to [@S33G](https://github.com/S33G))
|
||||
* perf: menu bar is now draggable (thanks to [@forumi0721](https://github.com/forumi0721))
|
||||
* perf: image list support auto-refresh (thanks to [@forumi0721](https://github.com/forumi0721))
|
||||
* perf: update translations
|
||||
|
||||
## 2.2.1 [b5e48a0](https://github.com/sipeed/NanoKVM/commit/b5e48a07e82df3aedd60442342ae50b95684a697) (2025-02-21)
|
||||
|
||||
* fix: mounted image were not being detected correctly
|
||||
* perf: add support for CD-ROM mode when mounting image (thanks to [@scpcom](https://github.com/scpcom))
|
||||
* perf: add a loading state during login
|
||||
* perf: add changelog link in settings
|
||||
* perf: update translation and cleanup the code (thanks to [@ChokunPlayZ](https://github.com/ChokunPlayZ) [@Stoufiler](https://github.com/Stoufiler) [@polyzium](https://github.com/polyzium) [@Jonher937](https://github.com/Jonher937) [@S33G](https://github.com/S33G))
|
||||
|
||||
## 2.2.0 [0dbf8c0](https://github.com/sipeed/NanoKVM/commit/0dbf8c007f2d0183d0f0601c3da6d3c3fccd8b31) (2025-02-17)
|
||||
|
||||
NanoKVM [Image v1.4.0](https://github.com/sipeed/NanoKVM/releases/tag/v1.4.0) has been released!
|
||||
|
||||
> Please refer to the [wiki](https://wiki.sipeed.com/hardware/en/kvm/NanoKVM/system/introduction.html) for more details about the image and application.
|
||||
|
||||
* fix: improve password update notification logic (thanks to [@li20034](https://github.com/li20034))
|
||||
* perf: increase update wait time to 10s (from 6s)
|
||||
* perf: update Korean translation (thanks to [@forumi0721](https://github.com/forumi0721))
|
||||
* perf: update Traditional Chinese translation (thanks to [@protonchang](https://github.com/protonchang))
|
||||
* refactor: update `libkvm.so` and `libkvm_mmf.so` libraries
|
||||
|
||||
## 2.1.6 [6eb4a4e](https://github.com/sipeed/NanoKVM/commit/6eb4a4ea6254f465a47f9881d13934c686649061) (2025-02-14)
|
||||
|
||||
* feat: support downloading image from online URL (thanks to [@Itxaka](https://github.com/Itxaka))
|
||||
* feat: add keyboard shortcut `Ctrl+Alt+Del` (thanks to [@CaffeeLake](https://github.com/CaffeeLake))
|
||||
* fix: fix the CSRF issue
|
||||
* perf: add an option to configure custom ICE servers (thanks to [@VMFortress](https://github.com/VMFortress))
|
||||
* perf: removed unnecessary modifications to DNS configuration
|
||||
* perf: add an SSH enable/disable toggle in the web UI
|
||||
* perf: add a Tailscale enable/disable toggle in the web UI
|
||||
* perf: download Tailscale installation package from the official source
|
||||
* perf: automatic enable/disable GOMEMLIMIT on tailscale start/stop
|
||||
* perf: add JWT configuration
|
||||
* secretKey: customize secret key. If empty, generated a random 64-byte secret key by default
|
||||
* refreshTokenDuration: customize token expiration time
|
||||
* revokeTokensOnLogout: invalidate all JWT tokens on logout
|
||||
* perf: implement secure password storage using bcrypt hashing
|
||||
* perf: implement integrity checks for online updates
|
||||
* refactor: refactor HDMI module and remove the dependency `libmaixcam_lib.so`
|
||||
* refactor: web terminal use pty instead of SSH
|
||||
* refactor: move Tailscale APIs from the `network` module to the `extensions` module
|
||||
|
||||
## 2.1.5 [85f6447](https://github.com/sipeed/NanoKVM/commit/85f6447a16cc2591c6459b7d3dfda4d4cb75e98c) (2025-01-14)
|
||||
|
||||
* feat: add HDMI reset for NanoKVM-PCIe
|
||||
* fix: remove unnecessary lock acquisition during HID reset
|
||||
* refactor: refactor Tailscale
|
||||
|
||||
## 2.1.4 [d7ca7c4](https://github.com/sipeed/NanoKVM/commit/d7ca7c453d821ad099bf79b463969419041279cb) (2025-01-10)
|
||||
|
||||
* feat: support configuring OLED sleep settings
|
||||
* feat: support setting the `GOMEMLIMIT` environment variable
|
||||
* fix: fix Wi-Fi configuration
|
||||
* perf: password changes now update both the web user and the system root user
|
||||
* perf: add MAC address verification for Wake-on-LAN
|
||||
* refactor: a lot update to web UI
|
||||
* refactor: refactor Tailscale
|
||||
|
||||
## 2.1.3 [26078fe](https://github.com/sipeed/NanoKVM/commit/26078fe46e43d4543d7b09901b4992e4fbe4f01f) (2024-12-27)
|
||||
|
||||
* feat: add API to retrieve Wi-Fi information
|
||||
* fix: fix keyboard modifier keys
|
||||
* fix: update keyboard and mouse HID codes
|
||||
* fix: update hardware version information
|
||||
|
||||
## 2.1.2 [5a39562](https://github.com/sipeed/NanoKVM/commit/5a39562f2d32695933f4e7e86866136236cc9903) (2024-12-04)
|
||||
|
||||
* feat: add hardware version to configuration
|
||||
* feat: add Wi-Fi configuration support for NanoKVM-PCIe
|
||||
* perf: update web UI
|
||||
* chore: add dependency libraries
|
||||
|
||||
## 2.1.1 [74a303b](https://github.com/sipeed/NanoKVM/commit/74a303bd5cbb58f9d8ddd81abaaf4919dbbfb71b) (2024-11-06)
|
||||
|
||||
* feat: support h264
|
||||
114
README.md
114
README.md
@@ -15,19 +15,50 @@ English | [中文](./README_ZH.md) | [日本語](./README_JA.md)
|
||||
|
||||
</div>
|
||||
|
||||
> Your NanoKVM Power by RISC-V !
|
||||
> Your NanoKVM is powered by [RISC-V](https://en.wikipedia.org/wiki/RISC-V)!
|
||||
> If you have any issues or suggestions, creating issue here, or tell us with [MaixHub Discussion](https://maixhub.com/discussion/nanokvm).
|
||||
|
||||
## Opensource & Contribution
|
||||
|
||||
### 2025.03.04 Update Content
|
||||
+ Updated directory structure
|
||||
|
||||
``` shell
|
||||
├── kvmapp # APP update package
|
||||
│ ├── jpg_stream # Compatible for direct updates from very old versions
|
||||
│ ├── kvm_new_app # Triggers necessary components for kvm_system update
|
||||
│ ├── kvm_system # kvm_system application
|
||||
│ ├── server # NanoKVM front-end and back-end applications
|
||||
│ └── system # Necessary system components
|
||||
├── server # NanoKVM back-end
|
||||
├── support # Auxiliary functions (image subsystem, system status, system updates, screen, keys, etc.)
|
||||
│ ├── sg2002 # NanoKVM-Lite/Full/PCIe
|
||||
│ └── h618 # NanoKVM-Pro
|
||||
├── web # NanoKVM front-end
|
||||
├── LICENSE
|
||||
├── README_JA.md
|
||||
├── README.md
|
||||
├── README_ZH.md
|
||||
└── CHANGELOG.md
|
||||
```
|
||||
|
||||
+ Add NanoKVM app installation package `kvmapp`
|
||||
+ Add compilation scripts and compilation instructions
|
||||
|
||||
### Update 2025.02.19
|
||||
All components of NanoKVM were open-sourced, including the [front-end](https://github.com/sipeed/NanoKVM/tree/main/web), [back-end](https://github.com/sipeed/NanoKVM/tree/main/server), [kvm_vision](https://github.com/sipeed/NanoKVM/tree/main/vision/components/kvm), [kvm_mmf](https://github.com/sipeed/NanoKVM/tree/main/vision/components/kvm_mmf), [kvm_system](https://github.com/sipeed/NanoKVM/tree/main/support), the [kvmapp update package](https://github.com/sipeed/NanoKVM-System/tree/main/kvmapp), [system sdk](https://github.com/sipeed/LicheeRV-Nano-Build/tree/NanoKVM), and [packaging methods](https://github.com/sipeed/LicheeRV-Nano-Build/blob/NanoKVM/kvm/NanoKVM_img.sh).
|
||||
|
||||
### Update 2025.02.14
|
||||
Newest APP v2.1.6 add most security enhancements&bug fix, recommend regular users upgrade to this version or newer.
|
||||
|
||||
### Update 2025.02.05
|
||||
For users concerned about security, you can read this issue: https://github.com/sipeed/NanoKVM/issues/301 , you will find explanations addressing all security concerns.
|
||||
GitHub, as an open and transparent platform, provides a space for discussing so-called "backdoor" concerns. Open-source makes products more secure!
|
||||
|
||||
### Update 2024.10.18
|
||||
|
||||
We have opensourced the backend code in 10.8, and thank you for Civil PR it immediately, we will give you newest NanoKVM-PCIe as a reward!
|
||||
The mid-Oct batch is starting testing and packaging, most order before 10.1 will ship out next and next next week.
|
||||
We have open-sourced the backend code in 10.8, and thank you for civil PR it immediately, we will give you newest NanoKVM-PCIe as a reward!
|
||||
The mid-Oct batch is starting testing and packaging, most orders before 10.1 will ship out next and next next week.
|
||||
The aliexpress shipping date is change to Dec since 10.1, it is a conservative delivery time. the order in Oct. should send out during 11.15~12.15.
|
||||
|
||||
## Introduction
|
||||
@@ -37,33 +68,33 @@ The Lichee NanoKVM is available in two versions:
|
||||
NanoKVM Lite is a basic configuration that is suitable for individual users with certain DIY capabilities and enterprise users with bulk requirements.
|
||||
The NanoKVM Full is a full version with a sophisticated case and complete accessories, as well as a built-in system mirror card that is ready to use at boot, and is recommended for individual users.
|
||||
|
||||

|
||||

|
||||
|
||||
## Tech Specs
|
||||
|
||||
| Products | NanoKVM (Lite) | NanoKVM (Full) | PiKVM V4 |
|
||||
| --- | --- | --- | --- |
|
||||
| Compute Units | LicheeRV Nano(RISCV) | LicheeRV Nano(RISCV) | CM4 (ARM) |
|
||||
| Resolution | 1080P @ 60fps | 1080P @ 60fps | 1080P @ 60fps |
|
||||
| Video encoding | MJPEG, H264(developing) | MJPEG, H264(developing) | MJPEG, H264 |
|
||||
| Video delay | 90~230ms | 90~230ms | 100~230ms |
|
||||
| UEFI/BIOS | ✓ | ✓ | ✓ |
|
||||
| Virtual HID | ✓ | ✓ | ✓ |
|
||||
| Virtual CD-ROM | ✓ | ✓ | ✓ |
|
||||
| IPMI | ✓ | ✓ | ✓ |
|
||||
| Wake-on-LAN | ✓ | ✓ | ✓ |
|
||||
| ETH | 100M/10M | 100M/10M | 1000M/100M/10M |
|
||||
| ATX power control | None,Users can connect by their own | USB interface IO control board | RJ-45 interface IO control board |
|
||||
| OLED | None,Users can connect by their own | 128x64 0.96" white | 128x32 0.91" white |
|
||||
| UART | 2 | 2 | 1 |
|
||||
| TF Card | None | ✓ | ✓ |
|
||||
| Expansion | None | PoE | WiFi/LTE |
|
||||
| Power consumption | 0.2A@5V | 0.2A@5V | Peak 2.6A@5V |
|
||||
| Power input | PC USB can be powered | PC USB or auxiliary power supply | DC 5V 3A power supply is required |
|
||||
| Cooling | Silent fanless | Silent fanless | Fan cooling |
|
||||
| Size | 23x37x15mm <br> ~1/30 PiKVM V4 size | 40x36x36mm <br/> ~1/7 PiKVM V4 size | 120x68x44mm |
|
||||
| Products | NanoKVM (Lite) | NanoKVM (Full) | PiKVM V4 |
|
||||
|------------------- |-------------------------------------- |---------------------------------- |----------------------------------- |
|
||||
| Compute Units | LicheeRV Nano(RISCV) | LicheeRV Nano(RISCV) | CM4 (ARM) |
|
||||
| Resolution | 1080P @ 60fps | 1080P @ 60fps | 1080P @ 60fps |
|
||||
| Video encoding | MJPEG, H264 | MJPEG, H264 | MJPEG, H264 |
|
||||
| Video delay | 90~230ms | 90~230ms | 100~230ms |
|
||||
| UEFI/BIOS | ✓ | ✓ | ✓ |
|
||||
| Virtual HID | ✓ | ✓ | ✓ |
|
||||
| Virtual CD-ROM | ✓ | ✓ | ✓ |
|
||||
| IPMI | ✓ | ✓ | ✓ |
|
||||
| Wake-on-LAN | ✓ | ✓ | ✓ |
|
||||
| ETH | 100M/10M | 100M/10M | 1000M/100M/10M |
|
||||
| ATX power control | None,Users can connect by their own | USB interface IO control board | RJ-45 interface IO control board |
|
||||
| OLED | None,Users can connect by their own | 128x64 0.96" white | 128x32 0.91" white |
|
||||
| UART | 2 | 2 | 1 |
|
||||
| TF Card | None | ✓ | ✓ |
|
||||
| Expansion | None | PoE | WiFi/LTE |
|
||||
| Power consumption | 0.2A@5V | 0.2A@5V | Peak 2.6A@5V |
|
||||
| Power input | PC USB can be powered | PC USB or auxiliary power supply | DC 5V 3A power supply is required |
|
||||
| Cooling | Silent fanless | Silent fanless | Fan cooling |
|
||||
| Size | 23x37x15mm ~1/30 PiKVM V4 size | 40x36x36mm ~1/7 PiKVM V4 size | 120x68x44mm |
|
||||
|
||||

|
||||

|
||||
|
||||
## Hardware platform NanoKVM
|
||||
|
||||
@@ -73,10 +104,10 @@ The NanoKVM Lite is constructed by the LicheeRV Nano and HDMItoCSI board, and th
|
||||
|
||||
The NanoKVM image is built on LicheeRV Nano SDK and MaixCDK, and is compatible with materials that use the LicheeRV Nano, opposite the KVM software cannot be used with the LicheeRV Nano or other SG2002 products. If you would like to build an HDMI input application on LicheeRV Nano or MaixCam, please contact us for technical support.
|
||||
|
||||
Note: Out of the 256MB memory in SG2002, 158MB is currently allocated for the multimedia subsystem, which NanoKVM will use for video image acquisition and processing
|
||||
Note: Out of the 256MB memory in SG2002, 158MB is currently allocated for the multimedia subsystem, which NanoKVM will use for video image acquisition and processing.
|
||||
|
||||
+ [NanoKVM-A Schematic](https://cn.dl.sipeed.com/fileList/KVM/nanoKVM/HDK/02_Schematic/SCH_RV_Nano_KVM_A_30111.pdf)
|
||||
+ [NanoKVM-B Schematic](https://cn.dl.sipeed.com/fileList/KVM/nanoKVM/HDK/02_Schematic/SCH_HDMI_MIPI_31011.pdf)
|
||||
+ [NanoKVM-B Schematic](https://cn.dl.sipeed.com/fileList/KVM/nanoKVM/HDK/02_Schematic/SCH_RV_Nano_KVM_B_30131.pdf)
|
||||
+ [NanoKVM img](https://github.com/sipeed/NanoKVM/releases/tag/NanoKVM)
|
||||
|
||||
## Roadmap
|
||||
@@ -85,21 +116,28 @@ Note: Out of the 256MB memory in SG2002, 158MB is currently allocated for the mu
|
||||
|
||||
**Enhancements:**
|
||||
|
||||
- [ ] Refactor the HDMI module and remove the libmaixcam_lib.so dependency
|
||||
- [ ] Disable SSH by default and add an enable/disable toggle in the web UI
|
||||
- [ ] Disable Tailscale by default and add an enable/disable toggle in the web UI
|
||||
- [ ] Add JWT configuration options to the configuration file
|
||||
- [x] Refactor the HDMI module and remove the libmaixcam_lib.so dependency
|
||||
- [x] Disable SSH by default and add an enable/disable toggle in the web UI
|
||||
- [x] Disable Tailscale by default and add an enable/disable toggle in the web UI
|
||||
- [x] Add JWT configuration options to the configuration file
|
||||
- [x] Secure password storage with bcrypt
|
||||
|
||||
**Distribution:**
|
||||
|
||||
- [ ] Distribute applications via GitHub
|
||||
- [ ] Implement integrity checks for online updates
|
||||
- [x] Distribute applications via GitHub
|
||||
- [x] Implement integrity checks for online updates
|
||||
- [ ] Support offline updates
|
||||
|
||||
**Bug Fixes:**
|
||||
|
||||
- [ ] Fix the DNS issue
|
||||
- [ ] Fix the CSRF vulnerability
|
||||
- [x] Fix the DNS issue
|
||||
- [x] Fix the CSRF vulnerability
|
||||
|
||||
**Features**
|
||||
|
||||
- [ ] [79](https://github.com/sipeed/NanoKVM/issues/79) Support Zerotier
|
||||
- [ ] [99](https://github.com/sipeed/NanoKVM/issues/99) Support WireGuard
|
||||
- [ ] [249](https://github.com/sipeed/NanoKVM/issues/249) Add Mouse Jiggler
|
||||
|
||||
## Where to buy
|
||||
|
||||
@@ -107,11 +145,7 @@ Note: Out of the 256MB memory in SG2002, 158MB is currently allocated for the mu
|
||||
* [淘宝](https://item.taobao.com/item.htm?id=811206560480)
|
||||
* [Preorder (anyother country that not support in Aliexpress or Taobao)](https://sipeed.com/nanokvm)
|
||||
|
||||
|
||||
## Community
|
||||
|
||||
* [MaixHub Discussion](https://maixhub.com/discussion/nanokvm)
|
||||
* QQ group: 703230713
|
||||
|
||||
|
||||
|
||||
|
||||
110
README_JA.md
110
README_JA.md
@@ -1,11 +1,13 @@
|
||||
# NanoKVM
|
||||
NanoKVM
|
||||
======
|
||||
|
||||
<div align="center">
|
||||
|
||||

|
||||
|
||||
<h3>
|
||||
<a href="https://wiki.sipeed.com/hardware/zh/lichee/RV_Nano/1_intro.html"> クイックスタート </a> |
|
||||
<a href="https://wiki.sipeed.com/hardware/en/kvm/NanoKVM/introduction.html"> クイックスタート </a>
|
||||
|
|
||||
<a href="https://cn.dl.sipeed.com/shareURL/KVM/nanoKVM"> ハードウェア </a>
|
||||
</h3>
|
||||
|
||||
@@ -13,18 +15,29 @@
|
||||
|
||||
</div>
|
||||
|
||||
> Your NanoKVM Power by RISC-V !
|
||||
> NanoKVM は [RISC-V](https://en.wikipedia.org/wiki/RISC-V) を搭載しています!
|
||||
> 使用中に問題や提案がある場合は、ここで issue を提出するか、[MaixHub Discussion](https://maixhub.com/discussion/nanokvm) でお知らせください。
|
||||
|
||||
## オープンソース & 貢献 & 出荷
|
||||
## オープンソース & コントリビューション
|
||||
|
||||
スターが2Kに近づいています!10月中旬にバックエンドのソースコードを整理してオープンソース化します!(また、10.1前のすべての予約注文が出荷される時期でもあります)
|
||||
### 2025.02.19 更新
|
||||
|
||||
### 10.18 更新
|
||||
NanoKVM のすべてのコンポーネントはオープンソース化されており、[front-end](https://github.com/sipeed/NanoKVM/tree/main/web)、[back-end](https://github.com/sipeed/NanoKVM/tree/main/server)、[kvm_vision](https://github.com/sipeed/NanoKVM/tree/main/vision/components/kvm)、[kvm_mmf](https://github.com/sipeed/NanoKVM/tree/main/vision/components/kvm_mmf)、[kvm_system](https://github.com/sipeed/NanoKVM/tree/main/support)、[kvmapp update package](https://github.com/sipeed/NanoKVM-System/tree/main/kvmapp)、[system sdk](https://github.com/sipeed/LicheeRV-Nano-Build/tree/NanoKVM)、[packaging methods](https://github.com/sipeed/LicheeRV-Nano-Build/blob/NanoKVM/kvm/NanoKVM_img.sh)が含まれます。
|
||||
|
||||
10.8にバックエンドコードをオープンソース化しました。すぐにPRを提出してくれたCivilに感謝します。最新のNanoKVM-PCIeを報酬として提供します!
|
||||
10月中旬のバッチはテストとパッケージングを開始しており、10.1前のほとんどの注文は次週と次々週に出荷されます。
|
||||
10.1以降、アリエクスプレスの出荷日は12月に変更されました。これは保守的な納期です。10月の注文は11.15〜12.15の間に発送される予定です。
|
||||
### 2025.02.14 更新
|
||||
|
||||
最新のアプリケーションバージョン v2.1.6 には、多くのセキュリティ強化とバグ修正が追加されているため、一般のユーザーはこのバージョン以降へのアップグレードをお勧めします。
|
||||
|
||||
### 2025.02.05 更新
|
||||
|
||||
セキュリティを懸念しているユーザーは、この issue をお読みください: <https://github.com/sipeed/NanoKVM/issues/301>、セキュリティに関するすべての懸念事項について説明されています。
|
||||
GitHub は、オープンで透明性のあるプラットフォームとして、いわゆる「バックドア」の懸念事項を議論する場を提供しています。オープンソースであることにより、製品のセキュリティがさらに強化されます!
|
||||
|
||||
### 2024.10.18 更新
|
||||
|
||||
10/08にバックエンドコードをオープンソース化しました。すぐにPRを提出してくれたCivilに感謝します。最新のNanoKVM-PCIeを報酬として提供します!
|
||||
10月中旬のバッチはテストとパッケージングを開始しており、10/01以前のほとんどの注文は次週と次々週に出荷されます。
|
||||
10/01以降、アリエクスプレスの出荷日は12月に変更されました。これは保守的な納期です。10月の注文は11/15〜12/15の間に発送される予定です。
|
||||
|
||||
## 紹介
|
||||
|
||||
@@ -33,35 +46,35 @@ Lichee NanoKVMは2つのバージョンがあります:
|
||||
NanoKVM Liteは基本構成で、一定のDIY能力を持つ個人ユーザーや大量のニーズを持つ企業ユーザーに適しています。
|
||||
NanoKVM Fullは完全版で、洗練されたケースと完全なアクセサリーを備え、起動時にすぐに使用できるシステムミラーカードが内蔵されており、個人ユーザーに推奨されます。
|
||||
|
||||

|
||||

|
||||
|
||||
## 技術仕様
|
||||
|
||||
| 製品 | NanoKVM (Lite) | NanoKVM (Full) | PiKVM V4 |
|
||||
| --- | --- | --- | --- |
|
||||
| 計算ユニット | LicheeRV Nano(RISCV) | LicheeRV Nano(RISCV) | CM4 (ARM) |
|
||||
| 解像度 | 1080P @ 60fps | 1080P @ 60fps | 1080P @ 60fps |
|
||||
| ビデオエンコーディング | MJPEG, H264(開発中) | MJPEG, H264(開発中) | MJPEG, H264 |
|
||||
| ビデオ遅延 | 90~230ms | 90~230ms | 100~230ms |
|
||||
| UEFI/BIOS | ✓ | ✓ | ✓ |
|
||||
| 仮想HID | ✓ | ✓ | ✓ |
|
||||
| 仮想CD-ROM | ✓ | ✓ | ✓ |
|
||||
| IPMI | ✓ | ✓ | ✓ |
|
||||
| Wake-on-LAN | ✓ | ✓ | ✓ |
|
||||
| ETH | 100M/10M | 100M/10M | 1000M/100M/10M |
|
||||
| ATX電源制御 | なし、ユーザーが自分で接続 | USBインターフェースIO制御ボード | RJ-45インターフェースIO制御ボード |
|
||||
| OLED | なし、ユーザーが自分で接続 | 128x64 0.96" 白 | 128x32 0.91" 白 |
|
||||
| UART | 2 | 2 | 1 |
|
||||
| TFカード | なし | ✓ | ✓ |
|
||||
| 拡張 | なし | PoE | WiFi/LTE |
|
||||
| 消費電力 | 0.2A@5V | 0.2A@5V | ピーク2.6A@5V |
|
||||
| 電源入力 | PC USBで給電可能 | PC USBまたは補助電源 | DC 5V 3A電源が必要 |
|
||||
| 冷却 | 静音ファンレス | 静音ファンレス | ファン冷却 |
|
||||
| サイズ | 23x37x15mm <br> ~1/30 PiKVM V4サイズ | 40x36x36mm <br/> ~1/7 PiKVM V4サイズ | 120x68x44mm |
|
||||
| 製品 | NanoKVM (Lite) | NanoKVM (Full) | PiKVM V4 |
|
||||
|----------------------- |-------------------------------------- |---------------------------------- |----------------------------------- |
|
||||
| 計算ユニット | LicheeRV Nano(RISCV) | LicheeRV Nano(RISCV) | CM4 (ARM) |
|
||||
| 解像度 | 1080P @ 60fps | 1080P @ 60fps | 1080P @ 60fps |
|
||||
| ビデオエンコーディング | MJPEG, H264 | MJPEG, H264 | MJPEG, H264 |
|
||||
| ビデオ遅延 | 90~230ms | 90~230ms | 100~230ms |
|
||||
| UEFI/BIOS | ✓ | ✓ | ✓ |
|
||||
| 仮想HID | ✓ | ✓ | ✓ |
|
||||
| 仮想CD-ROM | ✓ | ✓ | ✓ |
|
||||
| IPMI | ✓ | ✓ | ✓ |
|
||||
| Wake-on-LAN | ✓ | ✓ | ✓ |
|
||||
| ETH | 100M/10M | 100M/10M | 1000M/100M/10M |
|
||||
| ATX電源制御 | なし、ユーザーが自分で接続 | USBインターフェースIO制御ボード | RJ-45インターフェースIO制御ボード |
|
||||
| OLED | なし、ユーザーが自分で接続 | 128x64 0.96" 白 | 128x32 0.91" 白 |
|
||||
| UART | 2 | 2 | 1 |
|
||||
| TFカード | なし | ✓ | ✓ |
|
||||
| 拡張 | なし | PoE | WiFi/LTE |
|
||||
| 消費電力 | 0.2A@5V | 0.2A@5V | ピーク時 2.6A@5V |
|
||||
| 電源入力 | PC USBで給電可能 | PC USBまたは補助電源 | DC 5V 3A電源が必要 |
|
||||
| 冷却 | 静音ファンレス | 静音ファンレス | ファン冷却 |
|
||||
| サイズ | 23x37x15mm ~1/30 PiKVM V4サイズ | 40x36x36mm ~1/7 PiKVM V4サイズ | 120x68x44mm |
|
||||
|
||||

|
||||

|
||||
|
||||
## ハードウェアプラットフォーム NanoKVM
|
||||
## NanoKVM ハードウェアプラットフォーム
|
||||
|
||||
NanoKVMはSipeed [LicheeRV Nano](https://wiki.sipeed.com/hardware/zh/lichee/RV_Nano/1_intro.html)に基づいており、仕様、回路図、寸法図は[こちら](http://cn.dl.sipeed.com/shareURL/LICHEE/LicheeRV_Nano)で確認できます。
|
||||
|
||||
@@ -69,12 +82,41 @@ NanoKVM LiteはLicheeRV NanoとHDMItoCSIボードで構成され、NanoKVM FULL
|
||||
|
||||
NanoKVMイメージはLicheeRV Nano SDKおよびMaixCDKに基づいて構築されており、LicheeRV Nanoを使用する資料と互換性があります。逆に、KVMソフトウェアはLicheeRV Nanoや他のSG2002製品では使用できません。LicheeRV NanoやMaixCamでHDMI入力アプリケーションを構築したい場合は、技術サポートを受けるためにお問い合わせください。
|
||||
|
||||
注:SG2002の256MBメモリのうち、現在105MBがマルチメディアサブシステムに割り当てられており、NanoKVMはビデオ画像の取得と処理にこのメモリを使用します。
|
||||
注:SG2002の256MBメモリのうち、現在158MBがマルチメディアサブシステムに割り当てられており、NanoKVMはビデオ画像の取得と処理にこのメモリを使用します。
|
||||
|
||||
+ [NanoKVM-A 回路図](https://cn.dl.sipeed.com/fileList/KVM/nanoKVM/HDK/02_Schematic/SCH_RV_Nano_KVM_A_30111.pdf)
|
||||
+ [NanoKVM-B 回路図](https://cn.dl.sipeed.com/fileList/KVM/nanoKVM/HDK/02_Schematic/SCH_HDMI_MIPI_31011.pdf)
|
||||
+ [NanoKVM イメージダウンロード](https://github.com/sipeed/NanoKVM/releases/tag/NanoKVM)
|
||||
|
||||
## ロードマップ
|
||||
|
||||
### Q1 2025
|
||||
|
||||
**改善:**
|
||||
|
||||
- [x] HDMI モジュールをリファクタリングし、libmaixcam_lib.so の依存関係を削除
|
||||
- [x] SSH をデフォルトで無効にし、Web UI に有効/無効の切り替えを追加
|
||||
- [x] Tailscale をデフォルトで無効にし、Web UI に有効/無効の切り替えを追加
|
||||
- [x] 設定ファイルに JWT 設定オプションを追加
|
||||
- [x] bcrypt を利用したパスワードストレージのセキュリティ保護
|
||||
|
||||
**配布:**
|
||||
|
||||
- [x] GitHub 経由でのアプリケーション配布
|
||||
- [x] オンラインでの更新の際に整合性チェックを実施
|
||||
- [ ] オフラインでの更新をサポート
|
||||
|
||||
**バグ修正:**
|
||||
|
||||
- [x] DNS の問題を修正
|
||||
- [x] CSRF の脆弱性を修正
|
||||
|
||||
**機能**
|
||||
|
||||
- [ ] [79](https://github.com/sipeed/NanoKVM/issues/79) Zerotier をサポート
|
||||
- [ ] [99](https://github.com/sipeed/NanoKVM/issues/99) WireGuard をサポート
|
||||
- [ ] [249](https://github.com/sipeed/NanoKVM/issues/249) Mouse Jiggler を追加
|
||||
|
||||
## 購入方法
|
||||
|
||||
* [Aliexpress(グローバル、米国とロシアを除く)](https://www.aliexpress.com/item/1005007369816019.html)
|
||||
|
||||
48
README_ZH.md
48
README_ZH.md
@@ -6,8 +6,9 @@ NanoKVM
|
||||

|
||||
|
||||
<h3>
|
||||
<a href="https://wiki.sipeed.com/hardware/zh/lichee/RV_Nano/1_intro.html"> 快速开始 </a> |
|
||||
<a href="https://cn.dl.sipeed.com/shareURL/KVM/nanoKVM"> 硬件资料 </a>
|
||||
<a href="https://wiki.sipeed.com/hardware/en/kvm/NanoKVM/introduction.html"> Quick Start </a>
|
||||
|
|
||||
<a href="https://cn.dl.sipeed.com/shareURL/KVM/nanoKVM"> Hardware </a>
|
||||
</h3>
|
||||
|
||||
[English](./README.md) | 中文 | [日本語](./README_JA.md)
|
||||
@@ -17,6 +18,47 @@ NanoKVM
|
||||
> Your NanoKVM Power by RISC-V !
|
||||
> 如果您在使用过程中有任何问题或建议,请在这里提交 issue, 或在 [MaixHub Discussion](https://maixhub.com/discussion/nanokvm)告诉我们.
|
||||
|
||||
## 开源与贡献
|
||||
|
||||
### 2025.03.04 更新内容
|
||||
+ 更新目录结构
|
||||
|
||||
``` shell
|
||||
├── kvmapp # APP更新包
|
||||
│ ├── jpg_stream # 兼容从非常老的版本中直接更新
|
||||
│ ├── kvm_new_app # 触发 kvm_system 更新必要组件
|
||||
│ ├── kvm_system # kvm_system 应用
|
||||
│ ├── server # NanoKVM 前后端应用
|
||||
│ └── system # 必要系统组件
|
||||
├── server # NanoKVM 后端
|
||||
├── support # 辅助功能(图像子系统、系统状态、系统更新、屏幕、按键……)
|
||||
│ ├── sg2002 # NanoKVM-Lite/Full/PCIe
|
||||
│ └── h618 # NanoKVM-Pro
|
||||
├── web # NanoKVM 前端
|
||||
├── LICENSE
|
||||
├── README_JA.md
|
||||
├── README.md
|
||||
├── README_ZH.md
|
||||
└── CHANGELOG.md
|
||||
```
|
||||
|
||||
+ 添加 NanoKVM app安装包`kvmapp`
|
||||
+ 添加编译脚本和编译说明
|
||||
|
||||
### 2025.02.19 更新内容
|
||||
所有 NanoKVM 组件已开源,包括
|
||||
[前端](https://github.com/sipeed/NanoKVM/tree/main/web)、[后端](https://github.com/sipeed/NanoKVM/tree/main/server)、[kvm_vision](https://github.com/sipeed/NanoKVM/tree/main/vision/components/kvm)、[kvm_mmf](https://github.com/sipeed/NanoKVM/tree/main/vision/components/kvm_mmf)、[kvm_system](https://github.com/sipeed/NanoKVM/tree/main/support)、[kvmapp 更新包](https://github.com/sipeed/NanoKVM-System/tree/main/kvmapp)、[系统 SDK](https://github.com/sipeed/LicheeRV-Nano-Build/tree/NanoKVM) 和 [固件打包方法](https://github.com/sipeed/LicheeRV-Nano-Build/blob/NanoKVM/kvm/NanoKVM_img.sh)。
|
||||
|
||||
### 2025.02.14 更新内容
|
||||
最新的 APP v2.1.6 添加了大多数安全增强和 bug 修复,建议普通用户升级到此版本或更新版本。
|
||||
|
||||
### 2025.02.05 更新内容
|
||||
对于关注安全的用户,可以阅读此问题:https://github.com/sipeed/NanoKVM/issues/301,您会找到有关所有安全问题的解释。GitHub 作为一个开放和透明的平台,为讨论所谓的“后门”担忧提供了空间。开源使产品更加安全!
|
||||
|
||||
### 2024.10.18 更新内容
|
||||
我们在 10.8 中开源了后端代码,感谢您立即提交的文明 PR,我们将赠送您最新的 NanoKVM-PCIe 作为奖励!
|
||||
十月中旬的批次正在开始测试和打包,大多数在 10.1 之前的订单将在下周和下下周发货。
|
||||
自 10.1 起,AliExpress 的发货日期已更改为 12 月,这是一个保守的交货时间。十月的订单应在 11.15~12.15 期间发出。
|
||||
|
||||
## 简介
|
||||
|
||||
@@ -33,7 +75,7 @@ NanoKVM Full 为完整版配置,带精致外壳和完整配件,内置开机
|
||||
| --- | --- | --- | --- |
|
||||
| 计算单元 | LicheeRV Nano(RISCV) | LicheeRV Nano(RISCV) | CM4 (ARM) |
|
||||
| 分辨率 | 1080P @ 60fps | 1080P @ 60fps | 1080P @ 60fps |
|
||||
| 视频编码 | MJPEG, H264(developing) | MJPEG, H264(developing) | MJPEG, H264 |
|
||||
| 视频编码 | MJPEG, H264 | MJPEG, H264 | MJPEG, H264 |
|
||||
| 视频延迟 | 90~230ms | 90~230ms | 100~230ms |
|
||||
| UEFI/BIOS | ✓ | ✓ | ✓ |
|
||||
| 模拟USB键鼠 | ✓ | ✓ | ✓ |
|
||||
|
||||
91
kvmapp/jpg_stream/S95nanokvm
Executable file
91
kvmapp/jpg_stream/S95nanokvm
Executable file
@@ -0,0 +1,91 @@
|
||||
#!/bin/sh
|
||||
# nanokvm Rev2.1
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
echo -n kvm > /boot/hostname.prefix
|
||||
cp /mnt/data/sensor_cfg.ini.LT /mnt/data/sensor_cfg.ini
|
||||
|
||||
str_value=$(cat /sys/class/cvi-base/base_uid | awk '{print $2}')
|
||||
first_uint=$(echo $str_value | cut -d'_' -f1)
|
||||
second_uint=$(echo $str_value | cut -d'_' -f2)
|
||||
result="$first_uint$second_uint"
|
||||
echo $result > /device_key
|
||||
|
||||
# if [ ! -e /etc/kvm/hw ]
|
||||
# then
|
||||
|
||||
# fi
|
||||
|
||||
# echo 504 > /sys/class/gpio/export # pwr led
|
||||
# echo 505 > /sys/class/gpio/export # hdd led
|
||||
# echo 503 > /sys/class/gpio/export # pwr key
|
||||
# echo 507 > /sys/class/gpio/export # rst key
|
||||
|
||||
# echo in > /sys/class/gpio/gpio504/direction # pwr led
|
||||
# echo in > /sys/class/gpio/gpio505/direction # hdd led
|
||||
# echo out > /sys/class/gpio/gpio503/direction # pwr key
|
||||
# echo out > /sys/class/gpio/gpio507/direction # rst key
|
||||
|
||||
iptables -A INPUT -i eth0 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
|
||||
iptables -A OUTPUT -o eth0 -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT
|
||||
iptables -A OUTPUT -o eth0 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
|
||||
iptables -A INPUT -i eth0 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
|
||||
iptables -A OUTPUT -o eth0 -p tcp --sport 8000 -m state --state ESTABLISHED -j DROP
|
||||
|
||||
cp -r /kvmapp/kvm_system /tmp/
|
||||
/tmp/kvm_system/kvm_system &
|
||||
|
||||
# if [ -e /kvmapp/kvm_stream ]
|
||||
# then
|
||||
# cp -r /kvmapp/kvm_stream /tmp/
|
||||
# /tmp/kvm_stream/kvm_stream &
|
||||
# fi
|
||||
|
||||
cp -r /kvmapp/server /tmp/
|
||||
/tmp/server/NanoKVM-Server &
|
||||
;;
|
||||
stop)
|
||||
killall kvm_system
|
||||
# killall kvm_stream
|
||||
killall NanoKVM-Server
|
||||
rm -r /tmp/kvm_system
|
||||
# rm -r /tmp/kvm_stream
|
||||
rm -r /tmp/server
|
||||
echo "OK"
|
||||
;;
|
||||
# restart_stream)
|
||||
# killall kvm_stream
|
||||
# rm -r /tmp/kvm_stream
|
||||
# if [ -e /kvmapp/kvm_stream ]
|
||||
# then
|
||||
# cp -r /kvmapp/kvm_stream /tmp/
|
||||
# /tmp/kvm_stream/kvm_stream &
|
||||
# echo "OK"
|
||||
# fi
|
||||
# ;;
|
||||
restart)
|
||||
killall kvm_system
|
||||
# killall kvm_stream
|
||||
killall NanoKVM-Server
|
||||
rm -r /tmp/kvm_system
|
||||
# rm -r /tmp/kvm_stream
|
||||
rm -r /tmp/server
|
||||
|
||||
cp -r /kvmapp/kvm_system /tmp/
|
||||
/tmp/kvm_system/kvm_system &
|
||||
|
||||
# if [ -e /kvmapp/kvm_stream ]
|
||||
# then
|
||||
# cp -r /kvmapp/kvm_stream /tmp/
|
||||
# /tmp/kvm_stream/kvm_stream &
|
||||
# fi
|
||||
|
||||
cp -r /kvmapp/server /tmp/
|
||||
/tmp/server/NanoKVM-Server &
|
||||
|
||||
sync
|
||||
|
||||
echo "OK"
|
||||
;;
|
||||
esac
|
||||
5
kvmapp/jpg_stream/jpg_stream
Executable file
5
kvmapp/jpg_stream/jpg_stream
Executable file
@@ -0,0 +1,5 @@
|
||||
rm /etc/init.d/S95webkvm
|
||||
cp /kvmapp/jpg_stream/S95nanokvm /etc/init.d/
|
||||
cp /kvmapp/jpg_stream/dl_lib/libmaixcam_lib.so /kvmapp/kvm_system/dl_lib
|
||||
rm -r /kvmapp/jpg_stream
|
||||
/etc/init.d/S95nanokvm restart
|
||||
0
kvmapp/kvm_system/kvm_stream
Executable file
0
kvmapp/kvm_system/kvm_stream
Executable file
42
kvmapp/system/init.d/S01fs
Executable file
42
kvmapp/system/init.d/S01fs
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ "$1" = "start" ]
|
||||
then
|
||||
# use all sdcard free space for data
|
||||
parted -s /dev/mmcblk0 "resizepart 2 -0"
|
||||
echo "yes
|
||||
8192MB
|
||||
" | parted ---pretend-input-tty /dev/mmcblk0 "resizepart 2 8192MB"
|
||||
# resize data filesystem
|
||||
(resize2fs /dev/mmcblk0p2) &
|
||||
|
||||
|
||||
. /etc/profile
|
||||
printf "mounting filesystem : "
|
||||
mkdir -p /boot
|
||||
mount -t vfat /dev/mmcblk0p1 /boot
|
||||
mount -t configfs configfs /sys/kernel/config
|
||||
mount -t debugfs debugfs /sys/kernel/debug
|
||||
|
||||
if [ -e /boot/usb.disk0 ]
|
||||
then
|
||||
if [ ! -e /etc/kvm.disk0 ]
|
||||
then
|
||||
touch /etc/kvm.disk0
|
||||
# use all sdcard free space for data
|
||||
parted -s /dev/mmcblk0 "mkpart primary 8193MB 100%"
|
||||
sleep 1
|
||||
# resize data filesystem
|
||||
(mkfs.exfat /dev/mmcblk0p3) &
|
||||
sleep 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -e /dev/mmcblk0p3 ]
|
||||
then
|
||||
mkdir -p /data
|
||||
mount /dev/mmcblk0p3 /data
|
||||
fi
|
||||
|
||||
echo "OK"
|
||||
fi
|
||||
139
kvmapp/system/init.d/S03usbdev
Executable file
139
kvmapp/system/init.d/S03usbdev
Executable file
@@ -0,0 +1,139 @@
|
||||
#!/bin/sh
|
||||
# kvmhwd Rev2.0
|
||||
|
||||
start_usb_dev(){
|
||||
. /etc/profile
|
||||
echo "usb mode: device"
|
||||
cd /sys/kernel/config/usb_gadget
|
||||
|
||||
mkdir g0
|
||||
cd g0
|
||||
|
||||
echo 0x3346 > idVendor
|
||||
echo 0x1009 > idProduct
|
||||
mkdir strings/0x409
|
||||
echo '0123456789ABCDEF' > strings/0x409/serialnumber
|
||||
echo 'sipeed' > strings/0x409/manufacturer
|
||||
echo 'NanoKVM' > strings/0x409/product
|
||||
|
||||
mkdir configs/c.1
|
||||
echo 0xE0 > configs/c.1/bmAttributes
|
||||
echo 120 > configs/c.1/MaxPower
|
||||
mkdir configs/c.1/strings/0x409
|
||||
echo "NanoKVM" > configs/c.1/strings/0x409/configuration
|
||||
|
||||
if [ -e /boot/usb.ncm ]
|
||||
then
|
||||
mkdir functions/ncm.usb0
|
||||
ln -s functions/ncm.usb0 configs/c.1/
|
||||
else
|
||||
if [ -e /boot/usb.rndis0 ]
|
||||
then
|
||||
mkdir functions/rndis.usb0
|
||||
ln -s functions/rndis.usb0 configs/c.1/
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -e /boot/disable_hid ]
|
||||
then
|
||||
# keyboard
|
||||
mkdir functions/hid.GS0
|
||||
if [ -e /boot/BIOS ]
|
||||
then
|
||||
echo 1 > functions/hid.GS0/subclass
|
||||
fi
|
||||
if [ ! -e /boot/usb.notwakeup ]
|
||||
then
|
||||
echo 1 > functions/hid.GS0/wakeup_on_write
|
||||
fi
|
||||
echo 1 > functions/hid.GS0/protocol
|
||||
echo 6 > functions/hid.GS0/report_length
|
||||
echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > functions/hid.GS0/report_desc
|
||||
ln -s functions/hid.GS0 configs/c.1
|
||||
|
||||
# mouse
|
||||
mkdir functions/hid.GS1
|
||||
if [ -e /boot/BIOS ]
|
||||
then
|
||||
echo 1 > functions/hid.GS1/subclass
|
||||
fi
|
||||
if [ ! -e /boot/usb.notwakeup ]
|
||||
then
|
||||
echo 1 > functions/hid.GS1/wakeup_on_write
|
||||
fi
|
||||
echo 2 > functions/hid.GS1/protocol
|
||||
echo -ne \\x34 > functions/hid.GS1/report_length
|
||||
echo -ne \\x5\\x1\\x9\\x2\\xa1\\x1\\x9\\x1\\xa1\\x0\\x5\\x9\\x19\\x1\\x29\\x3\\x15\\x0\\x25\\x1\\x95\\x3\\x75\\x1\\x81\\x2\\x95\\x1\\x75\\x5\\x81\\x3\\x5\\x1\\x9\\x30\\x9\\x31\\x9\\x38\\x15\\x81\\x25\\x7f\\x75\\x8\\x95\\x3\\x81\\x6\\xc0\\xc0 > functions/hid.GS1/report_desc
|
||||
ln -s functions/hid.GS1 configs/c.1
|
||||
|
||||
# touchpad
|
||||
mkdir functions/hid.GS2
|
||||
if [ -e /boot/BIOS ]
|
||||
then
|
||||
echo 1 > functions/hid.GS2/subclass
|
||||
fi
|
||||
if [ ! -e /boot/usb.notwakeup ]
|
||||
then
|
||||
echo 1 > functions/hid.GS2/wakeup_on_write
|
||||
fi
|
||||
echo 2 > functions/hid.GS2/protocol
|
||||
echo 6 > functions/hid.GS2/report_length
|
||||
echo -ne \\x05\\x01\\x09\\x02\\xa1\\x01\\x09\\x01\\xa1\\x00\\x05\\x09\\x19\\x01\\x29\\x03\\x15\\x00\\x25\\x01\\x95\\x03\\x75\\x01\\x81\\x02\\x95\\x01\\x75\\x05\\x81\\x01\\x05\\x01\\x09\\x30\\x09\\x31\\x15\\x00\\x26\\xff\\x7f\\x35\\x00\\x46\\xff\\x7f\\x75\\x10\\x95\\x02\\x81\\x02\\x05\\x01\\x09\\x38\\x15\\x81\\x25\\x7f\\x35\\x00\\x45\\x00\\x75\\x08\\x95\\x01\\x81\\x06\\xc0\\xc0 > functions/hid.GS2/report_desc
|
||||
ln -s functions/hid.GS2 configs/c.1
|
||||
fi
|
||||
|
||||
if [ -e /boot/usb.disk0 ]
|
||||
then
|
||||
mkdir functions/mass_storage.disk0
|
||||
ln -s functions/mass_storage.disk0 configs/c.1/
|
||||
echo 1 > functions/mass_storage.disk0/lun.0/removable
|
||||
if [ -e /boot/usb.disk0.ro ]
|
||||
then
|
||||
echo 1 > functions/mass_storage.disk0/lun.0/ro
|
||||
echo 0 > functions/mass_storage.disk0/lun.0/cdrom
|
||||
fi
|
||||
disk=$(cat /boot/usb.disk0)
|
||||
if [ -z "${disk}" ]
|
||||
then
|
||||
# if [ ! -e /mnt/usbdisk.img ]
|
||||
# then
|
||||
# fallocate -l 8G /mnt/usbdisk.img
|
||||
# mkfs.vfat /mnt/usbdisk.img
|
||||
# fi
|
||||
echo /dev/mmcblk0p3 > functions/mass_storage.disk0/lun.0/file
|
||||
else
|
||||
cat /boot/usb.disk0 > functions/mass_storage.disk0/lun.0/file
|
||||
fi
|
||||
fi
|
||||
|
||||
ls /sys/class/udc/ | cat > UDC
|
||||
echo device > /proc/cviusb/otg_role
|
||||
}
|
||||
|
||||
start_usb_host(){
|
||||
echo '' > /sys/kernel/config/usb_gadget/g0/UDC
|
||||
echo host > /proc/cviusb/otg_role
|
||||
}
|
||||
|
||||
restart_usb_dev(){
|
||||
echo > /sys/kernel/config/usb_gadget/g0/UDC
|
||||
sleep 1
|
||||
ls /sys/class/udc/ | cat > /sys/kernel/config/usb_gadget/g0/UDC
|
||||
echo "USB Restart OK!"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start_usb_dev
|
||||
;;
|
||||
restart)
|
||||
restart_usb_dev
|
||||
;;
|
||||
stop)
|
||||
start_usb_host
|
||||
;;
|
||||
stop_start)
|
||||
start_usb_host
|
||||
start_usb_dev
|
||||
;;
|
||||
esac
|
||||
234
kvmapp/system/init.d/S15kvmhwd
Executable file
234
kvmapp/system/init.d/S15kvmhwd
Executable file
@@ -0,0 +1,234 @@
|
||||
#!/bin/sh
|
||||
# kvmhwd Rev2.4
|
||||
|
||||
Alpha_OLED_RST_Pin=371
|
||||
Beta_OLED_RST_Pin=502
|
||||
Beta_OLED_SCL=595
|
||||
Beta_OLED_SDA=507
|
||||
WiFi_EN_Pin=506
|
||||
Alpha_PWR_LED_Pin=504
|
||||
Alpha_HDD_LED_Pin=505
|
||||
Alpha_PWR_KEY_Pin=503
|
||||
Alpha_RST_KEY_Pin=507
|
||||
Beta_PWR_LED_Pin=504
|
||||
Beta_PWR_KEY_Pin=503
|
||||
Beta_RST_KEY_Pin=505
|
||||
PCIe_HDMI_RST_Pin=451
|
||||
|
||||
init_alpha_hw(){
|
||||
devmem 0x030010D0 32 0x2 # I2C1_SCL
|
||||
devmem 0x030010DC 32 0x2 # I2C1_SDA
|
||||
devmem 0x030010D4 32 0x3 # OLED_RST
|
||||
devmem 0x0300103C 32 0x3 # GPIOA15
|
||||
devmem 0x03001050 32 0x3 # GPIOA22
|
||||
devmem 0x0300105C 32 0x3 # GPIOA23
|
||||
devmem 0x03001060 32 0x3 # GPIOA24
|
||||
devmem 0x03001054 32 0x3 # GPIOA25
|
||||
devmem 0x03001058 32 0x3 # GPIOA27
|
||||
|
||||
devmem 0x03001068 32 0x6 # GPIOA 18 UART1 RX
|
||||
devmem 0x03001064 32 0x6 # GPIOA 19 UART1 TX
|
||||
devmem 0x03001070 32 0x2 # GPIOA 28 UART2 TX
|
||||
devmem 0x03001074 32 0x2 # GPIOA 29 UART2 RX
|
||||
|
||||
echo ${Alpha_OLED_RST_Pin} > /sys/class/gpio/export # OLED_RST
|
||||
echo out > /sys/class/gpio/gpio${Alpha_OLED_RST_Pin}/direction
|
||||
echo 1 > /sys/class/gpio/gpio${Alpha_OLED_RST_Pin}/value
|
||||
|
||||
echo ${Alpha_PWR_LED_Pin} > /sys/class/gpio/export # pwr led
|
||||
echo ${Alpha_HDD_LED_Pin} > /sys/class/gpio/export # hdd led
|
||||
echo ${Alpha_PWR_KEY_Pin} > /sys/class/gpio/export # pwr key
|
||||
echo ${Alpha_RST_KEY_Pin} > /sys/class/gpio/export # rst key
|
||||
|
||||
echo in > /sys/class/gpio/gpio${Alpha_PWR_LED_Pin}/direction # pwr led
|
||||
echo in > /sys/class/gpio/gpio${Alpha_HDD_LED_Pin}/direction # hdd led
|
||||
echo out > /sys/class/gpio/gpio${Alpha_PWR_KEY_Pin}/direction # pwr key
|
||||
echo out > /sys/class/gpio/gpio${Alpha_RST_KEY_Pin}/direction # rst key
|
||||
|
||||
rmmod /mnt/system/ko/i2c-gpio.ko
|
||||
rmmod /mnt/system/ko/i2c-algo-bit.ko
|
||||
|
||||
# rm /etc/init.d/S25wifimod
|
||||
# rm /etc/init.d/S30wifi
|
||||
}
|
||||
|
||||
init_beta_pcie_hw(){
|
||||
devmem 0x0300103C 32 0x3 # GPIOA15
|
||||
devmem 0x03001050 32 0x3 # GPIOA22
|
||||
devmem 0x0300105C 32 0x3 # GPIOA23
|
||||
devmem 0x03001060 32 0x3 # GPIOA24
|
||||
devmem 0x03001054 32 0x3 # GPIOA25
|
||||
devmem 0x03001058 32 0x3 # GPIOA27
|
||||
|
||||
devmem 0x030010E4 32 0x0 # SDIO CLK
|
||||
devmem 0x030010E0 32 0x0 # SDIO CMD
|
||||
devmem 0x030010DC 32 0x0 # SDIO D0
|
||||
devmem 0x030010D8 32 0x0 # SDIO D1
|
||||
devmem 0x030010D4 32 0x0 # SDIO D2
|
||||
devmem 0x030010D0 32 0x0 # SDIO D3
|
||||
|
||||
devmem 0x03001068 32 0x6 # GPIOA 18 UART1 RX
|
||||
devmem 0x03001064 32 0x6 # GPIOA 19 UART1 TX
|
||||
devmem 0x03001070 32 0x2 # GPIOA 28 UART2 TX
|
||||
devmem 0x03001074 32 0x2 # GPIOA 29 UART2 RX
|
||||
|
||||
echo ${Beta_OLED_RST_Pin} > /sys/class/gpio/export # Beta OLED_RST
|
||||
echo out > /sys/class/gpio/gpio${Beta_OLED_RST_Pin}/direction
|
||||
echo 1 > /sys/class/gpio/gpio${Beta_OLED_RST_Pin}/value
|
||||
|
||||
echo ${Beta_PWR_LED_Pin} > /sys/class/gpio/export # pwr led
|
||||
echo ${Beta_PWR_KEY_Pin} > /sys/class/gpio/export # pwr key
|
||||
echo ${Beta_RST_KEY_Pin} > /sys/class/gpio/export # rst key
|
||||
echo ${PCIe_HDMI_RST_Pin} > /sys/class/gpio/export # hdmi rst key
|
||||
|
||||
echo in > /sys/class/gpio/gpio${Beta_PWR_LED_Pin}/direction # pwr led
|
||||
echo out > /sys/class/gpio/gpio${Beta_PWR_KEY_Pin}/direction # pwr key
|
||||
echo out > /sys/class/gpio/gpio${Beta_RST_KEY_Pin}/direction # rst key
|
||||
echo out > /sys/class/gpio/gpio${PCIe_HDMI_RST_Pin}/direction # rst key
|
||||
|
||||
echo 1 > /sys/class/gpio/gpio${PCIe_HDMI_RST_Pin}/value # rst key
|
||||
|
||||
rmmod /mnt/system/ko/i2c-gpio.ko
|
||||
rmmod /mnt/system/ko/i2c-algo-bit.ko
|
||||
insmod /mnt/system/ko/i2c-algo-bit.ko
|
||||
insmod /mnt/system/ko/i2c-gpio.ko
|
||||
}
|
||||
|
||||
kvm_hw_detect(){
|
||||
if [ ! -e /etc/kvm/hdmi_version ]
|
||||
then
|
||||
rm /etc/kvm/hw
|
||||
fi
|
||||
if [ -e /etc/kvm/hw ]
|
||||
then
|
||||
echo "/etc/kvm/hw exist"
|
||||
else
|
||||
echo "/etc/kvm/hw not exist"
|
||||
if [ -d "/etc/kvm/" ]
|
||||
then
|
||||
echo "/etc/kvm/ exist"
|
||||
else
|
||||
mkdir /etc/kvm/
|
||||
fi
|
||||
devmem 0x0300104C 32 0x3 # GPIOA26 / WiFi_EN
|
||||
echo ${WiFi_EN_Pin} > /sys/class/gpio/export # WiFi_EN
|
||||
echo out > /sys/class/gpio/gpio${WiFi_EN_Pin}/direction
|
||||
echo 0 > /sys/class/gpio/gpio${WiFi_EN_Pin}/value
|
||||
|
||||
devmem 0x0300103C 32 0x3 # GPIOA15
|
||||
devmem 0x03001050 32 0x3 # GPIOA22
|
||||
devmem 0x0300105C 32 0x3 # GPIOA23
|
||||
devmem 0x03001060 32 0x3 # GPIOA24
|
||||
devmem 0x03001054 32 0x3 # GPIOA25
|
||||
devmem 0x03001058 32 0x3 # GPIOA27
|
||||
|
||||
devmem 0x030010D0 32 0x2 # I2C1_SCL
|
||||
devmem 0x030010DC 32 0x2 # I2C1_SDA
|
||||
devmem 0x030010D4 32 0x3 # OLED_RST
|
||||
echo ${Alpha_OLED_RST_Pin} > /sys/class/gpio/export # OLED_RST
|
||||
echo out > /sys/class/gpio/gpio${Alpha_OLED_RST_Pin}/direction
|
||||
echo 1 > /sys/class/gpio/gpio${Alpha_OLED_RST_Pin}/value
|
||||
|
||||
kvm_tmp=$(i2cdetect -ry 1 0x3d 0x3d | grep 3d)
|
||||
if [ -n "$kvm_tmp" ]
|
||||
then
|
||||
# alpha hw
|
||||
echo "alpha" > /etc/kvm/hw
|
||||
else
|
||||
# beta/pcie hw
|
||||
echo ${Alpha_OLED_RST_Pin} > /sys/class/gpio/unexport # OLED_RST
|
||||
echo ${Beta_OLED_RST_Pin} > /sys/class/gpio/export # Beta_OLED_RST_Pin
|
||||
echo out > /sys/class/gpio/gpio${Beta_OLED_RST_Pin}/direction
|
||||
echo 1 > /sys/class/gpio/gpio${Beta_OLED_RST_Pin}/value
|
||||
|
||||
rmmod /mnt/system/ko/i2c-gpio.ko
|
||||
rmmod /mnt/system/ko/i2c-algo-bit.ko
|
||||
insmod /mnt/system/ko/i2c-algo-bit.ko
|
||||
insmod /mnt/system/ko/i2c-gpio.ko
|
||||
|
||||
kvm_tmp=$(i2cdetect -ry 4 0x2c 0x2c | grep 2c)
|
||||
if [ -n "$kvm_tmp" ]
|
||||
then
|
||||
echo "c" > /etc/kvm/hdmi_version
|
||||
else
|
||||
kvm_tmp1=$(i2cdetect -ry 4 0x2b 0x2b | grep 2b)
|
||||
if [ -n "$kvm_tmp1" ]
|
||||
then
|
||||
echo "ux" > /etc/kvm/hdmi_version
|
||||
else
|
||||
echo "ue" > /etc/kvm/hdmi_version
|
||||
fi
|
||||
fi
|
||||
|
||||
kvm_tmp=$(i2cdetect -ry 5 0x3c 0x3c | grep 3c)
|
||||
if [ -n "$kvm_tmp" ]
|
||||
then
|
||||
echo "pcie" > /etc/kvm/hw
|
||||
sync
|
||||
reboot
|
||||
else
|
||||
echo "beta" > /etc/kvm/hw
|
||||
# rm /etc/init.d/S25wifimod
|
||||
# rm /etc/init.d/S30wifi
|
||||
sync
|
||||
fi
|
||||
|
||||
echo 1 > /sys/class/gpio/gpio${WiFi_EN_Pin}/value
|
||||
echo ${WiFi_EN_Pin} > /sys/class/gpio/unexport # WiFi_EN
|
||||
fi
|
||||
fi
|
||||
sync
|
||||
}
|
||||
|
||||
kvm_hw_init(){
|
||||
FIND_FILE="/etc/kvm/hw"
|
||||
if [ `grep -c "alpha" $FIND_FILE` -ne '0' ]
|
||||
then
|
||||
echo "hw = alpha!"
|
||||
init_alpha_hw
|
||||
fi
|
||||
if [ `grep -c "beta" $FIND_FILE` -ne '0' ]
|
||||
then
|
||||
echo "hw = beta!"
|
||||
init_beta_pcie_hw
|
||||
fi
|
||||
if [ `grep -c "pcie" $FIND_FILE` -ne '0' ]
|
||||
then
|
||||
echo "hw = pcie!"
|
||||
init_beta_pcie_hw
|
||||
fi
|
||||
}
|
||||
|
||||
kvm_hdmi_version_detect(){
|
||||
hdmi_tmp=$(i2cdetect -ry 4 0x2c 0x2c | grep 2c)
|
||||
if [ -n "$hdmi_tmp" ]
|
||||
then
|
||||
echo "c" > /etc/kvm/hdmi_version
|
||||
else
|
||||
hdmi_tmp1=$(i2cdetect -ry 4 0x2b 0x2b | grep 2b)
|
||||
if [ -n "$hdmi_tmp1" ]
|
||||
then
|
||||
echo "ux" > /etc/kvm/hdmi_version
|
||||
else
|
||||
echo "ue" > /etc/kvm/hdmi_version
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
kvm_hw_detect
|
||||
kvm_hw_init
|
||||
;;
|
||||
re-detect)
|
||||
rm /etc/kvm/hw
|
||||
kvm_hw_detect
|
||||
kvm_hw_init
|
||||
;;
|
||||
re-init)
|
||||
kvm_hw_init
|
||||
;;
|
||||
get_hdmi_version)
|
||||
kvm_hdmi_version_detect
|
||||
;;
|
||||
esac
|
||||
115
kvmapp/system/init.d/S30eth
Executable file
115
kvmapp/system/init.d/S30eth
Executable file
@@ -0,0 +1,115 @@
|
||||
#!/bin/sh
|
||||
|
||||
. /etc/profile
|
||||
|
||||
# /boot/eth.nodhcp for example
|
||||
# ipaddr/net gw[optional]
|
||||
# 192.168.0.101/24 192.168.0.1
|
||||
# 192.168.3.116/22
|
||||
|
||||
RESERVE_INET="192.168.0.1/24"
|
||||
|
||||
start() {
|
||||
printf "start ethernet: "
|
||||
if [ -e /boot/eth.nodhcp ]
|
||||
then
|
||||
[ -e /boot/eth.nodhcp ] &&
|
||||
cat /boot/eth.nodhcp | while read inet gw
|
||||
do
|
||||
addr=${inet%/*}
|
||||
netid=${inet#*/}
|
||||
[ -z $gw ] &&
|
||||
gw=$( echo $addr| ( IFS='.' read a b c d; echo $((
|
||||
(((((($a<<8)+$b)<<8)+$c)<<8)+$d)
|
||||
& (((1<<$netid)-1)<<(32-$netid))
|
||||
))
|
||||
)) &&
|
||||
gw=$(($gw>>24&0xff)).$(($gw>>16&0xff)).$(($gw>>8&0xff)).$((1+( $gw>>0&0xff )))
|
||||
|
||||
arping -Dqc2 -Ieth0 $addr || continue
|
||||
ip a add $inet brd + dev eth0
|
||||
ip r add default via $gw dev eth0
|
||||
cat > /etc/resolve.conf << EOF
|
||||
nameserver $gw
|
||||
nameserver 8.8.8.8
|
||||
nameserver 114.114.114.114
|
||||
EOF
|
||||
break
|
||||
done &&
|
||||
ip a show dev eth0|grep inet || (
|
||||
udhcpc -i eth0 -t 3 -T 1 -A 5 -b -p /run/udhcpc.eth0.pid &>/dev/null
|
||||
ip a show dev eth0|grep inet
|
||||
) || (
|
||||
# failed to apply dynamic addr, need a available static addr to visit the LAN
|
||||
inet=$RESERVE_INET
|
||||
addr=${inet%/*}
|
||||
ip a add $inet brd + dev eth0
|
||||
) || exit 1
|
||||
else
|
||||
(udhcpc -i eth0 -t 10 -T 1 -A 5 -b -p /run/udhcpc.eth0.pid) &
|
||||
fi
|
||||
|
||||
echo "OK"
|
||||
}
|
||||
stop() {
|
||||
[[ ! -e "/run/udhcpc.eth0.pid" ]] && echo "udhcpc is not running..." && exit 1
|
||||
kill `cat /run/udhcpc.eth0.pid`
|
||||
rm /run/udhcpc.eth0.pid
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
restart|reload)
|
||||
$0 stop
|
||||
$0 start
|
||||
;;
|
||||
doc)
|
||||
cat <<EOF
|
||||
If you want to set your own static ip address
|
||||
rather then fetch it by dhcp.
|
||||
Then you need to create a file \`/boot/eth.nodhcp\`,
|
||||
and edit it following the format below:
|
||||
\`addr/netid gw[optional]\` in one line
|
||||
for example (support muti definations, checks with arp):
|
||||
1. 192.168.0.101/24 192.168.0.1
|
||||
2. 192.168.3.116/22
|
||||
Delete this file if you change to use dhcp default.
|
||||
ATTENTION: If all the STATIC address you set in the file is
|
||||
already in use on this LAN checked with arp, then none
|
||||
of them will be yours. And it will trigger dhcp, try to
|
||||
fetch DYNAMIC one. If it's also failed, your address will
|
||||
be the REVERSE one, 192.168.0.1/24. Just to be sure that
|
||||
you can visit your NanoKVM device through eth to fix some
|
||||
problems by ssh, unless you must disassemble it and modify
|
||||
or rewrite the TFCard once again.
|
||||
EOF
|
||||
echo ""
|
||||
cat <<EOF
|
||||
如果您想自行设置静态 ip 地址而不是使用 dhcp 来获取它。
|
||||
|
||||
那么您需要新建一个文件 \`/boot/eth.nodhcp\`,然后按照以下格式编辑它:
|
||||
\`addr/netid gw[optional]\` 在一行内
|
||||
示例(支持多个预设,会通过 arp 检查是否占用):
|
||||
1. 192.168.0.101/24 192.168.0.1
|
||||
2. 192.168.3.116/22
|
||||
|
||||
如果您想改回默认 dhcp 获取 ip 地址的方式,删除该文件后即可恢复。
|
||||
|
||||
注意:如果您预设的所有静态 ip 地址都被 arp 检测到已占用,则不会成功设置。
|
||||
此时会触发 dhcp 尝试获取 ip 地址,如果仍失败,那么最终会强制设置成
|
||||
192.168.90.1/24。这是为了保证您的 NanoKVM 设备总是能有一个可用的
|
||||
ip 地址,方便您通过局域网 ssh 连接设备进行一些修复,否则您可能需要
|
||||
拆开外壳以手动修改 TF 卡或重新烧卡。
|
||||
|
||||
|
||||
EOF
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart}"
|
||||
exit 1
|
||||
esac
|
||||
175
kvmapp/system/init.d/S30wifi
Executable file
175
kvmapp/system/init.d/S30wifi
Executable file
@@ -0,0 +1,175 @@
|
||||
#!/bin/sh
|
||||
|
||||
. /etc/profile
|
||||
|
||||
gen_hostapd_conf() {
|
||||
ssid="${1}"
|
||||
pass="${2}"
|
||||
echo "ctrl_interface=/var/run/hostapd"
|
||||
echo "ctrl_interface_group=0"
|
||||
echo "ssid=${ssid}"
|
||||
echo "hw_mode=g"
|
||||
echo "channel=1"
|
||||
echo "beacon_int=100"
|
||||
echo "dtim_period=2"
|
||||
echo "max_num_sta=255"
|
||||
echo "rts_threshold=-1"
|
||||
echo "fragm_threshold=-1"
|
||||
echo "macaddr_acl=0"
|
||||
echo "auth_algs=3"
|
||||
echo "wpa=2"
|
||||
echo "wpa_passphrase=${pass}"
|
||||
echo "ieee80211n=1"
|
||||
}
|
||||
|
||||
gen_udhcpd_conf() {
|
||||
interface=${1}
|
||||
ipv4_prefix=${2}
|
||||
echo "start ${ipv4_prefix}.100"
|
||||
echo "end ${ipv4_prefix}.200"
|
||||
echo "interface ${interface}"
|
||||
echo "pidfile /var/run/udhcpd.${interface}.pid"
|
||||
echo "lease_file /var/lib/misc/udhcpd.${interface}.leases"
|
||||
echo "option subnet 255.255.255.0"
|
||||
echo "option lease 864000"
|
||||
}
|
||||
|
||||
gen_dnsmasq_conf() {
|
||||
ipv4_prefix=${1}
|
||||
echo "bind-interfaces"
|
||||
echo "interface=wlan0"
|
||||
echo "address=/config.kvm/${ipv4_prefix}.1"
|
||||
echo "no-hosts"
|
||||
echo "dhcp-range=${ipv4_prefix}.2,${ipv4_prefix}.254"
|
||||
echo "dhcp-option=6,${ipv4_prefix}.1"
|
||||
echo "dhcp-option=3,${ipv4_prefix}.1"
|
||||
echo "dhcp-authoritative"
|
||||
}
|
||||
|
||||
start() {
|
||||
echo "wifi mode: sta"
|
||||
ssid=""
|
||||
pass=""
|
||||
if [ -e /boot/wifi.ssid ] && [ -e /boot/wifi.pass ]; then
|
||||
echo "Updating WiFi credentials from /boot to /etc/kvm/"
|
||||
|
||||
rm -f /etc/kvm/wifi.ssid /etc/kvm/wifi.pass
|
||||
|
||||
mv /boot/wifi.ssid /etc/kvm/wifi.ssid
|
||||
mv /boot/wifi.pass /etc/kvm/wifi.pass
|
||||
|
||||
chown root:root /etc/kvm/wifi.ssid /etc/kvm/wifi.pass
|
||||
chmod 644 /etc/kvm/wifi.ssid /etc/kvm/wifi.pass
|
||||
fi
|
||||
if [ -e /etc/kvm/wifi.ssid ]
|
||||
then
|
||||
ssid=`cat /etc/kvm/wifi.ssid`
|
||||
fi
|
||||
if [ -e /etc/kvm/wifi.pass ]
|
||||
then
|
||||
pass=`cat /etc/kvm/wifi.pass`
|
||||
fi
|
||||
echo "ctrl_interface=/var/run/wpa_supplicant" > /etc/wpa_supplicant.conf
|
||||
wpa_passphrase "$ssid" "$pass" >> /etc/wpa_supplicant.conf
|
||||
wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant.conf
|
||||
if [ ! -e /boot/wifi.nodhcp ]
|
||||
then
|
||||
(udhcpc -i wlan0 -t 10 -T 1 -A 5 -b -p /run/udhcpc.wlan0.pid) &
|
||||
fi
|
||||
}
|
||||
|
||||
ap_start() {
|
||||
echo "wifi mode: ap"
|
||||
id2=$(printf "%d" 0x$(sha512sum /sys/class/cvi-base/base_uid | head -c 2))
|
||||
id3=$(printf "%d" 0x$(sha512sum /sys/class/cvi-base/base_uid | head -c 4 | tail -c 2))
|
||||
if [ "$id2" = "$id3" ]
|
||||
then
|
||||
id2=$((id2 + 1))
|
||||
fi
|
||||
if [ "$id2" -ge 255 ]
|
||||
then
|
||||
id2=253
|
||||
fi
|
||||
if [ "$id3" -ge 255 ]
|
||||
then
|
||||
id3=254
|
||||
fi
|
||||
ssid=""
|
||||
pass=""
|
||||
if [ -e /kvmapp/kvm/ap.ssid ]
|
||||
then
|
||||
ssid=`cat /kvmapp/kvm/ap.ssid`
|
||||
fi
|
||||
if [ -e /kvmapp/kvm/ap.pass ]
|
||||
then
|
||||
pass=`cat /kvmapp/kvm/ap.pass`
|
||||
fi
|
||||
gen_hostapd_conf "$ssid" "$pass" > /etc/hostapd.conf
|
||||
ipv4_prefix=10.$id3.$id2
|
||||
if [ ! -e /etc/udhcpd.wlan0.conf ]
|
||||
then
|
||||
gen_udhcpd_conf wlan0 "${ipv4_prefix}" > /etc/udhcpd.wlan0.conf
|
||||
fi
|
||||
# gen_dnsmasq_conf "${ipv4_prefix}" > /etc/dnsmasq.conf
|
||||
|
||||
# iptables --policy INPUT ACCEPT
|
||||
# iptables --policy FORWARD ACCEPT
|
||||
# iptables --policy OUTPUT ACCEPT
|
||||
# iptables -F
|
||||
# iptables -t nat -F
|
||||
# iptables -t nat -A PREROUTING -i wlan0 -p udp --dport 53 -j DNAT --to ${ipv4_prefix}.1
|
||||
|
||||
ifconfig wlan0 up
|
||||
ip route del default || true
|
||||
ip add flush dev wlan0
|
||||
ip addr add $ipv4_prefix.1/24 dev wlan0
|
||||
# dnsmasq --pid-file=/tmp/dnsmasq.run -C /etc/dnsmasq.conf
|
||||
hostapd -B -i wlan0 /etc/hostapd.conf
|
||||
udhcpd -S /etc/udhcpd.wlan0.conf
|
||||
}
|
||||
|
||||
stop() {
|
||||
ps -ef|grep hostapd|grep -v grep|awk '{print $1}'|xargs kill -2 || true
|
||||
ps -ef|grep "udhcpd -S /etc/udhcpd.wlan0.conf" |grep -v grep|awk '{print $1}'|xargs kill -2 || true
|
||||
killall wpa_supplicant || true
|
||||
if [ -e /run/udhcpc.wlan0.pid ]
|
||||
then
|
||||
kill `cat /run/udhcpc.wlan0.pid` || true
|
||||
rm -f /run/udhcpc.wlan0.pid
|
||||
fi
|
||||
if [ -e /var/run/udhcpd.wlan0.pid ]
|
||||
then
|
||||
kill `cat /var/run/udhcpd.wlan0.pid` || true
|
||||
rm -f /var/run/udhcpd.wlan0.pid
|
||||
fi
|
||||
if [ -e /tmp/dnsmasq.run ]
|
||||
then
|
||||
kill `cat /tmp/dnsmasq.run` || true
|
||||
rm -f /tmp/dnsmasq.run
|
||||
fi
|
||||
# if [ -e /etc/wpa_supplicant.conf]
|
||||
# then
|
||||
# echo > /etc/wpa_supplicant.conf
|
||||
# fi
|
||||
airmon-ng stop wlan0mon || true
|
||||
}
|
||||
|
||||
restart() {
|
||||
stop
|
||||
start
|
||||
}
|
||||
|
||||
if [ "${1}" = "start" ]
|
||||
then
|
||||
start
|
||||
elif [ "${1}" = "stop" ]
|
||||
then
|
||||
stop
|
||||
elif [ "${1}" = "ap" ]
|
||||
then
|
||||
stop
|
||||
ap_start
|
||||
elif [ "${1}" = "restart" ]
|
||||
then
|
||||
restart
|
||||
fi
|
||||
20
kvmapp/system/init.d/S50avahi-daemon
Executable file
20
kvmapp/system/init.d/S50avahi-daemon
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# avahi-daemon init script
|
||||
|
||||
DAEMON=/usr/sbin/avahi-daemon
|
||||
case "$1" in
|
||||
start)
|
||||
$DAEMON -c || $DAEMON -D
|
||||
;;
|
||||
stop)
|
||||
$DAEMON -c && $DAEMON -k
|
||||
;;
|
||||
reload)
|
||||
$DAEMON -c && $DAEMON -r
|
||||
;;
|
||||
*)
|
||||
echo "Usage: S50avahi-daemon {start|stop|reload}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
49
kvmapp/system/init.d/S50ssdpd
Executable file
49
kvmapp/system/init.d/S50ssdpd
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/bin/sh
|
||||
|
||||
DAEMON=ssdpd
|
||||
PIDFILE=/var/run/$DAEMON.pid
|
||||
CFGFILE=/etc/default/$DAEMON
|
||||
|
||||
DAEMON_ARGS=""
|
||||
|
||||
# Read configuration variable file if it is present
|
||||
# shellcheck source=/dev/null
|
||||
[ -r "$CFGFILE" ] && . "$CFGFILE"
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
start() {
|
||||
printf 'Starting %s: ' "$DAEMON"
|
||||
if start-stop-daemon -S -q -p "$PIDFILE" -x "$DAEMON" -- $DAEMON_ARGS; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FAIL"
|
||||
fi
|
||||
}
|
||||
|
||||
stop() {
|
||||
printf 'Stopping %s: ' "$DAEMON"
|
||||
if start-stop-daemon -K -q -p "$PIDFILE" -x "$DAEMON"; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FAIL"
|
||||
fi
|
||||
}
|
||||
|
||||
restart() {
|
||||
stop
|
||||
start
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start|stop|restart)
|
||||
"$1"
|
||||
;;
|
||||
reload)
|
||||
restart
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|reload}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit $?
|
||||
73
kvmapp/system/init.d/S50sshd
Executable file
73
kvmapp/system/init.d/S50sshd
Executable file
@@ -0,0 +1,73 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# sshd Starts sshd.
|
||||
#
|
||||
|
||||
# Ensure required binaries exist
|
||||
[ -x /usr/bin/ssh-keygen ] || exit 0
|
||||
[ -x /usr/sbin/sshd ] || exit 1
|
||||
|
||||
PIDFILE="/var/run/sshd.pid"
|
||||
|
||||
umask 077
|
||||
|
||||
startssh() {
|
||||
# Generate SSH keys if they do not exist
|
||||
[ ! -f /etc/ssh/ssh_host_rsa_key ] && /usr/bin/ssh-keygen -A
|
||||
|
||||
printf "Starting sshd: "
|
||||
/usr/sbin/sshd
|
||||
touch /var/lock/sshd
|
||||
echo "OK"
|
||||
}
|
||||
|
||||
start() {
|
||||
if [ -e /etc/kvm/ssh_stop ]; then
|
||||
if [ -e /boot/start_ssh_once ]; then
|
||||
rm -f /boot/start_ssh_once
|
||||
startssh
|
||||
else
|
||||
echo "SSH does not start"
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
startssh
|
||||
fi
|
||||
}
|
||||
|
||||
stop() {
|
||||
printf "Stopping sshd: "
|
||||
killall sshd 2>/dev/null
|
||||
rm -f /var/lock/sshd
|
||||
echo "OK"
|
||||
}
|
||||
|
||||
restart() {
|
||||
stop
|
||||
start
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
restart|reload)
|
||||
restart
|
||||
;;
|
||||
permanent_on)
|
||||
rm -f /etc/kvm/ssh_stop
|
||||
start
|
||||
;;
|
||||
permanent_off)
|
||||
touch /etc/kvm/ssh_stop
|
||||
stop
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|permanent_on|permanent_off}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit 0
|
||||
29
kvmapp/system/init.d/S80dnsmasq
Executable file
29
kvmapp/system/init.d/S80dnsmasq
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
|
||||
DAEMON="dnsmasq"
|
||||
PIDFILE="/var/run/$DAEMON.pid"
|
||||
|
||||
[ -f /etc/dnsmasq.conf ] || exit 0
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
printf "Starting dnsmasq: "
|
||||
start-stop-daemon -S -p "$PIDFILE" -x "/usr/sbin/$DAEMON" -- \
|
||||
--pid-file="$PIDFILE"
|
||||
[ $? = 0 ] && echo "OK" || echo "FAIL"
|
||||
;;
|
||||
stop)
|
||||
printf "Stopping dnsmasq: "
|
||||
start-stop-daemon -K -q -p "$PIDFILE" -x "/usr/sbin/$DAEMON"
|
||||
[ $? = 0 ] && echo "OK" || echo "FAIL"
|
||||
;;
|
||||
restart|reload)
|
||||
$0 stop
|
||||
$0 start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit 0
|
||||
69
kvmapp/system/init.d/S95nanokvm
Executable file
69
kvmapp/system/init.d/S95nanokvm
Executable file
@@ -0,0 +1,69 @@
|
||||
#!/bin/sh
|
||||
# nanokvm Rev3.1
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
echo -n kvm > /boot/hostname.prefix
|
||||
|
||||
# Copy sensor config if the file exists
|
||||
[ -f /mnt/data/sensor_cfg.ini.LT ] && cp /mnt/data/sensor_cfg.ini.LT /mnt/data/sensor_cfg.ini
|
||||
|
||||
# Generate unique device key
|
||||
if [ -f /sys/class/cvi-base/base_uid ]; then
|
||||
str_value=$(awk '{print $2}' /sys/class/cvi-base/base_uid)
|
||||
first_uint=$(echo "$str_value" | cut -d'_' -f1)
|
||||
second_uint=$(echo "$str_value" | cut -d'_' -f2)
|
||||
echo "$first_uint$second_uint" > /device_key
|
||||
fi
|
||||
|
||||
# Set iptables rules (skip if already present)
|
||||
iptables -C INPUT -i eth0 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT 2>/dev/null || \
|
||||
iptables -A INPUT -i eth0 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
|
||||
|
||||
iptables -C OUTPUT -o eth0 -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT 2>/dev/null || \
|
||||
iptables -A OUTPUT -o eth0 -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT
|
||||
|
||||
iptables -C OUTPUT -o eth0 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT 2>/dev/null || \
|
||||
iptables -A OUTPUT -o eth0 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
|
||||
|
||||
iptables -C INPUT -i eth0 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT 2>/dev/null || \
|
||||
iptables -A INPUT -i eth0 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
|
||||
|
||||
iptables -C OUTPUT -o eth0 -p tcp --sport 8000 -m state --state ESTABLISHED -j DROP 2>/dev/null || \
|
||||
iptables -A OUTPUT -o eth0 -p tcp --sport 8000 -m state --state ESTABLISHED -j DROP
|
||||
|
||||
# Start services
|
||||
cp -r /kvmapp/kvm_system /tmp/
|
||||
/tmp/kvm_system/kvm_system &
|
||||
|
||||
cp -r /kvmapp/server /tmp/
|
||||
/tmp/server/NanoKVM-Server &
|
||||
;;
|
||||
|
||||
stop)
|
||||
killall kvm_system
|
||||
killall NanoKVM-Server
|
||||
rm -r /tmp/kvm_system /tmp/server
|
||||
echo "OK"
|
||||
;;
|
||||
|
||||
restart)
|
||||
killall kvm_system
|
||||
killall NanoKVM-Server
|
||||
rm -r /tmp/kvm_system /tmp/server
|
||||
|
||||
cp -r /kvmapp/kvm_system /tmp/
|
||||
/tmp/kvm_system/kvm_system &
|
||||
|
||||
cp -r /kvmapp/server /tmp/
|
||||
/tmp/server/NanoKVM-Server &
|
||||
|
||||
sync
|
||||
echo "OK"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
114
kvmapp/system/init.d/S98tailscaled
Executable file
114
kvmapp/system/init.d/S98tailscaled
Executable file
@@ -0,0 +1,114 @@
|
||||
#!/bin/sh
|
||||
|
||||
DAEMON="tailscaled"
|
||||
PIDFILE="/var/run/$DAEMON.pid"
|
||||
|
||||
# Set the port to listen on for incoming VPN packets.
|
||||
# Remote nodes will automatically be informed about the new port number,
|
||||
# but you might want to configure this in order to set external firewall
|
||||
# settings.
|
||||
PORT="41641"
|
||||
|
||||
# Extra flags you might want to pass to tailscaled.
|
||||
FLAGS=""
|
||||
|
||||
# You need tailscaled at /usr/sbin to server, and tailscale at /usr/bin to operate
|
||||
# STATIC version needed. Download page at https://pkgs.tailscale.com/stable/#static
|
||||
PKG_URL_LATEST="https://pkgs.tailscale.com/stable/tailscale_latest_riscv64.tgz"
|
||||
[ ! -x /usr/sbin/$DAEMON ] &&
|
||||
echo "/usr/sbin/$DAEMON not found, please download it from $PKG_URL_LATEST" &&
|
||||
echo "Then unpack it, copy $DAEMON to /usr/sbin and copy tailscale to /usr/bin" &&
|
||||
exit 1
|
||||
VERSION=$(/usr/sbin/$DAEMON --version|sed -n '1p'|xargs echo -n)
|
||||
|
||||
[ -x /usr/bin/tailscale ] || echo "/usr/bin/tailscale not found, your installation of tailscale may be broken"
|
||||
|
||||
# just for those need forwarding
|
||||
[ ! -f /etc/sysctl.d/99-tailscale.conf ] &&
|
||||
mkdir -p /etc/sysctl.d/ &&
|
||||
echo "missing /etc/sysctl.d/99-tailscale.conf, try make it below:" &&
|
||||
(tee /etc/sysctl.d/99-tailscale.conf <<EOF
|
||||
net.ipv4.ip_forward = 1
|
||||
net.ipv6.conf.all.forwarding = 1
|
||||
EOF
|
||||
) &&
|
||||
echo "if this message repeats showing, please look at $0 if you need forwarding"
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
tailscale set --accept-dns=false
|
||||
if [ -f /etc/kvm/GOMEMLIMIT ]; then
|
||||
value=$(cat /etc/kvm/GOMEMLIMIT)
|
||||
export GOMEMLIMIT="${value}MiB"
|
||||
else
|
||||
export GOMEMLIMIT=1024MiB
|
||||
fi
|
||||
|
||||
echo "GOMEMLIMIT set to ${GOMEMLIMIT}"
|
||||
printf "Starting $DAEMON[$VERSION]: "
|
||||
start-stop-daemon -S -bmq -p "$PIDFILE" -x "/usr/sbin/$DAEMON" -- \
|
||||
--state=/var/lib/tailscale/tailscaled.state \
|
||||
--socket=/var/run/tailscale/tailscaled.sock \
|
||||
--port=${PORT} \
|
||||
$FLAGS
|
||||
[ $? = 0 ] && echo "OK" || echo "FAIL"
|
||||
;;
|
||||
stop)
|
||||
printf "Stopping $DAEMON: "
|
||||
start-stop-daemon -K -p "$PIDFILE"
|
||||
[ $? = 0 ] && (echo "OK"; rm -f "$PIDFILE") || echo "FAIL"
|
||||
printf "cleaning tailscaled: "
|
||||
/usr/sbin/$DAEMON --cleanup &>/dev/null
|
||||
[ $? = 0 ] && echo "OK" || echo "FAIL"
|
||||
;;
|
||||
restart|reload)
|
||||
$0 stop
|
||||
$0 start
|
||||
;;
|
||||
doc)
|
||||
cat <<EOF
|
||||
To use tailscale, you need to meet these conditions below:
|
||||
1. Both tailscale and tailscaled were installed.
|
||||
2. At least one tailscale's account were registered.
|
||||
(https://login.tailscale.com/start)
|
||||
3. Your NanoKVM device must keep online.
|
||||
|
||||
Then, follow the steps below:
|
||||
1. Login in https://login.tailscale.com on your PC
|
||||
to enter into yout admin console.
|
||||
2. Run 'tailscale login' on your device, and copy the link showed
|
||||
to open on your PC, and confirm the auth.
|
||||
3. Congratulations, your NanoKVM device is now on the tailnet, try to
|
||||
visit it from all your device which on the same tailnet.
|
||||
4. run 'tailscale set --webclient' and manage your device
|
||||
on the web interface from localnet and tailenet.
|
||||
|
||||
Added: no more action to be repeat after your device reboot.
|
||||
EOF
|
||||
echo ""
|
||||
cat <<EOF
|
||||
为了正常使用 tailscale, 你需要满足以下几个条件:
|
||||
1. tailscale 和 tailscaled 这两个静态链接的二进制程序
|
||||
都已经正确安装在系统上。
|
||||
2. 至少注册一个 tailscale 的帐号才能使用其服务。
|
||||
(https://login.tailscale.com/start)
|
||||
3. 你的 NanoKVM 设备必须保持在线。
|
||||
|
||||
然后,跟着以下步骤操作:
|
||||
1. 在你的电脑上登录 https://login.tailscale.com 以进入控制台。
|
||||
2. 在你的 NanoKVM ssh 终端上执行 'tailscale login', 然后复制
|
||||
出现的链接到浏览器上,登录并通过验证。
|
||||
3. 祝贺,你的 NanoKVM 现在已经加入了 tailnet 然后你可以从任意
|
||||
同样已加入同一 tailnet 的设备去访问你的 NanoKVM。
|
||||
4. 执行 'tailscale set --webclient' 然后你就可以从 '局域网' 和
|
||||
'tailnet' 来访问特定网页,用来管理你的设备。
|
||||
|
||||
补充: 不需要重复上面的操作,哪怕你的设备重启过。
|
||||
EOF
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit 0
|
||||
BIN
kvmapp/system/ko/soph_mipi_rx.ko
Normal file
BIN
kvmapp/system/ko/soph_mipi_rx.ko
Normal file
Binary file not shown.
96
kvmapp/system/update-nanokvm.py
Executable file
96
kvmapp/system/update-nanokvm.py
Executable file
@@ -0,0 +1,96 @@
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
import zipfile
|
||||
import pathlib
|
||||
import requests
|
||||
|
||||
temporary: str = "/root/.kvm-cache/"
|
||||
|
||||
|
||||
def mkdir() -> None:
|
||||
if pathlib.Path(temporary).exists():
|
||||
shutil.rmtree(temporary)
|
||||
pathlib.Path(temporary).mkdir()
|
||||
print(f"Created temporary directory {temporary}")
|
||||
|
||||
|
||||
def read(file: str) -> str:
|
||||
return pathlib.Path(file).read_text().replace("\n", "")
|
||||
|
||||
|
||||
def download_firmware() -> None:
|
||||
print("Downloading firmware...")
|
||||
|
||||
now = int(time.time())
|
||||
url = f"https://cdn.sipeed.com/nanokvm/latest.zip?n={now}"
|
||||
print(f"Downloading firmware from {url}")
|
||||
|
||||
response = requests.get(url)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Failed to download firmware, status: {response.status_code}")
|
||||
|
||||
content_type = response.headers.get("content-type")
|
||||
if content_type != "application/zip":
|
||||
raise Exception(f"Failed to download firmware, content_type: {content_type}")
|
||||
|
||||
zip_file = f"{temporary}/latest.zip"
|
||||
with open(zip_file, "wb") as f:
|
||||
for chunk in response.iter_content(chunk_size=1024):
|
||||
f.write(chunk)
|
||||
|
||||
with zipfile.ZipFile(zip_file, "r") as f:
|
||||
f.extractall(temporary)
|
||||
|
||||
print("Completed downloading firmware.")
|
||||
|
||||
|
||||
def update() -> None:
|
||||
backup_dir = pathlib.Path("/root/old")
|
||||
firmware_dir = pathlib.Path("/kvmapp")
|
||||
|
||||
if backup_dir.exists():
|
||||
shutil.rmtree(backup_dir)
|
||||
|
||||
if firmware_dir.exists():
|
||||
firmware_dir.rename(backup_dir)
|
||||
|
||||
pathlib.Path(f"{temporary}/latest").rename(firmware_dir)
|
||||
|
||||
|
||||
def change_permissions() -> None:
|
||||
for root, dirs, files in os.walk("/kvmapp"):
|
||||
os.chmod(root, 0o755)
|
||||
|
||||
for file in files:
|
||||
file_path = os.path.join(root, file)
|
||||
os.chmod(file_path, 0o755)
|
||||
|
||||
print("change permissions done")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
try:
|
||||
print("Stopping nanokvm service...")
|
||||
os.system("/etc/init.d/S95nanokvm stop")
|
||||
|
||||
print("Staring update...")
|
||||
|
||||
mkdir()
|
||||
download_firmware()
|
||||
update()
|
||||
change_permissions()
|
||||
|
||||
version = read("/kvmapp/version")
|
||||
print(f"Successfully updated to version: {version}")
|
||||
print("restart service\nthe nanokvm will reboot")
|
||||
except Exception as e:
|
||||
print(f"update failed\n{e}")
|
||||
finally:
|
||||
shutil.rmtree(temporary)
|
||||
os.system("/etc/init.d/S95nanokvm restart")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -53,16 +53,16 @@ jwt:
|
||||
revokeTokensOnLogout: true
|
||||
|
||||
# Address for custom STUN server
|
||||
# Note: You can disable the STUN service by setting it to 'disable' (e.g., in a LAN environment)
|
||||
stun: stun.l.google.com:19302
|
||||
|
||||
# Address and authentication for custom TURN server
|
||||
turn:
|
||||
turnAddr: turn.cloudflare.com:3478
|
||||
turnAddr: example_addr
|
||||
turnUser: example_user
|
||||
turnCred: example_cred
|
||||
```
|
||||
|
||||
|
||||
## Compile & Deploy
|
||||
|
||||
Note: Use Linux operating system (x86-64). This build process is not compatible with ARM, Windows or macOS.
|
||||
|
||||
@@ -16,7 +16,7 @@ server
|
||||
├── middleware // サーバーミドルウェアコンポーネント
|
||||
├── proto // API リクエスト/レスポンス定義
|
||||
├── router // API ルートハンドラ
|
||||
├── service // コアサービスの実<EFBFBD><EFBFBD>
|
||||
├── service // コアサービスの実装
|
||||
├── utils // ユーティリティ関数
|
||||
└── main.go
|
||||
```
|
||||
@@ -39,17 +39,23 @@ cert:
|
||||
logger:
|
||||
level: info
|
||||
file: stdout
|
||||
|
||||
|
||||
# 認証設定 (enable/disable)
|
||||
# 注意: 認証を無効にするのは開発環境でのみ行ってください
|
||||
authentication: enable
|
||||
|
||||
# JWT 秘密鍵の設定
|
||||
# 空のままにすると、サーバー起動時にランダムな鍵が生成されます
|
||||
secretKey: ""
|
||||
jwt:
|
||||
# JWT 秘密鍵の設定。 空のままにすると、サーバー起動時にランダムな 64 バイトの鍵が自動的に生成されます。
|
||||
secretKey: ""
|
||||
# JWT トークンの有効期限(秒単位)。 デフォルト: 2678400 (31 日)
|
||||
refreshTokenDuration: 2678400
|
||||
# ユーザーがログアウトすると、すべての JWT トークンが無効になります。 デフォルト: true
|
||||
revokeTokensOnLogout: true
|
||||
|
||||
# カスタム STUN サーバーのアドレス
|
||||
stun: stun.l.google.com:19302
|
||||
|
||||
# カスタム TURN サーバーのアドレスと認証情報
|
||||
turn:
|
||||
turnAddr: turn.cloudflare.com:3478
|
||||
turnUser: example_user
|
||||
@@ -80,3 +86,12 @@ turn:
|
||||
1. デプロイ前に、ブラウザでアプリケーションを最新バージョンに更新します。手順は[こちら](https://wiki.sipeed.com/hardware/en/kvm/NanoKVM/system/updating.html)を参照してください。
|
||||
2. コンパイルして生成された `NanoKVM-Server` ファイルを使用して、NanoKVM の `/kvmapp/server/` ディレクトリ内の元のファイルを置き換えます。
|
||||
3. NanoKVM で `/etc/init.d/S95nanokvm restart` を実行してサービスを再起動します。
|
||||
|
||||
## 手動更新
|
||||
|
||||
> ファイルのアップロードには SSH が必要です。Web 設定で有効にしてください: `設定 > SSH`
|
||||
|
||||
1. [GitHub](https://github.com/sipeed/NanoKVM/releases) から最新のアプリケーションをダウンロードします。
|
||||
2. ダウンロードしたファイルを解凍し、解凍したフォルダーの名前を `kvmapp` に変更します。
|
||||
3. NanoKVM 上の既存の `/kvmapp` ディレクトリをバックアップし、新しい `kvmapp` フォルダーに置き換えます。
|
||||
4. NanoKVM で `/etc/init.d/S95nanokvm restart` を実行してサービスを再起動します。
|
||||
|
||||
@@ -50,10 +50,12 @@ jwt:
|
||||
# 在帐号登出时是否使所有 jwt token 失效。默认为 true
|
||||
revokeTokensOnLogout: true
|
||||
|
||||
# 自定义 STUN 服务器的地址
|
||||
# 注意:可以设置为“disable”来禁用 STUN 服务(例如在局域网环境中使用时)
|
||||
stun: stun.l.google.com:19302
|
||||
|
||||
turn:
|
||||
turnAddr: turn.cloudflare.com:3478
|
||||
turnAddr: example_addr
|
||||
turnUser: example_user
|
||||
turnCred: example_cred
|
||||
```
|
||||
|
||||
@@ -20,9 +20,7 @@ var (
|
||||
kvmVisionOnce sync.Once
|
||||
)
|
||||
|
||||
type KvmVision struct {
|
||||
mutex sync.Mutex
|
||||
}
|
||||
type KvmVision struct{}
|
||||
|
||||
func GetKvmVision() *KvmVision {
|
||||
kvmVisionOnce.Do(func() {
|
||||
@@ -44,9 +42,6 @@ func GetKvmVision() *KvmVision {
|
||||
}
|
||||
|
||||
func (k *KvmVision) ReadMjpeg(width uint16, height uint16, quality uint16) (data []byte, result int) {
|
||||
k.mutex.Lock()
|
||||
defer k.mutex.Unlock()
|
||||
|
||||
var (
|
||||
kvmData *C.uint8_t
|
||||
dataSize C.uint32_t
|
||||
@@ -72,10 +67,7 @@ func (k *KvmVision) ReadMjpeg(width uint16, height uint16, quality uint16) (data
|
||||
return
|
||||
}
|
||||
|
||||
func (k *KvmVision) ReadH264(width uint16, height uint16, bitRate uint16) (data []byte, sps []byte, pps []byte, result int) {
|
||||
k.mutex.Lock()
|
||||
defer k.mutex.Unlock()
|
||||
|
||||
func (k *KvmVision) ReadH264(width uint16, height uint16, bitRate uint16) (data []byte, result int) {
|
||||
var (
|
||||
kvmData *C.uint8_t
|
||||
dataSize C.uint32_t
|
||||
@@ -97,48 +89,10 @@ func (k *KvmVision) ReadH264(width uint16, height uint16, bitRate uint16) (data
|
||||
|
||||
data = C.GoBytes(unsafe.Pointer(kvmData), C.int(dataSize))
|
||||
|
||||
if result == 3 {
|
||||
sps, _ = k.ReadH264SPS()
|
||||
pps, _ = k.ReadH264PPS()
|
||||
}
|
||||
|
||||
log.Debugf("read kvm image: %v", result)
|
||||
return
|
||||
}
|
||||
|
||||
func (k *KvmVision) ReadH264SPS() ([]byte, int) {
|
||||
var (
|
||||
kvmData *C.uint8_t
|
||||
dataSize C.uint32_t
|
||||
)
|
||||
|
||||
result := int(C.kvmv_get_sps_frame(&kvmData, &dataSize))
|
||||
if result < 0 {
|
||||
log.Errorf("failed to read sps: %v", result)
|
||||
return nil, result
|
||||
}
|
||||
|
||||
data := C.GoBytes(unsafe.Pointer(kvmData), C.int(dataSize))
|
||||
|
||||
return data, result
|
||||
}
|
||||
|
||||
func (k *KvmVision) ReadH264PPS() ([]byte, int) {
|
||||
var (
|
||||
kvmData *C.uint8_t
|
||||
dataSize C.uint32_t
|
||||
)
|
||||
|
||||
result := int(C.kvmv_get_pps_frame(&kvmData, &dataSize))
|
||||
if result < 0 {
|
||||
log.Errorf("failed to read pps: %v", result)
|
||||
return nil, result
|
||||
}
|
||||
|
||||
data := C.GoBytes(unsafe.Pointer(kvmData), C.int(dataSize))
|
||||
return data, result
|
||||
}
|
||||
|
||||
func (k *KvmVision) SetHDMI(enable bool) int {
|
||||
hdmiEnable := C.uint8_t(0)
|
||||
if enable {
|
||||
@@ -154,10 +108,17 @@ func (k *KvmVision) SetHDMI(enable bool) int {
|
||||
return result
|
||||
}
|
||||
|
||||
func (k *KvmVision) Close() {
|
||||
k.mutex.Lock()
|
||||
defer k.mutex.Unlock()
|
||||
func (k *KvmVision) SetGop(gop uint8) {
|
||||
_gop := C.uint8_t(gop)
|
||||
C.set_h264_gop(_gop)
|
||||
}
|
||||
|
||||
func (k *KvmVision) SetFrameDetect(frame uint8) {
|
||||
_frame := C.uint8_t(frame)
|
||||
C.set_frame_detact(_frame)
|
||||
}
|
||||
|
||||
func (k *KvmVision) Close() {
|
||||
C.kvmv_deinit()
|
||||
log.Debugf("stop kvm vision...")
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ type Screen struct {
|
||||
FPS int
|
||||
Quality uint16
|
||||
BitRate uint16
|
||||
GOP uint8
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -46,6 +47,7 @@ func GetScreen() *Screen {
|
||||
Quality: 80,
|
||||
FPS: 30,
|
||||
BitRate: 3000,
|
||||
GOP: 30,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -70,6 +72,9 @@ func SetScreen(key string, value int) {
|
||||
|
||||
case "fps":
|
||||
screen.FPS = validateFPS(value)
|
||||
|
||||
case "gop":
|
||||
screen.GOP = uint8(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ var defaultConfig = &Config{
|
||||
},
|
||||
Stun: "stun.l.google.com:19302",
|
||||
Turn: Turn{
|
||||
TurnAddr: "turn.cloudflare.com:3478",
|
||||
TurnAddr: "",
|
||||
TurnUser: "",
|
||||
TurnCred: "",
|
||||
},
|
||||
|
||||
@@ -85,7 +85,7 @@ func getHardware() (h Hardware) {
|
||||
|
||||
default:
|
||||
h = HWAlpha
|
||||
log.Error("Unsupported hardware version: %s", version)
|
||||
log.Errorf("Unsupported hardware version: %s", version)
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,8 @@
|
||||
module NanoKVM-Server
|
||||
|
||||
go 1.22.1
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.23.2
|
||||
|
||||
require (
|
||||
github.com/creack/pty v1.1.24
|
||||
@@ -15,7 +17,7 @@ require (
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/unrolled/secure v1.15.0
|
||||
golang.org/x/crypto v0.31.0
|
||||
golang.org/x/crypto v0.36.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@@ -71,9 +73,9 @@ require (
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/net v0.37.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
)
|
||||
|
||||
@@ -160,17 +160,25 @@ golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -13,6 +13,7 @@ extern "C" {
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
|
||||
#define IMG_BUFFER_FULL -3
|
||||
#define IMG_VENC_ERROR -2
|
||||
@@ -23,7 +24,14 @@ extern "C" {
|
||||
#define IMG_H264_TYPE_IF 3
|
||||
#define IMG_H264_TYPE_PF 4
|
||||
|
||||
#define NORMAL_RES 0
|
||||
#define NEW_RES 1
|
||||
#define UNSUPPORT_RES 2
|
||||
#define UNKNOWN_RES 3
|
||||
#define ERROR_RES 4
|
||||
|
||||
void kvmv_init(uint8_t _debug_info_en);
|
||||
void set_venc_auto_recyc(uint8_t _enable);
|
||||
/**********************************************************************************
|
||||
* @name kvmv_read_img
|
||||
* @author Sipeed BuGu
|
||||
@@ -37,22 +45,25 @@ void kvmv_init(uint8_t _debug_info_en);
|
||||
* @param _pp_kvm_data @output: Encode data
|
||||
* @param _p_kvmv_data_size @output: Encode data size
|
||||
* @return
|
||||
-7: HDMI INPUT RES ERROR
|
||||
-6: Unsupported resolution, please modify it in the host settings.
|
||||
-5: Retrieving image, please wait
|
||||
-4: Modifying image resolution, please wait
|
||||
-3: img buffer full
|
||||
-2: VENC Error
|
||||
-1: No images were acquired
|
||||
0: Acquire MJPEG encoded images
|
||||
1: Acquire H264 encoded images(SPS)
|
||||
2: Acquire H264 encoded images(PPS)
|
||||
1: Acquire H264 encoded images(SPS)[Deprecated]
|
||||
2: Acquire H264 encoded images(PPS)[Deprecated]
|
||||
3: Acquire H264 encoded images(I)
|
||||
4: Acquire H264 encoded images(P)
|
||||
5: IMG not changed
|
||||
**********************************************************************************/
|
||||
int kvmv_read_img(uint16_t _width, uint16_t _height, uint8_t _type, uint16_t _qlty, uint8_t** _pp_kvm_data, uint32_t* _p_kvmv_data_size);
|
||||
int kvmv_get_sps_frame(uint8_t** _pp_kvm_data, uint32_t* _p_kvmv_data_size);
|
||||
int kvmv_get_pps_frame(uint8_t** _pp_kvm_data, uint32_t* _p_kvmv_data_size);
|
||||
int free_kvmv_data(uint8_t ** _pp_kvm_data);
|
||||
void free_all_kvmv_data();
|
||||
void set_h264_gop(uint8_t _gop);
|
||||
void set_frame_detact(uint8_t _frame_detact);
|
||||
void kvmv_deinit();
|
||||
uint8_t kvmv_hdmi_control(uint8_t _en);
|
||||
|
||||
@@ -60,4 +71,4 @@ uint8_t kvmv_hdmi_control(uint8_t _en);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // KVM_VISION_H_
|
||||
#endif // KVM_VISION_H_
|
||||
|
||||
@@ -22,7 +22,7 @@ func (f *formatter) Format(entry *logrus.Entry) ([]byte, error) {
|
||||
buffer = &bytes.Buffer{}
|
||||
}
|
||||
|
||||
now := entry.Time.Format("2006-01-02 15:04:05")
|
||||
now := entry.Time.Format("2006-01-02 15:04:05.000")
|
||||
|
||||
if entry.HasCaller() {
|
||||
fileName := filepath.Base(entry.Caller.File)
|
||||
|
||||
@@ -5,9 +5,14 @@ type GetImagesRsp struct {
|
||||
}
|
||||
|
||||
type MountImageReq struct {
|
||||
File string `json:"file" validate:"omitempty"`
|
||||
File string `json:"file" validate:"omitempty"`
|
||||
Cdrom bool `json:"cdrom" validate:"omitempty"`
|
||||
}
|
||||
|
||||
type GetMountedImageRsp struct {
|
||||
File string `json:"file"`
|
||||
}
|
||||
|
||||
type GetCdRomRsp struct {
|
||||
Cdrom int64 `json:"cdrom"`
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package proto
|
||||
|
||||
type GetFrameDetectRsp struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
type UpdateFrameDetectReq struct {
|
||||
Enabled bool `validate:"omitempty"`
|
||||
}
|
||||
|
||||
type UpdateFrameDetectRsp struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
type StopFrameDetectReq struct {
|
||||
Duration int `validate:"omitempty"`
|
||||
}
|
||||
|
||||
@@ -83,3 +83,7 @@ type GetOLEDRsp struct {
|
||||
type GetSSHStateRsp struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type GetMdnsStateRsp struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
@@ -14,4 +14,5 @@ func storageRouter(r *gin.Engine) {
|
||||
api.GET("/storage/image", service.GetImages) // get image list
|
||||
api.GET("/storage/image/mounted", service.GetMountedImage) // get mounted image
|
||||
api.POST("/storage/image/mount", service.MountImage) // mount image
|
||||
api.GET("/storage/cdrom", service.GetCdRom) // get CD-ROM flag
|
||||
}
|
||||
|
||||
@@ -12,8 +12,7 @@ func streamRouter(r *gin.Engine) {
|
||||
api := r.Group("/api").Use(middleware.CheckToken())
|
||||
|
||||
api.GET("/stream/mjpeg", mjpeg.Connect) // mjpeg stream
|
||||
api.GET("/stream/mjpeg/detect", mjpeg.GetFrameDetect) // get frame detect state
|
||||
api.POST("/stream/mjpeg/detect", mjpeg.UpdateFrameDetect) // update frame detect state
|
||||
api.POST("/stream/mjpeg/detect", mjpeg.UpdateFrameDetect) // update frame detect
|
||||
api.POST("/stream/mjpeg/detect/stop", mjpeg.StopFrameDetect) // temporary stop frame detect
|
||||
|
||||
api.GET("/stream/h264", h264.Connect) // h264 stream
|
||||
|
||||
@@ -39,4 +39,10 @@ func vmRouter(r *gin.Engine) {
|
||||
api.GET("/vm/ssh", service.GetSSHState) // get SSH state
|
||||
api.POST("/vm/ssh/enable", service.EnableSSH) // enable SSH
|
||||
api.POST("/vm/ssh/disable", service.DisableSSH) // disable SSH
|
||||
|
||||
api.GET("/vm/mdns", service.GetMdnsState) // get mDNS state
|
||||
api.POST("/vm/mdns/enable", service.EnableMdns) // enable mDNS
|
||||
api.POST("/vm/mdns/disable", service.DisableMdns) // disable mDNS
|
||||
|
||||
api.POST("/vm/system/reboot", service.Reboot) // reboot system
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ const AccountFile = "/etc/kvm/pwd"
|
||||
|
||||
type Account struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Password string `json:"password"` // should be named HashedPassword for clarity
|
||||
}
|
||||
|
||||
func GetAccount() (*Account, error) {
|
||||
@@ -40,10 +40,10 @@ func GetAccount() (*Account, error) {
|
||||
return &account, nil
|
||||
}
|
||||
|
||||
func SetAccount(username string, password string) error {
|
||||
func SetAccount(username string, hashedPassword string) error {
|
||||
account, err := json.Marshal(&Account{
|
||||
Username: username,
|
||||
Password: password,
|
||||
Password: hashedPassword,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("failed to marshal account information to json: %s", err)
|
||||
@@ -65,7 +65,7 @@ func SetAccount(username string, password string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func CompareAccount(username string, password string) bool {
|
||||
func CompareAccount(username string, plainPassword string) bool {
|
||||
account, err := GetAccount()
|
||||
if err != nil {
|
||||
return false
|
||||
@@ -75,16 +75,16 @@ func CompareAccount(username string, password string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
decryptedPassword, err := utils.DecodeDecrypt(password)
|
||||
if err != nil || decryptedPassword == "" {
|
||||
hashedPassword, err := utils.DecodeDecrypt(plainPassword)
|
||||
if err != nil || hashedPassword == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
err = bcrypt.CompareHashAndPassword([]byte(account.Password), []byte(decryptedPassword))
|
||||
err = bcrypt.CompareHashAndPassword([]byte(account.Password), []byte(hashedPassword))
|
||||
if err != nil {
|
||||
// Compatible with old versions
|
||||
accountDecryptedPassword, _ := utils.DecodeDecrypt(account.Password)
|
||||
if accountDecryptedPassword == decryptedPassword {
|
||||
accountHashedPassword, _ := utils.DecodeDecrypt(account.Password)
|
||||
if accountHashedPassword == hashedPassword {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -104,10 +104,10 @@ func DelAccount() error {
|
||||
}
|
||||
|
||||
func getDefaultAccount() *Account {
|
||||
password, _ := bcrypt.GenerateFromPassword([]byte("admin"), bcrypt.DefaultCost)
|
||||
hashedPassword, _ := bcrypt.GenerateFromPassword([]byte("admin"), bcrypt.DefaultCost)
|
||||
|
||||
return &Account{
|
||||
Username: "admin",
|
||||
Password: string(password),
|
||||
Password: string(hashedPassword),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package auth
|
||||
import (
|
||||
"NanoKVM-Server/proto"
|
||||
"NanoKVM-Server/utils"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -70,7 +71,9 @@ func (s *Service) IsPasswordUpdated(c *gin.Context) {
|
||||
err = bcrypt.CompareHashAndPassword([]byte(account.Password), []byte("admin"))
|
||||
|
||||
rsp.OkRspWithData(c, &proto.IsPasswordUpdatedRsp{
|
||||
IsUpdated: err == nil,
|
||||
// If the hash is not valid, still assume it's not updated
|
||||
// The error we want to see is password and hash not matching
|
||||
IsUpdated: errors.Is(err, bcrypt.ErrMismatchedHashAndPassword),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ const (
|
||||
TailscalePath = "/usr/bin/tailscale"
|
||||
TailscaledPath = "/usr/sbin/tailscaled"
|
||||
ConfigPath = "etc/sysctl.d/99-tailscale.conf"
|
||||
|
||||
GoMemLimit int64 = 75
|
||||
)
|
||||
|
||||
var StateMap = map[string]proto.TailscaleState{
|
||||
@@ -69,7 +71,7 @@ func (s *Service) Start(c *gin.Context) {
|
||||
}
|
||||
|
||||
if !utils.IsGoMemLimitExist() {
|
||||
_ = utils.SetGoMemLimit(50)
|
||||
_ = utils.SetGoMemLimit(GoMemLimit)
|
||||
}
|
||||
|
||||
rsp.OkRsp(c)
|
||||
@@ -165,7 +167,7 @@ func (s *Service) Login(c *gin.Context) {
|
||||
}
|
||||
|
||||
if !utils.IsGoMemLimitExist() {
|
||||
_ = utils.SetGoMemLimit(50)
|
||||
_ = utils.SetGoMemLimit(GoMemLimit)
|
||||
}
|
||||
|
||||
rsp.OkRspWithData(c, &proto.LoginTailscaleRsp{
|
||||
|
||||
@@ -33,7 +33,7 @@ func (s *Service) WakeOnLAN(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
command := fmt.Sprintf("ether-wake %s", mac)
|
||||
command := fmt.Sprintf("ether-wake -b %s", mac)
|
||||
cmd := exec.Command("sh", "-c", command)
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -18,9 +19,7 @@ const (
|
||||
imageNone = "/dev/mmcblk0p3"
|
||||
cdromFlag = "/sys/kernel/config/usb_gadget/g0/functions/mass_storage.disk0/lun.0/cdrom"
|
||||
mountDevice = "/sys/kernel/config/usb_gadget/g0/functions/mass_storage.disk0/lun.0/file"
|
||||
removableFlag = "/sys/kernel/config/usb_gadget/g0/functions/mass_storage.disk0/lun.0/removable"
|
||||
roFlag = "/sys/kernel/config/usb_gadget/g0/functions/mass_storage.disk0/lun.0/ro"
|
||||
roDisk = "/boot/usb.disk0.ro"
|
||||
)
|
||||
|
||||
func (s *Service) GetImages(c *gin.Context) {
|
||||
@@ -61,55 +60,43 @@ func (s *Service) MountImage(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// cdrom and ro flag
|
||||
// set to 0 when unmount image
|
||||
// set to 1 when mount image and the CD-ROM is enabled
|
||||
if req.File == "" || req.Cdrom {
|
||||
flag := "0"
|
||||
if req.File != "" && req.Cdrom {
|
||||
flag = "1"
|
||||
}
|
||||
|
||||
// unmount
|
||||
if err := os.WriteFile(mountDevice, []byte("\n"), 0o666); err != nil {
|
||||
log.Errorf("unmount file failed: %s", err)
|
||||
rsp.ErrRsp(c, -2, "unmount image failed")
|
||||
return
|
||||
}
|
||||
|
||||
// ro flag
|
||||
if err := os.WriteFile(roFlag, []byte(flag), 0o666); err != nil {
|
||||
log.Errorf("set ro flag failed: %s", err)
|
||||
rsp.ErrRsp(c, -2, "set ro flag failed")
|
||||
return
|
||||
}
|
||||
|
||||
// cdrom flag
|
||||
if err := os.WriteFile(cdromFlag, []byte(flag), 0o666); err != nil {
|
||||
log.Errorf("set cdrom flag failed: %s", err)
|
||||
rsp.ErrRsp(c, -2, "set cdrom flag failed")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// mount
|
||||
image := req.File
|
||||
if image == "" {
|
||||
image = imageNone
|
||||
}
|
||||
|
||||
imageRemovable := "1"
|
||||
|
||||
imageRo := "0"
|
||||
isImageRo, _ := isFlagExist(roDisk)
|
||||
if isImageRo {
|
||||
imageRo = "1"
|
||||
}
|
||||
|
||||
imageLow := strings.ToLower(image)
|
||||
imageCdrom := "0"
|
||||
if strings.HasSuffix(imageLow, ".iso") {
|
||||
imageRo = "1"
|
||||
imageCdrom = "1"
|
||||
}
|
||||
|
||||
// unmount
|
||||
if err := os.WriteFile(mountDevice, []byte("\n"), 0o666); err != nil {
|
||||
log.Errorf("unmount file failed: %s", err)
|
||||
rsp.ErrRsp(c, -2, "unmount image failed")
|
||||
return
|
||||
}
|
||||
|
||||
// removable flag
|
||||
if err := os.WriteFile(removableFlag, []byte(imageRemovable), 0o666); err != nil {
|
||||
log.Errorf("set removable flag failed: %s", err)
|
||||
rsp.ErrRsp(c, -2, "set removable flag failed")
|
||||
return
|
||||
}
|
||||
|
||||
// ro flag
|
||||
if err := os.WriteFile(roFlag, []byte(imageRo), 0o666); err != nil {
|
||||
log.Errorf("set ro flag failed: %s", err)
|
||||
rsp.ErrRsp(c, -2, "set ro flag failed")
|
||||
return
|
||||
}
|
||||
|
||||
// cdrom flag
|
||||
if err := os.WriteFile(cdromFlag, []byte(imageCdrom), 0o666); err != nil {
|
||||
log.Errorf("set cdrom flag failed: %s", err)
|
||||
rsp.ErrRsp(c, -2, "set cdrom flag failed")
|
||||
return
|
||||
}
|
||||
|
||||
// mount
|
||||
if err := os.WriteFile(mountDevice, []byte(image), 0o666); err != nil {
|
||||
log.Errorf("mount file %s failed: %s", image, err)
|
||||
rsp.ErrRsp(c, -2, "mount image failed")
|
||||
@@ -128,6 +115,7 @@ func (s *Service) MountImage(c *gin.Context) {
|
||||
rsp.ErrRsp(c, -2, "execute command failed")
|
||||
return
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
rsp.OkRsp(c)
|
||||
@@ -155,17 +143,25 @@ func (s *Service) GetMountedImage(c *gin.Context) {
|
||||
rsp.OkRspWithData(c, data)
|
||||
}
|
||||
|
||||
func isFlagExist(flag string) (bool, error) {
|
||||
_, err := os.Stat(flag)
|
||||
func (s *Service) GetCdRom(c *gin.Context) {
|
||||
var rsp proto.Response
|
||||
|
||||
if err == nil {
|
||||
return true, nil
|
||||
content, err := os.ReadFile(cdromFlag)
|
||||
if err != nil {
|
||||
rsp.ErrRsp(c, -1, "read failed")
|
||||
return
|
||||
}
|
||||
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return false, nil
|
||||
flag := strings.ReplaceAll(string(content), "\n", "")
|
||||
flatInt, err := strconv.ParseInt(flag, 10, 64)
|
||||
if err != nil {
|
||||
rsp.ErrRsp(c, -2, "parse failed")
|
||||
return
|
||||
}
|
||||
|
||||
log.Errorf("check file %s err: %s", flag, err)
|
||||
return false, err
|
||||
data := &proto.GetCdRomRsp{
|
||||
Cdrom: flatInt,
|
||||
}
|
||||
|
||||
rsp.OkRspWithData(c, data)
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func GetFrameRateCounter() *FrameRateCounter {
|
||||
data := fmt.Sprintf("%d", counter.fps)
|
||||
err := os.WriteFile("/kvmapp/kvm/now_fps", []byte(data), 0o666)
|
||||
if err != nil {
|
||||
log.Errorf("failed to wirte fps: %s", err)
|
||||
log.Errorf("failed to write fps: %s", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -2,6 +2,7 @@ package h264
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sync"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/pion/webrtc/v4"
|
||||
@@ -9,8 +10,9 @@ import (
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
ws *websocket.Conn
|
||||
pc *webrtc.PeerConnection
|
||||
ws *websocket.Conn
|
||||
pc *webrtc.PeerConnection
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
@@ -36,9 +38,7 @@ func (c *Client) addTrack() {
|
||||
return
|
||||
}
|
||||
|
||||
mutex.Lock()
|
||||
trackMap[c.ws] = videoTrack
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
// register callback events
|
||||
@@ -61,13 +61,9 @@ func (c *Client) register() {
|
||||
// ICE connection state has changed
|
||||
c.pc.OnICEConnectionStateChange(func(state webrtc.ICEConnectionState) {
|
||||
if state == webrtc.ICEConnectionStateConnected && !isSending {
|
||||
mutex.Lock()
|
||||
if !isSending {
|
||||
// start sending h264 data
|
||||
go send()
|
||||
isSending = true
|
||||
}
|
||||
mutex.Unlock()
|
||||
// start sending h264 data
|
||||
go send()
|
||||
isSending = true
|
||||
}
|
||||
|
||||
log.Debugf("ice connection state has changed to %s", state.String())
|
||||
@@ -81,13 +77,11 @@ func (c *Client) readMessage() {
|
||||
for {
|
||||
_, raw, err := c.ws.ReadMessage()
|
||||
if err != nil {
|
||||
mutex.Lock()
|
||||
delete(trackMap, c.ws)
|
||||
if len(trackMap) == 0 && isSending {
|
||||
if isSending && len(trackMap) == 0 {
|
||||
// stop sending when all websocket connections are closed
|
||||
exitSig <- true
|
||||
isSending = false
|
||||
}
|
||||
mutex.Unlock()
|
||||
|
||||
log.Debugf("failed to read message: %s", err)
|
||||
return
|
||||
@@ -144,9 +138,6 @@ func (c *Client) readMessage() {
|
||||
return
|
||||
}
|
||||
|
||||
case "heartbeat":
|
||||
_ = c.sendMessage("heartbeat", "")
|
||||
|
||||
default:
|
||||
log.Debugf("unhandled message event: %s", message.Event)
|
||||
}
|
||||
@@ -155,8 +146,8 @@ func (c *Client) readMessage() {
|
||||
|
||||
// send websocket message
|
||||
func (c *Client) sendMessage(event string, data string) error {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
message := &Message{
|
||||
Event: event,
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"NanoKVM-Server/config"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
@@ -18,9 +19,7 @@ var (
|
||||
},
|
||||
}
|
||||
trackMap = make(map[*websocket.Conn]*webrtc.TrackLocalStaticSample)
|
||||
mutex = sync.RWMutex{}
|
||||
isSending = false
|
||||
exitSig = make(chan bool, 1)
|
||||
)
|
||||
|
||||
func Connect(c *gin.Context) {
|
||||
@@ -35,11 +34,14 @@ func Connect(c *gin.Context) {
|
||||
log.Debugf("h264 websocket disconnected")
|
||||
}()
|
||||
|
||||
var zeroTime time.Time
|
||||
_ = wsConn.SetReadDeadline(zeroTime)
|
||||
|
||||
conf := config.GetInstance()
|
||||
|
||||
var iceServers []webrtc.ICEServer
|
||||
|
||||
if conf.Stun != "" {
|
||||
if conf.Stun != "" && conf.Stun != "disable" {
|
||||
iceServers = append(iceServers, webrtc.ICEServer{
|
||||
URLs: []string{"stun:" + conf.Stun},
|
||||
})
|
||||
@@ -53,11 +55,9 @@ func Connect(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
rtc_config := webrtc.Configuration{
|
||||
peerConn, err := webrtc.NewPeerConnection(webrtc.Configuration{
|
||||
ICEServers: iceServers,
|
||||
}
|
||||
|
||||
peerConn, err := webrtc.NewPeerConnection(rtc_config)
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("failed to create PeerConnection: %s", err)
|
||||
return
|
||||
@@ -69,8 +69,9 @@ func Connect(c *gin.Context) {
|
||||
}()
|
||||
|
||||
client := &Client{
|
||||
ws: wsConn,
|
||||
pc: peerConn,
|
||||
ws: wsConn,
|
||||
pc: peerConn,
|
||||
mutex: sync.Mutex{},
|
||||
}
|
||||
|
||||
client.addTrack()
|
||||
|
||||
@@ -2,7 +2,6 @@ package h264
|
||||
|
||||
import (
|
||||
"NanoKVM-Server/common"
|
||||
"NanoKVM-Server/service/stream"
|
||||
"time"
|
||||
|
||||
"github.com/pion/webrtc/v4/pkg/media"
|
||||
@@ -18,63 +17,45 @@ func send() {
|
||||
defer ticker.Stop()
|
||||
|
||||
vision := common.GetKvmVision()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
height := screen.Height
|
||||
width, ok := common.ResolutionMap[height]
|
||||
if !ok {
|
||||
width = 0
|
||||
height = 0
|
||||
}
|
||||
|
||||
bitRate := screen.BitRate
|
||||
if _, ok := common.BitRateMap[bitRate]; !ok {
|
||||
bitRate = 3000
|
||||
}
|
||||
|
||||
data, sps, pps, result := vision.ReadH264(width, height, bitRate)
|
||||
if result < 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if result == 3 {
|
||||
writeSample(sps, duration)
|
||||
writeSample(pps, duration)
|
||||
}
|
||||
writeSample(data, duration)
|
||||
|
||||
stream.GetFrameRateCounter().Update()
|
||||
|
||||
if screen.FPS != fps {
|
||||
fps = screen.FPS
|
||||
duration = time.Second / time.Duration(fps)
|
||||
ticker.Reset(duration)
|
||||
}
|
||||
|
||||
case <-exitSig:
|
||||
mutex.Lock()
|
||||
isSending = false
|
||||
mutex.Unlock()
|
||||
for range ticker.C {
|
||||
if !isSending && len(trackMap) == 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeSample(data []byte, duration time.Duration) {
|
||||
sample := media.Sample{
|
||||
Data: data,
|
||||
Duration: duration,
|
||||
}
|
||||
height := screen.Height
|
||||
width, ok := common.ResolutionMap[height]
|
||||
if !ok {
|
||||
width = 0
|
||||
height = 0
|
||||
}
|
||||
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
bitRate := screen.BitRate
|
||||
if _, ok := common.BitRateMap[bitRate]; !ok {
|
||||
bitRate = 3000
|
||||
}
|
||||
|
||||
for _, track := range trackMap {
|
||||
if err := track.WriteSample(sample); err != nil {
|
||||
log.Errorf("failed to send h264 data: %s", err)
|
||||
data, result := vision.ReadH264(width, height, bitRate)
|
||||
if result < 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
sample := media.Sample{
|
||||
Data: data,
|
||||
Duration: duration,
|
||||
}
|
||||
|
||||
for _, track := range trackMap {
|
||||
if err := track.WriteSample(sample); err != nil {
|
||||
log.Errorf("failed to send h264 data: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("send h264 data: %d", len(data))
|
||||
|
||||
if screen.FPS != fps {
|
||||
fps = screen.FPS
|
||||
duration = time.Second / time.Duration(fps)
|
||||
ticker.Reset(duration)
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("send h264 data: %d", len(data))
|
||||
}
|
||||
|
||||
@@ -1,132 +1,55 @@
|
||||
package mjpeg
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"NanoKVM-Server/common"
|
||||
"NanoKVM-Server/proto"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"NanoKVM-Server/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
frameDetect = "/etc/kvm/frame_detact"
|
||||
frameDetectTmp = "/etc/kvm/frame_detact_tmp"
|
||||
)
|
||||
|
||||
type UpdateFrameDetectRsp struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
func GetFrameDetect(c *gin.Context) {
|
||||
var rsp proto.Response
|
||||
|
||||
isEnabled, err := isFrameDetectEnabled()
|
||||
if err != nil {
|
||||
rsp.ErrRsp(c, -1, "unknown frame detect status")
|
||||
return
|
||||
}
|
||||
|
||||
rsp.OkRspWithData(c, &proto.GetFrameDetectRsp{
|
||||
Enabled: isEnabled,
|
||||
})
|
||||
log.Debugf("get frame detect success, enabled: %t", isEnabled)
|
||||
}
|
||||
const FrameDetectInterval uint8 = 60
|
||||
|
||||
func UpdateFrameDetect(c *gin.Context) {
|
||||
var req proto.UpdateFrameDetectReq
|
||||
var rsp proto.Response
|
||||
|
||||
isEnabled, err := isFrameDetectEnabled()
|
||||
if err != nil {
|
||||
rsp.ErrRsp(c, -1, "unknown frame status")
|
||||
if err := proto.ParseFormRequest(c, &req); err != nil {
|
||||
rsp.ErrRsp(c, -1, "invalid parameters")
|
||||
return
|
||||
}
|
||||
|
||||
if isEnabled {
|
||||
_ = os.Remove(frameDetect)
|
||||
_ = os.Remove(frameDetectTmp)
|
||||
} else {
|
||||
file, err2 := os.OpenFile(frameDetect, os.O_CREATE|os.O_TRUNC, 0o644)
|
||||
if err2 != nil {
|
||||
rsp.ErrRsp(c, -3, "enable frame detect failed")
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
var frame uint8 = 0
|
||||
if req.Enabled {
|
||||
frame = FrameDetectInterval
|
||||
}
|
||||
|
||||
isEnabled, err = isFrameDetectEnabled()
|
||||
if err != nil {
|
||||
rsp.ErrRsp(c, -4, "unknown frame status")
|
||||
return
|
||||
}
|
||||
common.GetKvmVision().SetFrameDetect(frame)
|
||||
|
||||
rsp.OkRspWithData(c, &UpdateFrameDetectRsp{
|
||||
Enabled: isEnabled,
|
||||
})
|
||||
log.Debugf("update frame detect success, enabled: %t", isEnabled)
|
||||
rsp.OkRsp(c)
|
||||
log.Debugf("update frame detect: %t", req.Enabled)
|
||||
}
|
||||
|
||||
func StopFrameDetect(c *gin.Context) {
|
||||
var req proto.StopFrameDetectReq
|
||||
var rsp proto.Response
|
||||
|
||||
exist, err := isFileExist(frameDetect)
|
||||
if err != nil {
|
||||
rsp.ErrRsp(c, -1, "unknown frame status")
|
||||
if err := proto.ParseFormRequest(c, &req); err != nil {
|
||||
rsp.ErrRsp(c, -1, "invalid parameters")
|
||||
return
|
||||
}
|
||||
|
||||
if !exist {
|
||||
rsp.OkRsp(c)
|
||||
return
|
||||
duration := 10 * time.Second
|
||||
if req.Duration > 0 {
|
||||
duration = time.Duration(req.Duration) * time.Second
|
||||
}
|
||||
|
||||
err = os.Rename(frameDetect, frameDetectTmp)
|
||||
if err != nil {
|
||||
rsp.ErrRsp(c, -2, "stop operation failed")
|
||||
return
|
||||
}
|
||||
vision := common.GetKvmVision()
|
||||
|
||||
go func() {
|
||||
time.Sleep(20 * time.Second)
|
||||
_ = os.Rename(frameDetectTmp, frameDetect)
|
||||
log.Debugf("frame detect started")
|
||||
}()
|
||||
vision.SetFrameDetect(0)
|
||||
time.Sleep(duration)
|
||||
vision.SetFrameDetect(FrameDetectInterval)
|
||||
|
||||
rsp.OkRsp(c)
|
||||
log.Debugf("frame detect stoped")
|
||||
}
|
||||
|
||||
func isFrameDetectEnabled() (bool, error) {
|
||||
exist, err := isFileExist(frameDetect)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if exist {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
exist, err = isFileExist(frameDetectTmp)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return exist, nil
|
||||
}
|
||||
|
||||
func isFileExist(name string) (bool, error) {
|
||||
_, err := os.Stat(name)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package mjpeg
|
||||
import (
|
||||
"NanoKVM-Server/common"
|
||||
"NanoKVM-Server/service/stream"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -12,25 +11,25 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
chanMap = make(map[*gin.Context]chan struct{})
|
||||
mutex = sync.RWMutex{}
|
||||
chanMap = make(map[*gin.Context]bool)
|
||||
mutex = sync.Mutex{}
|
||||
exitSig = make(chan bool, 1)
|
||||
)
|
||||
|
||||
func Connect(c *gin.Context) {
|
||||
c.Header("Content-Type", fmt.Sprintf("multipart/x-mixed-replace; boundary=frame"))
|
||||
c.Header("Content-Type", "multipart/x-mixed-replace; boundary=frame")
|
||||
c.Header("Cache-Control", "no-cache")
|
||||
c.Header("Connection", "keep-alive")
|
||||
c.Header("Pragma", "no-cache")
|
||||
|
||||
mutex.Lock()
|
||||
chanMap[c] = make(chan struct{}, 1)
|
||||
chanMap[c] = true
|
||||
if len(chanMap) == 1 {
|
||||
go send()
|
||||
}
|
||||
mutex.Unlock()
|
||||
|
||||
<-chanMap[c]
|
||||
<-c.Request.Context().Done()
|
||||
|
||||
mutex.Lock()
|
||||
delete(chanMap, c)
|
||||
@@ -64,14 +63,13 @@ func send() {
|
||||
}
|
||||
|
||||
data, result := vision.ReadMjpeg(width, height, quality)
|
||||
if result < 0 {
|
||||
if result < 0 || result == 5 {
|
||||
continue
|
||||
}
|
||||
|
||||
for c, ch := range chanMap {
|
||||
for c := range chanMap {
|
||||
if err := write(c, data); err != nil {
|
||||
log.Debugf("failed to write mjpeg data: %s", err)
|
||||
close(ch)
|
||||
}
|
||||
}
|
||||
log.Debugf("send mjpeg data: %d", len(data))
|
||||
|
||||
@@ -46,7 +46,7 @@ func (s *Service) SetGpio(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
log.Debugf("gpio %s set sucessfully", device)
|
||||
log.Debugf("gpio %s set successfully", device)
|
||||
rsp.OkRsp(c)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ var imageVersionMap = map[string]string{
|
||||
"2024-07-23-20-18-587710.img": "v1.1.0",
|
||||
"2024-08-08-19-44-bef2ca.img": "v1.2.0",
|
||||
"2024-11-13-09-59-9c961a.img": "v1.3.0",
|
||||
"2025-02-17-19-08-3649fe.img": "v1.4.0",
|
||||
}
|
||||
|
||||
func (s *Service) GetInfo(c *gin.Context) {
|
||||
@@ -52,6 +53,10 @@ func getIp() string {
|
||||
}
|
||||
|
||||
func getMdns() string {
|
||||
if pid := getAvahiDaemonPid(); pid == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
content, err := os.ReadFile("/etc/hostname")
|
||||
if err != nil {
|
||||
return ""
|
||||
|
||||
92
server/service/vm/mdns.go
Normal file
92
server/service/vm/mdns.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"NanoKVM-Server/proto"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
AvahiDaemonPid = "/run/avahi-daemon/pid"
|
||||
AvahiDaemonScript = "/etc/init.d/S50avahi-daemon"
|
||||
AvahiDaemonBackupScript = "/kvmapp/system/init.d/S50avahi-daemon"
|
||||
)
|
||||
|
||||
func (s *Service) GetMdnsState(c *gin.Context) {
|
||||
var rsp proto.Response
|
||||
|
||||
pid := getAvahiDaemonPid()
|
||||
|
||||
rsp.OkRspWithData(c, &proto.GetMdnsStateRsp{
|
||||
Enabled: pid != "",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Service) EnableMdns(c *gin.Context) {
|
||||
var rsp proto.Response
|
||||
|
||||
pid := getAvahiDaemonPid()
|
||||
if pid != "" {
|
||||
rsp.OkRsp(c)
|
||||
return
|
||||
}
|
||||
|
||||
commands := []string{
|
||||
fmt.Sprintf("cp -f %s %s", AvahiDaemonBackupScript, AvahiDaemonScript),
|
||||
fmt.Sprintf("%s start", AvahiDaemonScript),
|
||||
}
|
||||
|
||||
command := strings.Join(commands, " && ")
|
||||
err := exec.Command("sh", "-c", command).Run()
|
||||
if err != nil {
|
||||
log.Errorf("failed to start avahi-daemon: %s", err)
|
||||
rsp.ErrRsp(c, -1, "failed to enable mdns")
|
||||
return
|
||||
}
|
||||
|
||||
rsp.OkRsp(c)
|
||||
log.Debugf("avahi-daemon started")
|
||||
}
|
||||
|
||||
func (s *Service) DisableMdns(c *gin.Context) {
|
||||
var rsp proto.Response
|
||||
|
||||
pid := getAvahiDaemonPid()
|
||||
if pid == "" {
|
||||
rsp.OkRsp(c)
|
||||
return
|
||||
}
|
||||
|
||||
command := fmt.Sprintf("kill -9 %s", pid)
|
||||
err := exec.Command("sh", "-c", command).Run()
|
||||
if err != nil {
|
||||
log.Errorf("failed to stop avahi-daemon: %s", err)
|
||||
rsp.ErrRsp(c, -1, "failed to disable mdns")
|
||||
return
|
||||
}
|
||||
|
||||
_ = os.Remove(AvahiDaemonPid)
|
||||
_ = os.Remove(AvahiDaemonScript)
|
||||
|
||||
rsp.OkRsp(c)
|
||||
log.Debugf("avahi-daemon stopped")
|
||||
}
|
||||
|
||||
func getAvahiDaemonPid() string {
|
||||
if _, err := os.Stat(AvahiDaemonPid); err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(AvahiDaemonPid)
|
||||
if err != nil {
|
||||
log.Errorf("failed to read mdns pid: %s", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
return strings.ReplaceAll(string(content), "\n", "")
|
||||
}
|
||||
@@ -30,7 +30,7 @@ func (s *Service) SetMemoryLimit(c *gin.Context) {
|
||||
}
|
||||
|
||||
rsp.OkRsp(c)
|
||||
log.Debug("set memory limit successful, enabled: %t, limit: %s", req.Enabled, req.Limit)
|
||||
log.Debugf("set memory limit successful, enabled: %t, limit: %d", req.Enabled, req.Limit)
|
||||
}
|
||||
|
||||
func (s *Service) GetMemoryLimit(c *gin.Context) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"NanoKVM-Server/common"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -22,30 +23,34 @@ func (s *Service) SetScreen(c *gin.Context) {
|
||||
var req proto.SetScreenReq
|
||||
var rsp proto.Response
|
||||
|
||||
if err := proto.ParseFormRequest(c, &req); err != nil {
|
||||
err := proto.ParseFormRequest(c, &req)
|
||||
if err != nil {
|
||||
rsp.ErrRsp(c, -1, "invalid arguments")
|
||||
return
|
||||
}
|
||||
|
||||
file, ok := screenFileMap[req.Type]
|
||||
if !ok {
|
||||
rsp.ErrRsp(c, -2, "invalid arguments")
|
||||
return
|
||||
}
|
||||
|
||||
data := fmt.Sprintf("%d", req.Value)
|
||||
if req.Type == "type" {
|
||||
switch req.Type {
|
||||
case "type":
|
||||
data := "h264"
|
||||
if req.Value == 0 {
|
||||
data = "mjpeg"
|
||||
} else {
|
||||
data = "h264"
|
||||
}
|
||||
err = writeScreen("type", data)
|
||||
|
||||
case "gop":
|
||||
gop := 30
|
||||
if req.Value >= 1 && req.Value <= 100 {
|
||||
gop = req.Value
|
||||
}
|
||||
common.GetKvmVision().SetGop(uint8(gop))
|
||||
|
||||
default:
|
||||
data := strconv.Itoa(req.Value)
|
||||
err = writeScreen(req.Type, data)
|
||||
}
|
||||
|
||||
err := os.WriteFile(file, []byte(data), 0o666)
|
||||
if err != nil {
|
||||
log.Errorf("write kvm %s failed: %s", file, err)
|
||||
rsp.ErrRsp(c, -3, "update screen failed")
|
||||
rsp.ErrRsp(c, -2, "update screen failed")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -54,3 +59,18 @@ func (s *Service) SetScreen(c *gin.Context) {
|
||||
log.Debugf("update screen: %+v", req)
|
||||
rsp.OkRsp(c)
|
||||
}
|
||||
|
||||
func writeScreen(key string, value string) error {
|
||||
file, ok := screenFileMap[key]
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid argument %s", key)
|
||||
}
|
||||
|
||||
err := os.WriteFile(file, []byte(value), 0o666)
|
||||
if err != nil {
|
||||
log.Errorf("write kvm %s failed: %s", file, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ func (s *Service) RunScript(c *gin.Context) {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("run script %s faile: %s", req.Name, err)
|
||||
log.Errorf("run script %s failed: %s", req.Name, err.Error())
|
||||
rsp.ErrRsp(c, -2, "run script failed")
|
||||
return
|
||||
}
|
||||
|
||||
25
server/service/vm/system.go
Normal file
25
server/service/vm/system.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"NanoKVM-Server/proto"
|
||||
"os/exec"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (s *Service) Reboot(c *gin.Context) {
|
||||
var rsp proto.Response
|
||||
|
||||
log.Println("reboot system...")
|
||||
|
||||
err := exec.Command("reboot").Run()
|
||||
if err != nil {
|
||||
rsp.ErrRsp(c, -1, "operation failed")
|
||||
log.Errorf("failed to reboot: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
rsp.OkRsp(c)
|
||||
log.Debug("system rebooted")
|
||||
}
|
||||
@@ -71,12 +71,12 @@ func (c *WsClient) Read() error {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("receive message: %s", message)
|
||||
log.Debugf("received message: %s", message)
|
||||
|
||||
var event []int
|
||||
err = json.Unmarshal(message, &event)
|
||||
if err != nil {
|
||||
log.Debugf("receive invalid message: %s", message)
|
||||
log.Debugf("received invalid message: %s", message)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
## If you want to compile kvm_system yourself, please follow the steps below:
|
||||
# NanoKVM Support Instructions
|
||||
|
||||
1. Clone the MaixCDK repository: `git clone https://github.com/sipeed/MaixCDK.git`
|
||||
2. Improve the Compilation Environment, refer to [here](https://github.com/sipeed/MaixCDK/tree/main/docs/doc_zh#%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B)
|
||||
3. Copy `./kvm_system` to `~/MaixCDK` and open it in terminal
|
||||
4. Execute `maixcdk build` and Select `maixcam` to complete the compilation
|
||||
5. Replace `/kvmapp/kvm_system/kvm_system` in the NanoKVM system with `dist/kvm_system_release/kvm_system`
|
||||
6. Execute: `/etc/init.d/S95nanokvm restart` on NanoKVM
|
||||
`/support` contains auxiliary functions for NanoKVM, such as image subsystem, system status monitoring, system updates, screen key drivers, and a few system functions.
|
||||
|
||||
Currently, NanoKVM is divided into two versions based on the main control chip: SG2002 (which includes NanoKVM-Lite/Full/PCIe) and H618 (including NanoKVM-Pro). Different chips have significantly different projects and compilation environments. To distinguish between them, they are stored separately in `/support/sg2002` and `/support/h618`.
|
||||
|
||||
5
support/README_ZH.md
Normal file
5
support/README_ZH.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# NanoKVM support 说明
|
||||
|
||||
`/support`包含NanoKVM辅助性的功能,如图像子系统、系统状态监控、系统更新、屏幕按键驱动和少部分的系统功能
|
||||
|
||||
当前 NanoKVM 根据主控芯片的不同分为两个版本:SG2002(包含NanoKVM-Lite/Full/PCIe)和H618(包括NanoKVM-Pro),不同的芯片有差异较大的工程和编译环境,为做区分,将它们分别存放在`/support/sg2002`和`/support/h618`
|
||||
@@ -1,241 +0,0 @@
|
||||
#include "config.h"
|
||||
#include "system_init.h"
|
||||
|
||||
using namespace maix;
|
||||
using namespace maix::sys;
|
||||
|
||||
extern kvm_sys_state_t kvm_sys_state;
|
||||
extern kvm_oled_state_t kvm_oled_state;
|
||||
|
||||
void new_app_init(uint8_t safe_update)
|
||||
{
|
||||
// first start
|
||||
system("cp -f /kvmapp/system/update-nanokvm.py /etc/kvm/");
|
||||
system("rm /etc/init.d/S02udisk");
|
||||
system("cp /kvmapp/system/init.d/S01fs /etc/init.d/");
|
||||
system("cp /kvmapp/system/init.d/S03usbdev /etc/init.d/");
|
||||
system("cp /kvmapp/system/init.d/S15kvmhwd /etc/init.d/");
|
||||
system("cp /kvmapp/system/init.d/S30eth /etc/init.d/");
|
||||
system("cp /kvmapp/system/init.d/S50sshd /etc/init.d/");
|
||||
if(kvm_wifi_exist()) system("cp /kvmapp/system/init.d/S30wifi /etc/init.d/");
|
||||
else system("rm /etc/init.d/S30wifi");
|
||||
system("cp /kvmapp/system/init.d/S98tailscaled /etc/init.d/");
|
||||
if(safe_update == 0){
|
||||
system("cp /kvmapp/system/init.d/S95nanokvm /etc/init.d/"); // /kvmapp/system/init.d/S95nanokvm还没跑完直接覆盖可能出问题
|
||||
}
|
||||
|
||||
// system("rm /etc/init.d/S04backlight");
|
||||
// system("rm /etc/init.d/S05tp");
|
||||
// system("rm /etc/init.d/S40bluetoothd");
|
||||
// system("rm /etc/init.d/S99*");
|
||||
|
||||
system("mkdir /kvmapp/kvm");
|
||||
system("mkdir /etc/kvm");
|
||||
system("echo 0 > /kvmapp/kvm/now_fps");
|
||||
system("echo 30 > /kvmapp/kvm/fps");
|
||||
system("echo 2000 > /kvmapp/kvm/qlty");
|
||||
system("echo 720 > /kvmapp/kvm/res");
|
||||
system("echo h264 > /kvmapp/kvm/type");
|
||||
system("echo 0 > /kvmapp/kvm/state");
|
||||
system("touch /etc/kvm/frame_detact");
|
||||
|
||||
// rm jpg_stream & kvm_stream
|
||||
system("rm -r /kvmapp/jpg_stream");
|
||||
system("rm /kvmapp/kvm_system/kvm_stream");
|
||||
|
||||
// rm kvm_new_app
|
||||
system("rm /kvmapp/kvm_new_app");
|
||||
system("sync");
|
||||
// system("/etc/init.d/S95nanokvm restart");
|
||||
|
||||
// update ko
|
||||
FILE *fp;
|
||||
uint8_t RW_Data_0[30];
|
||||
uint8_t RW_Data_1[30];
|
||||
fp = popen("md5sum /mnt/system/ko/soph_mipi_rx.ko | grep 086ed01749188975afaa40fb569374f8 | awk '{print $2}'", "r");
|
||||
if ( NULL == fp )
|
||||
{
|
||||
pclose(fp);
|
||||
// return;
|
||||
}
|
||||
fgets((char*)RW_Data_0, 10, fp);
|
||||
pclose(fp);
|
||||
fp = popen("md5sum /mnt/system/ko/soph_mipi_rx.ko | grep 69be7eeded3777f750480a5dd5a1aa26 | awk '{print $2}'", "r");
|
||||
if ( NULL == fp )
|
||||
{
|
||||
pclose(fp);
|
||||
// return;
|
||||
}
|
||||
fgets((char*)RW_Data_1, 10, fp);
|
||||
pclose(fp);
|
||||
|
||||
int8_t hdmi_ver = -1;
|
||||
|
||||
if(access("/etc/kvm/hdmi_version", F_OK) == 0){
|
||||
uint8_t RW_Data[2];
|
||||
FILE *fp = fopen("/etc/kvm/hdmi_version", "r");
|
||||
fread(RW_Data, sizeof(char), 2, fp);
|
||||
fclose(fp);
|
||||
if(RW_Data[0] == 'c') hdmi_ver = 1;
|
||||
else if(RW_Data[0] == 'u') hdmi_ver = 2;
|
||||
}
|
||||
|
||||
if(hdmi_ver == 2){
|
||||
if(RW_Data_1[0] != '/'){
|
||||
system("cp /kvmapp/system/ko/soph_mipi_rx.ko /mnt/system/ko/soph_mipi_rx.ko");
|
||||
system("sync");
|
||||
system("reboot");
|
||||
} else {
|
||||
system("sync");
|
||||
system("/etc/init.d/S15kvmhwd start");
|
||||
system("/etc/init.d/S95nanokvm restart");
|
||||
}
|
||||
} else {
|
||||
if((RW_Data_0[0] != '/') && (RW_Data_1[0] != '/')){
|
||||
system("cp /kvmapp/system/ko/soph_mipi_rx.ko /mnt/system/ko/soph_mipi_rx.ko");
|
||||
system("sync");
|
||||
system("reboot");
|
||||
} else {
|
||||
system("sync");
|
||||
system("/etc/init.d/S15kvmhwd start");
|
||||
system("/etc/init.d/S95nanokvm restart");
|
||||
}
|
||||
}
|
||||
|
||||
if(access("/root/old/kvm_new_img", F_OK) == 0){
|
||||
system("rm -r /root/old");
|
||||
}
|
||||
}
|
||||
|
||||
void update_resolv_conf(void)
|
||||
{
|
||||
FILE *fp = NULL;
|
||||
// fp = fopen("/boot/resolv.conf", "w+");
|
||||
fp = fopen("/boot/resolv.conf", "w");
|
||||
// 阿里: 223.5.5.5
|
||||
// 腾讯: 119.29.29.29
|
||||
fprintf(fp, "nameserver 192.168.0.1\nnameserver 8.8.4.4\nnameserver 8.8.8.8\nnameserver 114.114.114.114\nnameserver 119.29.29.29\nnameserver 223.5.5.5");
|
||||
fclose(fp);
|
||||
system("rm -rf /etc/resolv.conf");
|
||||
system("cp -vf /etc/resolv.conf /etc/resolv.conf.old");
|
||||
system("cp -vf /boot/resolv.conf /etc/resolv.conf");
|
||||
}
|
||||
|
||||
void init_upadte(void)
|
||||
{
|
||||
if(access("/kvmapp/kvm_new_app", F_OK) == 0){
|
||||
new_app_init(0);
|
||||
}
|
||||
|
||||
// PCIe Patch
|
||||
if(access("/kvmapp/jpg_stream/dl_lib/libmaixcam_lib.so", F_OK) == 0){
|
||||
system("cp -f /kvmapp/jpg_stream/dl_lib/libmaixcam_lib.so /kvmapp/kvm_system/dl_lib/");
|
||||
new_app_init(1);
|
||||
}
|
||||
|
||||
// DNS Patch
|
||||
system("tailscale set --accept-dns=false");
|
||||
if(access("/boot/resolv.conf", F_OK) != 0){
|
||||
// 不存在直接覆盖
|
||||
update_resolv_conf();
|
||||
} else {
|
||||
// 存在则检测是否拷贝/DNS是否正确
|
||||
uint8_t RW_Data[10] = {0};
|
||||
FILE *fp;
|
||||
fp = popen("cat /etc/resolv.conf | grep 119.29.29.29", "r");
|
||||
fgets((char*)RW_Data, 2, fp);
|
||||
pclose(fp);
|
||||
if(RW_Data[0] != 'n') {
|
||||
update_resolv_conf();
|
||||
}
|
||||
}
|
||||
|
||||
// libmaixcam_lib.so
|
||||
if(access("/kvmapp/kvm_system/dl_lib/libmaixcam_lib.so", F_OK) == 0){
|
||||
// exist
|
||||
system("cp -f /kvmapp/kvm_system/dl_lib/libmaixcam_lib.so /kvmapp/server/dl_lib");
|
||||
} else {
|
||||
printf("kvm_system not exist lib!\n");
|
||||
}
|
||||
system("sync");
|
||||
system("killall NanoKVM-Server");
|
||||
system("rm -r /tmp/server");
|
||||
system("cp -r /kvmapp/server /tmp/");
|
||||
system("/tmp/server/NanoKVM-Server &");
|
||||
}
|
||||
|
||||
void first_start(void)
|
||||
{
|
||||
if(access("/kvmapp/kvm_new_img", F_OK) == 0){
|
||||
// init oled
|
||||
OLED_state = oled_exist();
|
||||
if(OLED_state){
|
||||
printf("oled exist\r\n");
|
||||
} else {
|
||||
printf("oled not exist\r\n");
|
||||
}
|
||||
if(OLED_state){
|
||||
OLED_Init();
|
||||
OLED_ColorTurn(0); //0正常显示 1 反色显示
|
||||
OLED_DisplayTurn(0); //0正常显示 1 屏幕翻转显示
|
||||
// OLED_INIT_UI();
|
||||
|
||||
if(kvm_hw_ver == 2){
|
||||
OLED_Revolve();
|
||||
}
|
||||
}
|
||||
uint8_t update_begin = 0;
|
||||
while(1){
|
||||
if(chack_net_state(ETH_ROUTE) && get_ip_addr(ETH_IP)){
|
||||
OLED_Clear();
|
||||
if(kvm_hw_ver != 2){
|
||||
if(update_begin == 0){
|
||||
update_begin = 1;
|
||||
OLED_ShowString(1, 2, "Auto-updating...", 16);
|
||||
OLED_ShowStringtoend(1, 4, (char*)kvm_sys_state.eth_addr, 16, 0);
|
||||
system("python /kvmapp/system/update-nanokvm.py");
|
||||
} else {
|
||||
OLED_ShowString(1, 0, "Auto-update fail", 16);
|
||||
OLED_ShowString(1, 2, "Please open the ", 16);
|
||||
OLED_ShowStringtoend(1, 4, (char*)kvm_sys_state.eth_addr, 16, 0);
|
||||
OLED_ShowString(1, 6, "update KVM APP ", 16);
|
||||
}
|
||||
} else {
|
||||
// PCIe
|
||||
if(update_begin == 0){
|
||||
update_begin = 1;
|
||||
OLED_ShowString(0, 1, "Updating...", 8);
|
||||
OLED_ShowString_AlignRight(AlignRightEND_P, 2, (char*)kvm_sys_state.eth_addr, 4);
|
||||
system("python /kvmapp/system/update-nanokvm.py");
|
||||
} else {
|
||||
OLED_ShowString(0, 0, "Update fail", 8);
|
||||
OLED_ShowString(0, 1, "Please open", 8);
|
||||
OLED_ShowString_AlignRight(AlignRightEND_P, 2, (char*)kvm_sys_state.eth_addr, 4);
|
||||
OLED_ShowString(0, 3, "update APP" , 8);
|
||||
}
|
||||
}
|
||||
while(1){
|
||||
time::sleep_ms(1000);
|
||||
if(chack_net_state(ETH_ROUTE) == 0) {
|
||||
OLED_Clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
get_ip_addr(ETH_ROUTE);
|
||||
if(kvm_hw_ver != 2){
|
||||
OLED_ShowString(1, 2, " Please connect ", 16);
|
||||
OLED_ShowString(1, 4, " to the network ", 16);
|
||||
OLED_ShowString(1, 6, " to update app ", 16);
|
||||
} else {
|
||||
// PCIe
|
||||
OLED_ShowString(0, 0, "Please link", 8);
|
||||
OLED_ShowString(0, 1, "to network", 8);
|
||||
OLED_ShowString(0, 2, "update APP" , 8);
|
||||
}
|
||||
|
||||
time::sleep_ms(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#ifndef SYSTEM_INIT_H_
|
||||
#define SYSTEM_INIT_H_
|
||||
#include "config.h"
|
||||
|
||||
void new_app_init(uint8_t safe_update);
|
||||
void update_resolv_conf(void);
|
||||
void init_upadte(void);
|
||||
void first_start(void);
|
||||
|
||||
#endif // SYSTEM_INIT_H_
|
||||
@@ -1,30 +0,0 @@
|
||||
#ifndef SYSTEM_STATE_H_
|
||||
#define SYSTEM_STATE_H_
|
||||
#include "config.h"
|
||||
|
||||
enum ip_addr_t
|
||||
{
|
||||
ETH_IP=1, WiFi_IP, Tailscale_IP, RNDIS_IP, ETH_ROUTE, WiFi_ROUTE, NULL_IP
|
||||
};
|
||||
|
||||
// net_port
|
||||
int get_ip_addr(ip_addr_t ip_type);
|
||||
int chack_net_state(ip_addr_t use_ip_type);
|
||||
void patch_eth_wifi(void);
|
||||
int kvm_eth_cable_exist();
|
||||
int kvm_wifi_exist();
|
||||
int kvm_rndis_exist();
|
||||
int kvm_tailscale_exist();
|
||||
void kvm_update_usb_state();
|
||||
void kvm_update_hdmi_state();
|
||||
void kvm_update_stream_fps(void);
|
||||
void kvm_update_stream_type(void);
|
||||
void kvm_update_stream_qlty(void);
|
||||
void kvm_update_hdmi_res(void);
|
||||
void kvm_update_eth_state(void);
|
||||
void kvm_update_wifi_state(void);
|
||||
void kvm_update_rndis_state(void);
|
||||
void kvm_update_tailscale_state(void);
|
||||
uint8_t ion_free_space(void);
|
||||
|
||||
#endif // SYSTEM_STATE_H_
|
||||
26
support/sg2002/README.md
Normal file
26
support/sg2002/README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# NanoKVM Support Instructions
|
||||
|
||||
## Environment Preparation
|
||||
1. NanoKVM-Lite/Full/PCIe is based on the SG2002 as the main control chip. The projects in the support section are compiled under the [MaixCDK](https://github.com/sipeed/MaixCDK) framework. Before compiling, please ensure that the `MaixCDK` environment is correctly configured. For configuration instructions, click [here](https://github.com/sipeed/MaixCDK/blob/main/docs/doc_zh/README.md).
|
||||
|
||||
## kvm_system Compilation Instructions
|
||||
|
||||
> The `kvm_system` is responsible for monitoring the NanoKVM system status, system updates, screen key drivers, and a few system functions, compiled with MaixCDK.
|
||||
|
||||
1. Before compiling, please ensure that the above-mentioned `MaixCDK` environment is correctly configured.
|
||||
2. Modify the paths of `MAIXCDK_PATH` and `NanoKVM_PATH` in `./build`.
|
||||
3. Execute `./build kvm_system` to compile `kvm_system`.
|
||||
4. Use `scp ./kvm_system/dist/kvm_system_release/kvm_system root@192.168.x.x:/kvmapp/kvm_system` to copy it to NanoKVM for testing.
|
||||
5. Use `./build add_to_kvmapp` to place the executable file into the `/kvmapp` installation package.
|
||||
6. Use `./build kvm_system clean` to clean the compilation of `kvm_system`.
|
||||
|
||||
## kvm_vision Compilation Instructions
|
||||
|
||||
> `kvm_vision` refers to the image acquisition and encoding subsystem of NanoKVM, compiled with MaixCDK to produce dynamic libraries for Go calls. Use `kvm_vision_test` to compile and test the dynamic library.
|
||||
|
||||
1. Before compiling, please ensure that the above-mentioned `MaixCDK` environment is correctly configured.
|
||||
2. Modify the paths of `MAIXCDK_PATH` and `NanoKVM_PATH` in `./build`.
|
||||
3. Execute `./build kvm_vision` to compile `kvm_vision_test`.
|
||||
4. You can test the dynamic libraries in `kvm_vision_test/dist/kvm_vision_test_release/dl_lib/`.
|
||||
5. Use `./build add_to_kvmapp` to place the dynamic libraries into the `/kvmapp` installation package.
|
||||
6. Use `./build kvm_vision clean` to clean the compilation of `kvm_vision`.
|
||||
26
support/sg2002/README_ZH.md
Normal file
26
support/sg2002/README_ZH.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# NanoKVM support 说明
|
||||
|
||||
## 环境准备
|
||||
1. NanoKVM-Lite/Full/PCIe 以SG2002为主控芯片,support 部分的工程在[MaixCDK](https://github.com/sipeed/MaixCDK) 框架下编译。编译前,请保证`MaixCDK`环境已正确配置,配置教程点击[这里](https://github.com/sipeed/MaixCDK/blob/main/docs/doc_zh/README.md)
|
||||
|
||||
## kvm_system 编译说明
|
||||
|
||||
> `kvm_system` 负责 NanoKVM 系统状态监控、系统更新、屏幕按键驱动和少部分的系统功能,借助 MaixCDK 编译
|
||||
|
||||
1. 编译前,请保证上述 1.`MaixCDK`环境已正确配置
|
||||
2. 修改 `./build` 中 `MAIXCDK_PATH` 和 `NanoKVM_PATH`的路径
|
||||
3. 执行 `./build kvm_system` 编译 kvm_system
|
||||
4. 使用 `scp ./kvm_system/dist/kvm_system_release/kvm_system root@192.168.x.x:/kvmapp/kvm_system` 拷贝入 NanoKVM 测试
|
||||
5. 使用 `./build add_to_kvmapp` 可以将可执行文件放入 `/kvmapp` 安装包内
|
||||
6. 使用 `./build kvm_system clean` 可以清除 kvm_system 的编译
|
||||
|
||||
## kvm_vision 编译说明
|
||||
|
||||
> `kvm_vision` 是 NanoKVM 图像获取编码子系统的统称,使用MaixCDK编译出动态库供Go调用,使用 `kvm_vision_test` 编译和测试动态库
|
||||
|
||||
1. 编译前,请保证上述 1.`MaixCDK`环境已正确配置
|
||||
2. 修改 `./build` 中 `MAIXCDK_PATH` 和 `NanoKVM_PATH`的路径
|
||||
3. 执行 `./build kvm_vision` 编译 kvm_vision_test
|
||||
4. 可使用 `kvm_vision_test/dist/kvm_vision_test_release/dl_lib/` 中的动态库测试
|
||||
5. 使用 `./build add_to_kvmapp` 可以将动态库放入 `/kvmapp` 安装包内
|
||||
6. 使用 `./build kvm_vision clean` 可以清除 kvm_system 的编译
|
||||
6
support/sg2002/additional/kvm/CMakeLists.txt
Normal file
6
support/sg2002/additional/kvm/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
list(APPEND ADD_REQUIREMENTS vision basic peripheral)
|
||||
list(APPEND ADD_INCLUDE "include")
|
||||
|
||||
append_srcs_dir(ADD_SRCS "src")
|
||||
|
||||
register_component(DYNAMIC)
|
||||
0
support/sg2002/additional/kvm/Kconfig
Normal file
0
support/sg2002/additional/kvm/Kconfig
Normal file
82
support/sg2002/additional/kvm/include/kvm_vision.h
Normal file
82
support/sg2002/additional/kvm/include/kvm_vision.h
Normal file
@@ -0,0 +1,82 @@
|
||||
|
||||
|
||||
#ifndef KVM_VISION_H_
|
||||
#define KVM_VISION_H_
|
||||
|
||||
#include "kvm_mmf.hpp"
|
||||
#include "maix_camera.hpp"
|
||||
#include "maix_basic.hpp"
|
||||
|
||||
#include "maix_i2c.hpp"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <fcntl.h> /* low-level i/o */
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
|
||||
// #include "kvm_mmf.hpp"
|
||||
|
||||
#define IMG_BUFFER_FULL -3
|
||||
#define IMG_VENC_ERROR -2
|
||||
#define IMG_NOT_EXIST -1
|
||||
#define IMG_MJPEG_TYPE 0
|
||||
#define IMG_H264_TYPE_SPS 1
|
||||
#define IMG_H264_TYPE_PPS 2
|
||||
#define IMG_H264_TYPE_IF 3
|
||||
#define IMG_H264_TYPE_PF 4
|
||||
|
||||
#define NORMAL_RES 0
|
||||
#define NEW_RES 1
|
||||
#define UNSUPPORT_RES 2
|
||||
#define UNKNOWN_RES 3
|
||||
#define ERROR_RES 4
|
||||
|
||||
void kvmv_init(uint8_t _debug_info_en = 0);
|
||||
void set_venc_auto_recyc(uint8_t _enable);
|
||||
/**********************************************************************************
|
||||
* @name kvmv_read_img
|
||||
* @author Sipeed BuGu
|
||||
* @date 2024/10/25
|
||||
* @version R1.0
|
||||
* @brief Acquire the encoded image with auto init
|
||||
* @param _width @input: Output image width
|
||||
* @param _height @input: Output image height
|
||||
* @param _type @input: Encode type
|
||||
* @param _qlty @input: MJPEG: (50-100) | H264: (500-10000)
|
||||
* @param _pp_kvm_data @output: Encode data
|
||||
* @param _p_kvmv_data_size @output: Encode data size
|
||||
* @return
|
||||
-7: HDMI INPUT RES ERROR
|
||||
-6: Unsupported resolution, please modify it in the host settings.
|
||||
-5: Retrieving image, please wait
|
||||
-4: Modifying image resolution, please wait
|
||||
-3: img buffer full
|
||||
-2: VENC Errorl
|
||||
-1: No images were acquired
|
||||
0: Acquire MJPEG encoded images
|
||||
1: Acquire H264 encoded images(SPS)[Deprecated]
|
||||
2: Acquire H264 encoded images(PPS)[Deprecated]
|
||||
3: Acquire H264 encoded images(I)
|
||||
4: Acquire H264 encoded images(P)
|
||||
5: IMG not changed
|
||||
**********************************************************************************/
|
||||
int kvmv_read_img(uint16_t _width, uint16_t _height, uint8_t _type, uint16_t _qlty, uint8_t** _pp_kvm_data, uint32_t* _p_kvmv_data_size);
|
||||
int free_kvmv_data(uint8_t ** _pp_kvm_data);
|
||||
void free_all_kvmv_data();
|
||||
void set_h264_gop(uint8_t _gop);
|
||||
void set_frame_detact(uint8_t _frame_detact);
|
||||
void kvmv_deinit();
|
||||
uint8_t kvmv_hdmi_control(uint8_t _en);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // KVM_VISION_H_
|
||||
1855
support/sg2002/additional/kvm/src/kvm_vision.cpp
Normal file
1855
support/sg2002/additional/kvm/src/kvm_vision.cpp
Normal file
File diff suppressed because it is too large
Load Diff
66
support/sg2002/additional/kvm_mmf/CMakeLists.txt
Normal file
66
support/sg2002/additional/kvm_mmf/CMakeLists.txt
Normal file
@@ -0,0 +1,66 @@
|
||||
|
||||
list(APPEND ADD_REQUIREMENTS basic ini)
|
||||
list(APPEND ADD_INCLUDE "include")
|
||||
|
||||
# sophgo-middleware
|
||||
set(middleware_src_path "${CMAKE_CURRENT_SOURCE_DIR}/../3rd_party/sophgo-middleware/sophgo-middleware")
|
||||
|
||||
|
||||
set(source_dir "src")
|
||||
append_srcs_dir(ADD_SRCS "${source_dir}")
|
||||
|
||||
# middleware
|
||||
set(middleware_include_dir .
|
||||
${middleware_src_path}/v2/component/panel/${CONFIG_SOPHGO_MIDDLEWARE_CHIP}
|
||||
${middleware_src_path}/v2/include
|
||||
${middleware_src_path}/v2/include/isp/${CONFIG_SOPHGO_MIDDLEWARE_CHIP}
|
||||
${middleware_src_path}/v2/sample/common
|
||||
${middleware_src_path}/v2/uapi
|
||||
${middleware_src_path}/v2/3rdparty/inih
|
||||
${middleware_src_path}/v2/modules/ive/include/
|
||||
)
|
||||
list(APPEND ADD_INCLUDE ${middleware_include_dir})
|
||||
set_property(SOURCE ${middleware_include_dir} PROPERTY GENERATED 1)
|
||||
|
||||
append_srcs_dir(middleware_src_dir ${middleware_src_path}/v2/sample/common
|
||||
${middleware_src_path}/v2/component/isp/sensor/sg200x/lontium_lt6911
|
||||
)
|
||||
list(APPEND ADD_SRCS ${middleware_src_dir}
|
||||
"${source_dir}/kvm_mmf.cpp")
|
||||
set_property(SOURCE ${middleware_src_dir} PROPERTY GENERATED 1)
|
||||
|
||||
set(middleware_static_lib_file "")
|
||||
list(APPEND ADD_STATIC_LIB ${middleware_static_lib_file})
|
||||
set_property(SOURCE ${middleware_static_lib_file} PROPERTY GENERATED 1)
|
||||
|
||||
set(mmf_lib_dir ${middleware_src_path}/v2/lib)
|
||||
|
||||
if(CONFIG_SOPHGO_MIDDLEWARE_C_LIBRARY STREQUAL "musl")
|
||||
set(middleware_dynamic_lib_file ${mmf_lib_dir}/libcvi_bin.so
|
||||
${mmf_lib_dir}/libae.so
|
||||
${mmf_lib_dir}/libaf.so
|
||||
${mmf_lib_dir}/libawb.so
|
||||
${mmf_lib_dir}/libcvi_bin_isp.so
|
||||
${mmf_lib_dir}/libisp_algo.so
|
||||
${mmf_lib_dir}/libisp.so
|
||||
${mmf_lib_dir}/libsys.so
|
||||
${mmf_lib_dir}/libvdec.so
|
||||
${mmf_lib_dir}/libvenc.so
|
||||
${mmf_lib_dir}/libvpu.so
|
||||
${mmf_lib_dir}/3rd/libini.so)
|
||||
elseif(CONFIG_SOPHGO_MIDDLEWARE_C_LIBRARY STREQUAL "glibc")
|
||||
set(middleware_dynamic_lib_file ${mmf_lib_dir}/libcvi_vb.so
|
||||
${mmf_lib_dir}/libcvi_sys.so
|
||||
${mmf_lib_dir}/libcvi_debug.so
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR "No sophgo middleware c library support, please check menuoconfig to select c library")
|
||||
endif()
|
||||
list(APPEND ADD_DYNAMIC_LIB ${middleware_dynamic_lib_file})
|
||||
set_property(SOURCE ${middleware_dynamic_lib_file} PROPERTY GENERATED 1)
|
||||
|
||||
list(APPEND ADD_DEFINITIONS_PRIVATE -DSENSOR_LONTIUM_LT6911
|
||||
-DSENSOR2_TYPE=LONTIUM_LT6911_2M_60FPS_8BIT)
|
||||
|
||||
|
||||
register_component(DYNAMIC)
|
||||
32
support/sg2002/additional/kvm_mmf/Kconfig
Normal file
32
support/sg2002/additional/kvm_mmf/Kconfig
Normal file
@@ -0,0 +1,32 @@
|
||||
# sophgo middleware version major minor patch
|
||||
menu "sophgo middleware version"
|
||||
config SOPHGO_MIDDLEWARE_CHIP
|
||||
string "sophgo chip"
|
||||
default "cv181x"
|
||||
help
|
||||
sophgo chip. cv180x or cv181x
|
||||
|
||||
config SOPHGO_MIDDLEWARE_C_LIBRARY
|
||||
string "sophgo c standard library"
|
||||
default "musl"
|
||||
help
|
||||
sophgo c standard library. glibc or musl
|
||||
|
||||
config SOPHGO_MIDDLEWARE_VERSION_MAJOR
|
||||
int "sophgo middleware package major version"
|
||||
default 0
|
||||
help
|
||||
sophgo middleware package major version, 0 means auto select according to board
|
||||
|
||||
config SOPHGO_MIDDLEWARE_VERSION_MINOR
|
||||
int "sophgo middleware package minor version"
|
||||
default 0
|
||||
help
|
||||
sophgo middleware package minor version
|
||||
|
||||
config SOPHGO_MIDDLEWARE_VERSION_PATCH
|
||||
int "sophgo middleware package patch version"
|
||||
default 4
|
||||
help
|
||||
sophgo middleware package patch version
|
||||
endmenu
|
||||
68
support/sg2002/additional/kvm_mmf/include/kvm_mmf.hpp
Normal file
68
support/sg2002/additional/kvm_mmf/include/kvm_mmf.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef __KVM_MMF_HPP__
|
||||
#define __KVM_MMF_HPP__
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
typedef struct {
|
||||
uint8_t *data[8];
|
||||
int data_size[8];
|
||||
int count;
|
||||
} mmf_stream_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t type; // 0, jpg; 1, h265; 2, h264
|
||||
int w;
|
||||
int h;
|
||||
int fmt;
|
||||
uint8_t jpg_quality; // jpeg
|
||||
int gop; // h264
|
||||
int intput_fps; // h264
|
||||
int output_fps; // h264
|
||||
int bitrate; // h264
|
||||
} mmf_venc_cfg_t;
|
||||
|
||||
// init sys
|
||||
int mmf_init(void);
|
||||
int mmf_deinit(void);
|
||||
int mmf_try_deinit(bool force);
|
||||
bool mmf_is_init(void);
|
||||
|
||||
// manage vi channels(vi->vpssgroup->vpss->frame)
|
||||
int mmf_get_vi_unused_channel(void);
|
||||
int mmf_vi_init(void);
|
||||
int mmf_vi_deinit(void);
|
||||
int mmf_add_vi_channel_with_enc(int ch, int width, int height, int format);
|
||||
int mmf_add_vi_channel(int ch, int width, int height, int format);
|
||||
int mmf_del_vi_channel(int ch);
|
||||
int mmf_del_vi_channel_all(void);
|
||||
int mmf_reset_vi_channel(int ch, int width, int height, int format);
|
||||
bool mmf_vi_chn_is_open(int ch);
|
||||
int mmf_vi_aligned_width(int ch);
|
||||
void mmf_set_vi_hmirror(int ch, bool en);
|
||||
void mmf_set_vi_vflip(int ch, bool en);
|
||||
void mmf_get_vi_hmirror(int ch, bool *en);
|
||||
void mmf_get_vi_vflip(int ch, bool *en);
|
||||
|
||||
// get vi frame
|
||||
int mmf_vi_frame_pop(int ch, void **data, int *len, int *width, int *height, int *format);
|
||||
void mmf_vi_frame_free(int ch);
|
||||
|
||||
// invert format
|
||||
int mmf_invert_format_to_maix(int mmf_format);
|
||||
int mmf_invert_format_to_mmf(int maix_format);
|
||||
|
||||
// venc
|
||||
int mmf_enc_jpg_init(int ch, int w, int h, int format, int quality);
|
||||
int mmf_enc_jpg_deinit(int ch);
|
||||
int mmf_enc_jpg_push(int ch, uint8_t *data, int w, int h, int format);
|
||||
int mmf_enc_jpg_push_with_quality(int ch, uint8_t *data, int w, int h, int format, int quality);
|
||||
int mmf_enc_jpg_pop(int ch, uint8_t **data, int *size);
|
||||
int mmf_enc_jpg_free(int ch);
|
||||
int mmf_add_venc_channel(int ch, mmf_venc_cfg_t *cfg);
|
||||
int mmf_del_venc_channel(int ch);
|
||||
int mmf_del_venc_channel_all();
|
||||
int mmf_venc_push(int ch, uint8_t *data, int w, int h, int format);
|
||||
int mmf_venc_pop(int ch, mmf_stream_t *stream);
|
||||
int mmf_venc_free(int ch);
|
||||
|
||||
#endif // __KVM_MMF_HPP__
|
||||
2352
support/sg2002/additional/kvm_mmf/src/kvm_mmf.cpp
Normal file
2352
support/sg2002/additional/kvm_mmf/src/kvm_mmf.cpp
Normal file
File diff suppressed because it is too large
Load Diff
322
support/sg2002/additional/peripheral/port/maixcam/maix_i2c.cpp
Normal file
322
support/sg2002/additional/peripheral/port/maixcam/maix_i2c.cpp
Normal file
@@ -0,0 +1,322 @@
|
||||
/**
|
||||
* @author neucrack@sipeed, lxowalle@sipeed
|
||||
* @copyright Sipeed Ltd 2023-
|
||||
* @license Apache 2.0
|
||||
* @update 2023.9.8: Add framework, create this file.
|
||||
*/
|
||||
|
||||
|
||||
#include "maix_i2c.hpp"
|
||||
#include "maix_basic.hpp"
|
||||
#include "cstdio"
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/i2c-dev.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#define DEV_PATH "/dev/i2c-%d"
|
||||
|
||||
namespace maix::peripheral::i2c
|
||||
{
|
||||
|
||||
std::vector<int> list_devices()
|
||||
{
|
||||
std::vector<int> data;
|
||||
// list all files in /dev, find i2c-*
|
||||
std::vector<std::string> *files = fs::listdir("/dev");
|
||||
for (auto &file : *files)
|
||||
{
|
||||
if (file.find("i2c-") != std::string::npos)
|
||||
{
|
||||
try
|
||||
{
|
||||
data.push_back(std::stoi(file.substr(4)));
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
log::error("i2c bus id %s format error", file.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
delete files;
|
||||
return data;
|
||||
}
|
||||
|
||||
I2C::I2C(int id, i2c::Mode mode, int freq, i2c::AddrSize addr_size)
|
||||
{
|
||||
char buf[32];
|
||||
|
||||
snprintf(buf, sizeof(buf), DEV_PATH, id);
|
||||
if(access(buf, F_OK) == 0){
|
||||
if (mode == i2c::SLAVE)
|
||||
{
|
||||
throw err::Exception(err::Err::ERR_NOT_IMPL, "i2c::SLAVE mode not implemented");
|
||||
}
|
||||
if (addr_size != i2c::SEVEN_BIT)
|
||||
{
|
||||
throw err::Exception(err::Err::ERR_NOT_IMPL, "addr_size " + std::to_string(addr_size) + " not support");
|
||||
}
|
||||
|
||||
int fd = ::open(buf, O_RDWR);
|
||||
if (fd < 0)
|
||||
{
|
||||
throw err::Exception(err::Err::ERR_IO, "open " + std::string(buf) + " failed");
|
||||
}
|
||||
_fd = fd;
|
||||
_freq = freq;
|
||||
_mode = mode;
|
||||
_addr_size = addr_size;
|
||||
} else {
|
||||
printf("not exit i2c\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
I2C::~I2C()
|
||||
{
|
||||
if (_fd > 0)
|
||||
{
|
||||
::close(_fd);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> I2C::scan(int addr)
|
||||
{
|
||||
std::vector<int> data;
|
||||
int addr_start = 0x08;
|
||||
int addr_end = 0x77;
|
||||
if(addr > 0)
|
||||
{
|
||||
addr_start = addr;
|
||||
addr_end = addr;
|
||||
}
|
||||
|
||||
if (_mode != i2c::Mode::MASTER)
|
||||
{
|
||||
log::error("Only for master mode");
|
||||
return data;
|
||||
}
|
||||
|
||||
switch (_addr_size)
|
||||
{
|
||||
case i2c::AddrSize::SEVEN_BIT:
|
||||
for (int address = addr_start; address <= addr_end; ++address)
|
||||
{
|
||||
if (::ioctl(_fd, I2C_SLAVE, address) < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned char buffer[1];
|
||||
if (::read(_fd, buffer, sizeof(buffer)) >= 0)
|
||||
{
|
||||
data.push_back(address);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log::error("bit %d not support", _addr_size);
|
||||
return data;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
int I2C::writeto(int addr, const uint8_t *data, int len)
|
||||
{
|
||||
if (_mode != i2c::Mode::MASTER)
|
||||
{
|
||||
log::error("Only for master mode");
|
||||
return (int)-err::Err::ERR_NOT_PERMIT;
|
||||
}
|
||||
|
||||
if (0 != ioctl(_fd, I2C_SLAVE, addr))
|
||||
{
|
||||
// log::error("set slave address failed");
|
||||
return (int)-err::Err::ERR_IO;
|
||||
}
|
||||
|
||||
if (len != ::write(_fd, data, len))
|
||||
{
|
||||
log::error("write failed");
|
||||
return (int)-err::Err::ERR_IO;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int I2C::writeto(int addr, const std::vector<unsigned char> data)
|
||||
{
|
||||
return writeto(addr, data.data(), (int)data.size());
|
||||
}
|
||||
|
||||
int I2C::writeto(int addr, const Bytes &data)
|
||||
{
|
||||
return writeto(addr, data.data, (int)data.size());
|
||||
}
|
||||
|
||||
Bytes* I2C::readfrom(int addr, int len)
|
||||
{
|
||||
Bytes *data = new Bytes(nullptr, len);
|
||||
|
||||
if (_mode != i2c::Mode::MASTER)
|
||||
{
|
||||
log::error("Only for master mode");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (0 != ioctl(_fd, I2C_SLAVE, addr))
|
||||
{
|
||||
// log::error("set slave address failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (len != ::read(_fd, data->data, len))
|
||||
{
|
||||
log::error("read failed");
|
||||
delete data;
|
||||
return nullptr;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
int I2C::writeto_mem(int addr, int mem_addr, const uint8_t *data, int len, int mem_addr_size, bool mem_addr_le)
|
||||
{
|
||||
if (_mode != i2c::Mode::MASTER)
|
||||
{
|
||||
log::error("Only for master mode");
|
||||
return (int)-err::Err::ERR_NOT_PERMIT;
|
||||
}
|
||||
if(mem_addr_size % 8 != 0)
|
||||
{
|
||||
log::error("mem_addr_size must be multiple of 8");
|
||||
return (int)-err::Err::ERR_IO;
|
||||
}
|
||||
|
||||
if (0 != ioctl(_fd, I2C_SLAVE, addr))
|
||||
{
|
||||
// log::error("set slave address failed");
|
||||
return (int)-err::Err::ERR_IO;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> data_final;
|
||||
|
||||
if(mem_addr_size == 8)
|
||||
{
|
||||
data_final.push_back((unsigned char)mem_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(mem_addr_le)
|
||||
{
|
||||
for(int i = 0; i < mem_addr_size / 8; i++)
|
||||
{
|
||||
data_final.push_back((unsigned char)(mem_addr & 0xff));
|
||||
mem_addr >>= 8;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < mem_addr_size / 8; i++)
|
||||
{
|
||||
data_final.push_back((unsigned char)(mem_addr >> (8 * (mem_addr_size / 8 - i - 1))));
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < len; i++)
|
||||
{
|
||||
data_final.push_back(data[i]);
|
||||
}
|
||||
int write_len = ::write(_fd, data_final.data(), data_final.size());
|
||||
if (data_final.size() != (size_t)write_len)
|
||||
{
|
||||
log::error("write failed, write_len: %d", write_len);
|
||||
return (int)-err::Err::ERR_IO;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int I2C::writeto_mem(int addr, int mem_addr, const std::vector<unsigned char> data, int mem_addr_size, bool mem_addr_le)
|
||||
{
|
||||
return writeto_mem(addr, mem_addr, data.data(), (int)data.size(), mem_addr_size, mem_addr_le);
|
||||
}
|
||||
|
||||
int I2C::writeto_mem(int addr, int mem_addr, const Bytes &data, int mem_addr_size, bool mem_addr_le)
|
||||
{
|
||||
return writeto_mem(addr, mem_addr, data.data, (int)data.size(), mem_addr_size, mem_addr_le);
|
||||
}
|
||||
|
||||
Bytes* I2C::readfrom_mem(int addr, int mem_addr, int len, int mem_addr_size, bool mem_addr_le)
|
||||
{
|
||||
// write mem_addr and restart to read
|
||||
if (_mode != i2c::Mode::MASTER)
|
||||
{
|
||||
log::error("Only for master mode");
|
||||
return nullptr;
|
||||
}
|
||||
if(mem_addr_size % 8 != 0)
|
||||
{
|
||||
log::error("mem_addr_size must be multiple of 8");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// write mem_addr first and restart to read
|
||||
if (0 != ioctl(_fd, I2C_SLAVE, addr))
|
||||
{
|
||||
// log::error("set slave address failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> data_final;
|
||||
if(mem_addr_size == 8)
|
||||
{
|
||||
data_final.push_back((unsigned char)mem_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(mem_addr_le)
|
||||
{
|
||||
for(int i = 0; i < mem_addr_size / 8; i++)
|
||||
{
|
||||
data_final.push_back((unsigned char)(mem_addr & 0xff));
|
||||
mem_addr >>= 8;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
for(int i = 0; i < mem_addr_size / 8; i++)
|
||||
{
|
||||
data_final.push_back((unsigned char)(mem_addr >> (8 * (mem_addr_size / 8 - i - 1))));
|
||||
}
|
||||
}
|
||||
}
|
||||
// write read with I2C_RDWR
|
||||
struct i2c_msg msgs[2];
|
||||
msgs[0].addr = addr;
|
||||
msgs[0].flags = 0;
|
||||
msgs[0].len = data_final.size();
|
||||
msgs[0].buf = data_final.data();
|
||||
|
||||
Bytes *data = new Bytes(nullptr, len);
|
||||
msgs[1].addr = addr;
|
||||
msgs[1].flags = I2C_M_RD;
|
||||
msgs[1].len = len;
|
||||
msgs[1].buf = data->data;
|
||||
|
||||
struct i2c_rdwr_ioctl_data msgset;
|
||||
msgset.msgs = msgs;
|
||||
msgset.nmsgs = 2;
|
||||
|
||||
int read_len = ioctl(_fd, I2C_RDWR, &msgset);
|
||||
if (read_len != 2)
|
||||
{
|
||||
log::error("read failed");
|
||||
delete data;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
77
support/sg2002/additional/vision/CMakeLists.txt
Normal file
77
support/sg2002/additional/vision/CMakeLists.txt
Normal file
@@ -0,0 +1,77 @@
|
||||
# Config enable component2 or not in Kconfig
|
||||
################# Add include #################
|
||||
list(APPEND ADD_INCLUDE "include"
|
||||
"include/base"
|
||||
)
|
||||
if(PLATFORM_LINUX)
|
||||
list(APPEND ADD_PRIVATE_INCLUDE "port/linux")
|
||||
elseif(PLATFORM_MAIXCAM)
|
||||
list(APPEND ADD_PRIVATE_INCLUDE "port/maixcam")
|
||||
endif()
|
||||
list(APPEND ADD_PRIVATE_INCLUDE "include_private")
|
||||
###############################################
|
||||
|
||||
############## Add source files ###############
|
||||
# list(APPEND ADD_SRCS "src/lib2.c"
|
||||
# )
|
||||
# FILE(GLOB_RECURSE EXTRA_SRC "src/*.c")
|
||||
# FILE(GLOB EXTRA_SRC "src/*.c")
|
||||
# list(APPEND ADD_SRCS ${EXTRA_SRC})
|
||||
# aux_source_directory(src ADD_SRCS) # collect all source file in src dir, will set var ADD_SRCS
|
||||
append_srcs_dir(ADD_SRCS "src") # append source file in src dir to var ADD_SRCS
|
||||
if(PLATFORM_LINUX)
|
||||
append_srcs_dir(ADD_SRCS "port/linux")
|
||||
elseif(PLATFORM_MAIXCAM)
|
||||
append_srcs_dir(ADD_SRCS "port/maixcam")
|
||||
endif()
|
||||
# list(REMOVE_ITEM COMPONENT_SRCS "src/test.c")
|
||||
# set(ADD_ASM_SRCS "src/asm.S")
|
||||
# list(APPEND ADD_SRCS ${ADD_ASM_SRCS})
|
||||
# SET_PROPERTY(SOURCE ${ADD_ASM_SRCS} PROPERTY LANGUAGE C) # set .S ASM file as C language
|
||||
# SET_SOURCE_FILES_PROPERTIES(${ADD_ASM_SRCS} PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp -D BBBBB")
|
||||
###############################################
|
||||
|
||||
|
||||
###### Add required/dependent components ######
|
||||
list(APPEND ADD_REQUIREMENTS basic opencv opencv_freetype websocket peripheral)
|
||||
list(APPEND ADD_REQUIREMENTS omv)
|
||||
list(APPEND ADD_REQUIREMENTS kvm_mmf)
|
||||
###############################################
|
||||
|
||||
###### Add link search path for requirements/libs ######
|
||||
# list(APPEND ADD_LINK_SEARCH_PATH "${CONFIG_TOOLCHAIN_PATH}/lib")
|
||||
# list(APPEND ADD_REQUIREMENTS pthread m) # add system libs, pthread and math lib for example here
|
||||
# set (OpenCV_DIR opencv/lib/cmake/opencv4)
|
||||
# find_package(OpenCV REQUIRED)
|
||||
###############################################
|
||||
|
||||
############ Add static libs ##################
|
||||
# list(APPEND ADD_STATIC_LIB "lib/libtest.a")
|
||||
###############################################
|
||||
|
||||
############ Add dynamic libs ##################
|
||||
# list(APPEND ADD_DYNAMIC_LIB "lib/arch/v831/libmaix_nn.so"
|
||||
# "lib/arch/v831/libmaix_cam.so"
|
||||
# )
|
||||
###############################################
|
||||
|
||||
#### Add compile option for this component ####
|
||||
#### Just for this component, won't affect other
|
||||
#### modules, including component that depend
|
||||
#### on this component
|
||||
# list(APPEND ADD_DEFINITIONS_PRIVATE -DAAAAA=1)
|
||||
|
||||
#### Add compile option for this component
|
||||
#### and components denpend on this component
|
||||
# list(APPEND ADD_DEFINITIONS -DAAAAA222=1
|
||||
# -DAAAAA333=1)
|
||||
###############################################
|
||||
|
||||
############ Add static libs ##################
|
||||
#### Update parent's variables like CMAKE_C_LINK_FLAGS
|
||||
# set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--start-group libmaix/libtest.a -ltest2 -Wl,--end-group" PARENT_SCOPE)
|
||||
###############################################
|
||||
|
||||
# register component, DYNAMIC or SHARED flags will make component compiled to dynamic(shared) lib
|
||||
register_component()
|
||||
|
||||
0
support/sg2002/additional/vision/Kconfig
Normal file
0
support/sg2002/additional/vision/Kconfig
Normal file
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* @file maix_camera_base.hpp
|
||||
* @brief Maix camera SDL implementation
|
||||
* @author neucrack@sipeed.com
|
||||
* @license Apache 2.0 Sipeed Ltd
|
||||
* @update date 2023-10-23 Create by neucrack
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "maix_image.hpp"
|
||||
#include "maix_err.hpp"
|
||||
|
||||
namespace maix::camera
|
||||
{
|
||||
class CameraBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new Camera object
|
||||
* @param device camera device name, you can get devices by list_devices method, by default(value is NULL(None in MaixPy)) means the first device
|
||||
* @param width camera width, by default(value is -1) means auto detect,
|
||||
* if width > max device supported width, will auto set to max device supported width
|
||||
* @param height camera height, by default(value is -1) means auto detect,
|
||||
* if height > max device supported height, will auto set to max device supported height
|
||||
* @param format camera format, by default(value is FMT_RGB888)
|
||||
* @param buff_num camera buffer number, by default(value is 3)
|
||||
*/
|
||||
CameraBase(const char *device = nullptr, int width = -1, int height = -1, image::Format format = image::Format::FMT_RGB888, int buff_num = 3){};
|
||||
|
||||
/**
|
||||
* @brief Judge if the given format is supported by the camera
|
||||
*/
|
||||
virtual bool is_support_format(image::Format format) = 0;
|
||||
|
||||
/**
|
||||
* @brief open camera device
|
||||
* @param width camera width, by default(value is -1) means auto detect,
|
||||
* if width > max device supported width, will auto set to max device supported width
|
||||
* @param height camera height, by default(value is -1) means auto detect,
|
||||
* if height > max device supported height, will auto set to max device supported height
|
||||
* @param format camera format, by default(value is FMT_RGB888)
|
||||
* @param buff_num camera buffer number, by default(value is 3)
|
||||
* @return error code
|
||||
*/
|
||||
virtual err::Err open(int width = -1, int height = -1, image::Format format = image::Format::FMT_RGB888, int buff_num = 3) = 0;
|
||||
|
||||
/**
|
||||
* @brief read a frame from camera
|
||||
* @param buff buffer to store image data, if NULL, will alloc a new buffer
|
||||
* @return image data
|
||||
*/
|
||||
virtual image::Image *read(void *buff = NULL, size_t buff_size = 0) = 0;
|
||||
|
||||
/**
|
||||
* @brief close camera device
|
||||
* @return none
|
||||
*/
|
||||
virtual void close() = 0;
|
||||
|
||||
/**
|
||||
* Add a new channel and return a new Camera object, you can use close() to close this channel.
|
||||
* @param width camera width, default is -1, means auto, mostly means max width of camera support
|
||||
* @param height camera height, default is -1, means auto, mostly means max height of camera support
|
||||
* @param format camera output format, default is RGB888
|
||||
* @param buff_num camera buffer number, default is 3, means 3 buffer, one used by user, one used for cache the next frame,
|
||||
* more than one buffer will accelerate image read speed, but will cost more memory.
|
||||
* @return new Camera object
|
||||
*/
|
||||
virtual camera::CameraBase *add_channel(int width = -1, int height = -1, image::Format format = image::FMT_RGB888, int buff_num = 3) = 0;
|
||||
|
||||
/**
|
||||
* @brief clear all buffer
|
||||
* @return none
|
||||
*/
|
||||
virtual void clear_buff() = 0;
|
||||
|
||||
/**
|
||||
* @brief check camera device is opened or not
|
||||
* @return opened or not, bool type
|
||||
*/
|
||||
virtual bool is_opened() = 0;
|
||||
|
||||
/**
|
||||
* Get camera supported channels(layers)
|
||||
*/
|
||||
virtual int get_ch_nums() = 0;
|
||||
|
||||
/**
|
||||
* Get channel number of camera.
|
||||
*/
|
||||
virtual int get_channel() = 0;
|
||||
|
||||
/**
|
||||
* Set/Get camera mirror
|
||||
* @param en enable/disable mirror
|
||||
*/
|
||||
virtual int hmirror(int en) = 0;
|
||||
|
||||
/**
|
||||
* Set/Get camera flip
|
||||
* @param en enable/disable flip
|
||||
*/
|
||||
virtual int vflip(int en) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
305
support/sg2002/additional/vision/include/maix_camera.hpp
Normal file
305
support/sg2002/additional/vision/include/maix_camera.hpp
Normal file
@@ -0,0 +1,305 @@
|
||||
/**
|
||||
* @author neucrack@sipeed, lxowalle@sipeed
|
||||
* @copyright Sipeed Ltd 2023-
|
||||
* @license Apache 2.0
|
||||
* @update 2023.9.8: Add framework, create this file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "maix_tensor.hpp"
|
||||
#include "maix_log.hpp"
|
||||
#include "maix_image.hpp"
|
||||
#include "maix_err.hpp"
|
||||
#include "maix_camera_base.hpp"
|
||||
#include <stdlib.h>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
/**
|
||||
* @brief maix.camera module, access camera device and get image from it
|
||||
* @maixpy maix.camera
|
||||
*/
|
||||
namespace maix::camera
|
||||
{
|
||||
/**
|
||||
* List all supported camera devices.
|
||||
* @return Returns the path to the camera device.
|
||||
* @maixpy maix.camera.list_devices
|
||||
*/
|
||||
std::vector<std::string> list_devices();
|
||||
|
||||
/**
|
||||
* Enable set camera registers, default is false, if set to true, will not set camera registers, you can manually set registers by write_reg API.
|
||||
* @param enable enable/disable set camera registers
|
||||
* @maixpy maix.camera.set_regs_enable
|
||||
*/
|
||||
void set_regs_enable(bool enable = true);
|
||||
|
||||
/**
|
||||
* Camera class
|
||||
* @maixpy maix.camera.Camera
|
||||
*/
|
||||
class Camera
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new Camera object.
|
||||
* Maximum resolution support 2560x1440.
|
||||
* @param width camera width, default is -1, means auto, mostly means max width of camera support
|
||||
* @param height camera height, default is -1, means auto, mostly means max height of camera support
|
||||
* @param format camera output format, default is image.Format.FMT_RGB888
|
||||
* @param device camera device path, you can get devices by list_devices method, by default(value is NULL(None in MaixPy)) means the first device
|
||||
* @param fps camera fps, default is -1, means auto, mostly means max fps of camera support
|
||||
* @param buff_num camera buffer number, default is 3, means 3 buffer, one used by user, one used for cache the next frame,
|
||||
* more than one buffer will accelerate image read speed, but will cost more memory.
|
||||
* @param open If true, camera will automatically call open() after creation. default is true.
|
||||
* @maixpy maix.camera.Camera.__init__
|
||||
* @maixcdk maix.camera.Camera.Camera
|
||||
*/
|
||||
Camera(int width = -1, int height = -1, image::Format format = image::FMT_RGB888, const char *device = nullptr, int fps = -1, int buff_num = 3, bool open = true);
|
||||
|
||||
/**
|
||||
* @brief Construct a new Camera object.
|
||||
* @attention CameraBase * parameter need to be set manually, otherwise the operation of this object will be invalid.
|
||||
* @param device camera device path, you can get devices by list_devices method, by default(value is NULL(None in MaixPy)) means the first device
|
||||
* @param base basic operation objects.
|
||||
* @param width camera width, default is -1, means auto, mostly means max width of camera support
|
||||
* @param height camera height, default is -1, means auto, mostly means max height of camera support
|
||||
* @param format camera output format, default is image.Format.FMT_RGB888
|
||||
* @param fps camera fps, default is -1, means auto, mostly means max fps of camera support
|
||||
* @param buff_num camera buffer number, default is 3, means 3 buffer, one used by user, one used for cache the next frame,
|
||||
* more than one buffer will accelerate image read speed, but will cost more memory.
|
||||
* @param open If true, camera will automatically call open() after creation. default is true.
|
||||
* @maixcdk maix.camera.Camera.Camera
|
||||
*/
|
||||
Camera(const char *device, CameraBase *base, int width = -1, int height = -1, image::Format format = image::FMT_RGB888, int fps = -1, int buff_num = -1, bool open = true);
|
||||
|
||||
~Camera();
|
||||
|
||||
void restart(int width = -1, int height = -1, image::Format format = image::FMT_RGB888, const char *device = nullptr, int fps = -1, int buff_num = 3, bool open = true);
|
||||
|
||||
/**
|
||||
* Get the number of channels supported by the camera.
|
||||
* @return Returns the maximum number of channels.
|
||||
* @maixpy maix.camera.Camera.get_ch_nums
|
||||
*/
|
||||
int get_ch_nums();
|
||||
|
||||
/**
|
||||
* Open camera and run
|
||||
* @param width camera width, default is -1, means auto, mostly means max width of camera support
|
||||
* @param height camera height, default is -1, means auto, mostly means max height of camera support
|
||||
* @param format camera output format, default same as the constructor's format argument
|
||||
* @param fps camera fps, default is -1, means auto, mostly means max fps of camera support
|
||||
* @param buff_num camera buffer number, default is 3, means 3 buffer, one used by user, one used for cache the next frame,
|
||||
* more than one buffer will accelerate image read speed, but will cost more memory.
|
||||
* @return error code, err::ERR_NONE means success, others means failed
|
||||
* @maixpy maix.camera.Camera.open
|
||||
*/
|
||||
err::Err open(int width = -1, int height = -1, image::Format format = image::FMT_INVALID, int fps = -1, int buff_num = -1);
|
||||
|
||||
/**
|
||||
* Get one frame image from camera buffer, must call open method before read.
|
||||
* If open method not called, will call it automatically, if open failed, will throw exception!
|
||||
* So call open method before read is recommended.
|
||||
* @param buff buffer to store image data, if buff is nullptr, will alloc memory automatically.
|
||||
* In MaixPy, default to None, you can create a image.Image object, then pass img.data() to buff.
|
||||
* @param block block read, default is true, means block util read image successfully,
|
||||
* if set to false, will return nullptr if no image in buffer
|
||||
* @return image::Image object, if failed, return nullptr, you should delete if manually in C++
|
||||
* @maixpy maix.camera.Camera.read
|
||||
*/
|
||||
image::Image *read(void *buff = nullptr, size_t buff_size = 0, bool block = true);
|
||||
|
||||
/**
|
||||
* Clear buff to ensure the next read image is the latest image
|
||||
* @maixpy maix.camera.Camera.clear_buff
|
||||
*/
|
||||
void clear_buff();
|
||||
|
||||
/**
|
||||
* Read some frames and drop, this is usually used avoid read not stable image when camera just opened.
|
||||
* @param num number of frames to read and drop
|
||||
* @maixpy maix.camera.Camera.skip_frames
|
||||
*/
|
||||
void skip_frames(int num);
|
||||
|
||||
/**
|
||||
* Close camera
|
||||
* @maixpy maix.camera.Camera.close
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Add a new channel and return a new Camera object, you can use close() to close this channel.
|
||||
* @param width camera width, default is -1, means auto, mostly means max width of camera support
|
||||
* @param height camera height, default is -1, means auto, mostly means max height of camera support
|
||||
* @param format camera output format, default is RGB888
|
||||
* @param fps camera fps, default is -1, means auto, mostly means max fps of camera support
|
||||
* @param buff_num camera buffer number, default is 3, means 3 buffer, one used by user, one used for cache the next frame,
|
||||
* more than one buffer will accelerate image read speed, but will cost more memory.
|
||||
* @param open If true, camera will automatically call open() after creation. default is true.
|
||||
* @return new Camera object
|
||||
* @maixpy maix.camera.Camera.add_channel
|
||||
*/
|
||||
camera::Camera *add_channel(int width = -1, int height = -1, image::Format format = image::FMT_RGB888, int fps = -1, int buff_num = 3, bool open = true);
|
||||
|
||||
/**
|
||||
* Check if camera is opened
|
||||
* @return true if camera is opened, false if not
|
||||
* @maixpy maix.camera.Camera.is_opened
|
||||
*/
|
||||
bool is_opened();
|
||||
|
||||
/**
|
||||
* @brief check camera device is closed or not
|
||||
* @return closed or not, bool type
|
||||
* @maixpy maix.camera.Camera.is_closed
|
||||
*/
|
||||
bool is_closed() { return !is_opened();}
|
||||
|
||||
/**
|
||||
* Get camera width
|
||||
* @return camera width
|
||||
* @maixpy maix.camera.Camera.width
|
||||
*/
|
||||
int width()
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get camera height
|
||||
* @return camera height
|
||||
* @maixpy maix.camera.Camera.height
|
||||
*/
|
||||
int height()
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get camera fps
|
||||
* @return camera fps
|
||||
* @maixpy maix.camera.Camera.fps
|
||||
*/
|
||||
int fps()
|
||||
{
|
||||
return _fps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get camera output format
|
||||
* @return camera output format, image::Format object
|
||||
* @maixpy maix.camera.Camera.format
|
||||
*/
|
||||
image::Format format()
|
||||
{
|
||||
return _format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get camera buffer number
|
||||
* @return camera buffer number
|
||||
* @maixpy maix.camera.Camera.buff_num
|
||||
*/
|
||||
int buff_num()
|
||||
{
|
||||
return _buff_num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set/Get camera horizontal mirror
|
||||
* @return camera horizontal mirror
|
||||
* @maixpy maix.camera.Camera.hmirror
|
||||
*/
|
||||
int hmirror(int value = -1);
|
||||
|
||||
/**
|
||||
* Set/Get camera vertical flip
|
||||
* @return camera vertical flip
|
||||
* @maixpy maix.camera.Camera.vflip
|
||||
*/
|
||||
int vflip(int value = -1);
|
||||
|
||||
/**
|
||||
* Get camera device path
|
||||
* @return camera device path
|
||||
* @maixpy maix.camera.Camera.device
|
||||
*/
|
||||
std::string device()
|
||||
{
|
||||
return _device;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write camera register
|
||||
* @param addr register address
|
||||
* @param data register data
|
||||
* @param bit_width register data bit width, default is 8
|
||||
* @return error code, err::ERR_NONE means success, others means failed
|
||||
* @maixpy maix.camera.Camera.write_reg
|
||||
*/
|
||||
err::Err write_reg(int addr, int data, int bit_width = 8)
|
||||
{
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read camera register
|
||||
* @param addr register address
|
||||
* @return register data, -1 means failed
|
||||
* @param bit_width register data bit width, default is 8
|
||||
* @maixpy maix.camera.Camera.read_reg
|
||||
*/
|
||||
int read_reg(int addr, int bit_width = 8)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Camera output color bar image for test
|
||||
* @param enable enable/disable color bar
|
||||
* @return error code, err::ERR_NONE means success, others means failed
|
||||
* @maixpy maix.camera.Camera.show_colorbar
|
||||
*/
|
||||
err::Err show_colorbar(bool enable);
|
||||
|
||||
/**
|
||||
* Get channel of camera
|
||||
* @return channel number
|
||||
* @maixpy maix.camera.Camera.get_channel
|
||||
*/
|
||||
int get_channel();
|
||||
|
||||
/**
|
||||
* Set camera resolution
|
||||
* @param width new width
|
||||
* @param height new height
|
||||
* @return error code, err::ERR_NONE means success, others means failed
|
||||
* @maixpy maix.camera.Camera.set_resolution
|
||||
*/
|
||||
err::Err set_resolution(int width, int height);
|
||||
|
||||
private:
|
||||
std::string _device;
|
||||
int _ch;
|
||||
int _width;
|
||||
int _height;
|
||||
int _fps;
|
||||
int _buff_num;
|
||||
image::Format _format;
|
||||
image::Format _format_impl; // used by implement code and need convert to _format
|
||||
int _hmirror;
|
||||
int _vflip;
|
||||
float _exposure;
|
||||
float _gain;
|
||||
bool _show_colorbar;
|
||||
bool _open_set_regs;
|
||||
CameraBase *_impl; // used by implement code
|
||||
bool _check_format(image::Format format);
|
||||
};
|
||||
}
|
||||
1540
support/sg2002/additional/vision/include/maix_image.hpp
Normal file
1540
support/sg2002/additional/vision/include/maix_image.hpp
Normal file
File diff suppressed because it is too large
Load Diff
296
support/sg2002/additional/vision/include/maix_image_color.hpp
Normal file
296
support/sg2002/additional/vision/include/maix_image_color.hpp
Normal file
@@ -0,0 +1,296 @@
|
||||
/**
|
||||
* @author neucrack@sipeed, lxowalle@sipeed
|
||||
* @copyright Sipeed Ltd 2023-
|
||||
* @license Apache 2.0
|
||||
* @update 2023.9.8: Add framework, create this file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "maix_image_def.hpp"
|
||||
#include "maix_log.hpp"
|
||||
|
||||
namespace maix::image
|
||||
{
|
||||
/**
|
||||
* Color class
|
||||
* @maixpy maix.image.Color
|
||||
*/
|
||||
class Color
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Color constructor
|
||||
* @param alpha alpha channel, value range: 0 ~ 1
|
||||
* @maixpy maix.image.Color.__init__
|
||||
*/
|
||||
Color(uint8_t ch1, uint8_t ch2 = 0, uint8_t ch3 = 0, float alpha = 0, image::Format format = image::FMT_GRAYSCALE)
|
||||
{
|
||||
if(alpha > 1 || alpha < 0)
|
||||
throw std::runtime_error("alpha value range: 0 ~ 1");
|
||||
this->format = format;
|
||||
switch (format)
|
||||
{
|
||||
case image::FMT_RGB888:
|
||||
r = ch1;
|
||||
g = ch2;
|
||||
b = ch3;
|
||||
this->alpha = 1;
|
||||
break;
|
||||
case image::FMT_BGR888:
|
||||
b = ch1;
|
||||
g = ch2;
|
||||
r = ch3;
|
||||
this->alpha = 1;
|
||||
break;
|
||||
case image::FMT_GRAYSCALE:
|
||||
gray = ch1;
|
||||
break;
|
||||
case image::FMT_BGRA8888:
|
||||
b = ch1;
|
||||
g = ch2;
|
||||
r = ch3;
|
||||
this->alpha = alpha;
|
||||
break;
|
||||
case image::FMT_RGBA8888:
|
||||
r = ch1;
|
||||
g = ch2;
|
||||
b = ch3;
|
||||
this->alpha = alpha;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("not support format");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Color red channel
|
||||
* @maixpy maix.image.Color.r
|
||||
*/
|
||||
uint8_t r;
|
||||
|
||||
/**
|
||||
* Color green channel
|
||||
* @maixpy maix.image.Color.g
|
||||
*/
|
||||
uint8_t g;
|
||||
|
||||
/**
|
||||
* Color blue channel
|
||||
* @maixpy maix.image.Color.b
|
||||
*/
|
||||
uint8_t b;
|
||||
|
||||
/**
|
||||
* Color alpha channel, value from 0.0 to 1.0, float value
|
||||
* @maixpy maix.image.Color.alpha
|
||||
*/
|
||||
float alpha;
|
||||
|
||||
/**
|
||||
* Color gray channel
|
||||
* @maixpy maix.image.Color.gray
|
||||
*/
|
||||
uint8_t gray;
|
||||
|
||||
/**
|
||||
* Color format
|
||||
* @maixpy maix.image.Color.format
|
||||
*/
|
||||
image::Format format;
|
||||
|
||||
/**
|
||||
* Get color's hex value
|
||||
* @maixpy maix.image.Color.hex
|
||||
*/
|
||||
uint32_t hex()
|
||||
{
|
||||
uint32_t hex = 0;
|
||||
switch (format)
|
||||
{
|
||||
case image::FMT_RGB888:
|
||||
hex = r | (g << 8) | (b << 16);
|
||||
break;
|
||||
case image::FMT_BGR888:
|
||||
hex = b | (g << 8) | (r << 16);
|
||||
break;
|
||||
case image::FMT_GRAYSCALE:
|
||||
hex = gray;
|
||||
break;
|
||||
case image::FMT_BGRA8888:
|
||||
hex = b | (g << 8) | (r << 16) | ((uint8_t)(alpha*255) << 24);
|
||||
break;
|
||||
case image::FMT_RGBA8888:
|
||||
hex = r | (g << 8) | (b << 16) | ((uint8_t)(alpha*255) << 24);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("not support format");
|
||||
break;
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Color object from RGB channels
|
||||
* @maixpy maix.image.Color.from_rgb
|
||||
*/
|
||||
static image::Color from_rgb(uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
return Color(r, g, b, 1, image::Format::FMT_RGB888);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Color object from BGR channels
|
||||
* @maixpy maix.image.Color.from_bgr
|
||||
*/
|
||||
static image::Color from_bgr(uint8_t b, uint8_t g, uint8_t r)
|
||||
{
|
||||
return Color(b, g, r, 1, image::Format::FMT_BGR888);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Color object from gray channel
|
||||
* @maixpy maix.image.Color.from_gray
|
||||
*/
|
||||
static image::Color from_gray(uint8_t gray)
|
||||
{
|
||||
return Color(gray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Color object from RGBA channels
|
||||
* @param alpha alpha channel, float value, value range: 0 ~ 1
|
||||
* @maixpy maix.image.Color.from_rgba
|
||||
*/
|
||||
static image::Color from_rgba(uint8_t r, uint8_t g, uint8_t b, float alpha)
|
||||
{
|
||||
return Color(r, g, b, alpha, image::Format::FMT_RGBA8888);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Color object from BGRA channels
|
||||
* * @param alpha alpha channel, float value, value range: 0 ~ 1
|
||||
* @maixpy maix.image.Color.from_bgra
|
||||
*/
|
||||
static image::Color from_bgra(uint8_t b, uint8_t g, uint8_t r, float alpha)
|
||||
{
|
||||
return Color(b, g, r, alpha, image::Format::FMT_BGRA8888);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Color object from hex value
|
||||
* @param hex hex value, e.g. 0x0000FF00, lower address if first channel
|
||||
* @param format color format, @see image::Format
|
||||
* @maixpy maix.image.Color.from_hex
|
||||
*/
|
||||
static image::Color from_hex(uint32_t hex, image::Format &format)
|
||||
{
|
||||
return Color(hex & 0xFF, hex & 0xFF00, hex & 0xFF0000, (hex & 0xFF000000)/255.0, format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Color format
|
||||
* @param format format want to convert to, @see image::Format, only support RGB888, BGR888, RGBA8888, BGRA8888, GRAYSCALE.
|
||||
* @maixpy maix.image.Color.to_format
|
||||
*/
|
||||
void to_format(const image::Format &format)
|
||||
{
|
||||
if(!(format == image::FMT_RGB888 || format == image::FMT_BGR888 ||
|
||||
format == image::FMT_RGBA8888 || format == image::FMT_BGRA8888 ||
|
||||
format == image::FMT_GRAYSCALE))
|
||||
{
|
||||
log::error("convert format failed, not support format %d\n", format);
|
||||
return;
|
||||
}
|
||||
if(this->format == format)
|
||||
return;
|
||||
if((this->format == image::FMT_RGB888 || this->format == image::FMT_BGR888) &&
|
||||
(format == image::FMT_RGBA8888 || format == image::FMT_BGRA8888))
|
||||
{
|
||||
this->alpha = 1;
|
||||
}
|
||||
else if(this->format == image::FMT_GRAYSCALE && format != image::FMT_GRAYSCALE)
|
||||
{
|
||||
this->r = this->gray;
|
||||
this->g = this->gray;
|
||||
this->b = this->gray;
|
||||
this->alpha = 1;
|
||||
}
|
||||
else if((this->format == image::FMT_RGBA8888 || this->format == image::FMT_BGRA8888) &&
|
||||
(format == image::FMT_RGB888 || format == image::FMT_BGR888))
|
||||
{
|
||||
this->alpha = 0;
|
||||
}
|
||||
else if(this->format != image::FMT_GRAYSCALE && format == image::FMT_GRAYSCALE)
|
||||
{
|
||||
this->gray = (this->r + this->g + this->b) / 3;
|
||||
this->r = this->gray;
|
||||
this->g = this->gray;
|
||||
this->b = this->gray;
|
||||
this->alpha = 0;
|
||||
}
|
||||
this->format = format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert color format and return a new Color object
|
||||
* @param format format want to convert to, @see image::Format, only support RGB888, BGR888, RGBA8888, BGRA8888, GRAYSCALE.
|
||||
* @return new Color object, you need to delete it manually in C++.
|
||||
* @maixpy maix.image.Color.to_format2
|
||||
*/
|
||||
image::Color *to_format2(const image::Format &format)
|
||||
{
|
||||
image::Color *color = new image::Color(*this);
|
||||
color->to_format(format);
|
||||
return color;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Predefined color white
|
||||
* @maixpy maix.image.COLOR_WHITE
|
||||
*/
|
||||
const image::Color COLOR_WHITE = image::Color::from_rgb(255, 255, 255);
|
||||
/**
|
||||
* Predefined color black
|
||||
* @maixpy maix.image.COLOR_BLACK
|
||||
*/
|
||||
const image::Color COLOR_BLACK = image::Color::from_rgb(0, 0, 0);
|
||||
/**
|
||||
* Predefined color red
|
||||
* @maixpy maix.image.COLOR_RED
|
||||
*/
|
||||
const image::Color COLOR_RED = image::Color::from_rgb(255, 0, 0);
|
||||
/**
|
||||
* Predefined color green
|
||||
* @maixpy maix.image.COLOR_GREEN
|
||||
*/
|
||||
const image::Color COLOR_GREEN = image::Color::from_rgb(0, 255, 0);
|
||||
/**
|
||||
* Predefined color blue
|
||||
* @maixpy maix.image.COLOR_BLUE
|
||||
*/
|
||||
const image::Color COLOR_BLUE = image::Color::from_rgb(0, 0, 255);
|
||||
/**
|
||||
* Predefined color yellow
|
||||
* @maixpy maix.image.COLOR_YELLOW
|
||||
*/
|
||||
const image::Color COLOR_YELLOW = image::Color::from_rgb(255, 255, 0);
|
||||
/**
|
||||
* Predefined color purple
|
||||
* @maixpy maix.image.COLOR_PURPLE
|
||||
*/
|
||||
const image::Color COLOR_PURPLE = image::Color::from_rgb(143, 0, 255);
|
||||
/**
|
||||
* Predefined color orange
|
||||
* @maixpy maix.image.COLOR_ORANGE
|
||||
*/
|
||||
const image::Color COLOR_ORANGE = image::Color::from_rgb(255, 127, 0);
|
||||
/**
|
||||
* Predefined color gray
|
||||
* @maixpy maix.image.COLOR_GRAY
|
||||
*/
|
||||
const image::Color COLOR_GRAY = image::Color::from_rgb(127, 127, 127);
|
||||
|
||||
}
|
||||
313
support/sg2002/additional/vision/include/maix_image_def.hpp
Normal file
313
support/sg2002/additional/vision/include/maix_image_def.hpp
Normal file
@@ -0,0 +1,313 @@
|
||||
/**
|
||||
* @author neucrack@sipeed, lxowalle@sipeed
|
||||
* @copyright Sipeed Ltd 2023-
|
||||
* @license Apache 2.0
|
||||
* @update 2023.9.8: Add framework, create this file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <stdint.h>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace maix::image
|
||||
{
|
||||
|
||||
/**
|
||||
* Image formats
|
||||
* @attention for MaixPy firmware developers, update this enum will also need to update the fmt_size and fmt_names too !!!
|
||||
* @maixpy maix.image.Format
|
||||
*/
|
||||
enum Format
|
||||
{
|
||||
FMT_RGB888 = 0, // RGBRGB...RGB, R at the lowest address
|
||||
FMT_BGR888, // BGRBGR...BGR, B at the lowest address
|
||||
FMT_RGBA8888, // RGBARGBA...RGBA, R at the lowest address
|
||||
FMT_BGRA8888, // BGRABGRA...BGRA, B at the lowest address
|
||||
FMT_RGB565,
|
||||
FMT_BGR565,
|
||||
FMT_YUV422SP, // YYY...UVUVUV...UVUV
|
||||
FMT_YUV422P, // YYY...UUU...VVV
|
||||
FMT_YVU420SP, // YYY...VUVUVU...VUVU, NV21
|
||||
FMT_YUV420SP, // YYY...UVUVUV...UVUV, NV12
|
||||
FMT_YVU420P, // YYY...VVV...UUU
|
||||
FMT_YUV420P, // YYY...UUU...VVV
|
||||
FMT_GRAYSCALE,
|
||||
FMT_BGGR6, // 6-bit Bayer format with a BGGR pattern.
|
||||
FMT_GBRG6, // 6-bit Bayer format with a GBRG pattern.
|
||||
FMT_GRBG6, // 6-bit Bayer format with a GRBG pattern.
|
||||
FMT_RGGB6, // 6-bit Bayer format with a RGGB pattern.
|
||||
FMT_BGGR8, // 8-bit Bayer format with a BGGR pattern.
|
||||
FMT_GBRG8, // 8-bit Bayer format with a GBRG pattern.
|
||||
FMT_GRBG8, // 8-bit Bayer format with a GRBG pattern.
|
||||
FMT_RGGB8, // 8-bit Bayer format with a RGGB pattern.
|
||||
FMT_BGGR10, // 10-bit Bayer format with a BGGR pattern.
|
||||
FMT_GBRG10, // 10-bit Bayer format with a GBRG pattern.
|
||||
FMT_GRBG10, // 10-bit Bayer format with a GRBG pattern.
|
||||
FMT_RGGB10, // 10-bit Bayer format with a RGGB pattern.
|
||||
FMT_BGGR12, // 12-bit Bayer format with a BGGR pattern.
|
||||
FMT_GBRG12, // 12-bit Bayer format with a GBRG pattern.
|
||||
FMT_GRBG12, // 12-bit Bayer format with a GRBG pattern.
|
||||
FMT_RGGB12, // 12-bit Bayer format with a RGGB pattern.
|
||||
FMT_UNCOMPRESSED_MAX,
|
||||
|
||||
// compressed format below, not compressed should define upper
|
||||
FMT_COMPRESSED_MIN,
|
||||
FMT_JPEG,
|
||||
FMT_PNG,
|
||||
FMT_COMPRESSED_MAX,
|
||||
|
||||
FMT_INVALID = 0xFF // format not valid
|
||||
}; // !!!! update this section please update fmt_size and fmt_names too !!!!
|
||||
|
||||
/**
|
||||
* Image format size in bytes
|
||||
* @attention It's a copy of this variable in MaixPy,
|
||||
* so change it in C++ (e.g. update var in hello function) will not take effect the var inMaixPy.
|
||||
* So we add const for this var to avoid this mistake.
|
||||
* @maixpy maix.image.fmt_size
|
||||
*/
|
||||
const std::vector<float> fmt_size = {
|
||||
3,
|
||||
3,
|
||||
4,
|
||||
4,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
1.5,
|
||||
1.5,
|
||||
1.5,
|
||||
1.5,
|
||||
1, // grayscale
|
||||
0.75, // 6-bit Bayer format
|
||||
0.75, // 6-bit Bayer format
|
||||
0.75, // 6-bit Bayer format
|
||||
0.75, // 6-bit Bayer format
|
||||
1, // 8-bit Bayer format
|
||||
1, // 8-bit Bayer format
|
||||
1, // 8-bit Bayer format
|
||||
1, // 8-bit Bayer format
|
||||
1.25, // 10-bit Bayer format
|
||||
1.25, // 10-bit Bayer format
|
||||
1.25, // 10-bit Bayer format
|
||||
1.25, // 10-bit Bayer format
|
||||
1.5, // 12-bit Bayer format
|
||||
1.5, // 12-bit Bayer format
|
||||
1.5, // 12-bit Bayer format
|
||||
1.5, // 12-bit Bayer format
|
||||
0, // uncompereed_max
|
||||
0, // compressed_min
|
||||
1, // jpeg
|
||||
1, // png
|
||||
0, // compressed_max
|
||||
0 // invalid
|
||||
};
|
||||
|
||||
/**
|
||||
* Image format string
|
||||
* @maixpy maix.image.fmt_names
|
||||
*/
|
||||
const std::vector<std::string> fmt_names = {
|
||||
"RGB888",
|
||||
"BGR888",
|
||||
"RGBA8888",
|
||||
"BGRA8888",
|
||||
"RGB565",
|
||||
"BGR565",
|
||||
"YUV422SP",
|
||||
"YUV422P",
|
||||
"YVU420SP",
|
||||
"YUV420SP",
|
||||
"YVU420P",
|
||||
"YUV420P",
|
||||
"GRAYSCALE",
|
||||
"BGGR6",
|
||||
"GBRG6",
|
||||
"GRBG6",
|
||||
"RG6B6",
|
||||
"BGGR8",
|
||||
"GBRG8",
|
||||
"GRBG8",
|
||||
"RG6B8",
|
||||
"BGGR10",
|
||||
"GBRG10",
|
||||
"GRBG10",
|
||||
"RG6B10",
|
||||
"BGGR12",
|
||||
"GBRG12",
|
||||
"GRBG12",
|
||||
"RG6B12",
|
||||
"UNCOMPRESSED_MAX",
|
||||
"COMPRESSED_MIN",
|
||||
"JPEG",
|
||||
"PNG",
|
||||
"COMPRESSED_MAX",
|
||||
"INVALID"
|
||||
};
|
||||
|
||||
/**
|
||||
* Image size type
|
||||
* @maixpy maix.image.Size
|
||||
*/
|
||||
class Size
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Construct a new Size object
|
||||
* @param width image width
|
||||
* @param height image height
|
||||
* @maixpy maix.image.Size.__init__
|
||||
*/
|
||||
Size(int width = 0, int height = 0)
|
||||
{
|
||||
this->_width = width;
|
||||
this->_height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* width of size
|
||||
* @param width set new width, if not set, only return current width
|
||||
* @maixpy maix.image.Size.width
|
||||
*/
|
||||
int width(int width = -1)
|
||||
{
|
||||
if(width != -1)
|
||||
{
|
||||
this->_width = width;
|
||||
}
|
||||
return this->_width;
|
||||
}
|
||||
|
||||
/**
|
||||
* height of size
|
||||
* @param height set new height, if not set, only return current height
|
||||
* @maixpy maix.image.Size.height
|
||||
*/
|
||||
int height(int height = -1)
|
||||
{
|
||||
if(height != -1)
|
||||
{
|
||||
this->_height = height;
|
||||
}
|
||||
return this->_height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscript operator
|
||||
* @param index 0 for width, 1 for height
|
||||
* @return int& width or height
|
||||
* @maixpy maix.image.Size.__getitem__
|
||||
* @maixcdk maix.image.Size.operator[]
|
||||
*/
|
||||
int &operator[](int index)
|
||||
{
|
||||
if (index == 0)
|
||||
return _width;
|
||||
else if (index == 1)
|
||||
return _height;
|
||||
else
|
||||
throw std::out_of_range("Size index out of range");
|
||||
}
|
||||
|
||||
/**
|
||||
* to string
|
||||
* @maixpy maix.image.Size.__str__
|
||||
*/
|
||||
std::string __str__()
|
||||
{
|
||||
return "Size(" + std::to_string(_width) + "x" + std::to_string(_height) + ")";
|
||||
}
|
||||
private:
|
||||
int _width;
|
||||
int _height;
|
||||
};
|
||||
|
||||
/**
|
||||
* Object fit method
|
||||
* @maixpy maix.image.Fit
|
||||
*/
|
||||
enum Fit
|
||||
{
|
||||
FIT_NONE = -1, // no object fit, keep original
|
||||
FIT_FILL = 0, // width to new width, height to new height, may be stretch
|
||||
FIT_CONTAIN, // keep aspect ratio, fill blank area with black color
|
||||
FIT_COVER, // keep aspect ratio, crop image to fit new size
|
||||
FIT_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* Resize method
|
||||
* @maixpy maix.image.ResizeMethod
|
||||
*/
|
||||
enum ResizeMethod
|
||||
{
|
||||
NEAREST = 0,
|
||||
BILINEAR,
|
||||
BICUBIC,
|
||||
AREA,
|
||||
LANCZOS,
|
||||
HAMMING,
|
||||
RESIZE_METHOD_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* Family of apriltag
|
||||
* @maixpy maix.image.ApriltagFamilies
|
||||
*/
|
||||
enum ApriltagFamilies
|
||||
{
|
||||
TAG16H5 = 1,
|
||||
TAG25H7 = 2,
|
||||
TAG25H9 = 4,
|
||||
TAG36H10 = 8,
|
||||
TAG36H11 = 16,
|
||||
ARTOOLKIT = 32
|
||||
};
|
||||
|
||||
/**
|
||||
* Template match method
|
||||
* @maixpy maix.image.TemplateMatch
|
||||
*/
|
||||
enum TemplateMatch
|
||||
{
|
||||
SEARCH_EX, // Exhaustive search
|
||||
SEARCH_DS, // Diamond search
|
||||
};
|
||||
|
||||
/**
|
||||
* CornerDetector class
|
||||
* @maixpy maix.image.CornerDetector
|
||||
*/
|
||||
enum CornerDetector
|
||||
{
|
||||
CORNER_FAST,
|
||||
CORNER_AGAST
|
||||
};
|
||||
|
||||
/**
|
||||
* EdgeDetector class
|
||||
* @maixpy maix.image.EdgeDetector
|
||||
*/
|
||||
enum EdgeDetector
|
||||
{
|
||||
EDGE_CANNY,
|
||||
EDGE_SIMPLE,
|
||||
};
|
||||
|
||||
/**
|
||||
* FlipDir
|
||||
* @maixpy maix.image.FlipDir
|
||||
*/
|
||||
enum class FlipDir
|
||||
{
|
||||
X,
|
||||
Y,
|
||||
XY
|
||||
};
|
||||
|
||||
}
|
||||
2543
support/sg2002/additional/vision/include/maix_image_obj.hpp
Normal file
2543
support/sg2002/additional/vision/include/maix_image_obj.hpp
Normal file
File diff suppressed because it is too large
Load Diff
4
support/sg2002/additional/vision/include/maix_vision.hpp
Normal file
4
support/sg2002/additional/vision/include/maix_vision.hpp
Normal file
@@ -0,0 +1,4 @@
|
||||
#include "maix_image.hpp"
|
||||
#include "maix_display.hpp"
|
||||
#include "maix_camera.hpp"
|
||||
#include "maix_video.hpp"
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "maix_image.hpp"
|
||||
#include "omv.hpp"
|
||||
|
||||
namespace maix::image
|
||||
{
|
||||
/**
|
||||
* Convert image to openmv image format
|
||||
* @param image image
|
||||
* @param imlib_image openmv image
|
||||
* @return
|
||||
*/
|
||||
extern void convert_to_imlib_image(image::Image *image, image_t *imlib_image);
|
||||
extern void _convert_to_lab_thresholds(std::vector<std::vector<int>> &in, list_t *out);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,317 @@
|
||||
/**
|
||||
* @author lxowalle@sipeed
|
||||
* @copyright Sipeed Ltd 2023-
|
||||
* @license Apache 2.0
|
||||
* @update 2023.9.8: Add framework, create this file.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "maix_err.hpp"
|
||||
#include "maix_basic.hpp"
|
||||
#include "maix_log.hpp"
|
||||
#include "maix_image.hpp"
|
||||
#include "maix_time.hpp"
|
||||
#include "maix_camera_base.hpp"
|
||||
#include "kvm_mmf.hpp"
|
||||
#include <signal.h>
|
||||
|
||||
static void try_deinit_mmf()
|
||||
{
|
||||
static uint8_t is_called = 0;
|
||||
if (!is_called) {
|
||||
mmf_try_deinit(true);
|
||||
is_called = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void signal_handle(int signal)
|
||||
{
|
||||
const char *signal_msg = NULL;
|
||||
switch (signal) {
|
||||
case SIGILL: signal_msg = "SIGILL"; break;
|
||||
case SIGTRAP: signal_msg = "SIGTRAP"; break;
|
||||
case SIGABRT: signal_msg = "SIGABRT"; break;
|
||||
case SIGBUS: signal_msg = "SIGBUS"; break;
|
||||
case SIGFPE: signal_msg = "SIGFPE"; break;
|
||||
case SIGKILL: signal_msg = "SIGKILL"; break;
|
||||
case SIGSEGV: signal_msg = "SIGSEGV"; break;
|
||||
default: signal_msg = "UNKNOWN"; break;
|
||||
}
|
||||
|
||||
maix::log::error("Trigger signal, code:%s(%d)!\r\n", signal_msg, signal);
|
||||
try_deinit_mmf();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// FIXME: move this function to port/maix_vision_maixcam.cpp ?
|
||||
static __attribute__((constructor)) void maix_vision_register_signal(void)
|
||||
{
|
||||
signal(SIGILL, signal_handle);
|
||||
signal(SIGTRAP, signal_handle);
|
||||
signal(SIGABRT, signal_handle);
|
||||
signal(SIGBUS, signal_handle);
|
||||
signal(SIGFPE, signal_handle);
|
||||
signal(SIGKILL, signal_handle);
|
||||
signal(SIGSEGV, signal_handle);
|
||||
|
||||
maix::util::register_exit_function(try_deinit_mmf);
|
||||
}
|
||||
|
||||
namespace maix::camera
|
||||
{
|
||||
class CameraCviMmf final : public CameraBase
|
||||
{
|
||||
public:
|
||||
CameraCviMmf(const std::string device, int width, int height, image::Format format, int buff_num)
|
||||
{
|
||||
this->device = device;
|
||||
this->format = format;
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
this->buffer_num = buff_num;
|
||||
this->ch = -1;
|
||||
|
||||
if (0 != mmf_init()) {
|
||||
err::check_raise(err::ERR_RUNTIME, "mmf init failed");
|
||||
}
|
||||
|
||||
if (0 != mmf_vi_init()) {
|
||||
err::check_raise(err::ERR_RUNTIME, "mmf vi init failed");
|
||||
}
|
||||
}
|
||||
|
||||
CameraCviMmf(const std::string device, int ch, int width, int height, image::Format format, int buff_num)
|
||||
{
|
||||
this->device = device;
|
||||
this->format = format;
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
this->buffer_num = buff_num;
|
||||
this->ch = ch;
|
||||
|
||||
if (0 != mmf_init()) {
|
||||
err::check_raise(err::ERR_RUNTIME, "mmf init failed");
|
||||
}
|
||||
|
||||
if (0 != mmf_vi_init()) {
|
||||
err::check_raise(err::ERR_RUNTIME, "mmf vi init failed");
|
||||
}
|
||||
}
|
||||
|
||||
~CameraCviMmf()
|
||||
{
|
||||
mmf_del_vi_channel(this->ch);
|
||||
if (0 != mmf_vi_deinit()) {
|
||||
err::check_raise(err::ERR_RUNTIME, "mmf vi init failed");
|
||||
}
|
||||
mmf_try_deinit(true);
|
||||
}
|
||||
|
||||
err::Err open(int width, int height, image::Format format, int buff_num)
|
||||
{
|
||||
if (format == image::FMT_GRAYSCALE) {
|
||||
format = image::FMT_YVU420SP;
|
||||
}
|
||||
int ch = mmf_get_vi_unused_channel();
|
||||
if (ch < 0) {
|
||||
log::error("camera open: mmf get vi channel failed");
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
if (0 != mmf_add_vi_channel(ch, width, height, mmf_invert_format_to_mmf(format))) {
|
||||
log::error("camera open: mmf add vi channel failed");
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
|
||||
this->ch = ch;
|
||||
this->width = (width == -1) ? this->width : width;
|
||||
this->height = (height == -1) ? this->height : height;
|
||||
this->align_width = mmf_vi_aligned_width(this->ch);
|
||||
this->align_need = (this->width % this->align_width == 0) ? false : true; // Width need align only
|
||||
return err::ERR_NONE;
|
||||
} // open
|
||||
|
||||
bool is_support_format(image::Format format)
|
||||
{
|
||||
if(format == image::Format::FMT_RGB888 || format == image::Format::FMT_BGR888
|
||||
|| format == image::Format::FMT_YVU420SP|| format == image::Format::FMT_GRAYSCALE)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
if (mmf_vi_chn_is_open(this->ch) == true) {
|
||||
if (0 != mmf_del_vi_channel(this->ch)) {
|
||||
log::error("mmf del vi channel failed");
|
||||
}
|
||||
}
|
||||
} // close
|
||||
|
||||
// read
|
||||
image::Image *read(void *buff = NULL, size_t buff_size = 0)
|
||||
{
|
||||
// printf("Func: maix_image.cpp read\r\n");
|
||||
image::Image *img = NULL;
|
||||
|
||||
void *buffer = NULL;
|
||||
int buffer_len = 0, width = 0, height = 0, format = 0;
|
||||
|
||||
if (0 == mmf_vi_frame_pop(this->ch, &buffer, &buffer_len, &width, &height, &format)) {
|
||||
if (buffer == NULL) {
|
||||
mmf_vi_frame_free(this->ch);
|
||||
printf("mmf_vi_frame_free error\r\n");
|
||||
return NULL;
|
||||
}
|
||||
if(buff)
|
||||
{
|
||||
if(buff_size < (size_t)buffer_len)
|
||||
{
|
||||
log::error("camera read: buff size not enough, need %d, but %d", buffer_len, buff_size);
|
||||
mmf_vi_frame_free(this->ch);
|
||||
return NULL;
|
||||
}
|
||||
img = new image::Image(width, height, this->format, (uint8_t*)buff, buff_size, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
img = new image::Image(this->width, this->height, this->format);
|
||||
}
|
||||
void *image_data = img->data();
|
||||
switch (img->format()) {
|
||||
case image::Format::FMT_GRAYSCALE:
|
||||
if (this->align_need) {
|
||||
for (int h = 0; h < this->height; h ++) {
|
||||
memcpy((uint8_t *)image_data + h * this->width, (uint8_t *)buffer + h * width, this->width);
|
||||
}
|
||||
} else {
|
||||
memcpy(image_data, buffer, this->width * this->height);
|
||||
}
|
||||
break;
|
||||
case image::Format::FMT_BGR888: // fall through
|
||||
case image::Format::FMT_RGB888:
|
||||
if (this->align_need) {
|
||||
for (int h = 0; h < this->height; h++) {
|
||||
memcpy((uint8_t *)image_data + h * this->width * 3, (uint8_t *)buffer + h * width * 3, this->width * 3);
|
||||
}
|
||||
} else {
|
||||
memcpy(image_data, buffer, this->width * this->height * 3);
|
||||
}
|
||||
break;
|
||||
case image::Format::FMT_YVU420SP:
|
||||
if (this->align_need) {
|
||||
for (int h = 0; h < this->height * 3 / 2; h ++) {
|
||||
memcpy((uint8_t *)image_data + h * this->width, (uint8_t *)buffer + h * width, this->width);
|
||||
}
|
||||
} else {
|
||||
memcpy(image_data, buffer, this->width * this->height * 3 / 2);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("unknown format\n");
|
||||
delete img;
|
||||
mmf_vi_frame_free(this->ch);
|
||||
printf("switch (img->format()\r\n");
|
||||
return NULL;
|
||||
}
|
||||
mmf_vi_frame_free(this->ch);
|
||||
// printf("mmf_vi_frame_free\r\n");
|
||||
return img;
|
||||
}
|
||||
|
||||
return img;
|
||||
} // read
|
||||
|
||||
camera::CameraCviMmf *add_channel(int width, int height, image::Format format, int buff_num)
|
||||
{
|
||||
int new_channel = mmf_get_vi_unused_channel();
|
||||
if (new_channel < 0) {
|
||||
log::error("Support not more channel!\r\n");
|
||||
return NULL;
|
||||
}
|
||||
return new camera::CameraCviMmf(this->device, new_channel, width, height, format, buff_num);
|
||||
}
|
||||
|
||||
void clear_buff()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool is_opened() {
|
||||
return mmf_vi_chn_is_open(this->ch);
|
||||
}
|
||||
|
||||
int get_ch_nums() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int get_channel() {
|
||||
return this->ch;
|
||||
}
|
||||
|
||||
int hmirror(int en)
|
||||
{
|
||||
bool out;
|
||||
if (en == -1) {
|
||||
mmf_get_vi_hmirror(this->ch, &out);
|
||||
} else {
|
||||
bool need_open = false;
|
||||
if (this->is_opened()) {
|
||||
this->close();
|
||||
need_open = true;
|
||||
}
|
||||
|
||||
mmf_set_vi_hmirror(this->ch, en);
|
||||
|
||||
if (need_open) {
|
||||
err::check_raise(this->open(this->width, this->height, this->format, this->buffer_num), "Open failed");
|
||||
}
|
||||
out = en;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
int vflip(int en)
|
||||
{
|
||||
bool out;
|
||||
if (en == -1) {
|
||||
mmf_get_vi_vflip(this->ch, &out);
|
||||
} else {
|
||||
bool need_open = false;
|
||||
if (this->is_opened()) {
|
||||
this->close();
|
||||
need_open = true;
|
||||
}
|
||||
|
||||
mmf_set_vi_vflip(this->ch, en);
|
||||
|
||||
if (need_open) {
|
||||
err::check_raise(this->open(this->width, this->height, this->format, this->buffer_num), "Open failed");
|
||||
}
|
||||
out = en;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string device;
|
||||
image::Format format;
|
||||
int fd;
|
||||
int ch;
|
||||
uint32_t raw_format;
|
||||
std::vector<void*> buffers;
|
||||
std::vector<int> buffers_len;
|
||||
int buffer_num;
|
||||
int queue_id; // user directly used buffer id
|
||||
int width;
|
||||
int height;
|
||||
void *buff;
|
||||
bool buff_alloc;
|
||||
int align_width;
|
||||
bool align_need;
|
||||
};
|
||||
|
||||
} // namespace maix::camera
|
||||
425
support/sg2002/additional/vision/src/maix_camera.cpp
Normal file
425
support/sg2002/additional/vision/src/maix_camera.cpp
Normal file
@@ -0,0 +1,425 @@
|
||||
/**
|
||||
* @author neucrack@sipeed, lxowalle@sipeed
|
||||
* @copyright Sipeed Ltd 2023-
|
||||
* @license Apache 2.0
|
||||
* @update 2023.9.8: Add framework, create this file.
|
||||
*/
|
||||
|
||||
|
||||
#include "maix_camera.hpp"
|
||||
#include <dirent.h>
|
||||
#ifdef PLATFORM_LINUX
|
||||
#include "maix_camera_v4l2.hpp"
|
||||
#endif
|
||||
#ifdef PLATFORM_MAIXCAM
|
||||
#include "maix_camera_mmf.hpp"
|
||||
#endif
|
||||
|
||||
namespace maix::camera
|
||||
{
|
||||
static bool set_regs_flag = false;
|
||||
|
||||
std::vector<std::string> list_devices()
|
||||
{
|
||||
// find to /dev/video*
|
||||
std::vector<std::string> devices;
|
||||
std::string path = "/dev";
|
||||
DIR *dir = opendir(path.c_str());
|
||||
if (dir == NULL)
|
||||
{
|
||||
return devices;
|
||||
}
|
||||
struct dirent *ptr;
|
||||
while ((ptr = readdir(dir)) != NULL)
|
||||
{
|
||||
if (ptr->d_type == DT_CHR)
|
||||
{
|
||||
std::string name = ptr->d_name;
|
||||
if (name.find("video") != std::string::npos)
|
||||
{
|
||||
devices.push_back( path + "/" + name );
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
// sort devices with name
|
||||
std::sort(devices.begin(), devices.end());
|
||||
// print devices
|
||||
for (size_t i = 0; i < devices.size(); i++)
|
||||
{
|
||||
log::debug("find device: %s\n", devices[i].c_str());
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
void set_regs_enable(bool enable) {
|
||||
set_regs_flag = enable;
|
||||
}
|
||||
|
||||
err::Err Camera::show_colorbar(bool enable)
|
||||
{
|
||||
// only set variable now
|
||||
// should control camera to show colorbar
|
||||
_show_colorbar = enable;
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
static void generate_colorbar(image::Image &img)
|
||||
{
|
||||
int width = img.width();
|
||||
int height = img.height();
|
||||
int step = width / 8;
|
||||
int color_step = 255 / 7;
|
||||
int color = 0;
|
||||
uint8_t colors[8][3] = {
|
||||
{255, 255, 255},
|
||||
{255, 0, 0},
|
||||
{255, 127, 0},
|
||||
{255, 255, 0},
|
||||
{0, 255, 0},
|
||||
{0, 0, 255},
|
||||
{143, 0, 255},
|
||||
{0, 0, 0},
|
||||
};
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
image::Color _color(colors[i][0], colors[i][1], colors[i][2], 0, image::FMT_RGB888);
|
||||
img.draw_rect(i * step, 0, step, height, _color, -1);
|
||||
color += color_step;
|
||||
}
|
||||
}
|
||||
#ifdef PLATFORM_LINUX
|
||||
static char * _get_device(const char *device)
|
||||
{
|
||||
if (device)
|
||||
{
|
||||
return (char *)device;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<std::string> devices = list_devices();
|
||||
err::check_bool_raise(devices.size() > 0, "No camera device");
|
||||
return (char *)devices[0].c_str();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Camera::Camera(int width, int height, image::Format format, const char *device, int fps, int buff_num, bool open)
|
||||
{
|
||||
err::Err e;
|
||||
err::check_bool_raise(_check_format(format), "Format not support");
|
||||
|
||||
if (format == image::Format::FMT_RGB888 && width * height * 3 > 640 * 640 * 3) {
|
||||
log::warn("Note that we do not recommend using large resolution RGB888 images, which can take up a lot of memory!\r\n");
|
||||
}
|
||||
|
||||
_width = (width == -1) ? 640 : width;
|
||||
_height = (height == -1) ? 480 : height;
|
||||
_format = format;
|
||||
_fps = (fps == -1) ? 30 : fps;
|
||||
_buff_num = buff_num;
|
||||
|
||||
_show_colorbar = false;
|
||||
_open_set_regs = set_regs_flag;
|
||||
_impl = NULL;
|
||||
|
||||
#ifdef PLATFORM_LINUX
|
||||
_device = _get_device(device);
|
||||
_impl = new CameraV4L2(_device, _width, _height, _format, _buff_num);
|
||||
#endif
|
||||
|
||||
#ifdef PLATFORM_MAIXCAM
|
||||
_device = "";
|
||||
_impl = new CameraCviMmf(_device, _width, _height, _format, _buff_num);
|
||||
#endif
|
||||
|
||||
if (open) {
|
||||
e = this->open(_width, _height, _format, _buff_num);
|
||||
|
||||
// err::check_raise(e, "camera open failed");
|
||||
}
|
||||
}
|
||||
|
||||
Camera::Camera(const char *device, CameraBase *base, int width, int height, image::Format format, int fps, int buff_num, bool open)
|
||||
{
|
||||
err::Err e;
|
||||
err::check_bool_raise(_check_format(format), "Format not support");
|
||||
|
||||
_width = (width == -1) ? 640 : width;
|
||||
_height = (height == -1) ? 480 : height;
|
||||
_format = format;
|
||||
_fps = (fps == -1) ? 30 : fps;
|
||||
_buff_num = buff_num;
|
||||
|
||||
_show_colorbar = false;
|
||||
_open_set_regs = set_regs_flag;
|
||||
_impl = base;
|
||||
|
||||
if (open) {
|
||||
e = this->open(_width, _height, _format, _buff_num);
|
||||
err::check_raise(e, "camera open failed");
|
||||
}
|
||||
}
|
||||
|
||||
Camera::~Camera()
|
||||
{
|
||||
if (this->is_opened()) {
|
||||
this->close();
|
||||
}
|
||||
#ifdef PLATFORM_LINUX
|
||||
delete (CameraV4L2*)_impl;
|
||||
#endif
|
||||
|
||||
#ifdef PLATFORM_MAIXCAM
|
||||
delete (CameraCviMmf*)_impl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Camera::restart(int width, int height, image::Format format, const char *device, int fps, int buff_num, bool open)
|
||||
{
|
||||
if (this->is_opened()) {
|
||||
this->close();
|
||||
}
|
||||
#ifdef PLATFORM_LINUX
|
||||
delete (CameraV4L2*)_impl;
|
||||
#endif
|
||||
|
||||
#ifdef PLATFORM_MAIXCAM
|
||||
delete (CameraCviMmf*)_impl;
|
||||
#endif
|
||||
|
||||
err::Err e;
|
||||
err::check_bool_raise(_check_format(format), "Format not support");
|
||||
|
||||
if (format == image::Format::FMT_RGB888 && width * height * 3 > 640 * 640 * 3) {
|
||||
log::warn("Note that we do not recommend using large resolution RGB888 images, which can take up a lot of memory!\r\n");
|
||||
}
|
||||
|
||||
_width = (width == -1) ? 640 : width;
|
||||
_height = (height == -1) ? 480 : height;
|
||||
_format = format;
|
||||
_fps = (fps == -1) ? 30 : fps;
|
||||
_buff_num = buff_num;
|
||||
|
||||
_show_colorbar = false;
|
||||
_open_set_regs = set_regs_flag;
|
||||
_impl = NULL;
|
||||
|
||||
#ifdef PLATFORM_LINUX
|
||||
_device = _get_device(device);
|
||||
_impl = new CameraV4L2(_device, _width, _height, _format, _buff_num);
|
||||
#endif
|
||||
|
||||
#ifdef PLATFORM_MAIXCAM
|
||||
_device = "";
|
||||
_impl = new CameraCviMmf(_device, _width, _height, _format, _buff_num);
|
||||
#endif
|
||||
|
||||
if (open) {
|
||||
e = this->open(_width, _height, _format, _buff_num);
|
||||
|
||||
// err::check_raise(e, "camera open failed");
|
||||
}
|
||||
}
|
||||
|
||||
int Camera::get_ch_nums()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
int Camera::get_channel()
|
||||
{
|
||||
if (_impl == NULL)
|
||||
return err::ERR_NOT_INIT;
|
||||
|
||||
if (!this->is_opened()) {
|
||||
return err::ERR_NOT_OPEN;
|
||||
}
|
||||
|
||||
return _impl->get_channel();
|
||||
}
|
||||
|
||||
bool Camera::_check_format(image::Format format) {
|
||||
if (format == image::FMT_RGB888 || format == image::FMT_BGR888
|
||||
|| format == image::FMT_RGBA8888 || format == image::FMT_BGRA8888
|
||||
|| format == image::FMT_YVU420SP || format == image::FMT_GRAYSCALE) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
err::Err Camera::open(int width, int height, image::Format format, int fps, int buff_num)
|
||||
{
|
||||
if (_impl == NULL)
|
||||
return err::Err::ERR_RUNTIME;
|
||||
|
||||
int width_tmp = (width == -1) ? _width : width;
|
||||
int height_tmp = (height == -1) ? _height : height;
|
||||
image::Format format_tmp = (format == image::FMT_INVALID) ? _format : format;
|
||||
int fps_tmp = (fps == -1) ? 30 : fps;
|
||||
int buff_num_tmp =( buff_num == -1) ? _buff_num : buff_num;
|
||||
|
||||
err::check_bool_raise(_check_format(format_tmp), "Format not support");
|
||||
|
||||
if (this->is_opened()) {
|
||||
if (width == width_tmp && height == height_tmp && format == format_tmp && fps == fps_tmp && buff_num == buff_num_tmp) {
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
this->close(); // Get new param, close and reopen
|
||||
}
|
||||
|
||||
_width = width_tmp;
|
||||
_height = height_tmp;
|
||||
_fps = fps_tmp;
|
||||
_buff_num = buff_num_tmp;
|
||||
_format = format_tmp;
|
||||
_format_impl = _format;
|
||||
if(!_impl->is_support_format(_format))
|
||||
{
|
||||
if(_impl->is_support_format(image::FMT_RGB888))
|
||||
_format_impl = image::FMT_RGB888;
|
||||
else if(_impl->is_support_format(image::FMT_BGR888))
|
||||
_format_impl = image::FMT_BGR888;
|
||||
else if(_impl->is_support_format(image::FMT_YVU420SP))
|
||||
_format_impl = image::FMT_YVU420SP;
|
||||
else if(_impl->is_support_format(image::FMT_YUV420SP))
|
||||
_format_impl = image::FMT_YUV420SP;
|
||||
else if(_impl->is_support_format(image::FMT_RGBA8888))
|
||||
_format_impl = image::FMT_RGBA8888;
|
||||
else if(_impl->is_support_format(image::FMT_BGRA8888))
|
||||
_format_impl = image::FMT_BGRA8888;
|
||||
else if(_impl->is_support_format(image::FMT_GRAYSCALE))
|
||||
_format_impl = image::FMT_GRAYSCALE;
|
||||
else
|
||||
return err::ERR_ARGS;
|
||||
}
|
||||
|
||||
return _impl->open(_width, _height, _format_impl, _buff_num);;
|
||||
}
|
||||
|
||||
void Camera::close()
|
||||
{
|
||||
if (this->is_closed())
|
||||
return;
|
||||
|
||||
_impl->close();
|
||||
}
|
||||
|
||||
camera::Camera *Camera::add_channel(int width, int height, image::Format format, int fps, int buff_num, bool open)
|
||||
{
|
||||
err::check_bool_raise(_check_format(format), "Format not support");
|
||||
|
||||
int width_tmp = (width == -1) ? _width : width;
|
||||
int height_tmp = (height == -1) ? _height : height;
|
||||
image::Format format_tmp = (format == image::Format::FMT_INVALID) ? _format : format;
|
||||
int fps_tmp = (fps == -1) ? _fps : fps;
|
||||
int buff_num_tmp = buff_num == -1 ? _buff_num : buff_num;
|
||||
|
||||
Camera *cam = NULL;
|
||||
if (_impl) {
|
||||
CameraBase *cam_base = _impl->add_channel(width_tmp, height_tmp, format_tmp, buff_num);
|
||||
err::check_bool_raise(cam_base, "Unable to add a new channel. Please check the maximum number of supported channels.");
|
||||
cam = new Camera(_device.c_str(), cam_base, width_tmp, height_tmp, format_tmp, fps_tmp, buff_num_tmp, open);
|
||||
}
|
||||
return cam;
|
||||
}
|
||||
|
||||
bool Camera::is_opened()
|
||||
{
|
||||
if (_impl == NULL)
|
||||
return false;
|
||||
|
||||
return _impl->is_opened();
|
||||
}
|
||||
|
||||
image::Image *Camera::read(void *buff, size_t buff_size, bool block)
|
||||
{
|
||||
if (!this->is_opened()) {
|
||||
// err::Err e = open(_width, _height, _format, _buff_num);
|
||||
// err::check_raise(e, "open camera failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (_show_colorbar) {
|
||||
image::Image *img = new image::Image(_width, _height);
|
||||
generate_colorbar(*img);
|
||||
err::check_null_raise(img, "camera read failed");
|
||||
return img;
|
||||
} else {
|
||||
// it's better all done by impl to faster read, but if impl not support, we have to convert it
|
||||
if(_format_impl == _format)
|
||||
{
|
||||
image::Image *img = _impl->read(buff, buff_size);
|
||||
// err::check_null_raise(img, "camera read failed");
|
||||
return img;
|
||||
}
|
||||
else
|
||||
{
|
||||
image::Image *img = _impl->read();
|
||||
image::Image *img2 = img->to_format(_format, buff, buff_size);
|
||||
delete img;
|
||||
// err::check_null_raise(img2, "camera read failed");
|
||||
return img2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Camera::clear_buff()
|
||||
{
|
||||
if (_impl == NULL)
|
||||
return;
|
||||
_impl->clear_buff();
|
||||
}
|
||||
|
||||
void Camera::skip_frames(int num)
|
||||
{
|
||||
if (_impl == NULL)
|
||||
return;
|
||||
for(int i = 0; i < num; i++)
|
||||
{
|
||||
image::Image *img = _impl->read();
|
||||
delete img;
|
||||
}
|
||||
}
|
||||
|
||||
err::Err Camera::set_resolution(int width, int height)
|
||||
{
|
||||
err::Err e;
|
||||
if (_impl == NULL)
|
||||
return err::ERR_NOT_INIT;
|
||||
|
||||
if (this->is_opened()) {
|
||||
this->close();
|
||||
}
|
||||
|
||||
_width = width;
|
||||
_height = height;
|
||||
e = this->open(_width, _height, _format, _buff_num);
|
||||
err::check_raise(e, "camera open failed");
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
int Camera::hmirror(int value) {
|
||||
if (_impl == NULL)
|
||||
return err::ERR_NOT_INIT;
|
||||
|
||||
if (!this->is_opened()) {
|
||||
return err::ERR_NOT_OPEN;
|
||||
}
|
||||
|
||||
return _impl->hmirror(value);
|
||||
}
|
||||
|
||||
int Camera::vflip(int value) {
|
||||
if (_impl == NULL)
|
||||
return err::ERR_NOT_INIT;
|
||||
|
||||
if (!this->is_opened()) {
|
||||
return err::ERR_NOT_OPEN;
|
||||
}
|
||||
|
||||
return _impl->vflip(value);
|
||||
}
|
||||
}
|
||||
|
||||
1654
support/sg2002/additional/vision/src/maix_image.cpp
Normal file
1654
support/sg2002/additional/vision/src/maix_image.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1209
support/sg2002/additional/vision/src/maix_image_ops.cpp
Normal file
1209
support/sg2002/additional/vision/src/maix_image_ops.cpp
Normal file
File diff suppressed because it is too large
Load Diff
178
support/sg2002/build
Executable file
178
support/sg2002/build
Executable file
@@ -0,0 +1,178 @@
|
||||
#!/bin/bash
|
||||
|
||||
export MAIXCDK_PATH=~/MaixCDK
|
||||
export NanoKVM_PATH=~/NanoKVM
|
||||
|
||||
Project_Name="None"
|
||||
Project_PATH="None"
|
||||
Project_Action="None"
|
||||
KVMAPP_PATH="$NanoKVM_PATH/kvmapp"
|
||||
|
||||
maixcdk_build() {
|
||||
local input_value="$1"
|
||||
|
||||
DIST_PATH="./dist"
|
||||
rm -rf $DIST_PATH
|
||||
|
||||
if [ "$input_value" = "from_zero" ]
|
||||
then
|
||||
maixcdk build <<EOF
|
||||
2
|
||||
EOF
|
||||
else
|
||||
maixcdk build
|
||||
fi
|
||||
}
|
||||
|
||||
chack_build(){
|
||||
DIST_PATH="./dist"
|
||||
if [ -d "$DIST_PATH" ]
|
||||
then
|
||||
echo "SUCCESS"
|
||||
else
|
||||
echo "FAILURE"
|
||||
fi
|
||||
}
|
||||
|
||||
maixcdk_clean(){
|
||||
maixcdk distclean
|
||||
maixcdk clean
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
update_lib)
|
||||
cp -rf $NanoKVM_PATH/support/sg2002/additional/kvm $MAIXCDK_PATH/components
|
||||
cp -rf $NanoKVM_PATH/support/sg2002/additional/kvm_mmf $MAIXCDK_PATH/components
|
||||
cp -rf $NanoKVM_PATH/support/sg2002/additional/vision $MAIXCDK_PATH/components
|
||||
cp -f $NanoKVM_PATH/support/sg2002/additional/peripheral/port/maixcam/maix_i2c.cpp $MAIXCDK_PATH/components/peripheral/port/maixcam/maix_i2c.cpp
|
||||
cp -f $NanoKVM_PATH/support/sg2002/additional/sophgo-middleware/v2/sample/common/sample_common_sensor.c $MAIXCDK_PATH/components/3rd_party/sophgo-middleware/sophgo-middleware/v2/sample/common/
|
||||
|
||||
echo -e "\e[36m[build] \e[32mupdate_lib ok!\e[0m"
|
||||
exit 1
|
||||
;;
|
||||
kvm_system)
|
||||
Project_Name="kvm_system"
|
||||
Project_PATH="$NanoKVM_PATH/support/sg2002/kvm_system"
|
||||
echo -e "\e[36m[build] \e[32mProject: kvm_system \e[0m"
|
||||
;;
|
||||
kvm_vision)
|
||||
Project_Name="kvm_vision"
|
||||
Project_PATH="$NanoKVM_PATH/support/sg2002/kvm_vision_test"
|
||||
echo -e "\e[36m[build] \e[32mProject: kvm_system \e[0m"
|
||||
;;
|
||||
*)
|
||||
echo -e "\e[36m[build] \e[31mInvalid project name \e[0m"
|
||||
echo -e "\e[36m[build] \e[32mPlease select a project:\e[0m"
|
||||
echo -e "0: kvm_system"
|
||||
echo -e "1: kvm_vision"
|
||||
while true; do
|
||||
read -p "Please enter 0 or 1 : " input
|
||||
if [[ "$input" -eq 0 ]]
|
||||
then
|
||||
Project_Name="kvm_system"
|
||||
Project_PATH="$NanoKVM_PATH/support/sg2002/kvm_system"
|
||||
break
|
||||
elif [[ "$input" -eq 1 ]]
|
||||
then
|
||||
Project_Name="kvm_vision"
|
||||
Project_PATH="$NanoKVM_PATH/support/sg2002/kvm_vision_test"
|
||||
break
|
||||
else
|
||||
echo -e "\e[36m[build] \e[31mInvalid Index \e[0m"
|
||||
fi
|
||||
done
|
||||
esac
|
||||
|
||||
if [ -z "$2" ]
|
||||
then
|
||||
Project_Action="build"
|
||||
else
|
||||
Project_Action=$2
|
||||
fi
|
||||
|
||||
if [ ! "$Project_Name" = "None" ]
|
||||
then
|
||||
case "$Project_Action" in
|
||||
build)
|
||||
# echo -e "\e[36m[build] \e[32mStart using MaixCDK to compile kvm_system...\e[0m"
|
||||
|
||||
JSON_PATH="$Project_PATH/build/config/project_vars.json"
|
||||
Platform_Char="\"PLATFORM\": \"maixcam\""
|
||||
|
||||
if [ ! -e "$MAIXCDK_PATH/components/kvm" ]
|
||||
then
|
||||
echo -e "\e[36m[build] \e[32mDetected that the library has not been modified; first, move the library\e[0m"
|
||||
|
||||
mkdir $NanoKVM_PATH/support/sg2002/additional/original
|
||||
mv -f $MAIXCDK_PATH/components/vision $NanoKVM_PATH/support/sg2002/additional/original
|
||||
# mv $MAIXCDK_PATH/components/maixcam_lib $NanoKVM_PATH/support/sg2002/additional/original
|
||||
mv -f $MAIXCDK_PATH/components/peripheral/port/maixcam/maix_i2c.cpp $NanoKVM_PATH/support/sg2002/additional/original
|
||||
mv -f $MAIXCDK_PATH/components/3rd_party/sophgo-middleware/sophgo-middleware/v2/sample/common/sample_common_sensor.c $NanoKVM_PATH/support/sg2002/additional/original
|
||||
|
||||
cp -rf $NanoKVM_PATH/support/sg2002/additional/kvm $MAIXCDK_PATH/components
|
||||
cp -rf $NanoKVM_PATH/support/sg2002/additional/kvm_mmf $MAIXCDK_PATH/components
|
||||
cp -rf $NanoKVM_PATH/support/sg2002/additional/vision $MAIXCDK_PATH/components
|
||||
cp -f $NanoKVM_PATH/support/sg2002/additional/peripheral/port/maixcam/maix_i2c.cpp $MAIXCDK_PATH/components/peripheral/port/maixcam/maix_i2c.cpp
|
||||
cp -f $NanoKVM_PATH/support/sg2002/additional/sophgo-middleware/v2/sample/common/sample_common_sensor.c $MAIXCDK_PATH/components/3rd_party/sophgo-middleware/sophgo-middleware/v2/sample/common/
|
||||
|
||||
echo -e "\e[36m[build] \e[32mMove the library completed!\e[0m"
|
||||
fi
|
||||
|
||||
cd $Project_PATH
|
||||
if [ -f $JSON_PATH ]
|
||||
then
|
||||
if grep -q $Platform_Char $JSON_PATH; then
|
||||
maixcdk_build
|
||||
else
|
||||
maixcdk_clean
|
||||
maixcdk_build "from_zero"
|
||||
fi
|
||||
else
|
||||
maixcdk_build "from_zero"
|
||||
fi
|
||||
|
||||
RES=$(chack_build)
|
||||
|
||||
if [ "$RES" = "SUCCESS" ]
|
||||
then
|
||||
echo -e "\e[36m[build] \e[32mBuild completed! Use \e[34m./build $Project_Name add_to_kvmapp\e[32m to add it to kvmapp.\e[0m"
|
||||
else
|
||||
echo -e "\e[36m[build] \e[31mBuild Error!\e[0m"
|
||||
fi
|
||||
;;
|
||||
clean)
|
||||
cd $Project_PATH
|
||||
maixcdk_clean
|
||||
echo -e "\e[36m[build] \e[32m$Project_Name cleanup completed!\e[0m"
|
||||
;;
|
||||
add_to_kvmapp)
|
||||
if [ "$Project_Name" = "kvm_system" ]
|
||||
then
|
||||
SOURCE_PATH="$Project_PATH/dist/kvm_system_release/kvm_system"
|
||||
TARGET_PATH="$KVMAPP_PATH/kvm_system"
|
||||
chmod +x $SOURCE_PATH
|
||||
else
|
||||
SOURCE_PATH="$Project_PATH/dist/kvm_vision_test_release/dl_lib"
|
||||
TARGET_PATH="$KVMAPP_PATH/server/"
|
||||
fi
|
||||
if [ ! -d $TARGET_PATH ]
|
||||
then
|
||||
mkdir -p $TARGET_PATH
|
||||
fi
|
||||
if [ -e $SOURCE_PATH ]
|
||||
then
|
||||
cp -rf $SOURCE_PATH $TARGET_PATH
|
||||
echo -e "\e[36m[build] \e[32mkvm_system has been moved to kvmapp.\e[0m"
|
||||
else
|
||||
echo -e "\e[36m[build] \e[31mThe source file does not exist. Please compile using \e[34m./build kvm_system \e[31mfirst.\e[0m"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo -e "\e[36m[build] \e[31mInvalid Action: \e[34m$2 \e[0m"
|
||||
echo -e "\e[36m[build] \e[31mUsage: $0 [Project Name]{kvm_system|kvm_vision} [Action]{-/build|clean|add_to_kvmapp} e.g. \e[34m./build kvm_system clean \e[0m"
|
||||
exit 1
|
||||
esac
|
||||
else
|
||||
echo -e "\e[36m[build] \e[31mInvalid Name \e[0m"
|
||||
exit 1
|
||||
fi
|
||||
0
support/sg2002/kvm_system/main/Kconfig
Normal file
0
support/sg2002/kvm_system/main/Kconfig
Normal file
@@ -36,6 +36,8 @@
|
||||
#define WIFI_CONNECTION_DELAY 5000
|
||||
#define OLED_SLEEP_DELAY_MIN 10
|
||||
#define OLED_SLEEP_DELAY_DEFAULT 30
|
||||
#define KVM_WD_COUNT_MAX 10
|
||||
#define RM_Watchdog_times 60
|
||||
|
||||
typedef struct {
|
||||
int8_t page = 0;
|
||||
@@ -67,6 +69,7 @@ typedef struct {
|
||||
char wifi_ap_pass[9] = {0};
|
||||
uint8_t oled_sleep_state = 0; // 0:wakeup; 1:sleep;
|
||||
int8_t reconvery_update = 0; // 0:Undetected; 1:Needs Update; 2:Update finish; -1:not need to update
|
||||
uint8_t ping_allow = 1;
|
||||
} kvm_sys_state_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -95,6 +98,7 @@ typedef struct {
|
||||
uint8_t oled_sleep_param = 0;
|
||||
uint8_t oled_sleep_state = 0; // 0:wakeup; 1:sleep;
|
||||
uint64_t oled_sleep_start = 0;
|
||||
uint64_t ue_patch_state = 0;
|
||||
} kvm_oled_state_t;
|
||||
|
||||
#endif // CONFIG_H_
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user