mirror of
https://github.com/sipeed/NanoKVM.git
synced 2026-11-11 10:29:19 -06:00
Compare commits
81 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ef83cb22f | ||
|
|
1a3fd15fdf | ||
|
|
3e5a46570c | ||
|
|
2c4fbf7179 | ||
|
|
5409fb783c | ||
|
|
4cec610aba | ||
|
|
57d88d7889 | ||
|
|
7f305ae3cf | ||
|
|
eb4ac33fcd | ||
|
|
7a3c6c1690 | ||
|
|
ca63609545 | ||
|
|
bcaa8896ee | ||
|
|
13b19e3889 | ||
|
|
99d1fc0a64 | ||
|
|
86b7d676dd | ||
|
|
1adf6edffc | ||
|
|
a16af99648 | ||
|
|
58d5ab2d37 | ||
|
|
9aebabfd59 | ||
|
|
21b119294c | ||
|
|
e3a79daf89 | ||
|
|
4813e8bd50 | ||
|
|
93c0f3d353 | ||
|
|
be5a9f7515 | ||
|
|
36a1eb8eb8 | ||
|
|
20b452f3c0 | ||
|
|
b062569aab | ||
|
|
c91ce5ab62 | ||
|
|
602615e489 | ||
|
|
0e11855011 | ||
|
|
c5c768b143 | ||
|
|
5c0f098069 | ||
|
|
8bd7e02ec5 | ||
|
|
9233bd7c79 | ||
|
|
7d2664acb5 | ||
|
|
8c09cd7c44 | ||
|
|
aef8a7a851 | ||
|
|
8ed6e2eca3 | ||
|
|
a7b5091024 | ||
|
|
cd6da8b588 | ||
|
|
2d755fdb98 | ||
|
|
f231df4f65 | ||
|
|
95c8edcd3d | ||
|
|
d94eba0ab1 | ||
|
|
b48729c594 | ||
|
|
b9ba107587 | ||
|
|
c2d3224552 | ||
|
|
592939895b | ||
|
|
8d8c57bc48 | ||
|
|
8330b1b1fe | ||
|
|
9fdcb428c0 | ||
|
|
cee9bbb079 | ||
|
|
fcc09e2ef0 | ||
|
|
5d96a8060f | ||
|
|
023fb4b68b | ||
|
|
594c205a29 | ||
|
|
fc436e2743 | ||
|
|
125eddffe1 | ||
|
|
aad3d718e4 | ||
|
|
e3575e629c | ||
|
|
7bd7b77fa9 | ||
|
|
ba620db901 | ||
|
|
3259379345 | ||
|
|
08eb89dc5c | ||
|
|
1376929897 | ||
|
|
614ef4031c | ||
|
|
d9a19e132a | ||
|
|
7795191a46 | ||
|
|
5e3c4d98f2 | ||
|
|
de28273def | ||
|
|
148fb4e37b | ||
|
|
a920fcc58a | ||
|
|
5021ab8d2f | ||
|
|
0ae97e6dd7 | ||
|
|
8c2e744d42 | ||
|
|
1ea9dc0561 | ||
|
|
2ec5393bd0 | ||
|
|
01c837f6ac | ||
|
|
899aa48875 | ||
|
|
29dd37852d | ||
|
|
59d5944398 |
14
.gitignore
vendored
14
.gitignore
vendored
@@ -16,4 +16,16 @@ web/web
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
NanoKVM-Server
|
||||
NanoKVM-Server
|
||||
|
||||
support/sg2002/kvm_system/build
|
||||
support/sg2002/kvm_system/dist
|
||||
support/sg2002/kvm_system/CMakeLists.txt
|
||||
|
||||
support/sg2002/kvm_vision_test/build
|
||||
support/sg2002/kvm_vision_test/dist
|
||||
support/sg2002/kvm_vision_test/CMakeLists.txt
|
||||
|
||||
support/sg2002/additional/original
|
||||
kvmapp/server/dl_lib
|
||||
kvmapp/kvm_system/kvm_system
|
||||
|
||||
17
CHANGELOG.md
17
CHANGELOG.md
@@ -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
|
||||
|
||||
27
README.md
27
README.md
@@ -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
|
||||
|
||||
110
README_JA.md
110
README_JA.md
@@ -1,11 +1,13 @@
|
||||
# NanoKVM
|
||||
NanoKVM
|
||||
======
|
||||
|
||||
<div align="center">
|
||||
|
||||

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

|
||||

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

|
||||

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

|
||||
|
||||
<h3>
|
||||
<a href="https://wiki.sipeed.com/hardware/zh/lichee/RV_Nano/1_intro.html"> 快速开始 </a> |
|
||||
<a href="https://cn.dl.sipeed.com/shareURL/KVM/nanoKVM"> 硬件资料 </a>
|
||||
<a href="https://wiki.sipeed.com/hardware/en/kvm/NanoKVM/introduction.html"> Quick Start </a>
|
||||
|
|
||||
<a href="https://cn.dl.sipeed.com/shareURL/KVM/nanoKVM"> Hardware </a>
|
||||
</h3>
|
||||
|
||||
[English](./README.md) | 中文 | [日本語](./README_JA.md)
|
||||
@@ -17,6 +18,47 @@ NanoKVM
|
||||
> Your NanoKVM Power by RISC-V !
|
||||
> 如果您在使用过程中有任何问题或建议,请在这里提交 issue, 或在 [MaixHub Discussion](https://maixhub.com/discussion/nanokvm)告诉我们.
|
||||
|
||||
## 开源与贡献
|
||||
|
||||
### 2025.03.04 更新内容
|
||||
+ 更新目录结构
|
||||
|
||||
``` shell
|
||||
├── kvmapp # APP更新包
|
||||
│ ├── jpg_stream # 兼容从非常老的版本中直接更新
|
||||
│ ├── kvm_new_app # 触发 kvm_system 更新必要组件
|
||||
│ ├── kvm_system # kvm_system 应用
|
||||
│ ├── server # NanoKVM 前后端应用
|
||||
│ └── system # 必要系统组件
|
||||
├── server # NanoKVM 后端
|
||||
├── support # 辅助功能(图像子系统、系统状态、系统更新、屏幕、按键……)
|
||||
│ ├── sg2002 # NanoKVM-Lite/Full/PCIe
|
||||
│ └── h618 # NanoKVM-Pro
|
||||
├── web # NanoKVM 前端
|
||||
├── LICENSE
|
||||
├── README_JA.md
|
||||
├── README.md
|
||||
├── README_ZH.md
|
||||
└── CHANGELOG.md
|
||||
```
|
||||
|
||||
+ 添加 NanoKVM app安装包`kvmapp`
|
||||
+ 添加编译脚本和编译说明
|
||||
|
||||
### 2025.02.19 更新内容
|
||||
所有 NanoKVM 组件已开源,包括
|
||||
[前端](https://github.com/sipeed/NanoKVM/tree/main/web)、[后端](https://github.com/sipeed/NanoKVM/tree/main/server)、[kvm_vision](https://github.com/sipeed/NanoKVM/tree/main/vision/components/kvm)、[kvm_mmf](https://github.com/sipeed/NanoKVM/tree/main/vision/components/kvm_mmf)、[kvm_system](https://github.com/sipeed/NanoKVM/tree/main/support)、[kvmapp 更新包](https://github.com/sipeed/NanoKVM-System/tree/main/kvmapp)、[系统 SDK](https://github.com/sipeed/LicheeRV-Nano-Build/tree/NanoKVM) 和 [固件打包方法](https://github.com/sipeed/LicheeRV-Nano-Build/blob/NanoKVM/kvm/NanoKVM_img.sh)。
|
||||
|
||||
### 2025.02.14 更新内容
|
||||
最新的 APP v2.1.6 添加了大多数安全增强和 bug 修复,建议普通用户升级到此版本或更新版本。
|
||||
|
||||
### 2025.02.05 更新内容
|
||||
对于关注安全的用户,可以阅读此问题:https://github.com/sipeed/NanoKVM/issues/301,您会找到有关所有安全问题的解释。GitHub 作为一个开放和透明的平台,为讨论所谓的“后门”担忧提供了空间。开源使产品更加安全!
|
||||
|
||||
### 2024.10.18 更新内容
|
||||
我们在 10.8 中开源了后端代码,感谢您立即提交的文明 PR,我们将赠送您最新的 NanoKVM-PCIe 作为奖励!
|
||||
十月中旬的批次正在开始测试和打包,大多数在 10.1 之前的订单将在下周和下下周发货。
|
||||
自 10.1 起,AliExpress 的发货日期已更改为 12 月,这是一个保守的交货时间。十月的订单应在 11.15~12.15 期间发出。
|
||||
|
||||
## 简介
|
||||
|
||||
|
||||
91
kvmapp/jpg_stream/S95nanokvm
Executable file
91
kvmapp/jpg_stream/S95nanokvm
Executable file
@@ -0,0 +1,91 @@
|
||||
#!/bin/sh
|
||||
# nanokvm Rev2.1
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
echo -n kvm > /boot/hostname.prefix
|
||||
cp /mnt/data/sensor_cfg.ini.LT /mnt/data/sensor_cfg.ini
|
||||
|
||||
str_value=$(cat /sys/class/cvi-base/base_uid | awk '{print $2}')
|
||||
first_uint=$(echo $str_value | cut -d'_' -f1)
|
||||
second_uint=$(echo $str_value | cut -d'_' -f2)
|
||||
result="$first_uint$second_uint"
|
||||
echo $result > /device_key
|
||||
|
||||
# if [ ! -e /etc/kvm/hw ]
|
||||
# then
|
||||
|
||||
# fi
|
||||
|
||||
# echo 504 > /sys/class/gpio/export # pwr led
|
||||
# echo 505 > /sys/class/gpio/export # hdd led
|
||||
# echo 503 > /sys/class/gpio/export # pwr key
|
||||
# echo 507 > /sys/class/gpio/export # rst key
|
||||
|
||||
# echo in > /sys/class/gpio/gpio504/direction # pwr led
|
||||
# echo in > /sys/class/gpio/gpio505/direction # hdd led
|
||||
# echo out > /sys/class/gpio/gpio503/direction # pwr key
|
||||
# echo out > /sys/class/gpio/gpio507/direction # rst key
|
||||
|
||||
iptables -A INPUT -i eth0 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
|
||||
iptables -A OUTPUT -o eth0 -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT
|
||||
iptables -A OUTPUT -o eth0 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
|
||||
iptables -A INPUT -i eth0 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
|
||||
iptables -A OUTPUT -o eth0 -p tcp --sport 8000 -m state --state ESTABLISHED -j DROP
|
||||
|
||||
cp -r /kvmapp/kvm_system /tmp/
|
||||
/tmp/kvm_system/kvm_system &
|
||||
|
||||
# if [ -e /kvmapp/kvm_stream ]
|
||||
# then
|
||||
# cp -r /kvmapp/kvm_stream /tmp/
|
||||
# /tmp/kvm_stream/kvm_stream &
|
||||
# fi
|
||||
|
||||
cp -r /kvmapp/server /tmp/
|
||||
/tmp/server/NanoKVM-Server &
|
||||
;;
|
||||
stop)
|
||||
killall kvm_system
|
||||
# killall kvm_stream
|
||||
killall NanoKVM-Server
|
||||
rm -r /tmp/kvm_system
|
||||
# rm -r /tmp/kvm_stream
|
||||
rm -r /tmp/server
|
||||
echo "OK"
|
||||
;;
|
||||
# restart_stream)
|
||||
# killall kvm_stream
|
||||
# rm -r /tmp/kvm_stream
|
||||
# if [ -e /kvmapp/kvm_stream ]
|
||||
# then
|
||||
# cp -r /kvmapp/kvm_stream /tmp/
|
||||
# /tmp/kvm_stream/kvm_stream &
|
||||
# echo "OK"
|
||||
# fi
|
||||
# ;;
|
||||
restart)
|
||||
killall kvm_system
|
||||
# killall kvm_stream
|
||||
killall NanoKVM-Server
|
||||
rm -r /tmp/kvm_system
|
||||
# rm -r /tmp/kvm_stream
|
||||
rm -r /tmp/server
|
||||
|
||||
cp -r /kvmapp/kvm_system /tmp/
|
||||
/tmp/kvm_system/kvm_system &
|
||||
|
||||
# if [ -e /kvmapp/kvm_stream ]
|
||||
# then
|
||||
# cp -r /kvmapp/kvm_stream /tmp/
|
||||
# /tmp/kvm_stream/kvm_stream &
|
||||
# fi
|
||||
|
||||
cp -r /kvmapp/server /tmp/
|
||||
/tmp/server/NanoKVM-Server &
|
||||
|
||||
sync
|
||||
|
||||
echo "OK"
|
||||
;;
|
||||
esac
|
||||
5
kvmapp/jpg_stream/jpg_stream
Executable file
5
kvmapp/jpg_stream/jpg_stream
Executable file
@@ -0,0 +1,5 @@
|
||||
rm /etc/init.d/S95webkvm
|
||||
cp /kvmapp/jpg_stream/S95nanokvm /etc/init.d/
|
||||
cp /kvmapp/jpg_stream/dl_lib/libmaixcam_lib.so /kvmapp/kvm_system/dl_lib
|
||||
rm -r /kvmapp/jpg_stream
|
||||
/etc/init.d/S95nanokvm restart
|
||||
0
support/kvm_system/main/Kconfig → kvmapp/kvm_system/kvm_stream
Normal file → Executable file
0
support/kvm_system/main/Kconfig → kvmapp/kvm_system/kvm_stream
Normal file → Executable file
42
kvmapp/system/init.d/S01fs
Executable file
42
kvmapp/system/init.d/S01fs
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ "$1" = "start" ]
|
||||
then
|
||||
# use all sdcard free space for data
|
||||
parted -s /dev/mmcblk0 "resizepart 2 -0"
|
||||
echo "yes
|
||||
8192MB
|
||||
" | parted ---pretend-input-tty /dev/mmcblk0 "resizepart 2 8192MB"
|
||||
# resize data filesystem
|
||||
(resize2fs /dev/mmcblk0p2) &
|
||||
|
||||
|
||||
. /etc/profile
|
||||
printf "mounting filesystem : "
|
||||
mkdir -p /boot
|
||||
mount -t vfat /dev/mmcblk0p1 /boot
|
||||
mount -t configfs configfs /sys/kernel/config
|
||||
mount -t debugfs debugfs /sys/kernel/debug
|
||||
|
||||
if [ -e /boot/usb.disk0 ]
|
||||
then
|
||||
if [ ! -e /etc/kvm.disk0 ]
|
||||
then
|
||||
touch /etc/kvm.disk0
|
||||
# use all sdcard free space for data
|
||||
parted -s /dev/mmcblk0 "mkpart primary 8193MB 100%"
|
||||
sleep 1
|
||||
# resize data filesystem
|
||||
(mkfs.exfat /dev/mmcblk0p3) &
|
||||
sleep 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -e /dev/mmcblk0p3 ]
|
||||
then
|
||||
mkdir -p /data
|
||||
mount /dev/mmcblk0p3 /data
|
||||
fi
|
||||
|
||||
echo "OK"
|
||||
fi
|
||||
139
kvmapp/system/init.d/S03usbdev
Executable file
139
kvmapp/system/init.d/S03usbdev
Executable file
@@ -0,0 +1,139 @@
|
||||
#!/bin/sh
|
||||
# kvmhwd Rev2.0
|
||||
|
||||
start_usb_dev(){
|
||||
. /etc/profile
|
||||
echo "usb mode: device"
|
||||
cd /sys/kernel/config/usb_gadget
|
||||
|
||||
mkdir g0
|
||||
cd g0
|
||||
|
||||
echo 0x3346 > idVendor
|
||||
echo 0x1009 > idProduct
|
||||
mkdir strings/0x409
|
||||
echo '0123456789ABCDEF' > strings/0x409/serialnumber
|
||||
echo 'sipeed' > strings/0x409/manufacturer
|
||||
echo 'NanoKVM' > strings/0x409/product
|
||||
|
||||
mkdir configs/c.1
|
||||
echo 0xE0 > configs/c.1/bmAttributes
|
||||
echo 120 > configs/c.1/MaxPower
|
||||
mkdir configs/c.1/strings/0x409
|
||||
echo "NanoKVM" > configs/c.1/strings/0x409/configuration
|
||||
|
||||
if [ -e /boot/usb.ncm ]
|
||||
then
|
||||
mkdir functions/ncm.usb0
|
||||
ln -s functions/ncm.usb0 configs/c.1/
|
||||
else
|
||||
if [ -e /boot/usb.rndis0 ]
|
||||
then
|
||||
mkdir functions/rndis.usb0
|
||||
ln -s functions/rndis.usb0 configs/c.1/
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -e /boot/disable_hid ]
|
||||
then
|
||||
# keyboard
|
||||
mkdir functions/hid.GS0
|
||||
if [ -e /boot/BIOS ]
|
||||
then
|
||||
echo 1 > functions/hid.GS0/subclass
|
||||
fi
|
||||
if [ ! -e /boot/usb.notwakeup ]
|
||||
then
|
||||
echo 1 > functions/hid.GS0/wakeup_on_write
|
||||
fi
|
||||
echo 1 > functions/hid.GS0/protocol
|
||||
echo 6 > functions/hid.GS0/report_length
|
||||
echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > functions/hid.GS0/report_desc
|
||||
ln -s functions/hid.GS0 configs/c.1
|
||||
|
||||
# mouse
|
||||
mkdir functions/hid.GS1
|
||||
if [ -e /boot/BIOS ]
|
||||
then
|
||||
echo 1 > functions/hid.GS1/subclass
|
||||
fi
|
||||
if [ ! -e /boot/usb.notwakeup ]
|
||||
then
|
||||
echo 1 > functions/hid.GS1/wakeup_on_write
|
||||
fi
|
||||
echo 2 > functions/hid.GS1/protocol
|
||||
echo -ne \\x34 > functions/hid.GS1/report_length
|
||||
echo -ne \\x5\\x1\\x9\\x2\\xa1\\x1\\x9\\x1\\xa1\\x0\\x5\\x9\\x19\\x1\\x29\\x3\\x15\\x0\\x25\\x1\\x95\\x3\\x75\\x1\\x81\\x2\\x95\\x1\\x75\\x5\\x81\\x3\\x5\\x1\\x9\\x30\\x9\\x31\\x9\\x38\\x15\\x81\\x25\\x7f\\x75\\x8\\x95\\x3\\x81\\x6\\xc0\\xc0 > functions/hid.GS1/report_desc
|
||||
ln -s functions/hid.GS1 configs/c.1
|
||||
|
||||
# touchpad
|
||||
mkdir functions/hid.GS2
|
||||
if [ -e /boot/BIOS ]
|
||||
then
|
||||
echo 1 > functions/hid.GS2/subclass
|
||||
fi
|
||||
if [ ! -e /boot/usb.notwakeup ]
|
||||
then
|
||||
echo 1 > functions/hid.GS2/wakeup_on_write
|
||||
fi
|
||||
echo 2 > functions/hid.GS2/protocol
|
||||
echo 6 > functions/hid.GS2/report_length
|
||||
echo -ne \\x05\\x01\\x09\\x02\\xa1\\x01\\x09\\x01\\xa1\\x00\\x05\\x09\\x19\\x01\\x29\\x03\\x15\\x00\\x25\\x01\\x95\\x03\\x75\\x01\\x81\\x02\\x95\\x01\\x75\\x05\\x81\\x01\\x05\\x01\\x09\\x30\\x09\\x31\\x15\\x00\\x26\\xff\\x7f\\x35\\x00\\x46\\xff\\x7f\\x75\\x10\\x95\\x02\\x81\\x02\\x05\\x01\\x09\\x38\\x15\\x81\\x25\\x7f\\x35\\x00\\x45\\x00\\x75\\x08\\x95\\x01\\x81\\x06\\xc0\\xc0 > functions/hid.GS2/report_desc
|
||||
ln -s functions/hid.GS2 configs/c.1
|
||||
fi
|
||||
|
||||
if [ -e /boot/usb.disk0 ]
|
||||
then
|
||||
mkdir functions/mass_storage.disk0
|
||||
ln -s functions/mass_storage.disk0 configs/c.1/
|
||||
echo 1 > functions/mass_storage.disk0/lun.0/removable
|
||||
if [ -e /boot/usb.disk0.ro ]
|
||||
then
|
||||
echo 1 > functions/mass_storage.disk0/lun.0/ro
|
||||
echo 0 > functions/mass_storage.disk0/lun.0/cdrom
|
||||
fi
|
||||
disk=$(cat /boot/usb.disk0)
|
||||
if [ -z "${disk}" ]
|
||||
then
|
||||
# if [ ! -e /mnt/usbdisk.img ]
|
||||
# then
|
||||
# fallocate -l 8G /mnt/usbdisk.img
|
||||
# mkfs.vfat /mnt/usbdisk.img
|
||||
# fi
|
||||
echo /dev/mmcblk0p3 > functions/mass_storage.disk0/lun.0/file
|
||||
else
|
||||
cat /boot/usb.disk0 > functions/mass_storage.disk0/lun.0/file
|
||||
fi
|
||||
fi
|
||||
|
||||
ls /sys/class/udc/ > UDC
|
||||
echo device > /proc/cviusb/otg_role
|
||||
}
|
||||
|
||||
start_usb_host(){
|
||||
echo '' > /sys/kernel/config/usb_gadget/g0/UDC
|
||||
echo host > /proc/cviusb/otg_role
|
||||
}
|
||||
|
||||
restart_usb_dev(){
|
||||
echo > /sys/kernel/config/usb_gadget/g0/UDC
|
||||
sleep 1
|
||||
ls /sys/class/udc/ | cat > /sys/kernel/config/usb_gadget/g0/UDC
|
||||
echo "USB Restart OK!"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start_usb_dev
|
||||
;;
|
||||
restart)
|
||||
restart_usb_dev
|
||||
;;
|
||||
stop)
|
||||
start_usb_host
|
||||
;;
|
||||
stop_start)
|
||||
start_usb_host
|
||||
start_usb_dev
|
||||
;;
|
||||
esac
|
||||
234
kvmapp/system/init.d/S15kvmhwd
Executable file
234
kvmapp/system/init.d/S15kvmhwd
Executable file
@@ -0,0 +1,234 @@
|
||||
#!/bin/sh
|
||||
# kvmhwd Rev2.4
|
||||
|
||||
Alpha_OLED_RST_Pin=371
|
||||
Beta_OLED_RST_Pin=502
|
||||
Beta_OLED_SCL=595
|
||||
Beta_OLED_SDA=507
|
||||
WiFi_EN_Pin=506
|
||||
Alpha_PWR_LED_Pin=504
|
||||
Alpha_HDD_LED_Pin=505
|
||||
Alpha_PWR_KEY_Pin=503
|
||||
Alpha_RST_KEY_Pin=507
|
||||
Beta_PWR_LED_Pin=504
|
||||
Beta_PWR_KEY_Pin=503
|
||||
Beta_RST_KEY_Pin=505
|
||||
PCIe_HDMI_RST_Pin=451
|
||||
|
||||
init_alpha_hw(){
|
||||
devmem 0x030010D0 32 0x2 # I2C1_SCL
|
||||
devmem 0x030010DC 32 0x2 # I2C1_SDA
|
||||
devmem 0x030010D4 32 0x3 # OLED_RST
|
||||
devmem 0x0300103C 32 0x3 # GPIOA15
|
||||
devmem 0x03001050 32 0x3 # GPIOA22
|
||||
devmem 0x0300105C 32 0x3 # GPIOA23
|
||||
devmem 0x03001060 32 0x3 # GPIOA24
|
||||
devmem 0x03001054 32 0x3 # GPIOA25
|
||||
devmem 0x03001058 32 0x3 # GPIOA27
|
||||
|
||||
devmem 0x03001068 32 0x6 # GPIOA 18 UART1 RX
|
||||
devmem 0x03001064 32 0x6 # GPIOA 19 UART1 TX
|
||||
devmem 0x03001070 32 0x2 # GPIOA 28 UART2 TX
|
||||
devmem 0x03001074 32 0x2 # GPIOA 29 UART2 RX
|
||||
|
||||
echo ${Alpha_OLED_RST_Pin} > /sys/class/gpio/export # OLED_RST
|
||||
echo out > /sys/class/gpio/gpio${Alpha_OLED_RST_Pin}/direction
|
||||
echo 1 > /sys/class/gpio/gpio${Alpha_OLED_RST_Pin}/value
|
||||
|
||||
echo ${Alpha_PWR_LED_Pin} > /sys/class/gpio/export # pwr led
|
||||
echo ${Alpha_HDD_LED_Pin} > /sys/class/gpio/export # hdd led
|
||||
echo ${Alpha_PWR_KEY_Pin} > /sys/class/gpio/export # pwr key
|
||||
echo ${Alpha_RST_KEY_Pin} > /sys/class/gpio/export # rst key
|
||||
|
||||
echo in > /sys/class/gpio/gpio${Alpha_PWR_LED_Pin}/direction # pwr led
|
||||
echo in > /sys/class/gpio/gpio${Alpha_HDD_LED_Pin}/direction # hdd led
|
||||
echo out > /sys/class/gpio/gpio${Alpha_PWR_KEY_Pin}/direction # pwr key
|
||||
echo out > /sys/class/gpio/gpio${Alpha_RST_KEY_Pin}/direction # rst key
|
||||
|
||||
rmmod /mnt/system/ko/i2c-gpio.ko
|
||||
rmmod /mnt/system/ko/i2c-algo-bit.ko
|
||||
|
||||
# rm /etc/init.d/S25wifimod
|
||||
# rm /etc/init.d/S30wifi
|
||||
}
|
||||
|
||||
init_beta_pcie_hw(){
|
||||
devmem 0x0300103C 32 0x3 # GPIOA15
|
||||
devmem 0x03001050 32 0x3 # GPIOA22
|
||||
devmem 0x0300105C 32 0x3 # GPIOA23
|
||||
devmem 0x03001060 32 0x3 # GPIOA24
|
||||
devmem 0x03001054 32 0x3 # GPIOA25
|
||||
devmem 0x03001058 32 0x3 # GPIOA27
|
||||
|
||||
devmem 0x030010E4 32 0x0 # SDIO CLK
|
||||
devmem 0x030010E0 32 0x0 # SDIO CMD
|
||||
devmem 0x030010DC 32 0x0 # SDIO D0
|
||||
devmem 0x030010D8 32 0x0 # SDIO D1
|
||||
devmem 0x030010D4 32 0x0 # SDIO D2
|
||||
devmem 0x030010D0 32 0x0 # SDIO D3
|
||||
|
||||
devmem 0x03001068 32 0x6 # GPIOA 18 UART1 RX
|
||||
devmem 0x03001064 32 0x6 # GPIOA 19 UART1 TX
|
||||
devmem 0x03001070 32 0x2 # GPIOA 28 UART2 TX
|
||||
devmem 0x03001074 32 0x2 # GPIOA 29 UART2 RX
|
||||
|
||||
echo ${Beta_OLED_RST_Pin} > /sys/class/gpio/export # Beta OLED_RST
|
||||
echo out > /sys/class/gpio/gpio${Beta_OLED_RST_Pin}/direction
|
||||
echo 1 > /sys/class/gpio/gpio${Beta_OLED_RST_Pin}/value
|
||||
|
||||
echo ${Beta_PWR_LED_Pin} > /sys/class/gpio/export # pwr led
|
||||
echo ${Beta_PWR_KEY_Pin} > /sys/class/gpio/export # pwr key
|
||||
echo ${Beta_RST_KEY_Pin} > /sys/class/gpio/export # rst key
|
||||
echo ${PCIe_HDMI_RST_Pin} > /sys/class/gpio/export # hdmi rst key
|
||||
|
||||
echo in > /sys/class/gpio/gpio${Beta_PWR_LED_Pin}/direction # pwr led
|
||||
echo out > /sys/class/gpio/gpio${Beta_PWR_KEY_Pin}/direction # pwr key
|
||||
echo out > /sys/class/gpio/gpio${Beta_RST_KEY_Pin}/direction # rst key
|
||||
echo out > /sys/class/gpio/gpio${PCIe_HDMI_RST_Pin}/direction # rst key
|
||||
|
||||
echo 1 > /sys/class/gpio/gpio${PCIe_HDMI_RST_Pin}/value # rst key
|
||||
|
||||
rmmod /mnt/system/ko/i2c-gpio.ko
|
||||
rmmod /mnt/system/ko/i2c-algo-bit.ko
|
||||
insmod /mnt/system/ko/i2c-algo-bit.ko
|
||||
insmod /mnt/system/ko/i2c-gpio.ko
|
||||
}
|
||||
|
||||
kvm_hw_detect(){
|
||||
if [ ! -e /etc/kvm/hdmi_version ]
|
||||
then
|
||||
rm /etc/kvm/hw
|
||||
fi
|
||||
if [ -e /etc/kvm/hw ]
|
||||
then
|
||||
echo "/etc/kvm/hw exist"
|
||||
else
|
||||
echo "/etc/kvm/hw not exist"
|
||||
if [ -d "/etc/kvm/" ]
|
||||
then
|
||||
echo "/etc/kvm/ exist"
|
||||
else
|
||||
mkdir /etc/kvm/
|
||||
fi
|
||||
devmem 0x0300104C 32 0x3 # GPIOA26 / WiFi_EN
|
||||
echo ${WiFi_EN_Pin} > /sys/class/gpio/export # WiFi_EN
|
||||
echo out > /sys/class/gpio/gpio${WiFi_EN_Pin}/direction
|
||||
echo 0 > /sys/class/gpio/gpio${WiFi_EN_Pin}/value
|
||||
|
||||
devmem 0x0300103C 32 0x3 # GPIOA15
|
||||
devmem 0x03001050 32 0x3 # GPIOA22
|
||||
devmem 0x0300105C 32 0x3 # GPIOA23
|
||||
devmem 0x03001060 32 0x3 # GPIOA24
|
||||
devmem 0x03001054 32 0x3 # GPIOA25
|
||||
devmem 0x03001058 32 0x3 # GPIOA27
|
||||
|
||||
devmem 0x030010D0 32 0x2 # I2C1_SCL
|
||||
devmem 0x030010DC 32 0x2 # I2C1_SDA
|
||||
devmem 0x030010D4 32 0x3 # OLED_RST
|
||||
echo ${Alpha_OLED_RST_Pin} > /sys/class/gpio/export # OLED_RST
|
||||
echo out > /sys/class/gpio/gpio${Alpha_OLED_RST_Pin}/direction
|
||||
echo 1 > /sys/class/gpio/gpio${Alpha_OLED_RST_Pin}/value
|
||||
|
||||
kvm_tmp=$(i2cdetect -ry 1 0x3d 0x3d | grep 3d)
|
||||
if [ -n "$kvm_tmp" ]
|
||||
then
|
||||
# alpha hw
|
||||
echo "alpha" > /etc/kvm/hw
|
||||
else
|
||||
# beta/pcie hw
|
||||
echo ${Alpha_OLED_RST_Pin} > /sys/class/gpio/unexport # OLED_RST
|
||||
echo ${Beta_OLED_RST_Pin} > /sys/class/gpio/export # Beta_OLED_RST_Pin
|
||||
echo out > /sys/class/gpio/gpio${Beta_OLED_RST_Pin}/direction
|
||||
echo 1 > /sys/class/gpio/gpio${Beta_OLED_RST_Pin}/value
|
||||
|
||||
rmmod /mnt/system/ko/i2c-gpio.ko
|
||||
rmmod /mnt/system/ko/i2c-algo-bit.ko
|
||||
insmod /mnt/system/ko/i2c-algo-bit.ko
|
||||
insmod /mnt/system/ko/i2c-gpio.ko
|
||||
|
||||
kvm_tmp=$(i2cdetect -ry 4 0x2c 0x2c | grep 2c)
|
||||
if [ -n "$kvm_tmp" ]
|
||||
then
|
||||
echo "c" > /etc/kvm/hdmi_version
|
||||
else
|
||||
kvm_tmp1=$(i2cdetect -ry 4 0x2b 0x2b | grep 2b)
|
||||
if [ -n "$kvm_tmp1" ]
|
||||
then
|
||||
echo "ux" > /etc/kvm/hdmi_version
|
||||
else
|
||||
echo "ue" > /etc/kvm/hdmi_version
|
||||
fi
|
||||
fi
|
||||
|
||||
kvm_tmp=$(i2cdetect -ry 5 0x3c 0x3c | grep 3c)
|
||||
if [ -n "$kvm_tmp" ]
|
||||
then
|
||||
echo "pcie" > /etc/kvm/hw
|
||||
sync
|
||||
reboot
|
||||
else
|
||||
echo "beta" > /etc/kvm/hw
|
||||
# rm /etc/init.d/S25wifimod
|
||||
# rm /etc/init.d/S30wifi
|
||||
sync
|
||||
fi
|
||||
|
||||
echo 1 > /sys/class/gpio/gpio${WiFi_EN_Pin}/value
|
||||
echo ${WiFi_EN_Pin} > /sys/class/gpio/unexport # WiFi_EN
|
||||
fi
|
||||
fi
|
||||
sync
|
||||
}
|
||||
|
||||
kvm_hw_init(){
|
||||
FIND_FILE="/etc/kvm/hw"
|
||||
if [ `grep -c "alpha" $FIND_FILE` -ne '0' ]
|
||||
then
|
||||
echo "hw = alpha!"
|
||||
init_alpha_hw
|
||||
fi
|
||||
if [ `grep -c "beta" $FIND_FILE` -ne '0' ]
|
||||
then
|
||||
echo "hw = beta!"
|
||||
init_beta_pcie_hw
|
||||
fi
|
||||
if [ `grep -c "pcie" $FIND_FILE` -ne '0' ]
|
||||
then
|
||||
echo "hw = pcie!"
|
||||
init_beta_pcie_hw
|
||||
fi
|
||||
}
|
||||
|
||||
kvm_hdmi_version_detect(){
|
||||
hdmi_tmp=$(i2cdetect -ry 4 0x2c 0x2c | grep 2c)
|
||||
if [ -n "$hdmi_tmp" ]
|
||||
then
|
||||
echo "c" > /etc/kvm/hdmi_version
|
||||
else
|
||||
hdmi_tmp1=$(i2cdetect -ry 4 0x2b 0x2b | grep 2b)
|
||||
if [ -n "$hdmi_tmp1" ]
|
||||
then
|
||||
echo "ux" > /etc/kvm/hdmi_version
|
||||
else
|
||||
echo "ue" > /etc/kvm/hdmi_version
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
kvm_hw_detect
|
||||
kvm_hw_init
|
||||
;;
|
||||
re-detect)
|
||||
rm /etc/kvm/hw
|
||||
kvm_hw_detect
|
||||
kvm_hw_init
|
||||
;;
|
||||
re-init)
|
||||
kvm_hw_init
|
||||
;;
|
||||
get_hdmi_version)
|
||||
kvm_hdmi_version_detect
|
||||
;;
|
||||
esac
|
||||
115
kvmapp/system/init.d/S30eth
Executable file
115
kvmapp/system/init.d/S30eth
Executable file
@@ -0,0 +1,115 @@
|
||||
#!/bin/sh
|
||||
|
||||
. /etc/profile
|
||||
|
||||
# /boot/eth.nodhcp for example
|
||||
# ipaddr/net gw[optional]
|
||||
# 192.168.0.101/24 192.168.0.1
|
||||
# 192.168.3.116/22
|
||||
|
||||
RESERVE_INET="192.168.0.1/24"
|
||||
|
||||
start() {
|
||||
printf "start ethernet: "
|
||||
if [ -e /boot/eth.nodhcp ]
|
||||
then
|
||||
[ -e /boot/eth.nodhcp ] &&
|
||||
cat /boot/eth.nodhcp | while read inet gw
|
||||
do
|
||||
addr=${inet%/*}
|
||||
netid=${inet#*/}
|
||||
[ -z $gw ] &&
|
||||
gw=$( echo $addr| ( IFS='.' read a b c d; echo $((
|
||||
(((((($a<<8)+$b)<<8)+$c)<<8)+$d)
|
||||
& (((1<<$netid)-1)<<(32-$netid))
|
||||
))
|
||||
)) &&
|
||||
gw=$(($gw>>24&0xff)).$(($gw>>16&0xff)).$(($gw>>8&0xff)).$((1+( $gw>>0&0xff )))
|
||||
|
||||
arping -Dqc2 -Ieth0 $addr || continue
|
||||
ip a add $inet brd + dev eth0
|
||||
ip r add default via $gw dev eth0
|
||||
cat > /etc/resolve.conf << EOF
|
||||
nameserver $gw
|
||||
nameserver 8.8.8.8
|
||||
nameserver 114.114.114.114
|
||||
EOF
|
||||
break
|
||||
done &&
|
||||
ip a show dev eth0|grep inet || (
|
||||
udhcpc -i eth0 -t 3 -T 1 -A 5 -b -p /run/udhcpc.eth0.pid &>/dev/null
|
||||
ip a show dev eth0|grep inet
|
||||
) || (
|
||||
# failed to apply dynamic addr, need a available static addr to visit the LAN
|
||||
inet=$RESERVE_INET
|
||||
addr=${inet%/*}
|
||||
ip a add $inet brd + dev eth0
|
||||
) || exit 1
|
||||
else
|
||||
(udhcpc -i eth0 -t 10 -T 1 -A 5 -b -p /run/udhcpc.eth0.pid) &
|
||||
fi
|
||||
|
||||
echo "OK"
|
||||
}
|
||||
stop() {
|
||||
[[ ! -e "/run/udhcpc.eth0.pid" ]] && echo "udhcpc is not running..." && exit 1
|
||||
kill `cat /run/udhcpc.eth0.pid`
|
||||
rm /run/udhcpc.eth0.pid
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
restart|reload)
|
||||
$0 stop
|
||||
$0 start
|
||||
;;
|
||||
doc)
|
||||
cat <<EOF
|
||||
If you want to set your own static ip address
|
||||
rather then fetch it by dhcp.
|
||||
Then you need to create a file \`/boot/eth.nodhcp\`,
|
||||
and edit it following the format below:
|
||||
\`addr/netid gw[optional]\` in one line
|
||||
for example (support muti definations, checks with arp):
|
||||
1. 192.168.0.101/24 192.168.0.1
|
||||
2. 192.168.3.116/22
|
||||
Delete this file if you change to use dhcp default.
|
||||
ATTENTION: If all the STATIC address you set in the file is
|
||||
already in use on this LAN checked with arp, then none
|
||||
of them will be yours. And it will trigger dhcp, try to
|
||||
fetch DYNAMIC one. If it's also failed, your address will
|
||||
be the REVERSE one, 192.168.0.1/24. Just to be sure that
|
||||
you can visit your NanoKVM device through eth to fix some
|
||||
problems by ssh, unless you must disassemble it and modify
|
||||
or rewrite the TFCard once again.
|
||||
EOF
|
||||
echo ""
|
||||
cat <<EOF
|
||||
如果您想自行设置静态 ip 地址而不是使用 dhcp 来获取它。
|
||||
|
||||
那么您需要新建一个文件 \`/boot/eth.nodhcp\`,然后按照以下格式编辑它:
|
||||
\`addr/netid gw[optional]\` 在一行内
|
||||
示例(支持多个预设,会通过 arp 检查是否占用):
|
||||
1. 192.168.0.101/24 192.168.0.1
|
||||
2. 192.168.3.116/22
|
||||
|
||||
如果您想改回默认 dhcp 获取 ip 地址的方式,删除该文件后即可恢复。
|
||||
|
||||
注意:如果您预设的所有静态 ip 地址都被 arp 检测到已占用,则不会成功设置。
|
||||
此时会触发 dhcp 尝试获取 ip 地址,如果仍失败,那么最终会强制设置成
|
||||
192.168.90.1/24。这是为了保证您的 NanoKVM 设备总是能有一个可用的
|
||||
ip 地址,方便您通过局域网 ssh 连接设备进行一些修复,否则您可能需要
|
||||
拆开外壳以手动修改 TF 卡或重新烧卡。
|
||||
|
||||
|
||||
EOF
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart}"
|
||||
exit 1
|
||||
esac
|
||||
175
kvmapp/system/init.d/S30wifi
Executable file
175
kvmapp/system/init.d/S30wifi
Executable file
@@ -0,0 +1,175 @@
|
||||
#!/bin/sh
|
||||
|
||||
. /etc/profile
|
||||
|
||||
gen_hostapd_conf() {
|
||||
ssid="${1}"
|
||||
pass="${2}"
|
||||
echo "ctrl_interface=/var/run/hostapd"
|
||||
echo "ctrl_interface_group=0"
|
||||
echo "ssid=${ssid}"
|
||||
echo "hw_mode=g"
|
||||
echo "channel=1"
|
||||
echo "beacon_int=100"
|
||||
echo "dtim_period=2"
|
||||
echo "max_num_sta=255"
|
||||
echo "rts_threshold=-1"
|
||||
echo "fragm_threshold=-1"
|
||||
echo "macaddr_acl=0"
|
||||
echo "auth_algs=3"
|
||||
echo "wpa=2"
|
||||
echo "wpa_passphrase=${pass}"
|
||||
echo "ieee80211n=1"
|
||||
}
|
||||
|
||||
gen_udhcpd_conf() {
|
||||
interface=${1}
|
||||
ipv4_prefix=${2}
|
||||
echo "start ${ipv4_prefix}.100"
|
||||
echo "end ${ipv4_prefix}.200"
|
||||
echo "interface ${interface}"
|
||||
echo "pidfile /var/run/udhcpd.${interface}.pid"
|
||||
echo "lease_file /var/lib/misc/udhcpd.${interface}.leases"
|
||||
echo "option subnet 255.255.255.0"
|
||||
echo "option lease 864000"
|
||||
}
|
||||
|
||||
gen_dnsmasq_conf() {
|
||||
ipv4_prefix=${1}
|
||||
echo "bind-interfaces"
|
||||
echo "interface=wlan0"
|
||||
echo "address=/config.kvm/${ipv4_prefix}.1"
|
||||
echo "no-hosts"
|
||||
echo "dhcp-range=${ipv4_prefix}.2,${ipv4_prefix}.254"
|
||||
echo "dhcp-option=6,${ipv4_prefix}.1"
|
||||
echo "dhcp-option=3,${ipv4_prefix}.1"
|
||||
echo "dhcp-authoritative"
|
||||
}
|
||||
|
||||
start() {
|
||||
echo "wifi mode: sta"
|
||||
ssid=""
|
||||
pass=""
|
||||
if [ -e /boot/wifi.ssid ] && [ -e /boot/wifi.pass ]; then
|
||||
echo "Updating WiFi credentials from /boot to /etc/kvm/"
|
||||
|
||||
rm -f /etc/kvm/wifi.ssid /etc/kvm/wifi.pass
|
||||
|
||||
mv /boot/wifi.ssid /etc/kvm/wifi.ssid
|
||||
mv /boot/wifi.pass /etc/kvm/wifi.pass
|
||||
|
||||
chown root:root /etc/kvm/wifi.ssid /etc/kvm/wifi.pass
|
||||
chmod 644 /etc/kvm/wifi.ssid /etc/kvm/wifi.pass
|
||||
fi
|
||||
if [ -e /etc/kvm/wifi.ssid ]
|
||||
then
|
||||
ssid=`cat /etc/kvm/wifi.ssid`
|
||||
fi
|
||||
if [ -e /etc/kvm/wifi.pass ]
|
||||
then
|
||||
pass=`cat /etc/kvm/wifi.pass`
|
||||
fi
|
||||
echo "ctrl_interface=/var/run/wpa_supplicant" > /etc/wpa_supplicant.conf
|
||||
wpa_passphrase "$ssid" "$pass" >> /etc/wpa_supplicant.conf
|
||||
wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant.conf
|
||||
if [ ! -e /boot/wifi.nodhcp ]
|
||||
then
|
||||
(udhcpc -i wlan0 -t 10 -T 1 -A 5 -b -p /run/udhcpc.wlan0.pid) &
|
||||
fi
|
||||
}
|
||||
|
||||
ap_start() {
|
||||
echo "wifi mode: ap"
|
||||
id2=$(printf "%d" 0x$(sha512sum /sys/class/cvi-base/base_uid | head -c 2))
|
||||
id3=$(printf "%d" 0x$(sha512sum /sys/class/cvi-base/base_uid | head -c 4 | tail -c 2))
|
||||
if [ "$id2" = "$id3" ]
|
||||
then
|
||||
id2=$((id2 + 1))
|
||||
fi
|
||||
if [ "$id2" -ge 255 ]
|
||||
then
|
||||
id2=253
|
||||
fi
|
||||
if [ "$id3" -ge 255 ]
|
||||
then
|
||||
id3=254
|
||||
fi
|
||||
ssid=""
|
||||
pass=""
|
||||
if [ -e /kvmapp/kvm/ap.ssid ]
|
||||
then
|
||||
ssid=`cat /kvmapp/kvm/ap.ssid`
|
||||
fi
|
||||
if [ -e /kvmapp/kvm/ap.pass ]
|
||||
then
|
||||
pass=`cat /kvmapp/kvm/ap.pass`
|
||||
fi
|
||||
gen_hostapd_conf "$ssid" "$pass" > /etc/hostapd.conf
|
||||
ipv4_prefix=10.$id3.$id2
|
||||
if [ ! -e /etc/udhcpd.wlan0.conf ]
|
||||
then
|
||||
gen_udhcpd_conf wlan0 "${ipv4_prefix}" > /etc/udhcpd.wlan0.conf
|
||||
fi
|
||||
# gen_dnsmasq_conf "${ipv4_prefix}" > /etc/dnsmasq.conf
|
||||
|
||||
# iptables --policy INPUT ACCEPT
|
||||
# iptables --policy FORWARD ACCEPT
|
||||
# iptables --policy OUTPUT ACCEPT
|
||||
# iptables -F
|
||||
# iptables -t nat -F
|
||||
# iptables -t nat -A PREROUTING -i wlan0 -p udp --dport 53 -j DNAT --to ${ipv4_prefix}.1
|
||||
|
||||
ifconfig wlan0 up
|
||||
ip route del default || true
|
||||
ip add flush dev wlan0
|
||||
ip addr add $ipv4_prefix.1/24 dev wlan0
|
||||
# dnsmasq --pid-file=/tmp/dnsmasq.run -C /etc/dnsmasq.conf
|
||||
hostapd -B -i wlan0 /etc/hostapd.conf
|
||||
udhcpd -S /etc/udhcpd.wlan0.conf
|
||||
}
|
||||
|
||||
stop() {
|
||||
ps -ef|grep hostapd|grep -v grep|awk '{print $1}'|xargs kill -2 || true
|
||||
ps -ef|grep "udhcpd -S /etc/udhcpd.wlan0.conf" |grep -v grep|awk '{print $1}'|xargs kill -2 || true
|
||||
killall wpa_supplicant || true
|
||||
if [ -e /run/udhcpc.wlan0.pid ]
|
||||
then
|
||||
kill `cat /run/udhcpc.wlan0.pid` || true
|
||||
rm -f /run/udhcpc.wlan0.pid
|
||||
fi
|
||||
if [ -e /var/run/udhcpd.wlan0.pid ]
|
||||
then
|
||||
kill `cat /var/run/udhcpd.wlan0.pid` || true
|
||||
rm -f /var/run/udhcpd.wlan0.pid
|
||||
fi
|
||||
if [ -e /tmp/dnsmasq.run ]
|
||||
then
|
||||
kill `cat /tmp/dnsmasq.run` || true
|
||||
rm -f /tmp/dnsmasq.run
|
||||
fi
|
||||
# if [ -e /etc/wpa_supplicant.conf]
|
||||
# then
|
||||
# echo > /etc/wpa_supplicant.conf
|
||||
# fi
|
||||
airmon-ng stop wlan0mon || true
|
||||
}
|
||||
|
||||
restart() {
|
||||
stop
|
||||
start
|
||||
}
|
||||
|
||||
if [ "${1}" = "start" ]
|
||||
then
|
||||
start
|
||||
elif [ "${1}" = "stop" ]
|
||||
then
|
||||
stop
|
||||
elif [ "${1}" = "ap" ]
|
||||
then
|
||||
stop
|
||||
ap_start
|
||||
elif [ "${1}" = "restart" ]
|
||||
then
|
||||
restart
|
||||
fi
|
||||
20
kvmapp/system/init.d/S50avahi-daemon
Executable file
20
kvmapp/system/init.d/S50avahi-daemon
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# avahi-daemon init script
|
||||
|
||||
DAEMON=/usr/sbin/avahi-daemon
|
||||
case "$1" in
|
||||
start)
|
||||
$DAEMON -c || $DAEMON -D
|
||||
;;
|
||||
stop)
|
||||
$DAEMON -c && $DAEMON -k
|
||||
;;
|
||||
reload)
|
||||
$DAEMON -c && $DAEMON -r
|
||||
;;
|
||||
*)
|
||||
echo "Usage: S50avahi-daemon {start|stop|reload}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
49
kvmapp/system/init.d/S50ssdpd
Executable file
49
kvmapp/system/init.d/S50ssdpd
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/bin/sh
|
||||
|
||||
DAEMON=ssdpd
|
||||
PIDFILE=/var/run/$DAEMON.pid
|
||||
CFGFILE=/etc/default/$DAEMON
|
||||
|
||||
DAEMON_ARGS=""
|
||||
|
||||
# Read configuration variable file if it is present
|
||||
# shellcheck source=/dev/null
|
||||
[ -r "$CFGFILE" ] && . "$CFGFILE"
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
start() {
|
||||
printf 'Starting %s: ' "$DAEMON"
|
||||
if start-stop-daemon -S -q -p "$PIDFILE" -x "$DAEMON" -- $DAEMON_ARGS; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FAIL"
|
||||
fi
|
||||
}
|
||||
|
||||
stop() {
|
||||
printf 'Stopping %s: ' "$DAEMON"
|
||||
if start-stop-daemon -K -q -p "$PIDFILE" -x "$DAEMON"; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FAIL"
|
||||
fi
|
||||
}
|
||||
|
||||
restart() {
|
||||
stop
|
||||
start
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start|stop|restart)
|
||||
"$1"
|
||||
;;
|
||||
reload)
|
||||
restart
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|reload}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit $?
|
||||
73
kvmapp/system/init.d/S50sshd
Executable file
73
kvmapp/system/init.d/S50sshd
Executable file
@@ -0,0 +1,73 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# sshd Starts sshd.
|
||||
#
|
||||
|
||||
# Ensure required binaries exist
|
||||
[ -x /usr/bin/ssh-keygen ] || exit 0
|
||||
[ -x /usr/sbin/sshd ] || exit 1
|
||||
|
||||
PIDFILE="/var/run/sshd.pid"
|
||||
|
||||
umask 077
|
||||
|
||||
startssh() {
|
||||
# Generate SSH keys if they do not exist
|
||||
[ ! -f /etc/ssh/ssh_host_rsa_key ] && /usr/bin/ssh-keygen -A
|
||||
|
||||
printf "Starting sshd: "
|
||||
/usr/sbin/sshd
|
||||
touch /var/lock/sshd
|
||||
echo "OK"
|
||||
}
|
||||
|
||||
start() {
|
||||
if [ -e /etc/kvm/ssh_stop ]; then
|
||||
if [ -e /boot/start_ssh_once ]; then
|
||||
rm -f /boot/start_ssh_once
|
||||
startssh
|
||||
else
|
||||
echo "SSH does not start"
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
startssh
|
||||
fi
|
||||
}
|
||||
|
||||
stop() {
|
||||
printf "Stopping sshd: "
|
||||
killall sshd 2>/dev/null
|
||||
rm -f /var/lock/sshd
|
||||
echo "OK"
|
||||
}
|
||||
|
||||
restart() {
|
||||
stop
|
||||
start
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
restart|reload)
|
||||
restart
|
||||
;;
|
||||
permanent_on)
|
||||
rm -f /etc/kvm/ssh_stop
|
||||
start
|
||||
;;
|
||||
permanent_off)
|
||||
touch /etc/kvm/ssh_stop
|
||||
stop
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|permanent_on|permanent_off}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit 0
|
||||
29
kvmapp/system/init.d/S80dnsmasq
Executable file
29
kvmapp/system/init.d/S80dnsmasq
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
|
||||
DAEMON="dnsmasq"
|
||||
PIDFILE="/var/run/$DAEMON.pid"
|
||||
|
||||
[ -f /etc/dnsmasq.conf ] || exit 0
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
printf "Starting dnsmasq: "
|
||||
start-stop-daemon -S -p "$PIDFILE" -x "/usr/sbin/$DAEMON" -- \
|
||||
--pid-file="$PIDFILE"
|
||||
[ $? = 0 ] && echo "OK" || echo "FAIL"
|
||||
;;
|
||||
stop)
|
||||
printf "Stopping dnsmasq: "
|
||||
start-stop-daemon -K -q -p "$PIDFILE" -x "/usr/sbin/$DAEMON"
|
||||
[ $? = 0 ] && echo "OK" || echo "FAIL"
|
||||
;;
|
||||
restart|reload)
|
||||
$0 stop
|
||||
$0 start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit 0
|
||||
69
kvmapp/system/init.d/S95nanokvm
Executable file
69
kvmapp/system/init.d/S95nanokvm
Executable file
@@ -0,0 +1,69 @@
|
||||
#!/bin/sh
|
||||
# nanokvm Rev3.1
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
echo -n kvm > /boot/hostname.prefix
|
||||
|
||||
# Copy sensor config if the file exists
|
||||
[ -f /mnt/data/sensor_cfg.ini.LT ] && cp /mnt/data/sensor_cfg.ini.LT /mnt/data/sensor_cfg.ini
|
||||
|
||||
# Generate unique device key
|
||||
if [ -f /sys/class/cvi-base/base_uid ]; then
|
||||
str_value=$(awk '{print $2}' /sys/class/cvi-base/base_uid)
|
||||
first_uint=$(echo "$str_value" | cut -d'_' -f1)
|
||||
second_uint=$(echo "$str_value" | cut -d'_' -f2)
|
||||
echo "$first_uint$second_uint" > /device_key
|
||||
fi
|
||||
|
||||
# Set iptables rules (skip if already present)
|
||||
iptables -C INPUT -i eth0 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT 2>/dev/null || \
|
||||
iptables -A INPUT -i eth0 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
|
||||
|
||||
iptables -C OUTPUT -o eth0 -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT 2>/dev/null || \
|
||||
iptables -A OUTPUT -o eth0 -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT
|
||||
|
||||
iptables -C OUTPUT -o eth0 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT 2>/dev/null || \
|
||||
iptables -A OUTPUT -o eth0 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
|
||||
|
||||
iptables -C INPUT -i eth0 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT 2>/dev/null || \
|
||||
iptables -A INPUT -i eth0 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
|
||||
|
||||
iptables -C OUTPUT -o eth0 -p tcp --sport 8000 -m state --state ESTABLISHED -j DROP 2>/dev/null || \
|
||||
iptables -A OUTPUT -o eth0 -p tcp --sport 8000 -m state --state ESTABLISHED -j DROP
|
||||
|
||||
# Start services
|
||||
cp -r /kvmapp/kvm_system /tmp/
|
||||
/tmp/kvm_system/kvm_system &
|
||||
|
||||
cp -r /kvmapp/server /tmp/
|
||||
/tmp/server/NanoKVM-Server &
|
||||
;;
|
||||
|
||||
stop)
|
||||
killall kvm_system
|
||||
killall NanoKVM-Server
|
||||
rm -r /tmp/kvm_system /tmp/server
|
||||
echo "OK"
|
||||
;;
|
||||
|
||||
restart)
|
||||
killall kvm_system
|
||||
killall NanoKVM-Server
|
||||
rm -r /tmp/kvm_system /tmp/server
|
||||
|
||||
cp -r /kvmapp/kvm_system /tmp/
|
||||
/tmp/kvm_system/kvm_system &
|
||||
|
||||
cp -r /kvmapp/server /tmp/
|
||||
/tmp/server/NanoKVM-Server &
|
||||
|
||||
sync
|
||||
echo "OK"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
114
kvmapp/system/init.d/S98tailscaled
Executable file
114
kvmapp/system/init.d/S98tailscaled
Executable file
@@ -0,0 +1,114 @@
|
||||
#!/bin/sh
|
||||
|
||||
DAEMON="tailscaled"
|
||||
PIDFILE="/var/run/$DAEMON.pid"
|
||||
|
||||
# Set the port to listen on for incoming VPN packets.
|
||||
# Remote nodes will automatically be informed about the new port number,
|
||||
# but you might want to configure this in order to set external firewall
|
||||
# settings.
|
||||
PORT="41641"
|
||||
|
||||
# Extra flags you might want to pass to tailscaled.
|
||||
FLAGS=""
|
||||
|
||||
# You need tailscaled at /usr/sbin to server, and tailscale at /usr/bin to operate
|
||||
# STATIC version needed. Download page at https://pkgs.tailscale.com/stable/#static
|
||||
PKG_URL_LATEST="https://pkgs.tailscale.com/stable/tailscale_latest_riscv64.tgz"
|
||||
[ ! -x /usr/sbin/$DAEMON ] &&
|
||||
echo "/usr/sbin/$DAEMON not found, please download it from $PKG_URL_LATEST" &&
|
||||
echo "Then unpack it, copy $DAEMON to /usr/sbin and copy tailscale to /usr/bin" &&
|
||||
exit 1
|
||||
VERSION=$(/usr/sbin/$DAEMON --version|sed -n '1p'|xargs echo -n)
|
||||
|
||||
[ -x /usr/bin/tailscale ] || echo "/usr/bin/tailscale not found, your installation of tailscale may be broken"
|
||||
|
||||
# just for those need forwarding
|
||||
[ ! -f /etc/sysctl.d/99-tailscale.conf ] &&
|
||||
mkdir -p /etc/sysctl.d/ &&
|
||||
echo "missing /etc/sysctl.d/99-tailscale.conf, try make it below:" &&
|
||||
(tee /etc/sysctl.d/99-tailscale.conf <<EOF
|
||||
net.ipv4.ip_forward = 1
|
||||
net.ipv6.conf.all.forwarding = 1
|
||||
EOF
|
||||
) &&
|
||||
echo "if this message repeats showing, please look at $0 if you need forwarding"
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
tailscale set --accept-dns=false
|
||||
if [ -f /etc/kvm/GOMEMLIMIT ]; then
|
||||
value=$(cat /etc/kvm/GOMEMLIMIT)
|
||||
export GOMEMLIMIT="${value}MiB"
|
||||
else
|
||||
export GOMEMLIMIT=1024MiB
|
||||
fi
|
||||
|
||||
echo "GOMEMLIMIT set to ${GOMEMLIMIT}"
|
||||
printf "Starting $DAEMON[$VERSION]: "
|
||||
start-stop-daemon -S -bmq -p "$PIDFILE" -x "/usr/sbin/$DAEMON" -- \
|
||||
--state=/var/lib/tailscale/tailscaled.state \
|
||||
--socket=/var/run/tailscale/tailscaled.sock \
|
||||
--port=${PORT} \
|
||||
$FLAGS
|
||||
[ $? = 0 ] && echo "OK" || echo "FAIL"
|
||||
;;
|
||||
stop)
|
||||
printf "Stopping $DAEMON: "
|
||||
start-stop-daemon -K -p "$PIDFILE"
|
||||
[ $? = 0 ] && (echo "OK"; rm -f "$PIDFILE") || echo "FAIL"
|
||||
printf "cleaning tailscaled: "
|
||||
/usr/sbin/$DAEMON --cleanup &>/dev/null
|
||||
[ $? = 0 ] && echo "OK" || echo "FAIL"
|
||||
;;
|
||||
restart|reload)
|
||||
$0 stop
|
||||
$0 start
|
||||
;;
|
||||
doc)
|
||||
cat <<EOF
|
||||
To use tailscale, you need to meet these conditions below:
|
||||
1. Both tailscale and tailscaled were installed.
|
||||
2. At least one tailscale's account were registered.
|
||||
(https://login.tailscale.com/start)
|
||||
3. Your NanoKVM device must keep online.
|
||||
|
||||
Then, follow the steps below:
|
||||
1. Login in https://login.tailscale.com on your PC
|
||||
to enter into yout admin console.
|
||||
2. Run 'tailscale login' on your device, and copy the link showed
|
||||
to open on your PC, and confirm the auth.
|
||||
3. Congratulations, your NanoKVM device is now on the tailnet, try to
|
||||
visit it from all your device which on the same tailnet.
|
||||
4. run 'tailscale set --webclient' and manage your device
|
||||
on the web interface from localnet and tailenet.
|
||||
|
||||
Added: no more action to be repeat after your device reboot.
|
||||
EOF
|
||||
echo ""
|
||||
cat <<EOF
|
||||
为了正常使用 tailscale, 你需要满足以下几个条件:
|
||||
1. tailscale 和 tailscaled 这两个静态链接的二进制程序
|
||||
都已经正确安装在系统上。
|
||||
2. 至少注册一个 tailscale 的帐号才能使用其服务。
|
||||
(https://login.tailscale.com/start)
|
||||
3. 你的 NanoKVM 设备必须保持在线。
|
||||
|
||||
然后,跟着以下步骤操作:
|
||||
1. 在你的电脑上登录 https://login.tailscale.com 以进入控制台。
|
||||
2. 在你的 NanoKVM ssh 终端上执行 'tailscale login', 然后复制
|
||||
出现的链接到浏览器上,登录并通过验证。
|
||||
3. 祝贺,你的 NanoKVM 现在已经加入了 tailnet 然后你可以从任意
|
||||
同样已加入同一 tailnet 的设备去访问你的 NanoKVM。
|
||||
4. 执行 'tailscale set --webclient' 然后你就可以从 '局域网' 和
|
||||
'tailnet' 来访问特定网页,用来管理你的设备。
|
||||
|
||||
补充: 不需要重复上面的操作,哪怕你的设备重启过。
|
||||
EOF
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit 0
|
||||
BIN
kvmapp/system/ko/soph_mipi_rx.ko
Normal file
BIN
kvmapp/system/ko/soph_mipi_rx.ko
Normal file
Binary file not shown.
96
kvmapp/system/update-nanokvm.py
Executable file
96
kvmapp/system/update-nanokvm.py
Executable file
@@ -0,0 +1,96 @@
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
import zipfile
|
||||
import pathlib
|
||||
import requests
|
||||
|
||||
temporary: str = "/root/.kvm-cache/"
|
||||
|
||||
|
||||
def mkdir() -> None:
|
||||
if pathlib.Path(temporary).exists():
|
||||
shutil.rmtree(temporary)
|
||||
pathlib.Path(temporary).mkdir()
|
||||
print(f"Created temporary directory {temporary}")
|
||||
|
||||
|
||||
def read(file: str) -> str:
|
||||
return pathlib.Path(file).read_text().replace("\n", "")
|
||||
|
||||
|
||||
def download_firmware() -> None:
|
||||
print("Downloading firmware...")
|
||||
|
||||
now = int(time.time())
|
||||
url = f"https://cdn.sipeed.com/nanokvm/latest.zip?n={now}"
|
||||
print(f"Downloading firmware from {url}")
|
||||
|
||||
response = requests.get(url)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Failed to download firmware, status: {response.status_code}")
|
||||
|
||||
content_type = response.headers.get("content-type")
|
||||
if content_type != "application/zip":
|
||||
raise Exception(f"Failed to download firmware, content_type: {content_type}")
|
||||
|
||||
zip_file = f"{temporary}/latest.zip"
|
||||
with open(zip_file, "wb") as f:
|
||||
for chunk in response.iter_content(chunk_size=1024):
|
||||
f.write(chunk)
|
||||
|
||||
with zipfile.ZipFile(zip_file, "r") as f:
|
||||
f.extractall(temporary)
|
||||
|
||||
print("Completed downloading firmware.")
|
||||
|
||||
|
||||
def update() -> None:
|
||||
backup_dir = pathlib.Path("/root/old")
|
||||
firmware_dir = pathlib.Path("/kvmapp")
|
||||
|
||||
if backup_dir.exists():
|
||||
shutil.rmtree(backup_dir)
|
||||
|
||||
if firmware_dir.exists():
|
||||
firmware_dir.rename(backup_dir)
|
||||
|
||||
pathlib.Path(f"{temporary}/latest").rename(firmware_dir)
|
||||
|
||||
|
||||
def change_permissions() -> None:
|
||||
for root, dirs, files in os.walk("/kvmapp"):
|
||||
os.chmod(root, 0o755)
|
||||
|
||||
for file in files:
|
||||
file_path = os.path.join(root, file)
|
||||
os.chmod(file_path, 0o755)
|
||||
|
||||
print("change permissions done")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
try:
|
||||
print("Stopping nanokvm service...")
|
||||
os.system("/etc/init.d/S95nanokvm stop")
|
||||
|
||||
print("Staring update...")
|
||||
|
||||
mkdir()
|
||||
download_firmware()
|
||||
update()
|
||||
change_permissions()
|
||||
|
||||
version = read("/kvmapp/version")
|
||||
print(f"Successfully updated to version: {version}")
|
||||
print("restart service\nthe nanokvm will reboot")
|
||||
except Exception as e:
|
||||
print(f"update failed\n{e}")
|
||||
finally:
|
||||
shutil.rmtree(temporary)
|
||||
os.system("/etc/init.d/S95nanokvm restart")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -53,16 +53,16 @@ jwt:
|
||||
revokeTokensOnLogout: true
|
||||
|
||||
# Address for custom STUN server
|
||||
# Note: You can disable the STUN service by setting it to 'disable' (e.g., in a LAN environment)
|
||||
stun: stun.l.google.com:19302
|
||||
|
||||
# Address and authentication for custom TURN server
|
||||
turn:
|
||||
turnAddr: turn.cloudflare.com:3478
|
||||
turnAddr: example_addr
|
||||
turnUser: example_user
|
||||
turnCred: example_cred
|
||||
```
|
||||
|
||||
|
||||
## Compile & Deploy
|
||||
|
||||
Note: Use Linux operating system (x86-64). This build process is not compatible with ARM, Windows or macOS.
|
||||
|
||||
@@ -16,7 +16,7 @@ server
|
||||
├── middleware // サーバーミドルウェアコンポーネント
|
||||
├── proto // API リクエスト/レスポンス定義
|
||||
├── router // API ルートハンドラ
|
||||
├── service // コアサービスの実<EFBFBD><EFBFBD>
|
||||
├── service // コアサービスの実装
|
||||
├── utils // ユーティリティ関数
|
||||
└── main.go
|
||||
```
|
||||
@@ -39,17 +39,23 @@ cert:
|
||||
logger:
|
||||
level: info
|
||||
file: stdout
|
||||
|
||||
|
||||
# 認証設定 (enable/disable)
|
||||
# 注意: 認証を無効にするのは開発環境でのみ行ってください
|
||||
authentication: enable
|
||||
|
||||
# JWT 秘密鍵の設定
|
||||
# 空のままにすると、サーバー起動時にランダムな鍵が生成されます
|
||||
secretKey: ""
|
||||
jwt:
|
||||
# JWT 秘密鍵の設定。 空のままにすると、サーバー起動時にランダムな 64 バイトの鍵が自動的に生成されます。
|
||||
secretKey: ""
|
||||
# JWT トークンの有効期限(秒単位)。 デフォルト: 2678400 (31 日)
|
||||
refreshTokenDuration: 2678400
|
||||
# ユーザーがログアウトすると、すべての JWT トークンが無効になります。 デフォルト: true
|
||||
revokeTokensOnLogout: true
|
||||
|
||||
# カスタム STUN サーバーのアドレス
|
||||
stun: stun.l.google.com:19302
|
||||
|
||||
# カスタム TURN サーバーのアドレスと認証情報
|
||||
turn:
|
||||
turnAddr: turn.cloudflare.com:3478
|
||||
turnUser: example_user
|
||||
@@ -80,3 +86,12 @@ turn:
|
||||
1. デプロイ前に、ブラウザでアプリケーションを最新バージョンに更新します。手順は[こちら](https://wiki.sipeed.com/hardware/en/kvm/NanoKVM/system/updating.html)を参照してください。
|
||||
2. コンパイルして生成された `NanoKVM-Server` ファイルを使用して、NanoKVM の `/kvmapp/server/` ディレクトリ内の元のファイルを置き換えます。
|
||||
3. NanoKVM で `/etc/init.d/S95nanokvm restart` を実行してサービスを再起動します。
|
||||
|
||||
## 手動更新
|
||||
|
||||
> ファイルのアップロードには SSH が必要です。Web 設定で有効にしてください: `設定 > SSH`
|
||||
|
||||
1. [GitHub](https://github.com/sipeed/NanoKVM/releases) から最新のアプリケーションをダウンロードします。
|
||||
2. ダウンロードしたファイルを解凍し、解凍したフォルダーの名前を `kvmapp` に変更します。
|
||||
3. NanoKVM 上の既存の `/kvmapp` ディレクトリをバックアップし、新しい `kvmapp` フォルダーに置き換えます。
|
||||
4. NanoKVM で `/etc/init.d/S95nanokvm restart` を実行してサービスを再起動します。
|
||||
|
||||
@@ -50,10 +50,12 @@ jwt:
|
||||
# 在帐号登出时是否使所有 jwt token 失效。默认为 true
|
||||
revokeTokensOnLogout: true
|
||||
|
||||
# 自定义 STUN 服务器的地址
|
||||
# 注意:可以设置为“disable”来禁用 STUN 服务(例如在局域网环境中使用时)
|
||||
stun: stun.l.google.com:19302
|
||||
|
||||
turn:
|
||||
turnAddr: turn.cloudflare.com:3478
|
||||
turnAddr: example_addr
|
||||
turnUser: example_user
|
||||
turnCred: example_cred
|
||||
```
|
||||
|
||||
@@ -20,9 +20,7 @@ var (
|
||||
kvmVisionOnce sync.Once
|
||||
)
|
||||
|
||||
type KvmVision struct {
|
||||
mutex sync.Mutex
|
||||
}
|
||||
type KvmVision struct{}
|
||||
|
||||
func GetKvmVision() *KvmVision {
|
||||
kvmVisionOnce.Do(func() {
|
||||
@@ -44,9 +42,6 @@ func GetKvmVision() *KvmVision {
|
||||
}
|
||||
|
||||
func (k *KvmVision) ReadMjpeg(width uint16, height uint16, quality uint16) (data []byte, result int) {
|
||||
k.mutex.Lock()
|
||||
defer k.mutex.Unlock()
|
||||
|
||||
var (
|
||||
kvmData *C.uint8_t
|
||||
dataSize C.uint32_t
|
||||
@@ -72,10 +67,7 @@ func (k *KvmVision) ReadMjpeg(width uint16, height uint16, quality uint16) (data
|
||||
return
|
||||
}
|
||||
|
||||
func (k *KvmVision) ReadH264(width uint16, height uint16, bitRate uint16) (data []byte, sps []byte, pps []byte, result int) {
|
||||
k.mutex.Lock()
|
||||
defer k.mutex.Unlock()
|
||||
|
||||
func (k *KvmVision) ReadH264(width uint16, height uint16, bitRate uint16) (data []byte, result int) {
|
||||
var (
|
||||
kvmData *C.uint8_t
|
||||
dataSize C.uint32_t
|
||||
@@ -97,48 +89,10 @@ func (k *KvmVision) ReadH264(width uint16, height uint16, bitRate uint16) (data
|
||||
|
||||
data = C.GoBytes(unsafe.Pointer(kvmData), C.int(dataSize))
|
||||
|
||||
if result == 3 {
|
||||
sps, _ = k.ReadH264SPS()
|
||||
pps, _ = k.ReadH264PPS()
|
||||
}
|
||||
|
||||
log.Debugf("read kvm image: %v", result)
|
||||
return
|
||||
}
|
||||
|
||||
func (k *KvmVision) ReadH264SPS() ([]byte, int) {
|
||||
var (
|
||||
kvmData *C.uint8_t
|
||||
dataSize C.uint32_t
|
||||
)
|
||||
|
||||
result := int(C.kvmv_get_sps_frame(&kvmData, &dataSize))
|
||||
if result < 0 {
|
||||
log.Errorf("failed to read sps: %v", result)
|
||||
return nil, result
|
||||
}
|
||||
|
||||
data := C.GoBytes(unsafe.Pointer(kvmData), C.int(dataSize))
|
||||
|
||||
return data, result
|
||||
}
|
||||
|
||||
func (k *KvmVision) ReadH264PPS() ([]byte, int) {
|
||||
var (
|
||||
kvmData *C.uint8_t
|
||||
dataSize C.uint32_t
|
||||
)
|
||||
|
||||
result := int(C.kvmv_get_pps_frame(&kvmData, &dataSize))
|
||||
if result < 0 {
|
||||
log.Errorf("failed to read pps: %v", result)
|
||||
return nil, result
|
||||
}
|
||||
|
||||
data := C.GoBytes(unsafe.Pointer(kvmData), C.int(dataSize))
|
||||
return data, result
|
||||
}
|
||||
|
||||
func (k *KvmVision) SetHDMI(enable bool) int {
|
||||
hdmiEnable := C.uint8_t(0)
|
||||
if enable {
|
||||
@@ -154,10 +108,17 @@ func (k *KvmVision) SetHDMI(enable bool) int {
|
||||
return result
|
||||
}
|
||||
|
||||
func (k *KvmVision) Close() {
|
||||
k.mutex.Lock()
|
||||
defer k.mutex.Unlock()
|
||||
func (k *KvmVision) SetGop(gop uint8) {
|
||||
_gop := C.uint8_t(gop)
|
||||
C.set_h264_gop(_gop)
|
||||
}
|
||||
|
||||
func (k *KvmVision) SetFrameDetect(frame uint8) {
|
||||
_frame := C.uint8_t(frame)
|
||||
C.set_frame_detact(_frame)
|
||||
}
|
||||
|
||||
func (k *KvmVision) Close() {
|
||||
C.kvmv_deinit()
|
||||
log.Debugf("stop kvm vision...")
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ type Screen struct {
|
||||
FPS int
|
||||
Quality uint16
|
||||
BitRate uint16
|
||||
GOP uint8
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -46,6 +47,7 @@ func GetScreen() *Screen {
|
||||
Quality: 80,
|
||||
FPS: 30,
|
||||
BitRate: 3000,
|
||||
GOP: 30,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -70,6 +72,9 @@ func SetScreen(key string, value int) {
|
||||
|
||||
case "fps":
|
||||
screen.FPS = validateFPS(value)
|
||||
|
||||
case "gop":
|
||||
screen.GOP = uint8(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ var defaultConfig = &Config{
|
||||
},
|
||||
Stun: "stun.l.google.com:19302",
|
||||
Turn: Turn{
|
||||
TurnAddr: "turn.cloudflare.com:3478",
|
||||
TurnAddr: "",
|
||||
TurnUser: "",
|
||||
TurnCred: "",
|
||||
},
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,8 @@
|
||||
module NanoKVM-Server
|
||||
|
||||
go 1.22.1
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.23.2
|
||||
|
||||
require (
|
||||
github.com/creack/pty v1.1.24
|
||||
@@ -15,7 +17,7 @@ require (
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/unrolled/secure v1.15.0
|
||||
golang.org/x/crypto v0.31.0
|
||||
golang.org/x/crypto v0.36.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@@ -71,9 +73,9 @@ require (
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/net v0.37.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
)
|
||||
|
||||
@@ -160,17 +160,25 @@ golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -13,6 +13,7 @@ extern "C" {
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
|
||||
#define IMG_BUFFER_FULL -3
|
||||
#define IMG_VENC_ERROR -2
|
||||
@@ -23,7 +24,14 @@ extern "C" {
|
||||
#define IMG_H264_TYPE_IF 3
|
||||
#define IMG_H264_TYPE_PF 4
|
||||
|
||||
#define NORMAL_RES 0
|
||||
#define NEW_RES 1
|
||||
#define UNSUPPORT_RES 2
|
||||
#define UNKNOWN_RES 3
|
||||
#define ERROR_RES 4
|
||||
|
||||
void kvmv_init(uint8_t _debug_info_en);
|
||||
void set_venc_auto_recyc(uint8_t _enable);
|
||||
/**********************************************************************************
|
||||
* @name kvmv_read_img
|
||||
* @author Sipeed BuGu
|
||||
@@ -37,22 +45,25 @@ void kvmv_init(uint8_t _debug_info_en);
|
||||
* @param _pp_kvm_data @output: Encode data
|
||||
* @param _p_kvmv_data_size @output: Encode data size
|
||||
* @return
|
||||
-7: HDMI INPUT RES ERROR
|
||||
-6: Unsupported resolution, please modify it in the host settings.
|
||||
-5: Retrieving image, please wait
|
||||
-4: Modifying image resolution, please wait
|
||||
-3: img buffer full
|
||||
-2: VENC Error
|
||||
-1: No images were acquired
|
||||
0: Acquire MJPEG encoded images
|
||||
1: Acquire H264 encoded images(SPS)
|
||||
2: Acquire H264 encoded images(PPS)
|
||||
1: Acquire H264 encoded images(SPS)[Deprecated]
|
||||
2: Acquire H264 encoded images(PPS)[Deprecated]
|
||||
3: Acquire H264 encoded images(I)
|
||||
4: Acquire H264 encoded images(P)
|
||||
5: IMG not changed
|
||||
**********************************************************************************/
|
||||
int kvmv_read_img(uint16_t _width, uint16_t _height, uint8_t _type, uint16_t _qlty, uint8_t** _pp_kvm_data, uint32_t* _p_kvmv_data_size);
|
||||
int kvmv_get_sps_frame(uint8_t** _pp_kvm_data, uint32_t* _p_kvmv_data_size);
|
||||
int kvmv_get_pps_frame(uint8_t** _pp_kvm_data, uint32_t* _p_kvmv_data_size);
|
||||
int free_kvmv_data(uint8_t ** _pp_kvm_data);
|
||||
void free_all_kvmv_data();
|
||||
void set_h264_gop(uint8_t _gop);
|
||||
void set_frame_detact(uint8_t _frame_detact);
|
||||
void kvmv_deinit();
|
||||
uint8_t kvmv_hdmi_control(uint8_t _en);
|
||||
|
||||
@@ -60,4 +71,4 @@ uint8_t kvmv_hdmi_control(uint8_t _en);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // KVM_VISION_H_
|
||||
#endif // KVM_VISION_H_
|
||||
|
||||
@@ -22,7 +22,7 @@ func (f *formatter) Format(entry *logrus.Entry) ([]byte, error) {
|
||||
buffer = &bytes.Buffer{}
|
||||
}
|
||||
|
||||
now := entry.Time.Format("2006-01-02 15:04:05")
|
||||
now := entry.Time.Format("2006-01-02 15:04:05.000")
|
||||
|
||||
if entry.HasCaller() {
|
||||
fileName := filepath.Base(entry.Caller.File)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package proto
|
||||
|
||||
type GetFrameDetectRsp struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
type UpdateFrameDetectReq struct {
|
||||
Enabled bool `validate:"omitempty"`
|
||||
}
|
||||
|
||||
type UpdateFrameDetectRsp struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
type StopFrameDetectReq struct {
|
||||
Duration int `validate:"omitempty"`
|
||||
}
|
||||
|
||||
@@ -83,3 +83,7 @@ type GetOLEDRsp struct {
|
||||
type GetSSHStateRsp struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type GetMdnsStateRsp struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
@@ -12,8 +12,7 @@ func streamRouter(r *gin.Engine) {
|
||||
api := r.Group("/api").Use(middleware.CheckToken())
|
||||
|
||||
api.GET("/stream/mjpeg", mjpeg.Connect) // mjpeg stream
|
||||
api.GET("/stream/mjpeg/detect", mjpeg.GetFrameDetect) // get frame detect state
|
||||
api.POST("/stream/mjpeg/detect", mjpeg.UpdateFrameDetect) // update frame detect state
|
||||
api.POST("/stream/mjpeg/detect", mjpeg.UpdateFrameDetect) // update frame detect
|
||||
api.POST("/stream/mjpeg/detect/stop", mjpeg.StopFrameDetect) // temporary stop frame detect
|
||||
|
||||
api.GET("/stream/h264", h264.Connect) // h264 stream
|
||||
|
||||
@@ -39,4 +39,10 @@ func vmRouter(r *gin.Engine) {
|
||||
api.GET("/vm/ssh", service.GetSSHState) // get SSH state
|
||||
api.POST("/vm/ssh/enable", service.EnableSSH) // enable SSH
|
||||
api.POST("/vm/ssh/disable", service.DisableSSH) // disable SSH
|
||||
|
||||
api.GET("/vm/mdns", service.GetMdnsState) // get mDNS state
|
||||
api.POST("/vm/mdns/enable", service.EnableMdns) // enable mDNS
|
||||
api.POST("/vm/mdns/disable", service.DisableMdns) // disable mDNS
|
||||
|
||||
api.POST("/vm/system/reboot", service.Reboot) // reboot system
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ const (
|
||||
TailscalePath = "/usr/bin/tailscale"
|
||||
TailscaledPath = "/usr/sbin/tailscaled"
|
||||
ConfigPath = "etc/sysctl.d/99-tailscale.conf"
|
||||
|
||||
GoMemLimit int64 = 75
|
||||
)
|
||||
|
||||
var StateMap = map[string]proto.TailscaleState{
|
||||
@@ -69,7 +71,7 @@ func (s *Service) Start(c *gin.Context) {
|
||||
}
|
||||
|
||||
if !utils.IsGoMemLimitExist() {
|
||||
_ = utils.SetGoMemLimit(50)
|
||||
_ = utils.SetGoMemLimit(GoMemLimit)
|
||||
}
|
||||
|
||||
rsp.OkRsp(c)
|
||||
@@ -165,7 +167,7 @@ func (s *Service) Login(c *gin.Context) {
|
||||
}
|
||||
|
||||
if !utils.IsGoMemLimitExist() {
|
||||
_ = utils.SetGoMemLimit(50)
|
||||
_ = utils.SetGoMemLimit(GoMemLimit)
|
||||
}
|
||||
|
||||
rsp.OkRspWithData(c, &proto.LoginTailscaleRsp{
|
||||
|
||||
@@ -33,7 +33,7 @@ func (s *Service) WakeOnLAN(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
command := fmt.Sprintf("ether-wake %s", mac)
|
||||
command := fmt.Sprintf("ether-wake -b %s", mac)
|
||||
cmd := exec.Command("sh", "-c", command)
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
||||
@@ -2,6 +2,7 @@ package h264
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sync"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/pion/webrtc/v4"
|
||||
@@ -9,8 +10,9 @@ import (
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
ws *websocket.Conn
|
||||
pc *webrtc.PeerConnection
|
||||
ws *websocket.Conn
|
||||
pc *webrtc.PeerConnection
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
@@ -36,9 +38,7 @@ func (c *Client) addTrack() {
|
||||
return
|
||||
}
|
||||
|
||||
mutex.Lock()
|
||||
trackMap[c.ws] = videoTrack
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
// register callback events
|
||||
@@ -61,13 +61,9 @@ func (c *Client) register() {
|
||||
// ICE connection state has changed
|
||||
c.pc.OnICEConnectionStateChange(func(state webrtc.ICEConnectionState) {
|
||||
if state == webrtc.ICEConnectionStateConnected && !isSending {
|
||||
mutex.Lock()
|
||||
if !isSending {
|
||||
// start sending h264 data
|
||||
go send()
|
||||
isSending = true
|
||||
}
|
||||
mutex.Unlock()
|
||||
// start sending h264 data
|
||||
go send()
|
||||
isSending = true
|
||||
}
|
||||
|
||||
log.Debugf("ice connection state has changed to %s", state.String())
|
||||
@@ -81,13 +77,11 @@ func (c *Client) readMessage() {
|
||||
for {
|
||||
_, raw, err := c.ws.ReadMessage()
|
||||
if err != nil {
|
||||
mutex.Lock()
|
||||
delete(trackMap, c.ws)
|
||||
if len(trackMap) == 0 && isSending {
|
||||
if isSending && len(trackMap) == 0 {
|
||||
// stop sending when all websocket connections are closed
|
||||
exitSig <- true
|
||||
isSending = false
|
||||
}
|
||||
mutex.Unlock()
|
||||
|
||||
log.Debugf("failed to read message: %s", err)
|
||||
return
|
||||
@@ -144,9 +138,6 @@ func (c *Client) readMessage() {
|
||||
return
|
||||
}
|
||||
|
||||
case "heartbeat":
|
||||
_ = c.sendMessage("heartbeat", "")
|
||||
|
||||
default:
|
||||
log.Debugf("unhandled message event: %s", message.Event)
|
||||
}
|
||||
@@ -155,8 +146,8 @@ func (c *Client) readMessage() {
|
||||
|
||||
// send websocket message
|
||||
func (c *Client) sendMessage(event string, data string) error {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
message := &Message{
|
||||
Event: event,
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"NanoKVM-Server/config"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
@@ -18,9 +19,7 @@ var (
|
||||
},
|
||||
}
|
||||
trackMap = make(map[*websocket.Conn]*webrtc.TrackLocalStaticSample)
|
||||
mutex = sync.RWMutex{}
|
||||
isSending = false
|
||||
exitSig = make(chan bool, 1)
|
||||
)
|
||||
|
||||
func Connect(c *gin.Context) {
|
||||
@@ -35,11 +34,14 @@ func Connect(c *gin.Context) {
|
||||
log.Debugf("h264 websocket disconnected")
|
||||
}()
|
||||
|
||||
var zeroTime time.Time
|
||||
_ = wsConn.SetReadDeadline(zeroTime)
|
||||
|
||||
conf := config.GetInstance()
|
||||
|
||||
var iceServers []webrtc.ICEServer
|
||||
|
||||
if conf.Stun != "" {
|
||||
if conf.Stun != "" && conf.Stun != "disable" {
|
||||
iceServers = append(iceServers, webrtc.ICEServer{
|
||||
URLs: []string{"stun:" + conf.Stun},
|
||||
})
|
||||
@@ -53,11 +55,9 @@ func Connect(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
rtc_config := webrtc.Configuration{
|
||||
peerConn, err := webrtc.NewPeerConnection(webrtc.Configuration{
|
||||
ICEServers: iceServers,
|
||||
}
|
||||
|
||||
peerConn, err := webrtc.NewPeerConnection(rtc_config)
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("failed to create PeerConnection: %s", err)
|
||||
return
|
||||
@@ -69,8 +69,9 @@ func Connect(c *gin.Context) {
|
||||
}()
|
||||
|
||||
client := &Client{
|
||||
ws: wsConn,
|
||||
pc: peerConn,
|
||||
ws: wsConn,
|
||||
pc: peerConn,
|
||||
mutex: sync.Mutex{},
|
||||
}
|
||||
|
||||
client.addTrack()
|
||||
|
||||
@@ -2,7 +2,6 @@ package h264
|
||||
|
||||
import (
|
||||
"NanoKVM-Server/common"
|
||||
"NanoKVM-Server/service/stream"
|
||||
"time"
|
||||
|
||||
"github.com/pion/webrtc/v4/pkg/media"
|
||||
@@ -18,63 +17,45 @@ func send() {
|
||||
defer ticker.Stop()
|
||||
|
||||
vision := common.GetKvmVision()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
height := screen.Height
|
||||
width, ok := common.ResolutionMap[height]
|
||||
if !ok {
|
||||
width = 0
|
||||
height = 0
|
||||
}
|
||||
|
||||
bitRate := screen.BitRate
|
||||
if _, ok := common.BitRateMap[bitRate]; !ok {
|
||||
bitRate = 3000
|
||||
}
|
||||
|
||||
data, sps, pps, result := vision.ReadH264(width, height, bitRate)
|
||||
if result < 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if result == 3 {
|
||||
writeSample(sps, duration)
|
||||
writeSample(pps, duration)
|
||||
}
|
||||
writeSample(data, duration)
|
||||
|
||||
stream.GetFrameRateCounter().Update()
|
||||
|
||||
if screen.FPS != fps {
|
||||
fps = screen.FPS
|
||||
duration = time.Second / time.Duration(fps)
|
||||
ticker.Reset(duration)
|
||||
}
|
||||
|
||||
case <-exitSig:
|
||||
mutex.Lock()
|
||||
isSending = false
|
||||
mutex.Unlock()
|
||||
for range ticker.C {
|
||||
if !isSending && len(trackMap) == 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeSample(data []byte, duration time.Duration) {
|
||||
sample := media.Sample{
|
||||
Data: data,
|
||||
Duration: duration,
|
||||
}
|
||||
height := screen.Height
|
||||
width, ok := common.ResolutionMap[height]
|
||||
if !ok {
|
||||
width = 0
|
||||
height = 0
|
||||
}
|
||||
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
bitRate := screen.BitRate
|
||||
if _, ok := common.BitRateMap[bitRate]; !ok {
|
||||
bitRate = 3000
|
||||
}
|
||||
|
||||
for _, track := range trackMap {
|
||||
if err := track.WriteSample(sample); err != nil {
|
||||
log.Errorf("failed to send h264 data: %s", err)
|
||||
data, result := vision.ReadH264(width, height, bitRate)
|
||||
if result < 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
sample := media.Sample{
|
||||
Data: data,
|
||||
Duration: duration,
|
||||
}
|
||||
|
||||
for _, track := range trackMap {
|
||||
if err := track.WriteSample(sample); err != nil {
|
||||
log.Errorf("failed to send h264 data: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("send h264 data: %d", len(data))
|
||||
|
||||
if screen.FPS != fps {
|
||||
fps = screen.FPS
|
||||
duration = time.Second / time.Duration(fps)
|
||||
ticker.Reset(duration)
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("send h264 data: %d", len(data))
|
||||
}
|
||||
|
||||
@@ -1,132 +1,55 @@
|
||||
package mjpeg
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"NanoKVM-Server/common"
|
||||
"NanoKVM-Server/proto"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"NanoKVM-Server/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
frameDetect = "/etc/kvm/frame_detact"
|
||||
frameDetectTmp = "/etc/kvm/frame_detact_tmp"
|
||||
)
|
||||
|
||||
type UpdateFrameDetectRsp struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
func GetFrameDetect(c *gin.Context) {
|
||||
var rsp proto.Response
|
||||
|
||||
isEnabled, err := isFrameDetectEnabled()
|
||||
if err != nil {
|
||||
rsp.ErrRsp(c, -1, "unknown frame detect status")
|
||||
return
|
||||
}
|
||||
|
||||
rsp.OkRspWithData(c, &proto.GetFrameDetectRsp{
|
||||
Enabled: isEnabled,
|
||||
})
|
||||
log.Debugf("get frame detect success, enabled: %t", isEnabled)
|
||||
}
|
||||
const FrameDetectInterval uint8 = 60
|
||||
|
||||
func UpdateFrameDetect(c *gin.Context) {
|
||||
var req proto.UpdateFrameDetectReq
|
||||
var rsp proto.Response
|
||||
|
||||
isEnabled, err := isFrameDetectEnabled()
|
||||
if err != nil {
|
||||
rsp.ErrRsp(c, -1, "unknown frame status")
|
||||
if err := proto.ParseFormRequest(c, &req); err != nil {
|
||||
rsp.ErrRsp(c, -1, "invalid parameters")
|
||||
return
|
||||
}
|
||||
|
||||
if isEnabled {
|
||||
_ = os.Remove(frameDetect)
|
||||
_ = os.Remove(frameDetectTmp)
|
||||
} else {
|
||||
file, err2 := os.OpenFile(frameDetect, os.O_CREATE|os.O_TRUNC, 0o644)
|
||||
if err2 != nil {
|
||||
rsp.ErrRsp(c, -3, "enable frame detect failed")
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
var frame uint8 = 0
|
||||
if req.Enabled {
|
||||
frame = FrameDetectInterval
|
||||
}
|
||||
|
||||
isEnabled, err = isFrameDetectEnabled()
|
||||
if err != nil {
|
||||
rsp.ErrRsp(c, -4, "unknown frame status")
|
||||
return
|
||||
}
|
||||
common.GetKvmVision().SetFrameDetect(frame)
|
||||
|
||||
rsp.OkRspWithData(c, &UpdateFrameDetectRsp{
|
||||
Enabled: isEnabled,
|
||||
})
|
||||
log.Debugf("update frame detect success, enabled: %t", isEnabled)
|
||||
rsp.OkRsp(c)
|
||||
log.Debugf("update frame detect: %t", req.Enabled)
|
||||
}
|
||||
|
||||
func StopFrameDetect(c *gin.Context) {
|
||||
var req proto.StopFrameDetectReq
|
||||
var rsp proto.Response
|
||||
|
||||
exist, err := isFileExist(frameDetect)
|
||||
if err != nil {
|
||||
rsp.ErrRsp(c, -1, "unknown frame status")
|
||||
if err := proto.ParseFormRequest(c, &req); err != nil {
|
||||
rsp.ErrRsp(c, -1, "invalid parameters")
|
||||
return
|
||||
}
|
||||
|
||||
if !exist {
|
||||
rsp.OkRsp(c)
|
||||
return
|
||||
duration := 10 * time.Second
|
||||
if req.Duration > 0 {
|
||||
duration = time.Duration(req.Duration) * time.Second
|
||||
}
|
||||
|
||||
err = os.Rename(frameDetect, frameDetectTmp)
|
||||
if err != nil {
|
||||
rsp.ErrRsp(c, -2, "stop operation failed")
|
||||
return
|
||||
}
|
||||
vision := common.GetKvmVision()
|
||||
|
||||
go func() {
|
||||
time.Sleep(20 * time.Second)
|
||||
_ = os.Rename(frameDetectTmp, frameDetect)
|
||||
log.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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
92
server/service/vm/mdns.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"NanoKVM-Server/proto"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
AvahiDaemonPid = "/run/avahi-daemon/pid"
|
||||
AvahiDaemonScript = "/etc/init.d/S50avahi-daemon"
|
||||
AvahiDaemonBackupScript = "/kvmapp/system/init.d/S50avahi-daemon"
|
||||
)
|
||||
|
||||
func (s *Service) GetMdnsState(c *gin.Context) {
|
||||
var rsp proto.Response
|
||||
|
||||
pid := getAvahiDaemonPid()
|
||||
|
||||
rsp.OkRspWithData(c, &proto.GetMdnsStateRsp{
|
||||
Enabled: pid != "",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Service) EnableMdns(c *gin.Context) {
|
||||
var rsp proto.Response
|
||||
|
||||
pid := getAvahiDaemonPid()
|
||||
if pid != "" {
|
||||
rsp.OkRsp(c)
|
||||
return
|
||||
}
|
||||
|
||||
commands := []string{
|
||||
fmt.Sprintf("cp -f %s %s", AvahiDaemonBackupScript, AvahiDaemonScript),
|
||||
fmt.Sprintf("%s start", AvahiDaemonScript),
|
||||
}
|
||||
|
||||
command := strings.Join(commands, " && ")
|
||||
err := exec.Command("sh", "-c", command).Run()
|
||||
if err != nil {
|
||||
log.Errorf("failed to start avahi-daemon: %s", err)
|
||||
rsp.ErrRsp(c, -1, "failed to enable mdns")
|
||||
return
|
||||
}
|
||||
|
||||
rsp.OkRsp(c)
|
||||
log.Debugf("avahi-daemon started")
|
||||
}
|
||||
|
||||
func (s *Service) DisableMdns(c *gin.Context) {
|
||||
var rsp proto.Response
|
||||
|
||||
pid := getAvahiDaemonPid()
|
||||
if pid == "" {
|
||||
rsp.OkRsp(c)
|
||||
return
|
||||
}
|
||||
|
||||
command := fmt.Sprintf("kill -9 %s", pid)
|
||||
err := exec.Command("sh", "-c", command).Run()
|
||||
if err != nil {
|
||||
log.Errorf("failed to stop avahi-daemon: %s", err)
|
||||
rsp.ErrRsp(c, -1, "failed to disable mdns")
|
||||
return
|
||||
}
|
||||
|
||||
_ = os.Remove(AvahiDaemonPid)
|
||||
_ = os.Remove(AvahiDaemonScript)
|
||||
|
||||
rsp.OkRsp(c)
|
||||
log.Debugf("avahi-daemon stopped")
|
||||
}
|
||||
|
||||
func getAvahiDaemonPid() string {
|
||||
if _, err := os.Stat(AvahiDaemonPid); err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(AvahiDaemonPid)
|
||||
if err != nil {
|
||||
log.Errorf("failed to read mdns pid: %s", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
return strings.ReplaceAll(string(content), "\n", "")
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
25
server/service/vm/system.go
Normal file
25
server/service/vm/system.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"NanoKVM-Server/proto"
|
||||
"os/exec"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (s *Service) Reboot(c *gin.Context) {
|
||||
var rsp proto.Response
|
||||
|
||||
log.Println("reboot system...")
|
||||
|
||||
err := exec.Command("reboot").Run()
|
||||
if err != nil {
|
||||
rsp.ErrRsp(c, -1, "operation failed")
|
||||
log.Errorf("failed to reboot: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
rsp.OkRsp(c)
|
||||
log.Debug("system rebooted")
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
## If you want to compile kvm_system yourself, please follow the steps below:
|
||||
# NanoKVM Support Instructions
|
||||
|
||||
1. Clone the MaixCDK repository: `git clone https://github.com/sipeed/MaixCDK.git`
|
||||
2. Improve the Compilation Environment, refer to [here](https://github.com/sipeed/MaixCDK/tree/main/docs/doc_zh#%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B)
|
||||
3. Copy `./kvm_system` to `~/MaixCDK` and open it in terminal
|
||||
4. Execute `maixcdk build` and Select `maixcam` to complete the compilation
|
||||
5. Replace `/kvmapp/kvm_system/kvm_system` in the NanoKVM system with `dist/kvm_system_release/kvm_system`
|
||||
6. Execute: `/etc/init.d/S95nanokvm restart` on NanoKVM
|
||||
`/support` contains auxiliary functions for NanoKVM, such as image subsystem, system status monitoring, system updates, screen key drivers, and a few system functions.
|
||||
|
||||
Currently, NanoKVM is divided into two versions based on the main control chip: SG2002 (which includes NanoKVM-Lite/Full/PCIe) and H618 (including NanoKVM-Pro). Different chips have significantly different projects and compilation environments. To distinguish between them, they are stored separately in `/support/sg2002` and `/support/h618`.
|
||||
|
||||
5
support/README_ZH.md
Normal file
5
support/README_ZH.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# NanoKVM support 说明
|
||||
|
||||
`/support`包含NanoKVM辅助性的功能,如图像子系统、系统状态监控、系统更新、屏幕按键驱动和少部分的系统功能
|
||||
|
||||
当前 NanoKVM 根据主控芯片的不同分为两个版本:SG2002(包含NanoKVM-Lite/Full/PCIe)和H618(包括NanoKVM-Pro),不同的芯片有差异较大的工程和编译环境,为做区分,将它们分别存放在`/support/sg2002`和`/support/h618`
|
||||
26
support/sg2002/README.md
Normal file
26
support/sg2002/README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# NanoKVM Support Instructions
|
||||
|
||||
## Environment Preparation
|
||||
1. NanoKVM-Lite/Full/PCIe is based on the SG2002 as the main control chip. The projects in the support section are compiled under the [MaixCDK](https://github.com/sipeed/MaixCDK) framework. Before compiling, please ensure that the `MaixCDK` environment is correctly configured. For configuration instructions, click [here](https://github.com/sipeed/MaixCDK/blob/main/docs/doc_zh/README.md).
|
||||
|
||||
## kvm_system Compilation Instructions
|
||||
|
||||
> The `kvm_system` is responsible for monitoring the NanoKVM system status, system updates, screen key drivers, and a few system functions, compiled with MaixCDK.
|
||||
|
||||
1. Before compiling, please ensure that the above-mentioned `MaixCDK` environment is correctly configured.
|
||||
2. Modify the paths of `MAIXCDK_PATH` and `NanoKVM_PATH` in `./build`.
|
||||
3. Execute `./build kvm_system` to compile `kvm_system`.
|
||||
4. Use `scp ./kvm_system/dist/kvm_system_release/kvm_system root@192.168.x.x:/kvmapp/kvm_system` to copy it to NanoKVM for testing.
|
||||
5. Use `./build add_to_kvmapp` to place the executable file into the `/kvmapp` installation package.
|
||||
6. Use `./build kvm_system clean` to clean the compilation of `kvm_system`.
|
||||
|
||||
## kvm_vision Compilation Instructions
|
||||
|
||||
> `kvm_vision` refers to the image acquisition and encoding subsystem of NanoKVM, compiled with MaixCDK to produce dynamic libraries for Go calls. Use `kvm_vision_test` to compile and test the dynamic library.
|
||||
|
||||
1. Before compiling, please ensure that the above-mentioned `MaixCDK` environment is correctly configured.
|
||||
2. Modify the paths of `MAIXCDK_PATH` and `NanoKVM_PATH` in `./build`.
|
||||
3. Execute `./build kvm_vision` to compile `kvm_vision_test`.
|
||||
4. You can test the dynamic libraries in `kvm_vision_test/dist/kvm_vision_test_release/dl_lib/`.
|
||||
5. Use `./build add_to_kvmapp` to place the dynamic libraries into the `/kvmapp` installation package.
|
||||
6. Use `./build kvm_vision clean` to clean the compilation of `kvm_vision`.
|
||||
26
support/sg2002/README_ZH.md
Normal file
26
support/sg2002/README_ZH.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# NanoKVM support 说明
|
||||
|
||||
## 环境准备
|
||||
1. NanoKVM-Lite/Full/PCIe 以SG2002为主控芯片,support 部分的工程在[MaixCDK](https://github.com/sipeed/MaixCDK) 框架下编译。编译前,请保证`MaixCDK`环境已正确配置,配置教程点击[这里](https://github.com/sipeed/MaixCDK/blob/main/docs/doc_zh/README.md)
|
||||
|
||||
## kvm_system 编译说明
|
||||
|
||||
> `kvm_system` 负责 NanoKVM 系统状态监控、系统更新、屏幕按键驱动和少部分的系统功能,借助 MaixCDK 编译
|
||||
|
||||
1. 编译前,请保证上述 1.`MaixCDK`环境已正确配置
|
||||
2. 修改 `./build` 中 `MAIXCDK_PATH` 和 `NanoKVM_PATH`的路径
|
||||
3. 执行 `./build kvm_system` 编译 kvm_system
|
||||
4. 使用 `scp ./kvm_system/dist/kvm_system_release/kvm_system root@192.168.x.x:/kvmapp/kvm_system` 拷贝入 NanoKVM 测试
|
||||
5. 使用 `./build add_to_kvmapp` 可以将可执行文件放入 `/kvmapp` 安装包内
|
||||
6. 使用 `./build kvm_system clean` 可以清除 kvm_system 的编译
|
||||
|
||||
## kvm_vision 编译说明
|
||||
|
||||
> `kvm_vision` 是 NanoKVM 图像获取编码子系统的统称,使用MaixCDK编译出动态库供Go调用,使用 `kvm_vision_test` 编译和测试动态库
|
||||
|
||||
1. 编译前,请保证上述 1.`MaixCDK`环境已正确配置
|
||||
2. 修改 `./build` 中 `MAIXCDK_PATH` 和 `NanoKVM_PATH`的路径
|
||||
3. 执行 `./build kvm_vision` 编译 kvm_vision_test
|
||||
4. 可使用 `kvm_vision_test/dist/kvm_vision_test_release/dl_lib/` 中的动态库测试
|
||||
5. 使用 `./build add_to_kvmapp` 可以将动态库放入 `/kvmapp` 安装包内
|
||||
6. 使用 `./build kvm_vision clean` 可以清除 kvm_system 的编译
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
66
support/sg2002/additional/kvm_mmf/CMakeLists.txt
Normal file
66
support/sg2002/additional/kvm_mmf/CMakeLists.txt
Normal file
@@ -0,0 +1,66 @@
|
||||
|
||||
list(APPEND ADD_REQUIREMENTS basic ini)
|
||||
list(APPEND ADD_INCLUDE "include")
|
||||
|
||||
# sophgo-middleware
|
||||
set(middleware_src_path "${CMAKE_CURRENT_SOURCE_DIR}/../3rd_party/sophgo-middleware/sophgo-middleware")
|
||||
|
||||
|
||||
set(source_dir "src")
|
||||
append_srcs_dir(ADD_SRCS "${source_dir}")
|
||||
|
||||
# middleware
|
||||
set(middleware_include_dir .
|
||||
${middleware_src_path}/v2/component/panel/${CONFIG_SOPHGO_MIDDLEWARE_CHIP}
|
||||
${middleware_src_path}/v2/include
|
||||
${middleware_src_path}/v2/include/isp/${CONFIG_SOPHGO_MIDDLEWARE_CHIP}
|
||||
${middleware_src_path}/v2/sample/common
|
||||
${middleware_src_path}/v2/uapi
|
||||
${middleware_src_path}/v2/3rdparty/inih
|
||||
${middleware_src_path}/v2/modules/ive/include/
|
||||
)
|
||||
list(APPEND ADD_INCLUDE ${middleware_include_dir})
|
||||
set_property(SOURCE ${middleware_include_dir} PROPERTY GENERATED 1)
|
||||
|
||||
append_srcs_dir(middleware_src_dir ${middleware_src_path}/v2/sample/common
|
||||
${middleware_src_path}/v2/component/isp/sensor/sg200x/lontium_lt6911
|
||||
)
|
||||
list(APPEND ADD_SRCS ${middleware_src_dir}
|
||||
"${source_dir}/kvm_mmf.cpp")
|
||||
set_property(SOURCE ${middleware_src_dir} PROPERTY GENERATED 1)
|
||||
|
||||
set(middleware_static_lib_file "")
|
||||
list(APPEND ADD_STATIC_LIB ${middleware_static_lib_file})
|
||||
set_property(SOURCE ${middleware_static_lib_file} PROPERTY GENERATED 1)
|
||||
|
||||
set(mmf_lib_dir ${middleware_src_path}/v2/lib)
|
||||
|
||||
if(CONFIG_SOPHGO_MIDDLEWARE_C_LIBRARY STREQUAL "musl")
|
||||
set(middleware_dynamic_lib_file ${mmf_lib_dir}/libcvi_bin.so
|
||||
${mmf_lib_dir}/libae.so
|
||||
${mmf_lib_dir}/libaf.so
|
||||
${mmf_lib_dir}/libawb.so
|
||||
${mmf_lib_dir}/libcvi_bin_isp.so
|
||||
${mmf_lib_dir}/libisp_algo.so
|
||||
${mmf_lib_dir}/libisp.so
|
||||
${mmf_lib_dir}/libsys.so
|
||||
${mmf_lib_dir}/libvdec.so
|
||||
${mmf_lib_dir}/libvenc.so
|
||||
${mmf_lib_dir}/libvpu.so
|
||||
${mmf_lib_dir}/3rd/libini.so)
|
||||
elseif(CONFIG_SOPHGO_MIDDLEWARE_C_LIBRARY STREQUAL "glibc")
|
||||
set(middleware_dynamic_lib_file ${mmf_lib_dir}/libcvi_vb.so
|
||||
${mmf_lib_dir}/libcvi_sys.so
|
||||
${mmf_lib_dir}/libcvi_debug.so
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR "No sophgo middleware c library support, please check menuoconfig to select c library")
|
||||
endif()
|
||||
list(APPEND ADD_DYNAMIC_LIB ${middleware_dynamic_lib_file})
|
||||
set_property(SOURCE ${middleware_dynamic_lib_file} PROPERTY GENERATED 1)
|
||||
|
||||
list(APPEND ADD_DEFINITIONS_PRIVATE -DSENSOR_LONTIUM_LT6911
|
||||
-DSENSOR2_TYPE=LONTIUM_LT6911_2M_60FPS_8BIT)
|
||||
|
||||
|
||||
register_component(DYNAMIC)
|
||||
@@ -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;
|
||||
|
||||
322
support/sg2002/additional/peripheral/port/maixcam/maix_i2c.cpp
Normal file
322
support/sg2002/additional/peripheral/port/maixcam/maix_i2c.cpp
Normal file
@@ -0,0 +1,322 @@
|
||||
/**
|
||||
* @author neucrack@sipeed, lxowalle@sipeed
|
||||
* @copyright Sipeed Ltd 2023-
|
||||
* @license Apache 2.0
|
||||
* @update 2023.9.8: Add framework, create this file.
|
||||
*/
|
||||
|
||||
|
||||
#include "maix_i2c.hpp"
|
||||
#include "maix_basic.hpp"
|
||||
#include "cstdio"
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/i2c-dev.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#define DEV_PATH "/dev/i2c-%d"
|
||||
|
||||
namespace maix::peripheral::i2c
|
||||
{
|
||||
|
||||
std::vector<int> list_devices()
|
||||
{
|
||||
std::vector<int> data;
|
||||
// list all files in /dev, find i2c-*
|
||||
std::vector<std::string> *files = fs::listdir("/dev");
|
||||
for (auto &file : *files)
|
||||
{
|
||||
if (file.find("i2c-") != std::string::npos)
|
||||
{
|
||||
try
|
||||
{
|
||||
data.push_back(std::stoi(file.substr(4)));
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
log::error("i2c bus id %s format error", file.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
delete files;
|
||||
return data;
|
||||
}
|
||||
|
||||
I2C::I2C(int id, i2c::Mode mode, int freq, i2c::AddrSize addr_size)
|
||||
{
|
||||
char buf[32];
|
||||
|
||||
snprintf(buf, sizeof(buf), DEV_PATH, id);
|
||||
if(access(buf, F_OK) == 0){
|
||||
if (mode == i2c::SLAVE)
|
||||
{
|
||||
throw err::Exception(err::Err::ERR_NOT_IMPL, "i2c::SLAVE mode not implemented");
|
||||
}
|
||||
if (addr_size != i2c::SEVEN_BIT)
|
||||
{
|
||||
throw err::Exception(err::Err::ERR_NOT_IMPL, "addr_size " + std::to_string(addr_size) + " not support");
|
||||
}
|
||||
|
||||
int fd = ::open(buf, O_RDWR);
|
||||
if (fd < 0)
|
||||
{
|
||||
throw err::Exception(err::Err::ERR_IO, "open " + std::string(buf) + " failed");
|
||||
}
|
||||
_fd = fd;
|
||||
_freq = freq;
|
||||
_mode = mode;
|
||||
_addr_size = addr_size;
|
||||
} else {
|
||||
printf("not exit i2c\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
I2C::~I2C()
|
||||
{
|
||||
if (_fd > 0)
|
||||
{
|
||||
::close(_fd);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> I2C::scan(int addr)
|
||||
{
|
||||
std::vector<int> data;
|
||||
int addr_start = 0x08;
|
||||
int addr_end = 0x77;
|
||||
if(addr > 0)
|
||||
{
|
||||
addr_start = addr;
|
||||
addr_end = addr;
|
||||
}
|
||||
|
||||
if (_mode != i2c::Mode::MASTER)
|
||||
{
|
||||
log::error("Only for master mode");
|
||||
return data;
|
||||
}
|
||||
|
||||
switch (_addr_size)
|
||||
{
|
||||
case i2c::AddrSize::SEVEN_BIT:
|
||||
for (int address = addr_start; address <= addr_end; ++address)
|
||||
{
|
||||
if (::ioctl(_fd, I2C_SLAVE, address) < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned char buffer[1];
|
||||
if (::read(_fd, buffer, sizeof(buffer)) >= 0)
|
||||
{
|
||||
data.push_back(address);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log::error("bit %d not support", _addr_size);
|
||||
return data;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
int I2C::writeto(int addr, const uint8_t *data, int len)
|
||||
{
|
||||
if (_mode != i2c::Mode::MASTER)
|
||||
{
|
||||
log::error("Only for master mode");
|
||||
return (int)-err::Err::ERR_NOT_PERMIT;
|
||||
}
|
||||
|
||||
if (0 != ioctl(_fd, I2C_SLAVE, addr))
|
||||
{
|
||||
// log::error("set slave address failed");
|
||||
return (int)-err::Err::ERR_IO;
|
||||
}
|
||||
|
||||
if (len != ::write(_fd, data, len))
|
||||
{
|
||||
log::error("write failed");
|
||||
return (int)-err::Err::ERR_IO;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int I2C::writeto(int addr, const std::vector<unsigned char> data)
|
||||
{
|
||||
return writeto(addr, data.data(), (int)data.size());
|
||||
}
|
||||
|
||||
int I2C::writeto(int addr, const Bytes &data)
|
||||
{
|
||||
return writeto(addr, data.data, (int)data.size());
|
||||
}
|
||||
|
||||
Bytes* I2C::readfrom(int addr, int len)
|
||||
{
|
||||
Bytes *data = new Bytes(nullptr, len);
|
||||
|
||||
if (_mode != i2c::Mode::MASTER)
|
||||
{
|
||||
log::error("Only for master mode");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (0 != ioctl(_fd, I2C_SLAVE, addr))
|
||||
{
|
||||
// log::error("set slave address failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (len != ::read(_fd, data->data, len))
|
||||
{
|
||||
log::error("read failed");
|
||||
delete data;
|
||||
return nullptr;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
int I2C::writeto_mem(int addr, int mem_addr, const uint8_t *data, int len, int mem_addr_size, bool mem_addr_le)
|
||||
{
|
||||
if (_mode != i2c::Mode::MASTER)
|
||||
{
|
||||
log::error("Only for master mode");
|
||||
return (int)-err::Err::ERR_NOT_PERMIT;
|
||||
}
|
||||
if(mem_addr_size % 8 != 0)
|
||||
{
|
||||
log::error("mem_addr_size must be multiple of 8");
|
||||
return (int)-err::Err::ERR_IO;
|
||||
}
|
||||
|
||||
if (0 != ioctl(_fd, I2C_SLAVE, addr))
|
||||
{
|
||||
// log::error("set slave address failed");
|
||||
return (int)-err::Err::ERR_IO;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> data_final;
|
||||
|
||||
if(mem_addr_size == 8)
|
||||
{
|
||||
data_final.push_back((unsigned char)mem_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(mem_addr_le)
|
||||
{
|
||||
for(int i = 0; i < mem_addr_size / 8; i++)
|
||||
{
|
||||
data_final.push_back((unsigned char)(mem_addr & 0xff));
|
||||
mem_addr >>= 8;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < mem_addr_size / 8; i++)
|
||||
{
|
||||
data_final.push_back((unsigned char)(mem_addr >> (8 * (mem_addr_size / 8 - i - 1))));
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < len; i++)
|
||||
{
|
||||
data_final.push_back(data[i]);
|
||||
}
|
||||
int write_len = ::write(_fd, data_final.data(), data_final.size());
|
||||
if (data_final.size() != (size_t)write_len)
|
||||
{
|
||||
log::error("write failed, write_len: %d", write_len);
|
||||
return (int)-err::Err::ERR_IO;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int I2C::writeto_mem(int addr, int mem_addr, const std::vector<unsigned char> data, int mem_addr_size, bool mem_addr_le)
|
||||
{
|
||||
return writeto_mem(addr, mem_addr, data.data(), (int)data.size(), mem_addr_size, mem_addr_le);
|
||||
}
|
||||
|
||||
int I2C::writeto_mem(int addr, int mem_addr, const Bytes &data, int mem_addr_size, bool mem_addr_le)
|
||||
{
|
||||
return writeto_mem(addr, mem_addr, data.data, (int)data.size(), mem_addr_size, mem_addr_le);
|
||||
}
|
||||
|
||||
Bytes* I2C::readfrom_mem(int addr, int mem_addr, int len, int mem_addr_size, bool mem_addr_le)
|
||||
{
|
||||
// write mem_addr and restart to read
|
||||
if (_mode != i2c::Mode::MASTER)
|
||||
{
|
||||
log::error("Only for master mode");
|
||||
return nullptr;
|
||||
}
|
||||
if(mem_addr_size % 8 != 0)
|
||||
{
|
||||
log::error("mem_addr_size must be multiple of 8");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// write mem_addr first and restart to read
|
||||
if (0 != ioctl(_fd, I2C_SLAVE, addr))
|
||||
{
|
||||
// log::error("set slave address failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> data_final;
|
||||
if(mem_addr_size == 8)
|
||||
{
|
||||
data_final.push_back((unsigned char)mem_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(mem_addr_le)
|
||||
{
|
||||
for(int i = 0; i < mem_addr_size / 8; i++)
|
||||
{
|
||||
data_final.push_back((unsigned char)(mem_addr & 0xff));
|
||||
mem_addr >>= 8;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
for(int i = 0; i < mem_addr_size / 8; i++)
|
||||
{
|
||||
data_final.push_back((unsigned char)(mem_addr >> (8 * (mem_addr_size / 8 - i - 1))));
|
||||
}
|
||||
}
|
||||
}
|
||||
// write read with I2C_RDWR
|
||||
struct i2c_msg msgs[2];
|
||||
msgs[0].addr = addr;
|
||||
msgs[0].flags = 0;
|
||||
msgs[0].len = data_final.size();
|
||||
msgs[0].buf = data_final.data();
|
||||
|
||||
Bytes *data = new Bytes(nullptr, len);
|
||||
msgs[1].addr = addr;
|
||||
msgs[1].flags = I2C_M_RD;
|
||||
msgs[1].len = len;
|
||||
msgs[1].buf = data->data;
|
||||
|
||||
struct i2c_rdwr_ioctl_data msgset;
|
||||
msgset.msgs = msgs;
|
||||
msgset.nmsgs = 2;
|
||||
|
||||
int read_len = ioctl(_fd, I2C_RDWR, &msgset);
|
||||
if (read_len != 2)
|
||||
{
|
||||
log::error("read failed");
|
||||
delete data;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
179
support/sg2002/build
Executable 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
|
||||
@@ -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
Reference in New Issue
Block a user