Compare commits

..

81 Commits
2.2.1 ... 2.2.3

Author SHA1 Message Date
wj-xiao
6ef83cb22f feat: support setting H.264 GOP
perf: merge H.264 SPS and PPS into I-frame
perf: refactor MJPEG frame detection
perf: update log timestamp to millisecond precision
perf: update script `update-nanokvm.py`
chore: bump Go to 1.23
chore: bump golang.org/x/net to v0.37.0
2025-03-20 18:31:09 +08:00
BuGu
1a3fd15fdf Merge branch 'main' of https://github.com/sipeed/NanoKVM 2025-03-20 18:02:57 +08:00
wenjie
3e5a46570c Merge pull request #392 from jobsidi/patch-1
Update README.md
2025-03-20 03:00:20 -07:00
wenjie
2c4fbf7179 Merge pull request #382 from mekaris/feature/serial_greater_support
Add more serial features
2025-03-20 02:59:30 -07:00
wenjie
5409fb783c Merge pull request #380 from reishoku/reishoku.202503.improve_update-nanokvm-python3
Improve and optimize update-nanokvm.py
2025-03-20 02:58:53 -07:00
BuGu
4cec610aba Fix the issue of build script copying errors 2025-03-20 17:43:33 +08:00
BuGu
57d88d7889 New products default to disabling mDNS 2025-03-20 17:42:53 +08:00
BuGu
7f305ae3cf Fix deadlock issue when HDMI res error occurs 2025-03-20 17:28:05 +08:00
BuGu
eb4ac33fcd Restore frame difference detection function
Optimize the detection algorithm to reduce the detection time to 3-4ms per frame, using set_frame_detact (xx); Dynamically set the number of detection interval frames
2025-03-20 16:43:42 +08:00
BuGu
7a3c6c1690 Add production testing version restrictions 2025-03-20 16:40:33 +08:00
jobsidi
ca63609545 Update README.md
fixed link
2025-03-17 18:56:53 +01:00
mekaris
bcaa8896ee Add more serial features 2025-03-15 00:14:39 +01:00
KOSHIKAWA Kenichi
13b19e3889 Improve update-nanokvm script 2025-03-15 05:44:05 +09:00
BuGu
99d1fc0a64 Modify the method for retrieving I-frames in H.264.
Use a single data packet to replace the SPS + PPS + I three packets retrieval.
Note: This modification may require synchronization support from the server.
2025-03-12 17:34:35 +08:00
BuGu
86b7d676dd Restore S30eth
The new script has rendered the static address unavailable; reverting to the previous version.
2025-03-12 17:18:13 +08:00
BuGu
1adf6edffc Merge pull request #355 from BlwAvg/init-cleanup
Minor Init.d Cleanup
2025-03-12 16:24:28 +08:00
wj-xiao
a16af99648 update CHANGELOG.md 2025-03-11 11:52:55 +08:00
wj-xiao
58d5ab2d37 update libkvm.so 2025-03-11 10:59:48 +08:00
wj-xiao
9aebabfd59 feat: support reboot
perf: update menu bar draggable bounds
perf: unify logout logic and styles in settings
2025-03-10 19:04:18 +08:00
BuGu
21b119294c Update .gitignore 2025-03-10 18:17:28 +08:00
BuGu
e3a79daf89 Merge branch 'main' of https://github.com/sipeed/NanoKVM 2025-03-10 18:16:06 +08:00
BuGu
4813e8bd50 fix some important bug and add new features.
+ Add watchdog.
+ Add unsupported resolution output prompt.
+ Refactor automatic resolution detection function.
+ Add support for UE chip.
+ Fix the issue where the UE chip server cannot start.
2025-03-10 18:14:04 +08:00
BuGu
93c0f3d353 Add software watchdog. 2025-03-10 18:09:55 +08:00
BuGu
be5a9f7515 Add update_lib function
you can use `./build update_lib` to add necessary components to MaixCDK.
2025-03-10 18:08:56 +08:00
BuGu
36a1eb8eb8 Add startup scripts related to mDNS service. 2025-03-10 18:07:06 +08:00
BuGu
20b452f3c0 Add get_hdmi_version function 2025-03-10 18:06:05 +08:00
wj-xiao
b062569aab feat: support enable/disable mDNS
fix: remove redundant STUN server
perf:  support disable STUN server (for local networks)
perf:  update GOMEMLIMIT from 50 to 75
perf:  add `-b` parameter to `ether-wake`
chore: bump vite to v6.2.1
2025-03-07 17:22:57 +08:00
wenjie
c91ce5ab62 Merge pull request #367 from protonchang/zhtw-translation
Update zhTW Translation
2025-03-06 18:23:16 -08:00
wj-xiao
602615e489 refactor menu bar 2025-03-06 17:44:28 +08:00
protonchang
0e11855011 Update zhTW Translation 2025-03-06 16:00:06 +08:00
BuGu
c5c768b143 Refine chip version 2025-03-05 16:40:57 +08:00
wenjie
5c0f098069 Merge pull request #361 from Stoufiler/main
Fix missing translation according to en.ts
2025-03-05 14:11:18 +08:00
wenjie
8bd7e02ec5 Merge pull request #353 from polyzium/ru-translaton-fix
Add missing keys to the Russian locale file
2025-03-05 14:10:54 +08:00
wenjie
9233bd7c79 Merge pull request #351 from forumi0721/20250227/Draggable
Added Draggable Functionality for Main Menu Positioning
2025-03-05 14:10:39 +08:00
wenjie
7d2664acb5 Merge branch 'main' into 20250227/Draggable 2025-03-05 14:10:31 +08:00
wenjie
8c09cd7c44 Merge pull request #348 from forumi0721/20250223/ImageRefresh
Add Refresh Button for Image List
2025-03-05 14:05:51 +08:00
wenjie
aef8a7a851 Merge branch 'main' into 20250223/ImageRefresh 2025-03-05 14:04:53 +08:00
BuGu
8ed6e2eca3 Merge pull request #364 from scpcom/kvm_system-get_ping_allow_state-return-value
support: kvm_system: add return value to get_ping_allow_state
2025-03-05 10:49:02 +08:00
BuGu
a7b5091024 Merge pull request #363 from scpcom/kvm_system-fix-ip_address-empty-checks
support: kvm_system: fix 'const char' and 'std::nullptr_t' comparsion
2025-03-05 10:46:31 +08:00
BuGu
cd6da8b588 Update README 2025-03-04 16:43:08 +08:00
BuGu
2d755fdb98 Merge branch 'main' of https://github.com/sipeed/NanoKVM 2025-03-04 16:35:05 +08:00
BuGu
f231df4f65 Add the necessary dependencies for compiling the vision module. 2025-03-04 16:34:59 +08:00
BuGu
95c8edcd3d Modify the directory structure in preparation for NanoKVM-Pro 2025-03-04 16:34:50 +08:00
BuGu
d94eba0ab1 Add compilation scripts and guidelines to simplify the compilation process 2025-03-04 16:31:15 +08:00
wenjie
b48729c594 Merge pull request #345 from CaffeeLake/readme_ja
Update Japanese Docs.
2025-03-04 14:20:28 +08:00
wenjie
b9ba107587 Merge pull request #338 from S33G/tooltips
Add tooltips to menu
2025-03-04 14:19:34 +08:00
scpcom
c2d3224552 support: kvm_system: add return value to get_ping_allow_state and ion_free_space 2025-03-04 00:00:51 +01:00
scpcom
592939895b support: kvm_system: fix 'const char' and 'std::nullptr_t' comparsion 2025-03-03 23:54:11 +01:00
BuGu
8d8c57bc48 Re-add kvm_system, supplement compilation instructions and the compilation script. 2025-03-03 16:44:37 +08:00
Stoufiler
8330b1b1fe Fix missing tranlastion according to en.ts 2025-03-02 17:42:16 +01:00
BlwAvg
9fdcb428c0 No more spacing issues! Should be consistent 2025-02-27 13:12:57 -07:00
BlwAvg
cee9bbb079 i dont know how to space things still 2025-02-27 13:11:04 -07:00
BlwAvg
fcc09e2ef0 spacing cont... 2025-02-27 13:07:44 -07:00
BlwAvg
5d96a8060f spacing 2025-02-27 13:02:14 -07:00
BlwAvg
023fb4b68b S03usbdev: Removed erroneous cat command
S30eth: Refactored to remove subshell

S30eth: Fixed spelling error for /etc/resolv.conf (not /etc/resolve.conf)

S50shd: Optimized the sshd script

S95nanokvm: Checked for IPTables rules before implementing to prevent duplicates.
2025-02-27 12:43:36 -07:00
BuGu
594c205a29 Update README 2025-02-27 18:58:54 +08:00
BuGu
fc436e2743 Add the kvmapp installation package. 2025-02-27 18:46:07 +08:00
BuGu
125eddffe1 Modify the file directory to reserve a location for the Pro project 2025-02-27 18:44:25 +08:00
polyzium
aad3d718e4 Add missing keys to the Russian locale file 2025-02-27 13:27:05 +03:00
forumi0721
e3575e629c Added Draggable Functionality for Main Menu Positioning 2025-02-27 00:11:33 +09:00
BuGu
7bd7b77fa9 Add a maximum limit for vi.
In rare cases, the LT6911 I2C may return incorrect resolution information, which is passed to vi and causes the system to freeze.
2025-02-25 18:21:02 +08:00
BuGu
ba620db901 Add lock to kvmv_read_img
In some cases, it can enhance the stability of the vision subsystem.
2025-02-25 18:09:49 +08:00
BuGu
3259379345 Configurable VENC automatic recycling feature 2025-02-25 18:03:32 +08:00
BuGu
08eb89dc5c Merge pull request #340 from scpcom/optimize-vb-pools
Optimize vb pools
2025-02-25 16:49:10 +08:00
BuGu
1376929897 Merge pull request #341 from scpcom/cleanup-middleware-linking
vison: kvm_mmf: remove unused middleware libs
2025-02-25 16:48:23 +08:00
BuGu
614ef4031c Stop outputting the WiFi configuration QR code.
The QR code used for WiFi configuration is only output to the file during debugging; it was forgotten to be deleted.
2025-02-24 16:26:07 +08:00
BuGu
d9a19e132a Add stop ping mode
Before starting the program, execute touch /etc/kvm/stop_ping to stop pinging the gateway for network detection (test functionality).
2025-02-24 16:20:14 +08:00
BuGu
7795191a46 Query the status information of the network section using the Linux system API.
After testing, calling the system API still cannot effectively detect the insertion/removal of the ETH cable; modifications were made only to the status retrieval part.
2025-02-24 15:53:20 +08:00
BuGu
5e3c4d98f2 Merge pull request #307 from TuningYourCode/rework-nic-state-code
Reworked the system state code
2025-02-24 10:13:45 +08:00
BuGu
de28273def Merge branch 'main' into rework-nic-state-code 2025-02-24 10:13:08 +08:00
forumi0721
148fb4e37b Fix build error 2025-02-23 15:51:07 +09:00
forumi0721
a920fcc58a Add image list refresh button 2025-02-23 15:43:13 +09:00
forumi0721
5021ab8d2f Update korean translations 2025-02-23 13:52:44 +09:00
seeg
0ae97e6dd7 Remove unused code 2025-02-23 01:04:05 +00:00
CaffeeLake
8c2e744d42 Update Japanese Docs. 2025-02-23 09:31:51 +09:00
seeg
1ea9dc0561 Merge branch 'main' into tooltips 2025-02-21 21:40:35 +00:00
scpcom
2ec5393bd0 vison: kvm_mmf: remove unused middleware libs 2025-02-20 23:02:02 +01:00
scpcom
01c837f6ac vison: kvm: cleanup venc on type change 2025-02-20 22:59:53 +01:00
scpcom
899aa48875 vison: kvm_mmf: reduce vi pool size 2025-02-20 22:59:40 +01:00
s33g
29dd37852d Add tooltips to menu 2025-02-20 18:56:27 +00:00
TuningYourCode
59d5944398 Reworked the system state code 2025-02-09 20:06:05 +01:00
159 changed files with 6544 additions and 1593 deletions

14
.gitignore vendored
View File

@@ -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

View File

@@ -1,3 +1,20 @@
## 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

View File

@@ -20,6 +20,31 @@ English | [中文](./README_ZH.md) | [日本語](./README_JA.md)
## 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).
@@ -82,7 +107,7 @@ The NanoKVM image is built on LicheeRV Nano SDK and MaixCDK, and is compatible w
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

View File

@@ -1,11 +1,13 @@
# NanoKVM
NanoKVM
======
<div align="center">
![](https://wiki.sipeed.com/hardware/assets/NanoKVM/introduce/NanoKVM_3.png)
<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は完全版で、洗練されたケースと完全なアクセサリーを備え、起動時にすぐに使用できるシステムミラーカードが内蔵されており、個人ユーザーに推奨されます。
![](https://wiki.sipeed.com/hardware/zh/kvm/assets/NanoKVM/1_intro/NanoKVM_1.jpg)
![NanoKVM 分解画像](https://wiki.sipeed.com/hardware/zh/kvm/assets/NanoKVM/1_intro/NanoKVM_1.jpg)
## 技術仕様
| 製品 | 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 |
![](https://wiki.sipeed.com/hardware/zh/kvm/assets/NanoKVM/1_intro/NanoKVM_2.jpg)
![NanoKVM PCB ピン配置](https://wiki.sipeed.com/hardware/zh/kvm/assets/NanoKVM/1_intro/NanoKVM_2.jpg)
## ハードウェアプラットフォーム 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)

View File

@@ -6,8 +6,9 @@ NanoKVM
![](https://wiki.sipeed.com/hardware/assets/NanoKVM/introduce/NanoKVM_3.png)
<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 期间发出。
## 简介

91
kvmapp/jpg_stream/S95nanokvm Executable file
View 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
View 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

View File

42
kvmapp/system/init.d/S01fs Executable file
View 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
View 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/ > 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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

View 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

Binary file not shown.

96
kvmapp/system/update-nanokvm.py Executable file
View 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()

View File

@@ -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.

View File

@@ -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` を実行してサービスを再起動します。

View File

@@ -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
```

View File

@@ -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...")
}

View File

@@ -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)
}
}

View File

@@ -21,7 +21,7 @@ var defaultConfig = &Config{
},
Stun: "stun.l.google.com:19302",
Turn: Turn{
TurnAddr: "turn.cloudflare.com:3478",
TurnAddr: "",
TurnUser: "",
TurnCred: "",
},

Binary file not shown.

Binary file not shown.

View File

@@ -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
)

View File

@@ -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=

View File

@@ -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_

View File

@@ -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)

View File

@@ -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"`
}

View File

@@ -83,3 +83,7 @@ type GetOLEDRsp struct {
type GetSSHStateRsp struct {
Enabled bool `json:"enabled"`
}
type GetMdnsStateRsp struct {
Enabled bool `json:"enabled"`
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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{

View File

@@ -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()

View File

@@ -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,

View File

@@ -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()

View File

@@ -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))
}

View File

@@ -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.Debug("frame detect started")
}()
vision.SetFrameDetect(0)
time.Sleep(duration)
vision.SetFrameDetect(FrameDetectInterval)
rsp.OkRsp(c)
log.Debug("frame detect stopped")
}
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
}

View File

@@ -11,8 +11,8 @@ 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)
)
@@ -23,13 +23,13 @@ func Connect(c *gin.Context) {
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)
@@ -63,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))

View File

@@ -18,7 +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-16-59-3649fe.img": "v1.4.0",
"2025-02-17-19-08-3649fe.img": "v1.4.0",
}
func (s *Service) GetInfo(c *gin.Context) {
@@ -53,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
View 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", "")
}

View File

@@ -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
}

View 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")
}

View File

@@ -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
View File

@@ -0,0 +1,5 @@
# NanoKVM support 说明
`/support`包含NanoKVM辅助性的功能,如图像子系统、系统状态监控、系统更新、屏幕按键驱动和少部分的系统功能
当前 NanoKVM 根据主控芯片的不同分为两个版本:SG2002(包含NanoKVM-Lite/Full/PCIe)和H618(包括NanoKVM-Pro),不同的芯片有差异较大的工程和编译环境,为做区分,将它们分别存放在`/support/sg2002`和`/support/h618`

26
support/sg2002/README.md Normal file
View 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`.

View 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 的编译

View File

@@ -19,6 +19,7 @@ extern "C" {
#include <stdint.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>
// #include "kvm_mmf.hpp"
@@ -31,7 +32,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 = 0);
void set_venc_auto_recyc(uint8_t _enable);
/**********************************************************************************
* @name kvmv_read_img
* @author Sipeed BuGu
@@ -45,22 +53,25 @@ void kvmv_init(uint8_t _debug_info_en = 0);
* @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
-2: VENC Errorl
-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);

View File

@@ -19,6 +19,10 @@
#define KVMV_MAX_TRY_NUM 2
#define vi_min_width 32
#define vi_min_height 3
#define vi_max_width 1920
#define vi_max_height 1080
#define Farame_sample_size 76800 // 320*240
#define default_vi_width 1920
#define default_vi_height 1080
@@ -30,16 +34,21 @@
#define default_h264_gop 30
#define kvmv_data_buffer_size 4
#define Try_rounds_HDMI_err_res 5
#define vi_width_path "/kvmapp/kvm/width"
#define vi_height_path "/kvmapp/kvm/height"
#define hdmi_mode_path "/etc/kvm/hdmi_mode"
#define hdmi_state_path "/proc/lt_int"
#define watchdog_mode_path "/etc/kvm/watchdog"
#define watchdog_file "/tmp/nanokvm_wd"
#define LT6911_ADDR 0x2B
#define LT6911_READ 0xFF
#define LT6911_WRITE 0x00
pthread_mutex_t vi_mutex;
static char NanoKVM_edit[] = {
0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x41,0x0C,0x33,0xC2,0x66,0xBA,0x00,0x00,
0x2B,0x1F,0x01,0x04,0xA5,0x50,0x22,0x78,0x3B,0xCC,0xE5,0xAB,0x51,0x48,0xA6,0x26,
@@ -65,7 +74,7 @@ typedef struct {
uint16_t qlty;
uint8_t cam_state = 0;
uint8_t stream_stop;
uint8_t frame_detact;
uint8_t frame_detact = 0;
uint8_t display;
uint8_t reinit_flag = 1;
uint8_t reopen_cam_flag = 0;
@@ -78,7 +87,11 @@ typedef struct {
uint8_t hdmi_stop_flag = 0;
uint8_t hdmi_reading_flag = 0;
uint8_t hdmi_mode = 0;
uint8_t hdmi_res_type = 0;
uint8_t hdmi_res_err = 0;
uint8_t hdmi_try_rounds = 0;
uint8_t vi_detect_state = 0;
uint8_t venc_auto_recyc = 0;
} kvmv_cfg_t;
typedef struct {
@@ -100,8 +113,6 @@ camera::Camera *cam = new camera::Camera(default_vpss_width, default_vpss_height
kvmv_cfg_t kvmv_cfg;
kvmv_data_t kvmv_data_buffer[kvmv_data_buffer_size];
kvmv_data_t kvmv_SPS_buffer = {0};
kvmv_data_t kvmv_PPS_buffer = {0};
uint8_t kvmv_data_buffer_index = 0;
@@ -154,6 +165,44 @@ uint16_t hdmi_res_list[][2] = {
{800, 600},
};
uint16_t hdmi_unsupported_res_list[][2] = {
{1680, 1050},
{1440, 1050},
{1400, 1050},
{1368, 768},
{1366, 768},
{720, 576},
};
/* return 0 : normal res;
/* return 1 : new res;
* return 2 : unsupport res;
* return 3 : unknow res;
*/
uint8_t check_res(uint16_t _width, uint16_t _height)
{
uint8_t i;
uint8_t ret;
for(i = 0; i < sizeof(hdmi_res_list)/4; i++){
if(_width == hdmi_res_list[i][0] && _height == hdmi_res_list[i][1]) return NORMAL_RES;
}
for(i = 0; i < sizeof(hdmi_unsupported_res_list)/4; i++){
if(_width == hdmi_unsupported_res_list[i][0] && _height == hdmi_unsupported_res_list[i][1]) return UNSUPPORT_RES;
}
return UNKNOWN_RES;
}
void write_res_to_file(uint16_t _width, uint16_t _height)
{
char Cmd[100]={0};
sprintf(Cmd, "echo %d > %s", _width, vi_width_path);
system(Cmd);
sprintf(Cmd, "echo %d > %s", _height, vi_height_path);
system(Cmd);
system("sync");
}
/* return 0 : VI not init;
* return 1 : HDMI and CSI status are normal;
* return 2 : HDMI abnormal;
@@ -215,6 +264,19 @@ uint8_t get_vi_state()
return ret;
}
int set_hdmi_mode(uint8_t _hdmi_mode)
{
if(_hdmi_mode >= 0 && _hdmi_mode <= 2){
char Cmd[100]={0};
sprintf(Cmd, "echo %d > %s", _hdmi_mode, hdmi_mode_path);
system(Cmd);
return 1;
} else {
debug("[kvmv] Incorrect HDMI mode.\n");
return 0;
}
}
int get_hdmi_mode(void)
{
if(access(hdmi_mode_path, F_OK) == 0){
@@ -247,6 +309,26 @@ int get_hdmi_mode(void)
return 0;
}
uint8_t watchdog_sf_is_open()
{
if(access(watchdog_mode_path, F_OK) == 0) return 1;
else return 0;
}
int vision_update_watchdog()
{
FILE *file;
file = fopen(watchdog_file, "w");
if (file == NULL) {
debug("[kvmv] watchdog open error\n");
return -1;
}
// fprintf(file, "%s", 'v');
fclose(file);
return 1;
}
int get_manual_resolution(void)
{
uint8_t RW_Data[35];
@@ -294,6 +376,19 @@ int get_manual_resolution(void)
sprintf(Cmd, "echo %d > %s", vi_min_height, vi_height_path);
system(Cmd);
}
// res max limit
if(tmp_width > vi_max_width){
tmp_width = vi_max_width;
char Cmd[100]={0};
sprintf(Cmd, "echo %d > %s", vi_max_width, vi_width_path);
system(Cmd);
}
if(tmp_height > vi_max_height){
tmp_height = vi_max_height;
char Cmd[100]={0};
sprintf(Cmd, "echo %d > %s", vi_max_height, vi_height_path);
system(Cmd);
}
// res change ?
if(kvmv_cfg.vi_width != tmp_width){
@@ -309,6 +404,91 @@ int get_manual_resolution(void)
return res;
}
uint8_t auto_try_res()
{
char Cmd[100]={0};
uint8_t err_code;
uint8_t auto_trying_times = 0;
for (auto_trying_times = 0; auto_trying_times < sizeof(hdmi_res_list)/4; auto_trying_times++){
err_code = get_vi_state();
switch(err_code){
case 0:
// shouldn't be possible to run here
cam->restart(default_vpss_width, default_vpss_height, image::FMT_YVU420SP);
printf("[kvmv] VI not init\n");
break;
case 1:
printf("[kvmv] VI subsystem is normal\n");
return 1;
break;
case 2:
// HDMI not detected or resolution not supported; interval checks will continue
printf("[kvmv] Cannot obtain HDMI input\n");
auto_trying_times--;
break;
case 3: // width too small
case 4: // width too large
case 5: // height too small
case 6: // height too large
// CSI abnormal due to resolution error
// The test list is short; sequential testing can be performed
printf("[kvmv] Trying %d * %d res ..\n", hdmi_res_list[auto_trying_times][0], hdmi_res_list[auto_trying_times][1]);
sprintf(Cmd, "echo %d > %s", hdmi_res_list[auto_trying_times][0], vi_width_path);
system(Cmd);
sprintf(Cmd, "echo %d > %s", hdmi_res_list[auto_trying_times][1], vi_height_path);
system(Cmd);
kvmv_cfg.vi_width = hdmi_res_list[auto_trying_times][0];
kvmv_cfg.vi_height = hdmi_res_list[auto_trying_times][1];
printf("[kvmv] restart cam...\n");
cam->restart(default_vpss_width, default_vpss_height, image::FMT_YVU420SP);
time::sleep_ms(50);
break;
case 7: // Unknown reason
printf("[kvmv] CSI abnormal: Unknown reason\n");
break;
}
}
if (get_vi_state() == 1) return 1;
if (get_vi_state() == 2) return 2;
else return 0;
}
/* return :
* 0 : error
* 1 : out of mem
* 2 : normal
*/
uint8_t chack_ion()
{
// cat /sys/kernel/debug/ion/cvi_carveout_heap_dump/summary | grep "usage rate:" | awk -F '[:%]' '{print $2}'
uint8_t RW_Data[10];
uint8_t ATOI_Data[3] = {0};
uint8_t ion_usage_rate;
char Cmd[150]={0};
// sprintf( Cmd, "cat /sys/kernel/debug/ion/cvi_carveout_heap_dump/summary | grep \"usage rate:\" | awk -F '[:%]' '{print $2}'");
sprintf( Cmd, "cat /sys/kernel/debug/ion/cvi_carveout_heap_dump/summary | grep \"usage rate:\" | awk '{print $2}'");
FILE* fp = popen( Cmd, "r" );
if ( NULL == fp )
{
pclose(fp);
return 0;
}
fgets((char*)RW_Data, 8, fp);
pclose(fp);
RW_Data[8] = 0;
if (RW_Data[6] == '&') return 1;
else {
ATOI_Data[0] = RW_Data[5];
ATOI_Data[1] = RW_Data[6];
}
ion_usage_rate = atoi((char*)ATOI_Data);
if(ion_usage_rate >= 95) return 1;
else return 2;
}
void lt6911_enable()
{
uint8_t buf[2];
@@ -320,7 +500,7 @@ void lt6911_enable()
buf[1] = 0x01;
LT6911_i2c.writeto(LT6911_ADDR, buf, 2);
if(kvmv_cfg.hdmi_version == 1){
if(kvmv_cfg.hdmi_version != 0){
// disable watchdog
buf[0] = 0x10;
buf[1] = 0x00;
@@ -494,15 +674,13 @@ void lt6911_get_hdmi_clk()
uint8_t lt6911_get_csi_res(uint16_t *p_width, uint16_t *p_height)
{
uint8_t ret = 0;
uint8_t buf[2];
uint8_t revbuf[4];
uint8_t res_type;
static uint16_t old_Vactive;
static uint16_t old_Hactive;
uint16_t Vactive;
uint16_t Hactive;
char Cmd[100]={0};
if(kvmv_cfg.hdmi_version == 0){
// LT6911C
@@ -558,31 +736,41 @@ uint8_t lt6911_get_csi_res(uint16_t *p_width, uint16_t *p_height)
Vactive = (revbuf[0] << 8)|revbuf[1];
Hactive = (revbuf[2] << 8)|revbuf[3];
} else {
return ret;
return UNKNOWN_RES;
}
if(old_Hactive != Hactive || old_Vactive != Vactive){
old_Hactive = Hactive;
old_Vactive = Vactive;
// p_kvm_cfg->width = Hactive;
// p_kvm_cfg->height = Vactive;
*p_width = Hactive;
*p_height = Vactive;
ret = 1;
}
res_type = check_res(Hactive, Vactive);
if(res_type == NORMAL_RES)
{
if(old_Hactive != Hactive || old_Vactive != Vactive){
old_Hactive = Hactive;
old_Vactive = Vactive;
// p_kvm_cfg->width = Hactive;
// p_kvm_cfg->height = Vactive;
*p_width = Hactive;
*p_height = Vactive;
res_type = NEW_RES;
}
}
debug("[hdmi]CSI res: %d * %d; new res = %d\n", Hactive, Vactive, ret);
// setenv("KVM_CSI_HEIGHT", to_string(Hactive).c_str(), 1);
// setenv("KVM_CSI_WIDTH", to_string(Vactive).c_str(), 1);
switch (res_type)
{
case NORMAL_RES:
printf("[hdmi] get res : %d * %d\n", Hactive, Vactive);
break;
case NEW_RES:
printf("[hdmi] get new res : %d * %d\n", Hactive, Vactive);
write_res_to_file(Hactive, Vactive);
break;
case UNSUPPORT_RES:
printf("[hdmi] get unsupport res : %d * %d\n", Hactive, Vactive);
break;
case UNKNOWN_RES:
printf("[hdmi] get unknown res : %d * %d\n", Hactive, Vactive);
break;
}
sprintf(Cmd, "echo %d > %s", Hactive, vi_width_path);
system(Cmd);
sprintf(Cmd, "echo %d > %s", Vactive, vi_height_path);
system(Cmd);
system("sync");
return ret;
return res_type;
}
void lt6911_write_reg(uint8_t reg, uint8_t val)
@@ -817,6 +1005,57 @@ void lt6911_read_fw(void)
// ==============================================================
void* watchdog_sf_feed(void * arg)
{
while(true)
{
if(kvmv_cfg.try_exit_thread == 1)
break;
time::sleep_ms(500);
if (watchdog_sf_is_open()){
if (chack_ion() == 1){
debug("[kvmv] Ion memory is full reboot now!\n");
system("reboot");
}
debug("[kvmv] watchdog_sf_feed now!\n");
vision_update_watchdog();
}
}
}
void get_hdmi_version()
{
FILE *fp;
uint8_t RW_Data[2];
system("/kvmapp/system/init.d/S15kvmhwd get_hdmi_version");
if(access("/etc/kvm/hdmi_version", F_OK) == 0){
fp = fopen("/etc/kvm/hdmi_version", "r");
fread(RW_Data, sizeof(char), 2, fp);
fclose(fp);
if(RW_Data[0] == 'u'){
// 6911uxc
if(RW_Data[1] == 'e'){
kvmv_cfg.hdmi_version = 2;
debug("[hdmi]HDMI-UE exist!\n");
set_hdmi_mode(1);
} else if(RW_Data[1] == 'x') {
kvmv_cfg.hdmi_version = 1;
debug("[hdmi]HDMI-UX exist!\n");
} else {
kvmv_cfg.hdmi_version = 1;
debug("[hdmi]Incomplete version number, set to 'ux'\n");
}
} else {
// 6911c
kvmv_cfg.hdmi_version = 0;
debug("[hdmi]HDMI-C exist!\n");
}
RW_Data[0] = 0;
} else {
kvmv_cfg.hdmi_version = 0;
}
}
void* vi_subsystem_detection(void * arg)
{
uint64_t __attribute__((unused)) int_time;
@@ -828,47 +1067,25 @@ void* vi_subsystem_detection(void * arg)
uint8_t rising_times = 0;
uint8_t falling_times = 0;
uint8_t cam_need_restart = 0;
uint8_t auto_change_mode = 0;
kvmv_cfg.thread_is_running = 1;
if(access("/proc/lt_int", F_OK) != 0){
time::sleep_ms(10);
debug("[hdmi]/proc/lt_int not ok\n");
}
if(access("/etc/kvm/hdmi_version", F_OK) == 0){
fp = fopen("/etc/kvm/hdmi_version", "r");
fread(RW_Data, sizeof(char), 1, fp);
fclose(fp);
if(RW_Data[0] == 'u'){
// 6911uxc
kvmv_cfg.hdmi_version = 1;
debug("[hdmi]HDMI-UXC exist!\n");
} else {
// 6911c
kvmv_cfg.hdmi_version = 0;
debug("[hdmi]HDMI-C exist!\n");
}
RW_Data[0] = 0;
} else {
kvmv_cfg.hdmi_version = 0;
}
get_hdmi_version();
// while(!app::need_exit())
uint8_t while_count_get_hdmi_mode = 0;
uint8_t while_count_detect_res = 0;
uint8_t auto_trying_times = 0;
while(true)
{
uint8_t get_new_hdmi_mode = 0;
if(kvmv_cfg.try_exit_thread == 1)
break;
// Low-frequency detection mode change
if (while_count_get_hdmi_mode >= 100){
while_count_get_hdmi_mode = 0;
get_new_hdmi_mode = get_hdmi_mode();
} else {
while_count_get_hdmi_mode ++;
}
uint8_t get_new_hdmi_mode = get_hdmi_mode();
uint8_t try_res;
uint8_t err_code;
switch (kvmv_cfg.hdmi_mode){
case 0:
@@ -911,7 +1128,14 @@ void* vi_subsystem_detection(void * arg)
// hdmi get res
debug("[hdmi] C HDMI cable insertion!\n");
kvmv_cfg.hdmi_cable_state = 1;
kvmv_cfg.reopen_cam_flag = lt6911_get_csi_res(&kvmv_cfg.vi_width, &kvmv_cfg.vi_height);
kvmv_cfg.hdmi_res_type = lt6911_get_csi_res(&kvmv_cfg.vi_width, &kvmv_cfg.vi_height);
if (kvmv_cfg.hdmi_res_type == NEW_RES) kvmv_cfg.reopen_cam_flag = 1;
else if (kvmv_cfg.hdmi_res_type == UNKNOWN_RES){
/* Move HDMI resolution modification directly
to mode1 to solve deadlock problem */
auto_change_mode = 1;
set_hdmi_mode(1);
}
} else {
// HDMI res = 0*0/x*0
debug("[hdmi] C HDMI cable unplugged!\n");
@@ -929,7 +1153,14 @@ void* vi_subsystem_detection(void * arg)
// hdmi get res
debug("[hdmi] UXC HDMI cable insertion!\n");
kvmv_cfg.hdmi_cable_state = 1;
kvmv_cfg.reopen_cam_flag = lt6911_get_csi_res(&kvmv_cfg.vi_width, &kvmv_cfg.vi_height);
kvmv_cfg.hdmi_res_type = lt6911_get_csi_res(&kvmv_cfg.vi_width, &kvmv_cfg.vi_height);
if (kvmv_cfg.hdmi_res_type == NEW_RES) kvmv_cfg.reopen_cam_flag = 1;
else if (kvmv_cfg.hdmi_res_type == UNKNOWN_RES){
/* Move HDMI resolution modification directly
to mode1 to solve deadlock problem */
auto_change_mode = 1;
set_hdmi_mode(1);
}
} else {
// HDMI res = 0*0/x*0
debug("[hdmi] UXC HDMI cable unplugged!\n");
@@ -937,6 +1168,8 @@ void* vi_subsystem_detection(void * arg)
}
lt6911_disable();
}
} else {
debug("[hdmi] Chip not supported for reading \n");
}
kvmv_cfg.hdmi_reading_flag = 0;
}
@@ -952,62 +1185,43 @@ void* vi_subsystem_detection(void * arg)
* 1: Preparing / Testing in progress
* 2: Test completed: Suitable resolution found,
*/
if(get_new_hdmi_mode == 1){
kvmv_cfg.vi_detect_state = 1;
auto_trying_times = 0;
}
if(kvmv_cfg.vi_detect_state == 1){
uint8_t err_code = get_vi_state();
debug("[kvmv] get_vi_state() = %d\n", err_code);
switch(err_code){
case 0:
// shouldn't be possible to run here
printf("[kvmv] VI not init\n");
break;
case 1:
printf("[kvmv] VI subsystem is normal\n");
try_res = auto_try_res();
if (try_res == 1) {
kvmv_cfg.hdmi_res_err = NORMAL_RES;
kvmv_cfg.hdmi_try_rounds = 0;
kvmv_cfg.vi_detect_state = 2;
break;
case 2:
// HDMI not detected or resolution not supported; interval checks will continue
printf("[kvmv] Cannot obtain HDMI input\n");
break;
case 3: // width too small
case 4: // width too large
case 5: // height too small
case 6: // height too large
// CSI abnormal due to resolution error
// The test list is short; sequential testing can be performed
if(auto_trying_times < sizeof(hdmi_res_list)/4){
char Cmd[100]={0};
printf("[kvmv] Trying %d * %d res ..\n", hdmi_res_list[auto_trying_times][0], hdmi_res_list[auto_trying_times][1]);
sprintf(Cmd, "echo %d > %s", hdmi_res_list[auto_trying_times][0], vi_width_path);
system(Cmd);
sprintf(Cmd, "echo %d > %s", hdmi_res_list[auto_trying_times][1], vi_height_path);
system(Cmd);
printf("[kvmv] restart cam...\n");
cam->restart(default_vpss_width, default_vpss_height, image::FMT_YVU420SP);
auto_trying_times++;
time::sleep_ms(10);
} else {
printf("[kvmv] Suitable resolution not found, switching to manual input mode automatically\n");
} else if (try_res == 0) {
/* There may be a situation where the correct resolution cannot be recognized
after one round of detection. By default, Try_rounds_HDMI_err_res rounds will be detected,
and if it cannot be detected, jump to the next mode */
kvmv_cfg.hdmi_res_err = ERROR_RES;
kvmv_cfg.hdmi_try_rounds++;
if(kvmv_cfg.hdmi_try_rounds >= Try_rounds_HDMI_err_res){
kvmv_cfg.hdmi_try_rounds = 0;
kvmv_cfg.vi_detect_state = 1;
char Cmd[100]={0};
sprintf(Cmd, "echo 2 > %s", hdmi_mode_path);
system(Cmd);
while_count_get_hdmi_mode = 100; // Take effect immediately
// printf("[kvmv] Suitable resolution not found, switching to manual input mode automatically\n");
if(auto_change_mode == 1){
auto_change_mode = 0;
set_hdmi_mode(0);
} else {
set_hdmi_mode(2);
}
}
break;
case 7: // Unknown reason
printf("[kvmv] CSI abnormal: Unknown reason\n");
break;
} else if (try_res == 2) {
kvmv_cfg.hdmi_try_rounds = 0;
// Cannot obtain HDMI input / No signal on HDMI
}
} else if (kvmv_cfg.vi_detect_state == 2){
// Low-frequency detection of HDMI status, no log output
uint8_t err_code = get_vi_state();
printf("[kvmv] kvmv_cfg.vi_detect_state == 2\n");
err_code = get_vi_state();
if (err_code != 1) {
kvmv_cfg.vi_detect_state = 1;
auto_trying_times = 0;
}
time::sleep_ms(1000);
} else {
@@ -1016,17 +1230,17 @@ void* vi_subsystem_detection(void * arg)
break;
case 2:
// Manually initialize VI.
if (while_count_detect_res >= 100){
while_count_detect_res = 0;
while_count_detect_res = (while_count_detect_res + 1)%100;
if(while_count_detect_res == 1){
if (kvmv_cfg.vi_detect_state == 1){
// detect_res
if (get_manual_resolution()) {
printf("[kvmv] restart cam...\n");
debug("[kvmv] restart cam...\n");
cam->restart(default_vpss_width, default_vpss_height, image::FMT_YVU420SP);
}
// dbg info
uint8_t err_code = get_vi_state();
err_code = get_vi_state();
switch(err_code){
case 0:
debug("[kvmv] VI not init\n");
@@ -1056,14 +1270,11 @@ void* vi_subsystem_detection(void * arg)
}
} else if (kvmv_cfg.vi_detect_state == 2){
// detection of HDMI status, no log output
uint8_t err_code = get_vi_state();
err_code = get_vi_state();
if (err_code != 1) kvmv_cfg.vi_detect_state = 1;
} else {
kvmv_cfg.vi_detect_state = 1;
}
} else {
while_count_detect_res ++;
}
break;
@@ -1129,6 +1340,31 @@ int sync_vi_res()
return res;
}
uint8_t frame_changed(image::Image *raw)
{
static int raw_size = 0;
static uint8_t Farame_sample[Farame_sample_size] = {0};
uint8_t ret = 0;
uint8_t sample_byte;
if(raw->data_size() != raw_size){
raw_size = raw->data_size();
ret = 1;
}
int Detection_Pixel_Interval = raw_size/(Farame_sample_size*1.5);
for(int i = 0; i < Farame_sample_size; i ++){
// printf("[kvmv] i = %d\n", i);
if(i >= raw_size){
ret = 0;
}
sample_byte = *(uint8_t*)(raw->data()+(i*Detection_Pixel_Interval));
if(sample_byte != Farame_sample[i]){
Farame_sample[i] = sample_byte;
ret = 1;
}
}
return ret;
}
void jpg_dump(kvmv_data_t* dump_to, image::Image *raw)
{
dump_to->p_img_data = (uint8_t *)malloc(raw->data_size());
@@ -1173,29 +1409,17 @@ int h264_stream_dump(kvmv_data_t* dump_to, mmf_stream_t* dump_from)
static int8_t I_Frame_index = -1;
// debug("[kvmv]dump_from->count = %d\n", dump_from->count);
if (dump_from->count == 3) {
// debug("[kvmv]dump I-Frame\r\n");
if(kvmv_SPS_buffer.p_img_data != NULL || kvmv_PPS_buffer.p_img_data != NULL)
return IMG_BUFFER_FULL;
kvmv_SPS_buffer.p_img_data = (uint8_t *)malloc(dump_from->data_size[0]);
kvmv_PPS_buffer.p_img_data = (uint8_t *)malloc(dump_from->data_size[1]);
dump_to->p_img_data = (uint8_t *)malloc(dump_from->data_size[2]);
dump_to->p_img_data = (uint8_t *)malloc(dump_from->data_size[0]+dump_from->data_size[1]+dump_from->data_size[2]);
dump_to->img_data_size = dump_from->data_size[0]+dump_from->data_size[1]+dump_from->data_size[2];
dump_to->img_data_type = IMG_H264_TYPE_IF;
memcpy(dump_to->p_img_data, dump_from->data[0], dump_from->data_size[0]);
memcpy(dump_to->p_img_data+dump_from->data_size[0], dump_from->data[1], dump_from->data_size[1]);
memcpy(dump_to->p_img_data+dump_from->data_size[0]+dump_from->data_size[1], dump_from->data[2], dump_from->data_size[2]);
kvmv_SPS_buffer.img_data_size = dump_from->data_size[0];
kvmv_PPS_buffer.img_data_size = dump_from->data_size[1];
dump_to->img_data_size = dump_from->data_size[2];
kvmv_SPS_buffer.img_data_type = IMG_H264_TYPE_SPS;
kvmv_PPS_buffer.img_data_type = IMG_H264_TYPE_PPS;
dump_to->img_data_type = IMG_H264_TYPE_IF;
memcpy(kvmv_SPS_buffer.p_img_data, dump_from->data[0], dump_from->data_size[0]);
memcpy(kvmv_PPS_buffer.p_img_data, dump_from->data[1], dump_from->data_size[1]);
memcpy(dump_to->p_img_data, dump_from->data[2], dump_from->data_size[2]);
debug("[kvmv]SPS size = %d\n", kvmv_SPS_buffer.img_data_size);
debug("[kvmv]PPS size = %d\n", kvmv_PPS_buffer.img_data_size);
debug("[kvmv]I-Frame size = %d\n", dump_to->img_data_size);
debug("[kvmv]SPS size = %d\n", dump_from->data_size[0]);
debug("[kvmv]PPS size = %d\n", dump_from->data_size[1]);
debug("[kvmv]I-Frame size = %d\n", dump_from->data_size[2]);
return IMG_H264_TYPE_IF;
@@ -1216,18 +1440,17 @@ int h264_stream_dump(kvmv_data_t* dump_to, mmf_stream_t* dump_from)
void set_h264_gop(uint8_t _gop)
{
kvm_venc.enc_h264_init = 0; // call
kvmvenc_gop = maxmin_data(100, 10, (int)_gop);
kvmvenc_gop = maxmin_data(100, 1, (int)_gop);
debug("[kvmv] set_h264_gop = %d\n", kvmvenc_gop);
}
// uint8_t get_h264_gop(void)
// {
// return kvm_venc.kvm_venc_cfg.gop;
// }
// uint8_t get_hdmi_width(void)
// {
// return kvm_venc.kvm_venc_cfg.gop;
// }
void set_frame_detact(uint8_t _frame_detact)
{
uint8_t frame_detact = maxmin_data(100, 0, (int)_frame_detact);
kvmv_cfg.frame_detact = frame_detact;
debug("[kvmv] set_frame_detact = %d\n", kvmv_cfg.frame_detact);
kvmv_cfg.stream_stop = 0;
}
int8_t raw_to_h264(image::Image *raw, kvmv_data_t* ret_stream, uint16_t _qlty)
{
@@ -1298,6 +1521,7 @@ int8_t raw_to_h264(image::Image *raw, kvmv_data_t* ret_stream, uint16_t _qlty)
void kvmv_init(uint8_t _debug_info_en)
{
pthread_t thread;
pthread_mutex_init(&vi_mutex, NULL);
if(_debug_info_en == 0) debug_en = 0;
else debug_en = 1;
@@ -1309,8 +1533,6 @@ void kvmv_init(uint8_t _debug_info_en)
for(int i = 0; i < kvmv_data_buffer_size; i++){
kvmv_data_buffer[i].p_img_data = NULL;
}
kvmv_SPS_buffer.p_img_data = NULL;
kvmv_PPS_buffer.p_img_data = NULL;
kvmv_cfg.try_exit_thread = 0;
// debug("[kvmv]kvmv_init - 2\r\n");
@@ -1319,7 +1541,12 @@ void kvmv_init(uint8_t _debug_info_en)
debug("[kvmv]thread is running!\r\n");
} else {
if (0 != pthread_create(&thread, NULL, vi_subsystem_detection, NULL)) {
debug("[kvmv]create thread failed!\r\n");
debug("[kvmv]create vi_subsystem_detection thread failed!\r\n");
// return -1;
}
if (0 != pthread_create(&thread, NULL, watchdog_sf_feed, NULL)) {
debug("[kvmv]create watchdog_sf_feed thread failed!\r\n");
// return -1;
}
}
@@ -1349,6 +1576,11 @@ uint8_t check_kvmv(uint8_t _try_num)
return 1;
}
void set_venc_auto_recyc(uint8_t _enable)
{
if(_enable) kvmv_cfg.venc_auto_recyc = 1;
else kvmv_cfg.venc_auto_recyc = 0;
}
/**********************************************************************************
* @name kvmv_read_img
@@ -1363,20 +1595,43 @@ uint8_t check_kvmv(uint8_t _try_num)
* @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)
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)
{
static uint8_t frame_undetact_count = 0;
// uint64_t __attribute__((unused)) start_time = time::time_ms();
debug("[kvmv]kvmv_read_img type = %d...\n", _type);
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 1;
// pthread_mutex_lock(&vi_mutex); // Add lock
int mutex_res = pthread_mutex_timedlock(&vi_mutex, &ts);
if(mutex_res != 0){
return -5;
}
if (kvmv_cfg.hdmi_res_err == ERROR_RES){
pthread_mutex_unlock(&vi_mutex);
return -7;
}
if (kvmv_cfg.hdmi_res_type == UNSUPPORT_RES){
pthread_mutex_unlock(&vi_mutex);
return -6;
}
if (kvmv_cfg.vi_detect_state == 1){
pthread_mutex_unlock(&vi_mutex);
return -4;
}
uint8_t try_num = 0;
@@ -1413,6 +1668,27 @@ int kvmv_read_img(uint16_t _width, uint16_t _height, uint8_t _type, uint16_t _ql
// debug("[kvmv]befor read img: %d \r\n", (int)(time::time_ms() - start_time));
image::Image *img = cam->read();
// debug("[kvmv]read img: %d \r\n", (int)(time::time_ms() - start_time));
if(_type == VENC_MJPEG && kvmv_cfg.frame_detact != 0){
if(kvmv_cfg.stream_stop == 0){
frame_undetact_count++;
} else {
frame_undetact_count = kvmv_cfg.frame_detact;
}
if(frame_undetact_count == kvmv_cfg.frame_detact){
frame_undetact_count = 0;
if(frame_changed(img) == 0){
debug("[kvmv]frame not changed...\n");
kvmv_cfg.stream_stop = 1;
delete img;
pthread_mutex_unlock(&vi_mutex);
return 5;
} else {
kvmv_cfg.stream_stop = 0;
}
}
}
if(img != NULL){
if(kvmv_cfg.cam_state == 0) {
kvmv_cfg.cam_state = 1;
@@ -1427,15 +1703,26 @@ int kvmv_read_img(uint16_t _width, uint16_t _height, uint8_t _type, uint16_t _ql
delete img;
debug("[kvmv]can`t get img...\n");
continue;
// pthread_mutex_unlock(&vi_mutex);
// return IMG_NOT_EXIST;
}
// debug("[kvmv]cheak img null?: %d \r\n", (int)(time::time_ms() - start_time));
// img exist
// Encode
if(_type == VENC_H264 && kvmv_cfg.venc_type != _type){
if(kvmv_cfg.venc_type == VENC_MJPEG && kvmv_cfg.venc_type != _type){
if(kvmv_cfg.venc_auto_recyc == 1){
mmf_enc_jpg_deinit(0);
}
kvm_venc.enc_h264_init = 1;
// debug("[kvmv] change to h264\n");
}
if(kvmv_cfg.venc_type == VENC_H264 && kvmv_cfg.venc_type != _type){
if(kvmv_cfg.venc_auto_recyc == 1){
mmf_del_venc_channel(kvm_venc.mmf_venc_chn);
}
kvm_venc.enc_h264_init = 0;
}
kvmv_cfg.venc_type = _type;
if(kvmv_cfg.venc_type == VENC_MJPEG){
@@ -1447,6 +1734,7 @@ int kvmv_read_img(uint16_t _width, uint16_t _height, uint8_t _type, uint16_t _ql
delete img;
debug("[kvmv]jpg buffer full\n");
*_pp_kvm_data = NULL;
pthread_mutex_unlock(&vi_mutex);
return IMG_BUFFER_FULL;
}
jpg_dump(p_kvmv_data, jpg);
@@ -1454,6 +1742,7 @@ int kvmv_read_img(uint16_t _width, uint16_t _height, uint8_t _type, uint16_t _ql
delete img;
*_pp_kvm_data = p_kvmv_data->p_img_data;
*_p_kvmv_data_size = p_kvmv_data->img_data_size;
pthread_mutex_unlock(&vi_mutex);
return IMG_MJPEG_TYPE;
} else if (kvmv_cfg.venc_type == VENC_H264){
int ret;
@@ -1462,6 +1751,7 @@ int kvmv_read_img(uint16_t _width, uint16_t _height, uint8_t _type, uint16_t _ql
// buffer full
delete img;
*_pp_kvm_data = NULL;
pthread_mutex_unlock(&vi_mutex);
return IMG_BUFFER_FULL;
}
// debug("[kvmv]get_save_buffer: %d \r\n", (int)(time::time_ms() - start_time));
@@ -1470,35 +1760,13 @@ int kvmv_read_img(uint16_t _width, uint16_t _height, uint8_t _type, uint16_t _ql
delete img;
*_pp_kvm_data = p_kvmv_data->p_img_data;
*_p_kvmv_data_size = p_kvmv_data->img_data_size;
pthread_mutex_unlock(&vi_mutex);
return ret;
}
} while (check_kvmv(try_num++));
// debug("[kvmv]return: %d \r\n", (int)(time::time_ms() - start_time));
*_pp_kvm_data = NULL;
return IMG_NOT_EXIST;
}
int kvmv_get_sps_frame(uint8_t** _pp_kvm_data, uint32_t* _p_kvmv_data_size)
{
if(kvmv_SPS_buffer.p_img_data == NULL){
return IMG_NOT_EXIST;
} else {
*_pp_kvm_data = kvmv_SPS_buffer.p_img_data;
*_p_kvmv_data_size = kvmv_SPS_buffer.img_data_size;
return IMG_H264_TYPE_SPS;
}
return IMG_NOT_EXIST;
}
int kvmv_get_pps_frame(uint8_t** _pp_kvm_data, uint32_t* _p_kvmv_data_size)
{
if(kvmv_PPS_buffer.p_img_data == NULL){
return IMG_NOT_EXIST;
} else {
*_pp_kvm_data = kvmv_PPS_buffer.p_img_data;
*_p_kvmv_data_size = kvmv_PPS_buffer.img_data_size;
return IMG_H264_TYPE_PPS;
}
pthread_mutex_unlock(&vi_mutex);
return IMG_NOT_EXIST;
}
@@ -1514,12 +1782,6 @@ int free_kvmv_data(uint8_t ** _pp_kvm_data)
// debug("[kvmv]free_kvmv_data - 3\r\n");
kvmv_data_buffer[i].p_img_data = NULL;
uint8_t _type = kvmv_data_buffer[i].img_data_type;
if(_type == IMG_H264_TYPE_IF){
free(kvmv_SPS_buffer.p_img_data);
free(kvmv_PPS_buffer.p_img_data);
kvmv_SPS_buffer.p_img_data = NULL;
kvmv_PPS_buffer.p_img_data = NULL;
}
return _type;
} else {
return IMG_NOT_EXIST;
@@ -1537,14 +1799,11 @@ void free_all_kvmv_data()
kvmv_data_buffer[i].p_img_data = NULL;
}
}
if(kvmv_SPS_buffer.p_img_data != NULL) free(kvmv_SPS_buffer.p_img_data);
if(kvmv_PPS_buffer.p_img_data != NULL) free(kvmv_PPS_buffer.p_img_data);
kvmv_SPS_buffer.p_img_data = NULL;
kvmv_PPS_buffer.p_img_data = NULL;
}
void kvmv_deinit()
{
pthread_mutex_destroy(&vi_mutex);
kvmv_cfg.try_exit_thread = 1;
cam->close();
mmf_deinit();

View 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)

View File

@@ -663,12 +663,12 @@ static CVI_S32 _mmf_sys_init(SIZE_S stSize)
// vi
u32BlkSize = COMMON_GetPicBufferSize(stSize.u32Width, stSize.u32Height, PIXEL_FORMAT_UYVY,
DATA_BITWIDTH_12, enCompressMode, DEFAULT_ALIGN);
DATA_BITWIDTH_8, enCompressMode, DEFAULT_ALIGN);
u32BlkRotSize = COMMON_GetPicBufferSize(stSize.u32Height, stSize.u32Width, PIXEL_FORMAT_UYVY,
DATA_BITWIDTH_12, enCompressMode, DEFAULT_ALIGN);
DATA_BITWIDTH_8, enCompressMode, DEFAULT_ALIGN);
u32BlkSize = MAX(u32BlkSize, u32BlkRotSize);
stVbConf.astCommPool[MMF_VB_VI_ID].u32BlkSize = u32BlkSize;
stVbConf.astCommPool[MMF_VB_VI_ID].u32BlkCnt = 4;
stVbConf.astCommPool[MMF_VB_VI_ID].u32BlkCnt = 3;
stVbConf.astCommPool[MMF_VB_VI_ID].enRemapMode = VB_REMAP_MODE_CACHED;
stVbConf.u32MaxPoolCnt = 1;

View 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;
}
}

View File

@@ -18,7 +18,7 @@ namespace maix::image
/**
* Image formats
* @attention for developers, update this enum will also need to update the fmt_size in maix_image.cpp
* @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
@@ -36,6 +36,22 @@ namespace maix::image
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
@@ -45,32 +61,8 @@ namespace maix::image
FMT_COMPRESSED_MAX,
FMT_INVALID = 0xFF // format not valid
};
// enum Format
// {
// FMT_RGB888 = 0, // RGBRGB...RGB, R at the lowest address
// FMT_BGR888 = 1, // BGRBGR...BGR, B at the lowest address
// FMT_RGBA8888 = 2, // RGBARGBA...RGBA, R at the lowest address
// FMT_BGRA8888 = 3, // BGRABGRA...BGRA, B at the lowest address
// FMT_RGB565 = 4,
// FMT_BGR565 = 5,
// FMT_YUV422SP = 6, // YYY...UVUVUV...UVUV
// FMT_YUV422P = 7, // YYY...UUU...VVV
// FMT_YVU420SP = 8, // YYY...VUVUVU...VUVU, NV21
// FMT_YUV420SP = 9, // YYY...UVUVUV...UVUV, NV12
// FMT_YVU420P = 10, // YYY...VVV...UUU
// FMT_YUV420P = 11, // YYY...UUU...VVV
// FMT_GRAYSCALE = 12, // YYY...
// FMT_UNCOMPRESSED_MAX,
}; // !!!! update this section please update fmt_size and fmt_names too !!!!
// // compressed format below, not compressed should define upper
// FMT_COMPRESSED_MIN = 200,
// FMT_JPEG = 201,
// FMT_PNG = 202,
// FMT_COMPRESSED_MAX,
// FMT_INVALID = 0xFF // format not valid
// };
/**
* Image format size in bytes
* @attention It's a copy of this variable in MaixPy,
@@ -91,12 +83,29 @@ namespace maix::image
1.5,
1.5,
1.5,
1,
0,
0,
0,
0,
0
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
};
/**
@@ -114,10 +123,32 @@ namespace maix::image
"YUV422P",
"YVU420SP",
"YUV420SP",
"YVU420P"
"YVU420P",
"YUV420P",
"GRAYSCALE",
"MAX"};
"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
@@ -171,7 +202,7 @@ namespace maix::image
* @param index 0 for width, 1 for height
* @return int& width or height
* @maixpy maix.image.Size.__getitem__
* @maixcdk maix.image.Size.[]
* @maixcdk maix.image.Size.operator[]
*/
int &operator[](int index)
{
@@ -267,4 +298,16 @@ namespace maix::image
EDGE_CANNY,
EDGE_SIMPLE,
};
/**
* FlipDir
* @maixpy maix.image.FlipDir
*/
enum class FlipDir
{
X,
Y,
XY
};
}

179
support/sg2002/build Executable file
View File

@@ -0,0 +1,179 @@
#!/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
rm -f $TARGET_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

View File

@@ -36,6 +36,7 @@
#define WIFI_CONNECTION_DELAY 5000
#define OLED_SLEEP_DELAY_MIN 10
#define OLED_SLEEP_DELAY_DEFAULT 30
#define KVM_WD_COUNT_MAX 10
typedef struct {
int8_t page = 0;
@@ -67,6 +68,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 +97,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