mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-07-27 02:11:19 +08:00
🐶 Sync 2026-06-22 04:24:22
This commit is contained in:
parent
af01fe89d2
commit
d93d01a89d
176
luci-app-airoha-npu/LICENSE
Normal file
176
luci-app-airoha-npu/LICENSE
Normal file
@ -0,0 +1,176 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to the Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
14
luci-app-airoha-npu/Makefile
Normal file
14
luci-app-airoha-npu/Makefile
Normal file
@ -0,0 +1,14 @@
|
||||
# This is free software, licensed under the Apache License, Version 2.0 .
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_VERSION:=1.0.1
|
||||
PKG_RELEASE:=1
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
|
||||
LUCI_TITLE:=LuCI Airoha SoC Status (NPU, CPU, Frame Engine)
|
||||
LUCI_DEPENDS:=+luci-base @TARGET_airoha
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
||||
165
luci-app-airoha-npu/README.md
Normal file
165
luci-app-airoha-npu/README.md
Normal file
@ -0,0 +1,165 @@
|
||||
# luci-app-airoha-npu
|
||||
|
||||
Real-time monitoring and management dashboard for the Airoha AN7581 SoC on OpenWrt. Covers NPU offload, CPU frequency, WiFi band health, Frame Engine internals, and PPE flow tables.
|
||||
|
||||
**[Download](https://github.com/rchen14b/luci-app-airoha-npu/releases/latest)**
|
||||
|
||||

|
||||

|
||||

|
||||
[](https://buymeacoffee.com/rchen14b)
|
||||
|
||||
## Screenshots
|
||||
|
||||
### CPU Frequency & Overclock
|
||||

|
||||
|
||||
### NPU & Offload Engine
|
||||

|
||||
|
||||
### PPE Flow Offload Table
|
||||

|
||||
|
||||
## Features
|
||||
|
||||
### CPU Frequency Management
|
||||
- Current frequency display with visual bar graph
|
||||
- Governor selection (performance, ondemand, schedutil, etc.)
|
||||
- Max frequency selection from available OPP entries
|
||||
- Direct PLL overclock control (500-1600 MHz) with hardware register programming
|
||||
- Overclock detection and warning for unstable frequencies
|
||||
|
||||
### NPU & Offload Engine
|
||||
- NPU firmware version (TLB format), clock frequency, core count
|
||||
- NPU load status (active/inactive) and reserved memory regions
|
||||
- WiFi token pool health indicator with in-flight count
|
||||
- PPE flow offload summary (bound / total entries)
|
||||
- Per-band WiFi status cards (2.4 GHz, 5 GHz, 6 GHz):
|
||||
- Client count and link health indicator (Good / Fair / Poor)
|
||||
- NPU vs DMA path badge
|
||||
- TX retry rate percentage with color-coded thresholds
|
||||
|
||||
### Frame Engine Visualization
|
||||
- **PSE Shared Buffer** usage bar (congestion indicator)
|
||||
- **GDM port cards** with live TX/RX packet counters and drop counts:
|
||||
- GDM1: Internal Switch (1G LAN3/4)
|
||||
- GDM2: WAN (USXGMII 10G)
|
||||
- GDM4: LAN2 (USXGMII 10G)
|
||||
- **CDM offload ratio** bars — HW-forwarded (PPE) vs CPU-path packets
|
||||
- **PSE Port Queue Status** grid (P0-P9) with IQ/OQ queue depths and drop counts
|
||||
|
||||
### PPE Flow Offload Table
|
||||
- First 100 PPE entries with state (BND/UNB), type (IPv4/IPv6/L2B), 5-tuple, and MAC addresses
|
||||
- Auto-refreshes every 5 seconds
|
||||
|
||||
### Theme Support
|
||||
- Auto-detects dark/light mode by sampling page background luminance at runtime
|
||||
- Works with Glass, Bootstrap, Bootstrap-dark, and any LuCI theme
|
||||
- No hardcoded colors — uses CSS custom properties throughout
|
||||
|
||||
## Requirements
|
||||
|
||||
- OpenWrt with LuCI (24.10+)
|
||||
- Airoha AN7581 target (`@TARGET_airoha`)
|
||||
- PPE debugfs (`/sys/kernel/debug/ppe/entries`)
|
||||
- **`devmem`** busybox applet — required for Frame Engine register access and CPU overclock (`CONFIG_BUSYBOX_DEFAULT_DEVMEM=y`)
|
||||
- WiFi token_info debugfs for per-band WiFi stats (`/sys/kernel/debug/ieee80211/phy0/mt76/token_info`)
|
||||
- Optional: [air_tools](https://github.com/merbanan/air_tools) scripts for additional Frame Engine debugging
|
||||
|
||||
## Installation
|
||||
|
||||
### From OpenWrt build
|
||||
|
||||
```sh
|
||||
# Add to your build tree
|
||||
git clone https://github.com/rchen14b/luci-app-airoha-npu.git package/luci-app-airoha-npu
|
||||
|
||||
# Enable in menuconfig
|
||||
make menuconfig
|
||||
# Navigate to: LuCI -> Applications -> luci-app-airoha-npu
|
||||
|
||||
# Build
|
||||
make package/luci-app-airoha-npu/compile V=s
|
||||
```
|
||||
|
||||
### Manual install (dev)
|
||||
|
||||
```sh
|
||||
# Copy files to router
|
||||
scp root/usr/libexec/rpcd/luci.airoha_npu root@router:/usr/libexec/rpcd/
|
||||
scp root/usr/share/luci/menu.d/luci-app-airoha-npu.json root@router:/usr/share/luci/menu.d/
|
||||
scp root/usr/share/rpcd/acl.d/luci-app-airoha-npu.json root@router:/usr/share/rpcd/acl.d/
|
||||
scp htdocs/luci-static/resources/view/airoha_npu/status.js root@router:/www/luci-static/resources/view/airoha_npu/
|
||||
|
||||
# Set permissions and restart
|
||||
ssh root@router 'chmod +x /usr/libexec/rpcd/luci.airoha_npu && /etc/init.d/rpcd restart'
|
||||
```
|
||||
|
||||
## Data Sources
|
||||
|
||||
| Data | Source | Required |
|
||||
|------|--------|----------|
|
||||
| NPU status | `/sys/bus/platform/drivers/airoha-npu/`, `dmesg` | Yes |
|
||||
| CPU frequency | `/sys/devices/system/cpu/cpufreq/policy0/` | Yes |
|
||||
| Overclock PLL | `devmem` registers (0x1fa202b4, 0x1fa202b8) | devmem |
|
||||
| PPE entries | `/sys/kernel/debug/ppe/{entries,bind}` | Yes |
|
||||
| WiFi token pool | `/sys/kernel/debug/ieee80211/phy0/mt76/token_info` | Optional |
|
||||
| WiFi station stats | `iw dev <iface> station dump` | Optional |
|
||||
| Frame Engine (GDM/CDM/PSE) | `devmem` registers (0x1fb50xxx-0x1fb53xxx) | devmem |
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
luci-app-airoha-npu/
|
||||
├── Makefile # OpenWrt package build
|
||||
├── htdocs/
|
||||
│ └── luci-static/resources/view/airoha_npu/
|
||||
│ └── status.js # LuCI JavaScript view
|
||||
├── root/
|
||||
│ └── usr/
|
||||
│ ├── libexec/rpcd/
|
||||
│ │ └── luci.airoha_npu # RPC backend (shell)
|
||||
│ └── share/
|
||||
│ ├── luci/menu.d/
|
||||
│ │ └── luci-app-airoha-npu.json # Menu config
|
||||
│ └── rpcd/acl.d/
|
||||
│ └── luci-app-airoha-npu.json # ACL permissions
|
||||
└── screenshots/
|
||||
```
|
||||
|
||||
## RPC Methods
|
||||
|
||||
| Method | Description | Parameters |
|
||||
|--------|-------------|------------|
|
||||
| `getStatus` | NPU, CPU, PPE summary | — |
|
||||
| `getPpeEntries` | PPE flow table (first 100) | — |
|
||||
| `getTokenInfo` | WiFi token pool & station stats | — |
|
||||
| `getFrameEngine` | PSE/GDM/CDM register counters | — |
|
||||
| `setGovernor` | Change CPU governor | `governor` |
|
||||
| `setMaxFreq` | Set CPU max frequency | `freq` (kHz) |
|
||||
| `setOverclock` | Direct PLL frequency set | `freq_mhz` |
|
||||
|
||||
## Version History
|
||||
|
||||
### v1.0.1
|
||||
- Unified Frame Engine diagram with architectural layout matching AN7581 data paths
|
||||
- WiFi band cards moved into CDM4/WDMA section (reflects actual WiFi DMA path)
|
||||
- Added NPU and PPE Engine cards with live status and flow counts
|
||||
- Token pool and PPE flows removed from summary table (now in Frame Engine view)
|
||||
- Fixed GDM4 register addresses (0x2500, not 0x3500)
|
||||
- Improved compact WiFi band chip styling
|
||||
|
||||
### v1.0.0
|
||||
- CPU frequency management with governor and overclock controls
|
||||
- NPU & Offload Engine monitoring with WiFi band cards
|
||||
- Frame Engine visualization (GDM ports, CDM offload ratio, PSE queues)
|
||||
- PPE flow offload table with auto-refresh
|
||||
- Theme-adaptive dark/light mode detection
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
|
||||
## Author
|
||||
|
||||
Ryan Chen — Created for W1700K router (Airoha AN7581 + MT7996 BE19000)
|
||||
@ -0,0 +1,483 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require poll';
|
||||
'require rpc';
|
||||
'require ui';
|
||||
|
||||
var callNpuStatus = rpc.declare({ object: 'luci.airoha_npu', method: 'getStatus' });
|
||||
var callPpeEntries = rpc.declare({ object: 'luci.airoha_npu', method: 'getPpeEntries' });
|
||||
var callTokenInfo = rpc.declare({ object: 'luci.airoha_npu', method: 'getTokenInfo' });
|
||||
var callFrameEngine = rpc.declare({ object: 'luci.airoha_npu', method: 'getFrameEngine' });
|
||||
var callSetGovernor = rpc.declare({ object: 'luci.airoha_npu', method: 'setGovernor', params: ['governor'] });
|
||||
var callSetMaxFreq = rpc.declare({ object: 'luci.airoha_npu', method: 'setMaxFreq', params: ['freq'] });
|
||||
var callSetOverclock = rpc.declare({ object: 'luci.airoha_npu', method: 'setOverclock', params: ['freq_mhz'] });
|
||||
|
||||
/* ── Theme-adaptive CSS ── */
|
||||
var themeCSS = '\
|
||||
.soc-card{background:var(--soc-card-bg);border:1px solid var(--soc-border);border-radius:8px;padding:14px;transition:border-color .3s}\
|
||||
.soc-card-accent{border-left-width:3px;border-left-style:solid}\
|
||||
.soc-muted{color:var(--soc-muted)}\
|
||||
.soc-text{color:var(--soc-text)}\
|
||||
.soc-label{font-size:11px;color:var(--soc-muted)}\
|
||||
.soc-bar-track{background:var(--soc-bar-track);border-radius:4px;overflow:hidden}\
|
||||
.soc-pse-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(130px,1fr));gap:6px}\
|
||||
.soc-pse-cell{background:var(--soc-card-bg);border:1px solid var(--soc-border);border-radius:5px;padding:6px 8px;font-size:12px}\
|
||||
.soc-band-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-top:10px}\
|
||||
.soc-gdm-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:10px}\
|
||||
.soc-cdm-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:10px;margin-bottom:10px}\
|
||||
';
|
||||
|
||||
function isDarkMode() {
|
||||
// Sample multiple elements to get a reliable reading
|
||||
var els = [document.body, document.querySelector('.main-content'), document.querySelector('#maincontent'), document.querySelector('.cbi-map')];
|
||||
for (var i = 0; i < els.length; i++) {
|
||||
if (!els[i]) continue;
|
||||
var bg = window.getComputedStyle(els[i]).backgroundColor;
|
||||
var m = bg.match(/\d+/g);
|
||||
if (m && m.length >= 3) {
|
||||
var a = m.length >= 4 ? parseFloat(m[3]) : 1;
|
||||
if (a < 0.1) continue; // transparent, skip
|
||||
var lum = (parseInt(m[0]) * 299 + parseInt(m[1]) * 587 + parseInt(m[2]) * 114) / 1000;
|
||||
return lum < 128;
|
||||
}
|
||||
}
|
||||
// Fallback: check if any known dark theme stylesheet is loaded
|
||||
var sheets = document.querySelectorAll('link[href*="dark"], link[href*="glass"]');
|
||||
return sheets.length > 0;
|
||||
}
|
||||
|
||||
var _lastDarkMode = null;
|
||||
|
||||
function injectCSS() {
|
||||
var el = document.getElementById('soc-theme-css');
|
||||
if (!el) { el = document.createElement('style'); el.id = 'soc-theme-css'; document.head.appendChild(el); }
|
||||
|
||||
var dark = isDarkMode();
|
||||
if (dark === _lastDarkMode) return;
|
||||
_lastDarkMode = dark;
|
||||
|
||||
var vars = dark
|
||||
? ':root{--soc-card-bg:#1e1e1e;--soc-border:#333;--soc-muted:#999;--soc-text:#e0e0e0;--soc-bar-track:#333}'
|
||||
: ':root{--soc-card-bg:#fff;--soc-border:#d0d0d0;--soc-muted:#666;--soc-text:#222;--soc-bar-track:#e0e0e0}';
|
||||
el.textContent = themeCSS + vars;
|
||||
}
|
||||
|
||||
/* ── Helpers ── */
|
||||
var bandInfo = [
|
||||
{ name: '2.4 GHz', accent: '#ff9800' },
|
||||
{ name: '5 GHz', accent: '#2196f3' },
|
||||
{ name: '6 GHz', accent: '#9c27b0' }
|
||||
];
|
||||
|
||||
var psePortMap = [
|
||||
{ name: 'CDM1', label: 'CPU DMA 1', color: '#607d8b' },
|
||||
{ name: 'GDM1', label: 'Switch 1G', color: '#ff9800' },
|
||||
{ name: 'GDM2', label: 'WAN 10G', color: '#4caf50' },
|
||||
{ name: 'GDM3', label: 'GDM3', color: '#607d8b' },
|
||||
{ name: 'PPE1', label: 'PPE Eng 1', color: '#2196f3' },
|
||||
{ name: 'CDM2', label: 'CPU DMA 2', color: '#607d8b' },
|
||||
{ name: 'CDM3', label: 'CDM3', color: '#607d8b' },
|
||||
{ name: 'CDM4', label: 'WDMA WiFi', color: '#9c27b0' },
|
||||
{ name: 'PPE2', label: 'PPE Eng 2', color: '#2196f3' },
|
||||
{ name: 'GDM4', label: 'LAN2 10G', color: '#4caf50' }
|
||||
];
|
||||
|
||||
function fmtFreq(khz) { return (!khz || khz === 0) ? 'N/A' : (khz / 1000).toFixed(0) + ' MHz'; }
|
||||
function fmtK(n) {
|
||||
if (!n || n === 0) return '0';
|
||||
if (n >= 1e6) return (n / 1e6).toFixed(1) + 'M';
|
||||
if (n >= 1e3) return (n / 1e3).toFixed(1) + 'K';
|
||||
return n.toString();
|
||||
}
|
||||
|
||||
function calcTotalMem(regions) {
|
||||
var t = 0;
|
||||
(regions || []).forEach(function(r) {
|
||||
var m = (r.size || '').match(/(\d+)\s*(KiB|MiB|GiB)/i);
|
||||
if (m) { var s = parseInt(m[1]); var u = m[2][0].toUpperCase(); t += u === 'G' ? s*1048576 : u === 'M' ? s*1024 : s; }
|
||||
});
|
||||
return t >= 1024 ? (t/1024).toFixed(0)+' MiB' : t+' KiB';
|
||||
}
|
||||
|
||||
function tokenHealth(c, s) {
|
||||
if (!s) return { text: 'N/A', color: '#888' };
|
||||
var p = c/s*100;
|
||||
return p < 50 ? { text:'Healthy', color:'#4caf50' } : p < 80 ? { text:'Warning', color:'#ff9800' } : { text:'Critical', color:'#f44336' };
|
||||
}
|
||||
|
||||
function getBandStats(ti, b) {
|
||||
var c = Array.isArray(ti.station_counts) ? ti.station_counts : [];
|
||||
for (var i=0;i<c.length;i++) if (c[i].band===b) return c[i];
|
||||
return { band:b, count:0, tx_packets:0, tx_retries:0 };
|
||||
}
|
||||
|
||||
function getTxQueue(ti, b) {
|
||||
var q = Array.isArray(ti.tx_queues) ? ti.tx_queues : [];
|
||||
for (var i=0;i<q.length;i++) if (q[i].band===b) return q[i];
|
||||
return null;
|
||||
}
|
||||
|
||||
function bandHealth(s) {
|
||||
if (!s || s.count===0) return { text:'No clients', color:'#888' };
|
||||
if (!s.tx_packets) return { text:'Idle', color:'#888' };
|
||||
var r = s.tx_retries/(s.tx_packets+s.tx_retries);
|
||||
return r>0.5 ? {text:'Poor',color:'#f44336'} : r>0.2 ? {text:'Fair',color:'#ff9800'} : {text:'Good',color:'#4caf50'};
|
||||
}
|
||||
|
||||
function retryPct(s) {
|
||||
if (!s || !s.tx_packets) return '-';
|
||||
return (s.tx_retries/(s.tx_packets+s.tx_retries)*100).toFixed(1)+'%';
|
||||
}
|
||||
|
||||
/* ── Mini Band Chip (compact for FE diagram) ── */
|
||||
function renderBandChip(band, txQ, stats) {
|
||||
var info = bandInfo[band] || { name: 'Band '+band, accent: '#888' };
|
||||
var id = 'band-'+band;
|
||||
var h = bandHealth(stats);
|
||||
var type = txQ ? txQ.type : '?';
|
||||
var rp = retryPct(stats);
|
||||
|
||||
return E('div', { 'id': id, 'style': 'background:var(--soc-card-bg);border:1px solid var(--soc-border);border-left:2px solid '+info.accent+';border-radius:6px;padding:10px 12px' }, [
|
||||
E('div', { 'style': 'display:flex;justify-content:space-between;align-items:center;margin-bottom:6px' }, [
|
||||
E('span', { 'class': 'soc-text', 'style': 'font-size:13px;font-weight:bold' }, info.name),
|
||||
E('span', { 'style': 'background:'+(type==='npu'?'#1565c0':'#666')+';color:#fff;padding:1px 6px;border-radius:3px;font-size:9px;font-weight:600' }, type.toUpperCase())
|
||||
]),
|
||||
E('div', { 'style': 'display:flex;justify-content:space-between;align-items:center;font-size:12px' }, [
|
||||
E('div', { 'id': id+'-health', 'style': 'display:flex;align-items:center;gap:4px' }, [
|
||||
E('span', { 'style': 'width:7px;height:7px;border-radius:50%;background:'+h.color+';display:inline-block' }),
|
||||
E('span', { 'style': 'color:'+h.color+';font-weight:500' }, h.text)
|
||||
]),
|
||||
E('span', { 'id': id+'-clients', 'class': 'soc-muted' }, stats.count + ' sta'),
|
||||
(stats.tx_packets > 0) ? E('span', { 'id': id+'-retries', 'class': 'soc-muted' }, rp) : E('span')
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
function updateBandChip(band, stats) {
|
||||
var id = 'band-'+band, h = bandHealth(stats);
|
||||
var el = document.getElementById(id+'-health');
|
||||
if (el) { el.innerHTML = ''; el.appendChild(E('span',{'style':'width:6px;height:6px;border-radius:50%;background:'+h.color+';display:inline-block'})); el.appendChild(E('span',{'style':'color:'+h.color+';font-weight:500;font-size:11px'},h.text)); }
|
||||
var cl = document.getElementById(id+'-clients');
|
||||
if (cl) cl.textContent = stats.count+'sta';
|
||||
var re = document.getElementById(id+'-retries');
|
||||
if (re) { var rp2 = retryPct(stats); re.textContent = rp2; }
|
||||
}
|
||||
|
||||
/* ── Frame Engine Diagram (with WiFi bands, NPU, PPE flows) ── */
|
||||
function renderFeDiagram(fe, ti, st) {
|
||||
if (!fe || fe.error) return E('div', { 'class': 'soc-muted' }, 'devmem not available on this build');
|
||||
ti = ti || {}; st = st || {};
|
||||
|
||||
var ports = Array.isArray(fe.pse_ports) ? fe.pse_ports : [];
|
||||
|
||||
// Helper: GDM card
|
||||
function gdmCard(key, name, label, color, pse) {
|
||||
var d = fe[key] || {};
|
||||
var active = d.tx > 0 || d.rx > 0;
|
||||
return E('div', { 'class': 'soc-card soc-card-accent', 'style': 'border-left-color:'+color + (active?';border-color:'+color:'') }, [
|
||||
E('div', { 'style': 'display:flex;justify-content:space-between;align-items:baseline;margin-bottom:2px' }, [
|
||||
E('span', { 'style': 'font-weight:bold;color:'+color+';font-size:14px' }, name),
|
||||
E('span', { 'class': 'soc-label' }, pse)
|
||||
]),
|
||||
E('div', { 'class': 'soc-label', 'style': 'margin-bottom:6px' }, label),
|
||||
E('div', { 'style': 'display:grid;grid-template-columns:auto 1fr;gap:2px 10px;font-size:12px' }, [
|
||||
E('span', { 'class': 'soc-muted' }, 'TX'), E('span', { 'class': 'soc-text', 'style': 'text-align:right' }, fmtK(d.tx)),
|
||||
E('span', { 'class': 'soc-muted' }, 'RX'), E('span', { 'class': 'soc-text', 'style': 'text-align:right' }, fmtK(d.rx))
|
||||
].concat(d.tx_drop > 0 ? [
|
||||
E('span', { 'style': 'color:#f44336' }, 'TX Drop'), E('span', { 'style': 'color:#f44336;text-align:right' }, fmtK(d.tx_drop))
|
||||
] : []).concat(d.rx_drop > 0 ? [
|
||||
E('span', { 'style': 'color:#f44336' }, 'RX Drop'), E('span', { 'style': 'color:#f44336;text-align:right' }, fmtK(d.rx_drop))
|
||||
] : []))
|
||||
]);
|
||||
}
|
||||
|
||||
// Helper: CDM offload bar
|
||||
function cdmCard(key, name, label, pse) {
|
||||
var d = fe[key] || {};
|
||||
var total = (d.rx_cpu||0) + (d.rx_hwf||0);
|
||||
var pct = total > 0 ? ((d.rx_hwf/total)*100).toFixed(1) : '0.0';
|
||||
var barCol = total===0 ? 'var(--soc-border)' : parseFloat(pct)>80 ? '#4caf50' : parseFloat(pct)>50 ? '#ff9800' : '#f44336';
|
||||
return E('div', { 'class': 'soc-card' }, [
|
||||
E('div', { 'style': 'display:flex;justify-content:space-between;margin-bottom:4px' }, [
|
||||
E('span', { 'style': 'font-weight:bold;color:#607d8b;font-size:13px' }, name+' '+pse),
|
||||
E('span', { 'class': 'soc-label' }, label)
|
||||
]),
|
||||
E('div', { 'class': 'soc-text', 'style': 'font-size:12px;margin-bottom:4px' }, 'HW Offload: '+pct+'%'),
|
||||
E('div', { 'class': 'soc-bar-track', 'style': 'height:6px' }, [
|
||||
E('div', { 'style': 'background:'+barCol+';height:100%;width:'+pct+'%;transition:width .5s;border-radius:4px' })
|
||||
]),
|
||||
E('div', { 'style': 'display:flex;justify-content:space-between;font-size:11px;margin-top:4px' }, [
|
||||
E('span', { 'class': 'soc-muted' }, 'CPU: '+fmtK(d.rx_cpu||0)),
|
||||
E('span', { 'class': 'soc-muted' }, 'HWF: '+fmtK(d.rx_hwf||0)),
|
||||
E('span', { 'class': 'soc-muted' }, 'TX: '+fmtK(d.tx||0))
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
// WiFi band chips for CDM4
|
||||
var bandChips = [];
|
||||
for (var b = 0; b < 3; b++) bandChips.push(renderBandChip(b, getTxQueue(ti, b), getBandStats(ti, b)));
|
||||
|
||||
// CDM4/WDMA + WiFi bands grouped
|
||||
var p7 = ports[7] || { iq: 0, oq: 0, drops: 0 };
|
||||
var cdm4WiFi = E('div', { 'class': 'soc-card soc-card-accent', 'style': 'border-left-color:#9c27b0' }, [
|
||||
E('div', { 'style': 'display:flex;justify-content:space-between;align-items:baseline;margin-bottom:2px' }, [
|
||||
E('span', { 'style': 'font-weight:bold;color:#9c27b0;font-size:14px' }, 'CDM4 / WDMA'),
|
||||
E('span', { 'class': 'soc-label' }, 'P7 WiFi DMA')
|
||||
]),
|
||||
E('div', { 'style': 'display:flex;gap:12px;font-size:11px;margin-bottom:8px' }, [
|
||||
E('span', { 'class': 'soc-muted' }, 'IQ '+p7.iq),
|
||||
E('span', { 'class': 'soc-muted' }, 'OQ '+p7.oq),
|
||||
p7.drops > 0 ? E('span', { 'style': 'color:#f44336' }, 'Drop '+fmtK(p7.drops)) : null
|
||||
].filter(Boolean)),
|
||||
// WiFi bands inside
|
||||
E('div', { 'style': 'display:grid;grid-template-columns:repeat(3,1fr);gap:6px' }, bandChips)
|
||||
]);
|
||||
|
||||
// NPU indicator
|
||||
var npuActive = st.npu_loaded;
|
||||
var npuCard = E('div', { 'class': 'soc-card', 'style': 'border-color:'+(npuActive?'#00bcd4':'var(--soc-border)') }, [
|
||||
E('div', { 'style': 'display:flex;justify-content:space-between;align-items:center;margin-bottom:4px' }, [
|
||||
E('span', { 'style': 'font-weight:bold;color:#00bcd4;font-size:14px' }, 'NPU'),
|
||||
E('span', { 'style': 'background:'+(npuActive?'#00695c':'#666')+';color:#fff;padding:1px 7px;border-radius:3px;font-size:10px;font-weight:600' }, npuActive ? 'ACTIVE' : 'OFF')
|
||||
]),
|
||||
E('div', { 'class': 'soc-label', 'style': 'margin-bottom:4px' }, '8x RISC-V via PCIe RAM'),
|
||||
E('div', { 'style': 'font-size:11px' }, [
|
||||
E('span', { 'class': 'soc-muted' }, 'Manages: '),
|
||||
E('span', { 'class': 'soc-text', 'style': 'font-size:11px' }, 'PPE init, WDMA rings, flow stats')
|
||||
])
|
||||
]);
|
||||
|
||||
// PPE engines with flow count
|
||||
var ppeCard = E('div', { 'class': 'soc-card', 'style': 'border-color:#2196f3' }, [
|
||||
E('div', { 'style': 'display:flex;justify-content:space-between;align-items:center;margin-bottom:4px' }, [
|
||||
E('span', { 'style': 'font-weight:bold;color:#2196f3;font-size:14px' }, 'PPE Engines'),
|
||||
E('span', { 'class': 'soc-label' }, 'P4 + P8')
|
||||
]),
|
||||
E('div', { 'style': 'display:flex;gap:16px;font-size:12px' }, [
|
||||
E('span', {}, [
|
||||
E('span', { 'class': 'soc-muted' }, 'Bound '),
|
||||
E('span', { 'class': 'soc-text', 'style': 'font-weight:bold', 'id': 'fe-ppe-bound' }, (st.offload_bound||0).toString())
|
||||
]),
|
||||
E('span', {}, [
|
||||
E('span', { 'class': 'soc-muted' }, 'Total '),
|
||||
E('span', { 'class': 'soc-text', 'id': 'fe-ppe-total' }, (st.offload_total||0).toString())
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
// PSE buffer
|
||||
var pseT = (fe.pse_used||0)+(fe.pse_free||0);
|
||||
var pseP = pseT>0 ? ((fe.pse_used/pseT)*100).toFixed(1) : '0';
|
||||
var pseCol = parseFloat(pseP)>80?'#f44336':parseFloat(pseP)>50?'#ff9800':'#4caf50';
|
||||
|
||||
// PSE port cells (skip P7 since it's shown in CDM4/WiFi section)
|
||||
var portCells = ports.filter(function(p){ return p.port !== 7; }).map(function(p) {
|
||||
var info = psePortMap[p.port] || { name:'P'+p.port, label:'?', color:'#666' };
|
||||
var drop = p.drops > 0;
|
||||
return E('div', { 'class': 'soc-pse-cell', 'style': drop ? 'border-color:#f44336' : '' }, [
|
||||
E('div', { 'style': 'font-weight:600;color:'+info.color+';font-size:11px' }, 'P'+p.port+' '+info.name),
|
||||
E('div', { 'style': 'display:flex;gap:8px;font-size:11px;margin-top:2px' }, [
|
||||
E('span', { 'class': 'soc-muted' }, 'IQ '+p.iq),
|
||||
E('span', { 'class': 'soc-muted' }, 'OQ '+p.oq),
|
||||
drop ? E('span', { 'style': 'color:#f44336' }, fmtK(p.drops)) : null
|
||||
].filter(Boolean))
|
||||
]);
|
||||
});
|
||||
|
||||
return E('div', { 'id': 'fe-diagram' }, [
|
||||
// PSE buffer bar
|
||||
E('div', { 'class': 'soc-card', 'style': 'margin-bottom:10px' }, [
|
||||
E('div', { 'style': 'display:flex;justify-content:space-between;margin-bottom:4px' }, [
|
||||
E('span', { 'class': 'soc-text', 'style': 'font-weight:bold;font-size:13px' }, 'PSE Shared Buffer'),
|
||||
E('span', { 'class': 'soc-muted', 'style': 'font-size:12px' }, (fe.pse_used||0)+' used / '+(fe.pse_free||0)+' free ('+pseP+'%)')
|
||||
]),
|
||||
E('div', { 'class': 'soc-bar-track', 'style': 'height:8px' }, [
|
||||
E('div', { 'style': 'background:'+pseCol+';height:100%;width:'+pseP+'%;border-radius:4px;transition:width .5s' })
|
||||
])
|
||||
]),
|
||||
// Row 1: GDM ports
|
||||
E('div', { 'class': 'soc-gdm-grid' }, [
|
||||
gdmCard('gdm1', 'GDM1', 'Internal Switch (1G LAN3/4)', '#ff9800', 'P1'),
|
||||
gdmCard('gdm2', 'GDM2', 'WAN (USXGMII 10G)', '#4caf50', 'P2'),
|
||||
gdmCard('gdm4', 'GDM4', 'LAN2 (USXGMII 10G)', '#4caf50', 'P9')
|
||||
]),
|
||||
// Row 2: CDM1/CDM2 (CPU) + CDM4/WiFi
|
||||
E('div', { 'style': 'display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;margin-bottom:10px' }, [
|
||||
cdmCard('cdm1', 'CDM1', 'CPU DMA 1', 'P0'),
|
||||
cdmCard('cdm2', 'CDM2', 'CPU DMA 2', 'P5'),
|
||||
cdm4WiFi
|
||||
]),
|
||||
// Row 3: PPE + NPU
|
||||
E('div', { 'style': 'display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:10px' }, [
|
||||
ppeCard,
|
||||
npuCard
|
||||
]),
|
||||
// PSE port grid
|
||||
E('div', { 'class': 'soc-text', 'style': 'font-size:12px;font-weight:600;margin-bottom:6px' }, 'PSE Port Queue Status'),
|
||||
E('div', { 'class': 'soc-pse-grid' }, portCells)
|
||||
]);
|
||||
}
|
||||
|
||||
/* ── CPU Frequency ── */
|
||||
function freqBarState(hw, min, max, pll, gov) {
|
||||
var oc = gov==='performance' && pll>0 && (pll*1000)>max;
|
||||
return { freq: oc ? pll*1000 : Math.min(hw,max), max: oc ? pll*1000 : max, oc: oc };
|
||||
}
|
||||
|
||||
function renderFreqBar(hw, min, max, pll, gov) {
|
||||
if (!max) return E('span',{},'N/A');
|
||||
var s = freqBarState(hw,min,max,pll,gov);
|
||||
var pct = Math.round(((s.freq-min)/(s.max-min))*100);
|
||||
pct = Math.max(0,Math.min(100,pct));
|
||||
var bg = s.oc ? 'linear-gradient(90deg,#e65100,#ff9800)' : 'linear-gradient(90deg,#2e7d32,#66bb6a)';
|
||||
var label = s.oc ? (pll+' MHz (OC)') : fmtFreq(s.freq);
|
||||
|
||||
return E('div', { 'id':'cpu-freq-bar-wrap', 'style':'display:flex;align-items:center;gap:10px' }, [
|
||||
E('span', { 'class':'soc-muted', 'style':'font-size:90%' }, fmtFreq(min)),
|
||||
E('div', { 'style':'flex:1;border-radius:4px;height:22px;position:relative;min-width:180px;max-width:350px;overflow:hidden', 'class':'soc-bar-track' }, [
|
||||
E('div', { 'id':'cpu-freq-fill', 'style':'background:'+bg+';height:100%;border-radius:4px;width:'+pct+'%;transition:width .5s' }),
|
||||
E('span', { 'id':'cpu-freq-text', 'style':'position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-weight:bold;font-size:13px;color:#fff;text-shadow:0 1px 2px rgba(0,0,0,.6)' }, label)
|
||||
]),
|
||||
E('span', { 'id':'cpu-freq-max-label', 'class':'soc-muted', 'style':'font-size:90%' }, fmtFreq(s.max))
|
||||
]);
|
||||
}
|
||||
|
||||
function updateFreqBar(hw, min, max, pll, gov) {
|
||||
var s = freqBarState(hw,min,max,pll,gov);
|
||||
var el = document.getElementById('cpu-freq-text'), fl = document.getElementById('cpu-freq-fill'), ml = document.getElementById('cpu-freq-max-label');
|
||||
if (el) el.textContent = s.oc ? (pll+' MHz (OC)') : fmtFreq(s.freq);
|
||||
if (fl && s.max>0) { var pct=Math.max(0,Math.min(100,Math.round(((s.freq-min)/(s.max-min))*100))); fl.style.width=pct+'%'; fl.style.background=s.oc?'linear-gradient(90deg,#e65100,#ff9800)':'linear-gradient(90deg,#2e7d32,#66bb6a)'; }
|
||||
if (ml) ml.textContent = fmtFreq(s.max);
|
||||
}
|
||||
|
||||
function renderGovSelect(avail, active) {
|
||||
var gs = (avail||'').trim().split(/\s+/).filter(Boolean);
|
||||
if (!gs.length) return E('span',{},'N/A');
|
||||
return E('select', { 'id':'cpu-governor-select','class':'cbi-input-select','style':'min-width:140px','change':function(ev){
|
||||
var g=ev.target.value; ev.target.disabled=true;
|
||||
callSetGovernor(g).then(function(r){ev.target.disabled=false;if(r&&r.error) ui.addNotification(null,E('p',{},_('Error: ')+r.error),'error');}).catch(function(){ev.target.disabled=false;});
|
||||
}}, gs.map(function(g){return E('option',{'value':g,'selected':g===active?'':null},g);}));
|
||||
}
|
||||
|
||||
function renderMaxFreqSelect(avail, cur) {
|
||||
var fs = (avail||'').trim().split(/\s+/).filter(Boolean);
|
||||
if (!fs.length) return E('span',{},'N/A');
|
||||
return E('select', { 'id':'cpu-maxfreq-select','class':'cbi-input-select','style':'min-width:140px','change':function(ev){
|
||||
var f=ev.target.value; ev.target.disabled=true;
|
||||
callSetMaxFreq(parseInt(f)).then(function(r){ev.target.disabled=false;if(r&&r.error) ui.addNotification(null,E('p',{},_('Error: ')+r.error),'error');}).catch(function(){ev.target.disabled=false;});
|
||||
}}, fs.map(function(f){return E('option',{'value':f,'selected':parseInt(f)===parseInt(cur)?'':null},(parseInt(f)/1000).toFixed(0)+' MHz');}));
|
||||
}
|
||||
|
||||
function renderOcControls() {
|
||||
var inp = E('input',{'id':'oc-freq-input','type':'number','min':'500','max':'1600','step':'50','value':'1400','class':'cbi-input-text','style':'width:100px'});
|
||||
var btn = E('button',{'class':'cbi-button cbi-button-action','style':'margin-left:8px','click':function(){
|
||||
var f=parseInt(document.getElementById('oc-freq-input').value);
|
||||
if(isNaN(f)||f<500||f>1600){ui.addNotification(null,E('p',{},_('Must be 500-1600 MHz')),'error');return;}
|
||||
if(f>1400&&!confirm('Frequencies above 1400 MHz may be unstable. Continue?')) return;
|
||||
btn.disabled=true;btn.textContent=_('Applying...');
|
||||
callSetOverclock(f).then(function(r){btn.disabled=false;btn.textContent=_('Apply');
|
||||
if(r&&r.error) ui.addNotification(null,E('p',{},_('Failed: ')+r.error),'error');
|
||||
else if(r&&r.result==='ok') ui.addNotification(null,E('p',{},_('CPU set to ')+r.actual_mhz+' MHz'),'info');
|
||||
}).catch(function(e){btn.disabled=false;btn.textContent=_('Apply');});
|
||||
}},_('Apply'));
|
||||
return E('div',{'style':'display:flex;align-items:center;gap:8px;flex-wrap:wrap'},[
|
||||
inp, E('span',{'class':'soc-muted'},'MHz'), btn,
|
||||
E('span',{'class':'soc-muted','style':'font-size:85%;margin-left:8px'},_('Direct PLL. Stock max 1200 MHz. Stable up to 1500 MHz.'))
|
||||
]);
|
||||
}
|
||||
|
||||
/* ── PPE Table ── */
|
||||
function renderPpeRows(entries) {
|
||||
return entries.slice(0,100).map(function(e) {
|
||||
var eth = e.eth||''; if(eth==='00:00:00:00:00:00->00:00:00:00:00:00') eth='-';
|
||||
return E('tr',{'class':'tr'},[
|
||||
E('td',{'class':'td'},e.index), E('td',{'class':'td'},E('span',{'class':e.state==='BND'?'label-success':''},e.state)),
|
||||
E('td',{'class':'td'},e.type), E('td',{'class':'td'},e.orig||'-'), E('td',{'class':'td'},e.new_flow||'-'), E('td',{'class':'td'},eth)
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
/* ── Main View ── */
|
||||
return view.extend({
|
||||
load: function() {
|
||||
return Promise.all([ callNpuStatus(), callPpeEntries(), callTokenInfo(), callFrameEngine() ]);
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
injectCSS();
|
||||
var st = data[0]||{}, ppe = data[1]||{}, ti = data[2]||{}, fe = data[3]||{};
|
||||
var entries = Array.isArray(ppe.entries) ? ppe.entries : [];
|
||||
var memR = Array.isArray(st.memory_regions) ? st.memory_regions : [];
|
||||
|
||||
var view = E('div',{'class':'cbi-map'},[
|
||||
E('h2',{},_('Airoha SoC Status')),
|
||||
|
||||
// CPU Frequency
|
||||
E('div',{'class':'cbi-section'},[
|
||||
E('h3',{},_('CPU Frequency')),
|
||||
E('table',{'class':'table'},[
|
||||
E('tr',{'class':'tr'},[ E('td',{'class':'td','width':'33%'},E('strong',{},_('Current Frequency'))), E('td',{'class':'td'}, renderFreqBar(st.cpu_hw_freq,st.cpu_min_freq,st.cpu_max_freq,st.pll_freq_mhz,st.cpu_governor)) ]),
|
||||
E('tr',{'class':'tr'},[ E('td',{'class':'td'},E('strong',{},_('Governor'))), E('td',{'class':'td'}, renderGovSelect(st.cpu_avail_governors,st.cpu_governor)) ]),
|
||||
E('tr',{'class':'tr'},[ E('td',{'class':'td'},E('strong',{},_('Max Frequency'))), E('td',{'class':'td'}, renderMaxFreqSelect(st.cpu_avail_freqs,st.cpu_max_freq)) ]),
|
||||
E('tr',{'class':'tr'},[ E('td',{'class':'td'},E('strong',{},_('Overclock'))), E('td',{'class':'td'}, renderOcControls()) ]),
|
||||
E('tr',{'class':'tr'},[ E('td',{'class':'td'},E('strong',{},_('CPU Cores'))), E('td',{'class':'td'},(st.cpu_count||0).toString()) ])
|
||||
])
|
||||
]),
|
||||
|
||||
// NPU & Frame Engine (unified)
|
||||
E('div',{'class':'cbi-section'},[
|
||||
E('h3',{},_('NPU & Offload Engine')),
|
||||
E('table',{'class':'table'},[
|
||||
E('tr',{'class':'tr'},[ E('td',{'class':'td','width':'33%'},E('strong',{},_('NPU Status'))),
|
||||
E('td',{'class':'td','id':'npu-status'}, st.npu_loaded ?
|
||||
E('span',{'class':'label-success'},_('Active')+(st.npu_device?' ('+st.npu_device+')':'')) :
|
||||
E('span',{'class':'label-danger'},_('Not Active'))) ]),
|
||||
E('tr',{'class':'tr'},[ E('td',{'class':'td'},E('strong',{},_('Firmware / Clock / Cores'))),
|
||||
E('td',{'class':'td','id':'npu-info'}, (st.npu_version||'N/A')+' | '+(st.npu_clock?(st.npu_clock/1e6).toFixed(0)+' MHz':'N/A')+' | '+(st.npu_cores||0)+' cores') ]),
|
||||
E('tr',{'class':'tr'},[ E('td',{'class':'td'},E('strong',{},_('Reserved Memory'))),
|
||||
E('td',{'class':'td','id':'npu-memory'}, calcTotalMem(memR)+' ('+memR.length+' regions)') ])
|
||||
]),
|
||||
|
||||
// Frame Engine diagram (includes WiFi bands, PPE flows, NPU indicator)
|
||||
E('div',{'style':'margin-top:12px'},[ E('h4',{'class':'soc-text','style':'font-size:14px;margin-bottom:8px'},_('Frame Engine'))]),
|
||||
E('div',{'id':'fe-container'}, renderFeDiagram(fe, ti, st))
|
||||
]),
|
||||
|
||||
// PPE Flow Table
|
||||
E('div',{'class':'cbi-section'},[
|
||||
E('h3',{},_('PPE Flow Offload Entries')),
|
||||
E('table',{'class':'table','id':'ppe-entries-table'},[
|
||||
E('tr',{'class':'tr cbi-section-table-titles'},[
|
||||
E('th',{'class':'th'},_('Index')), E('th',{'class':'th'},_('State')), E('th',{'class':'th'},_('Type')),
|
||||
E('th',{'class':'th'},_('Original Flow')), E('th',{'class':'th'},_('New Flow')), E('th',{'class':'th'},_('Ethernet'))
|
||||
])
|
||||
].concat(renderPpeRows(entries)))
|
||||
])
|
||||
]);
|
||||
|
||||
poll.add(L.bind(function() {
|
||||
return Promise.all([ callNpuStatus(), callPpeEntries(), callTokenInfo(), callFrameEngine() ]).then(L.bind(function(d) {
|
||||
injectCSS();
|
||||
var st=d[0]||{}, ppe=d[1]||{}, ti=d[2]||{}, fe=d[3]||{};
|
||||
var entries = Array.isArray(ppe.entries)?ppe.entries:[];
|
||||
|
||||
updateFreqBar(st.cpu_hw_freq,st.cpu_min_freq,st.cpu_max_freq,st.pll_freq_mhz,st.cpu_governor);
|
||||
var gs=document.getElementById('cpu-governor-select'); if(gs&&!gs.matches(':focus')) gs.value=st.cpu_governor||'';
|
||||
var fs=document.getElementById('cpu-maxfreq-select'); if(fs&&!fs.matches(':focus')) fs.value=(st.cpu_max_freq||0).toString();
|
||||
|
||||
var se=document.getElementById('npu-status');
|
||||
if(se){se.innerHTML='';var sp=document.createElement('span');sp.className=st.npu_loaded?'label-success':'label-danger';sp.textContent=st.npu_loaded?(_('Active')+(st.npu_device?' ('+st.npu_device+')':'')):_('Not Active');se.appendChild(sp);}
|
||||
|
||||
var fc=document.getElementById('fe-container'); if(fc){fc.innerHTML='';fc.appendChild(renderFeDiagram(fe, ti, st));}
|
||||
|
||||
var tb=document.getElementById('ppe-entries-table');
|
||||
if(tb){while(tb.rows.length>1)tb.deleteRow(1);renderPpeRows(entries).forEach(function(r){tb.appendChild(r);});}
|
||||
},this));
|
||||
},this), 5);
|
||||
|
||||
return view;
|
||||
},
|
||||
|
||||
handleSaveApply: null, handleSave: null, handleReset: null
|
||||
});
|
||||
176
luci-app-airoha-npu/po/es/luci-app-airoha-npu.po
Normal file
176
luci-app-airoha-npu/po/es/luci-app-airoha-npu.po
Normal file
@ -0,0 +1,176 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Language: es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 3.8\n"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:327
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:406
|
||||
msgid "Active"
|
||||
msgstr "Activo"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:284
|
||||
msgid "Airoha SoC Status"
|
||||
msgstr "Estado del SoC Airoha"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:224
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:233
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:237
|
||||
msgid "Apply"
|
||||
msgstr "Aplicar"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:221
|
||||
msgid "Applying..."
|
||||
msgstr "Aplicando..."
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:350
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:420
|
||||
msgid "Bound:"
|
||||
msgstr "Atado:"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:362
|
||||
msgid "Bytes"
|
||||
msgstr "Bytes"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:310
|
||||
msgid "CPU Cores"
|
||||
msgstr "Núcleos de CPU"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:288
|
||||
msgid "CPU Frequency"
|
||||
msgstr "Frecuencia de la CPU"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:229
|
||||
msgid "CPU set to"
|
||||
msgstr "CPU configurada en"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:291
|
||||
msgid "Current Frequency"
|
||||
msgstr "Frecuencia actual"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:244
|
||||
msgid ""
|
||||
"Direct PLL programming. Governor locked to performance. Stock max: 1200 MHz. "
|
||||
"Tested stable up to 1500 MHz."
|
||||
msgstr ""
|
||||
"Programación directa por PLL. Regulador ajustado al rendimiento. Máxima de "
|
||||
"fábrica: 1200 MHz. Estabilidad comprobada hasta 1500 MHz."
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:360
|
||||
msgid "Ethernet"
|
||||
msgstr "Ethernet"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:85
|
||||
msgid "Failed to set governor:"
|
||||
msgstr "No se pudo establecer el regulador:"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:112
|
||||
msgid "Failed to set max frequency:"
|
||||
msgstr "No se pudo establecer la frecuencia máxima:"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:212
|
||||
msgid "Frequency must be 500-1600 MHz"
|
||||
msgstr "La frecuencia debe ser de 500-1600 MHz"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:296
|
||||
msgid "Governor"
|
||||
msgstr "Regulador"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/root/usr/share/rpcd/acl.d/luci-app-airoha-npu.json:3
|
||||
msgid "Grant access to Airoha SoC status (NPU + CPU + Overclock)"
|
||||
msgstr "Otorgar acceso al estado del SoC Airoha (NPU + CPU + Overclock)"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:355
|
||||
msgid "Index"
|
||||
msgstr "Índice"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:301
|
||||
msgid "Max Frequency"
|
||||
msgstr "Máx. frecuencia"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:331
|
||||
msgid "NPU Clock / Cores"
|
||||
msgstr "Reloj/núcleos de NPU"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:321
|
||||
msgid "NPU Firmware Version"
|
||||
msgstr "Versión de firmware de la NPU"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:318
|
||||
msgid "NPU Information"
|
||||
msgstr "Información de la NPU"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:325
|
||||
msgid "NPU Status"
|
||||
msgstr "Estado de la NPU"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:359
|
||||
msgid "New Flow"
|
||||
msgstr "Nuevo flujo"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:328
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:411
|
||||
msgid "Not Active"
|
||||
msgstr "No activo"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:322
|
||||
msgid "Not available"
|
||||
msgstr "No disponible"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:339
|
||||
msgid "Offload Statistics"
|
||||
msgstr "Estadísticas de descarga"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:358
|
||||
msgid "Original Flow"
|
||||
msgstr "Flujo original"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:306
|
||||
msgid "Overclock"
|
||||
msgstr "Overclock"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:226
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:234
|
||||
msgid "Overclock failed:"
|
||||
msgstr "Overclock fallido:"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:347
|
||||
msgid "PPE Flow Offload Entries"
|
||||
msgstr "Entradas de descarga de flujo de PPE"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:361
|
||||
msgid "Packets"
|
||||
msgstr "Packets"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:335
|
||||
msgid "Reserved Memory"
|
||||
msgstr "Memoria reservada"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/root/usr/share/luci/menu.d/luci-app-airoha-npu.json:3
|
||||
msgid "SoC Status"
|
||||
msgstr "Procesadores"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:356
|
||||
msgid "State"
|
||||
msgstr "Estado"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:349
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:419
|
||||
msgid "Total:"
|
||||
msgstr "Total:"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:357
|
||||
msgid "Type"
|
||||
msgstr "Tipo"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:351
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:421
|
||||
msgid "Unbound:"
|
||||
msgstr "Desatado:"
|
||||
164
luci-app-airoha-npu/po/templates/luci-app-airoha-npu.pot
Normal file
164
luci-app-airoha-npu/po/templates/luci-app-airoha-npu.pot
Normal file
@ -0,0 +1,164 @@
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:327
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:406
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:284
|
||||
msgid "Airoha SoC Status"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:224
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:233
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:237
|
||||
msgid "Apply"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:221
|
||||
msgid "Applying..."
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:350
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:420
|
||||
msgid "Bound:"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:362
|
||||
msgid "Bytes"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:310
|
||||
msgid "CPU Cores"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:288
|
||||
msgid "CPU Frequency"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:229
|
||||
msgid "CPU set to"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:291
|
||||
msgid "Current Frequency"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:244
|
||||
msgid ""
|
||||
"Direct PLL programming. Governor locked to performance. Stock max: 1200 MHz. "
|
||||
"Tested stable up to 1500 MHz."
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:360
|
||||
msgid "Ethernet"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:85
|
||||
msgid "Failed to set governor:"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:112
|
||||
msgid "Failed to set max frequency:"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:212
|
||||
msgid "Frequency must be 500-1600 MHz"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:296
|
||||
msgid "Governor"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/root/usr/share/rpcd/acl.d/luci-app-airoha-npu.json:3
|
||||
msgid "Grant access to Airoha SoC status (NPU + CPU + Overclock)"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:355
|
||||
msgid "Index"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:301
|
||||
msgid "Max Frequency"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:331
|
||||
msgid "NPU Clock / Cores"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:321
|
||||
msgid "NPU Firmware Version"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:318
|
||||
msgid "NPU Information"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:325
|
||||
msgid "NPU Status"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:359
|
||||
msgid "New Flow"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:328
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:411
|
||||
msgid "Not Active"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:322
|
||||
msgid "Not available"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:339
|
||||
msgid "Offload Statistics"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:358
|
||||
msgid "Original Flow"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:306
|
||||
msgid "Overclock"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:226
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:234
|
||||
msgid "Overclock failed:"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:347
|
||||
msgid "PPE Flow Offload Entries"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:361
|
||||
msgid "Packets"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:335
|
||||
msgid "Reserved Memory"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/root/usr/share/luci/menu.d/luci-app-airoha-npu.json:3
|
||||
msgid "SoC Status"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:356
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:349
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:419
|
||||
msgid "Total:"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:357
|
||||
msgid "Type"
|
||||
msgstr ""
|
||||
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:351
|
||||
#: feeds/luci/applications/luci-app-airoha-npu/htdocs/luci-static/resources/view/airoha_npu/status.js:421
|
||||
msgid "Unbound:"
|
||||
msgstr ""
|
||||
483
luci-app-airoha-npu/root/usr/libexec/rpcd/luci.airoha_npu
Executable file
483
luci-app-airoha-npu/root/usr/libexec/rpcd/luci.airoha_npu
Executable file
@ -0,0 +1,483 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Airoha SoC Status RPC backend for LuCI (NPU + CPU + Overclock)
|
||||
|
||||
. /lib/functions.sh
|
||||
|
||||
get_status() {
|
||||
# === NPU Status ===
|
||||
# Read the actually-loaded NPU firmware path from DTS (firmware-name property).
|
||||
local npu_fw=""
|
||||
local npu_dt="/proc/device-tree/soc/npu@1e900000/firmware-name"
|
||||
if [ -f "$npu_dt" ]; then
|
||||
# firmware-name is a string list (null-separated). First entry is the rv32 code.
|
||||
local fw_rel=$(tr '\0' '\n' < "$npu_dt" 2>/dev/null | grep -m1 "rv32")
|
||||
[ -n "$fw_rel" ] && npu_fw="/lib/firmware/$fw_rel"
|
||||
fi
|
||||
|
||||
local npu_ver="Unknown"
|
||||
if [ -n "$npu_fw" ] && [ -f "$npu_fw" ]; then
|
||||
npu_ver=$(strings "$npu_fw" 2>/dev/null | grep -oE '([0-9]+\.[0-9]+\.[0-9]+-)?TLB[0-9.]+[-_v0-9]*' | head -1)
|
||||
[ -z "$npu_ver" ] && npu_ver="Unknown"
|
||||
fi
|
||||
|
||||
local npu_loaded="false"
|
||||
local npu_device=""
|
||||
local npu_driver_path="/sys/bus/platform/drivers/airoha-npu"
|
||||
|
||||
if [ -d "$npu_driver_path" ]; then
|
||||
npu_device=$(ls -1 "$npu_driver_path" 2>/dev/null | grep '\.npu$' | head -1)
|
||||
if [ -n "$npu_device" ]; then
|
||||
npu_loaded="true"
|
||||
fi
|
||||
fi
|
||||
|
||||
local npu_clock=0
|
||||
if [ -f "/sys/kernel/debug/clk/npu/clk_rate" ]; then
|
||||
npu_clock=$(cat /sys/kernel/debug/clk/npu/clk_rate 2>/dev/null || echo 0)
|
||||
fi
|
||||
|
||||
local npu_cores=$(grep -c "airoha-npu-wdt" /proc/interrupts 2>/dev/null || echo 0)
|
||||
|
||||
local offload_bound=$(cat /sys/kernel/debug/ppe/bind 2>/dev/null | grep -c "^[0-9a-fA-F]")
|
||||
local offload_total=$(cat /sys/kernel/debug/ppe/entries 2>/dev/null | grep -c "^[0-9a-fA-F]")
|
||||
|
||||
local mem_info=$(dmesg | grep "reserved mem.*npu" | head -4)
|
||||
local regions="[]"
|
||||
|
||||
if [ -n "$mem_info" ]; then
|
||||
regions=$(echo "$mem_info" | awk '
|
||||
BEGIN { printf "[" }
|
||||
{
|
||||
if (NR > 1) printf ","
|
||||
match($0, /0x[0-9a-fA-F]+\.\.0x[0-9a-fA-F]+/)
|
||||
range = substr($0, RSTART, RLENGTH)
|
||||
split(range, a, "\\.\\.")
|
||||
match($0, /\([0-9]+ [KMG]i?B\)/)
|
||||
size = substr($0, RSTART+1, RLENGTH-2)
|
||||
match($0, /non-reusable [^ ]+/)
|
||||
name = substr($0, RSTART+13)
|
||||
gsub(/@.*/, "", name)
|
||||
printf "{\"name\":\"%s\",\"start\":\"%s\",\"end\":\"%s\",\"size\":\"%s\"}", name, a[1], a[2], size
|
||||
}
|
||||
END { printf "]" }
|
||||
')
|
||||
fi
|
||||
|
||||
# === CPU Frequency Status ===
|
||||
local cpufreq_path="/sys/devices/system/cpu/cpufreq/policy0"
|
||||
local cpu_cur_freq=0
|
||||
local cpu_hw_freq=0
|
||||
local cpu_governor="unknown"
|
||||
local cpu_avail_governors=""
|
||||
local cpu_min_freq=0
|
||||
local cpu_max_freq=0
|
||||
local cpu_avail_freqs=""
|
||||
|
||||
if [ -d "$cpufreq_path" ]; then
|
||||
cpu_cur_freq=$(cat "$cpufreq_path/scaling_cur_freq" 2>/dev/null || echo 0)
|
||||
cpu_hw_freq=$(cat "$cpufreq_path/cpuinfo_cur_freq" 2>/dev/null || echo 0)
|
||||
cpu_governor=$(cat "$cpufreq_path/scaling_governor" 2>/dev/null || echo "unknown")
|
||||
cpu_avail_governors=$(cat "$cpufreq_path/scaling_available_governors" 2>/dev/null || echo "")
|
||||
cpu_min_freq=$(cat "$cpufreq_path/scaling_min_freq" 2>/dev/null || echo 0)
|
||||
cpu_max_freq=$(cat "$cpufreq_path/scaling_max_freq" 2>/dev/null || echo 0)
|
||||
cpu_avail_freqs=$(cat "$cpufreq_path/scaling_available_frequencies" 2>/dev/null || echo "")
|
||||
fi
|
||||
|
||||
# Read actual PLL frequency from hardware (detects overclock)
|
||||
local pll_freq=0
|
||||
if command -v devmem > /dev/null 2>&1; then
|
||||
local pcw_raw=$(devmem 0x1fa202b4 2>/dev/null)
|
||||
local chg_raw=$(devmem 0x1fa202b8 2>/dev/null)
|
||||
if [ -n "$pcw_raw" ] && [ -n "$chg_raw" ]; then
|
||||
pcw_raw=$((pcw_raw))
|
||||
chg_raw=$((chg_raw))
|
||||
local pcw_int=$(( (pcw_raw >> 24) & 0x7F ))
|
||||
local pll_posdiv=$(( (chg_raw >> 4) & 7 ))
|
||||
if [ "$pll_posdiv" -eq 0 ]; then
|
||||
pll_freq=$((pcw_int * 50))
|
||||
else
|
||||
pll_freq=$((pcw_int * 50 / (1 << pll_posdiv)))
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Count online CPUs
|
||||
local cpu_count=0
|
||||
if [ -d "/sys/devices/system/cpu" ]; then
|
||||
cpu_count=$(ls -d /sys/devices/system/cpu/cpu[0-9]* 2>/dev/null | wc -l)
|
||||
fi
|
||||
|
||||
printf '{"npu_version":"%s","npu_loaded":%s,"npu_device":"%s","npu_clock":%d,"npu_cores":%d,"offload_bound":%d,"offload_total":%d,"memory_regions":%s,"cpu_cur_freq":%d,"cpu_hw_freq":%d,"cpu_governor":"%s","cpu_avail_governors":"%s","cpu_min_freq":%d,"cpu_max_freq":%d,"cpu_avail_freqs":"%s","cpu_count":%d,"pll_freq_mhz":%d}' \
|
||||
"${npu_ver:-Unknown}" "$npu_loaded" "${npu_device:-}" "${npu_clock:-0}" "${npu_cores:-0}" "${offload_bound:-0}" "${offload_total:-0}" "$regions" \
|
||||
"${cpu_cur_freq:-0}" "${cpu_hw_freq:-0}" "${cpu_governor:-unknown}" "${cpu_avail_governors:-}" "${cpu_min_freq:-0}" "${cpu_max_freq:-0}" "${cpu_avail_freqs:-}" "${cpu_count:-0}" "${pll_freq:-0}"
|
||||
}
|
||||
|
||||
get_ppe_entries() {
|
||||
local entries_file="/sys/kernel/debug/ppe/entries"
|
||||
|
||||
if [ ! -f "$entries_file" ]; then
|
||||
echo '{"entries":[]}'
|
||||
return
|
||||
fi
|
||||
|
||||
awk '
|
||||
BEGIN {
|
||||
printf "{\"entries\":["
|
||||
first = 1
|
||||
}
|
||||
/^[0-9a-fA-F]+ / {
|
||||
if (first == 0) printf ","
|
||||
first = 0
|
||||
|
||||
index_val = $1
|
||||
state = $2
|
||||
|
||||
type = ""
|
||||
subtype = ""
|
||||
if ($3 == "IPv4" || $3 == "IPv6") {
|
||||
type = $3
|
||||
subtype = $4
|
||||
} else if ($3 == "L2B" || $4 == "L2B") {
|
||||
type = "L2B"
|
||||
subtype = ""
|
||||
} else {
|
||||
type = $3
|
||||
subtype = $4
|
||||
}
|
||||
|
||||
orig = ""
|
||||
match($0, /orig=[^ ]+/)
|
||||
if (RSTART > 0) {
|
||||
orig = substr($0, RSTART+5, RLENGTH-5)
|
||||
}
|
||||
|
||||
new_flow = ""
|
||||
match($0, /new=[^ ]+/)
|
||||
if (RSTART > 0) {
|
||||
new_flow = substr($0, RSTART+4, RLENGTH-4)
|
||||
}
|
||||
|
||||
eth = ""
|
||||
match($0, /eth=[^ ]+/)
|
||||
if (RSTART > 0) {
|
||||
eth = substr($0, RSTART+4, RLENGTH-4)
|
||||
}
|
||||
|
||||
printf "{\"index\":\"%s\",\"state\":\"%s\",\"type\":\"%s %s\",\"orig\":\"%s\",\"new_flow\":\"%s\",\"eth\":\"%s\"}", \
|
||||
index_val, state, type, subtype, orig, new_flow, eth
|
||||
}
|
||||
END {
|
||||
printf "]}"
|
||||
}
|
||||
' "$entries_file" 2>/dev/null || echo '{"entries":[]}'
|
||||
}
|
||||
|
||||
get_token_info() {
|
||||
local token_file="/sys/kernel/debug/ieee80211/phy0/mt76/token_info"
|
||||
|
||||
# Gather per-band WiFi station stats (count, retries, failures)
|
||||
local sta_json="" band
|
||||
for band in 0 1 2; do
|
||||
local stats=$(
|
||||
for iface in $(iw dev 2>/dev/null | awk '/Interface phy0\.'$band'-/{print $2}'); do
|
||||
iw dev "$iface" station dump 2>/dev/null
|
||||
done | awk '
|
||||
BEGIN { n=0; tp=0; tr=0 }
|
||||
/^Station/ { n++ }
|
||||
/tx packets:/ { tp += $3 }
|
||||
/tx retries:/ { tr += $3 }
|
||||
END { printf "%d %d %d", n, tp, tr }'
|
||||
)
|
||||
set -- $stats
|
||||
[ -n "$sta_json" ] && sta_json="${sta_json},"
|
||||
sta_json="${sta_json}{\"band\":${band},\"count\":${1:-0},\"tx_packets\":${2:-0},\"tx_retries\":${3:-0}}"
|
||||
done
|
||||
|
||||
if [ ! -f "$token_file" ]; then
|
||||
printf '{"token_count":0,"token_size":0,"npu_active":false,"tx_queues":[],"station_counts":[%s]}' "$sta_json"
|
||||
return
|
||||
fi
|
||||
|
||||
awk -v stations="$sta_json" '
|
||||
BEGIN {
|
||||
tc=0; ts=0; npu=0
|
||||
tq_n=0
|
||||
}
|
||||
/^ token_count:/ { tc=$2+0; ts=$4+0 }
|
||||
/status:.*active/ { npu=1 }
|
||||
/band [0-9]/ {
|
||||
gsub(/\|/, " ")
|
||||
for(i=1;i<=NF;i++) {
|
||||
if($i=="band") tq_band[tq_n]=$(i+1)+0
|
||||
if($i=="npu" || $i=="dma") tq_type[tq_n]=$i
|
||||
}
|
||||
cnt=0
|
||||
for(i=1;i<=NF;i++) {
|
||||
if($i ~ /^[0-9]+$/ && $(i-1)!="band") {
|
||||
tq_v[tq_n,cnt++]=$i+0
|
||||
}
|
||||
}
|
||||
tq_n++
|
||||
}
|
||||
END {
|
||||
printf "{\"token_count\":%d,\"token_size\":%d,\"npu_active\":%s,\"tx_queues\":[", \
|
||||
tc, ts, (npu?"true":"false")
|
||||
|
||||
for(i=0;i<tq_n;i++) {
|
||||
if(i>0) printf ","
|
||||
printf "{\"band\":%d,\"type\":\"%s\",\"ndesc\":%d,\"queued\":%d,\"head\":%d,\"tail\":%d}", \
|
||||
tq_band[i], tq_type[i], tq_v[i,0], tq_v[i,1], tq_v[i,2], tq_v[i,3]
|
||||
}
|
||||
|
||||
printf "],\"station_counts\":[%s]}", stations
|
||||
}
|
||||
' "$token_file" 2>/dev/null || printf '{"token_count":0,"token_size":0,"npu_active":false,"tx_queues":[],"station_counts":[%s]}' "$sta_json"
|
||||
}
|
||||
|
||||
set_governor() {
|
||||
local governor
|
||||
governor=$(echo "$1" | jsonfilter -e "@.governor" 2>/dev/null)
|
||||
|
||||
if [ -z "$governor" ]; then
|
||||
echo '{"error":"missing governor parameter"}'
|
||||
return
|
||||
fi
|
||||
|
||||
# Validate governor is available
|
||||
local avail=$(cat /sys/devices/system/cpu/cpufreq/policy0/scaling_available_governors 2>/dev/null)
|
||||
if ! echo "$avail" | grep -qw "$governor"; then
|
||||
printf '{"error":"invalid governor: %s"}' "$governor"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "$governor" > /sys/devices/system/cpu/cpufreq/policy0/scaling_governor 2>/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
printf '{"result":"ok","governor":"%s"}' "$governor"
|
||||
else
|
||||
echo '{"error":"failed to set governor"}'
|
||||
fi
|
||||
}
|
||||
|
||||
set_max_freq() {
|
||||
local freq
|
||||
freq=$(echo "$1" | jsonfilter -e "@.freq" 2>/dev/null)
|
||||
|
||||
if [ -z "$freq" ]; then
|
||||
echo '{"error":"missing freq parameter"}'
|
||||
return
|
||||
fi
|
||||
|
||||
# Validate freq is in available list
|
||||
local avail=$(cat /sys/devices/system/cpu/cpufreq/policy0/scaling_available_frequencies 2>/dev/null)
|
||||
if ! echo "$avail" | grep -qw "$freq"; then
|
||||
printf '{"error":"invalid frequency: %s"}' "$freq"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "$freq" > /sys/devices/system/cpu/cpufreq/policy0/scaling_max_freq 2>/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
printf '{"result":"ok","freq":%d}' "$freq"
|
||||
else
|
||||
echo '{"error":"failed to set max frequency"}'
|
||||
fi
|
||||
}
|
||||
|
||||
set_overclock() {
|
||||
local target_mhz
|
||||
target_mhz=$(echo "$1" | jsonfilter -e "@.freq_mhz" 2>/dev/null)
|
||||
|
||||
if [ -z "$target_mhz" ]; then
|
||||
echo '{"error":"missing freq_mhz parameter"}'
|
||||
return
|
||||
fi
|
||||
|
||||
# Validate range (500-1600, crash likely above 1500 at stock voltage)
|
||||
if [ "$target_mhz" -lt 500 ] || [ "$target_mhz" -gt 1600 ]; then
|
||||
echo '{"error":"frequency must be 500-1600 MHz"}'
|
||||
return
|
||||
fi
|
||||
|
||||
if ! command -v devmem > /dev/null 2>&1; then
|
||||
echo '{"error":"devmem not available"}'
|
||||
return
|
||||
fi
|
||||
|
||||
# Calculate PCW and posdiv
|
||||
local posdiv pcw_int
|
||||
if [ "$target_mhz" -lt 1000 ]; then
|
||||
posdiv=1
|
||||
pcw_int=$((target_mhz / 25))
|
||||
else
|
||||
posdiv=0
|
||||
pcw_int=$((target_mhz / 50))
|
||||
fi
|
||||
|
||||
# Lock governor to performance to prevent cpufreq from fighting
|
||||
echo performance > /sys/devices/system/cpu/cpufreq/policy0/scaling_governor 2>/dev/null
|
||||
|
||||
# Read current register values
|
||||
local cur_clkmux=$(($(devmem 0x1fa201e0)))
|
||||
local cur_pcw=$(($(devmem 0x1fa202b4)))
|
||||
local cur_chg=$(($(devmem 0x1fa202b8)))
|
||||
|
||||
# Step 1: Enable PLL2 path in CLK_MUX (set bit 2)
|
||||
devmem 0x1fa201e0 32 $(printf "0x%08X" $((cur_clkmux | 0x4)))
|
||||
|
||||
# Step 2: MCUCFG switch to PLL2 (sel=3 in bits[10:9])
|
||||
local cur_u=$(($(devmem 0x1efbe640)))
|
||||
devmem 0x1efbe640 32 $(printf "0x%08X" $(((cur_u & 0xFFFFFFE0) | 0x12)))
|
||||
local cur_s=$(($(devmem 0x1efbe7c0)))
|
||||
devmem 0x1efbe7c0 32 $(printf "0x%08X" $(((cur_s & 0xFFFFF9FF) | (3 << 9))))
|
||||
|
||||
# Step 3: Unlock PLL registers
|
||||
local cur_p=$(($(devmem 0x1fa20268)))
|
||||
devmem 0x1fa20268 32 $(printf "0x%08X" $(((cur_p & 0xFFFFFF00) | 0x12)))
|
||||
|
||||
# Step 4: Write new PCW integer
|
||||
devmem 0x1fa202b4 32 $(printf "0x%08X" $(((cur_pcw & 0x00FFFFFF) | (pcw_int << 24))))
|
||||
|
||||
# Step 5: Write posdiv and toggle CHG bit
|
||||
local chg_bit=$((cur_chg & 1))
|
||||
local cleared=$((cur_chg & 0xFFFFFF8E))
|
||||
if [ "$chg_bit" -eq 1 ]; then
|
||||
devmem 0x1fa202b8 32 $(printf "0x%08X" $((cleared | (posdiv << 4))))
|
||||
else
|
||||
devmem 0x1fa202b8 32 $(printf "0x%08X" $((cleared | (posdiv << 4) | 1)))
|
||||
fi
|
||||
|
||||
# Step 6: Wait for PLL lock
|
||||
sleep 0.02
|
||||
|
||||
# Step 7: Enable ARMPLL path in CLK_MUX (set bit 0)
|
||||
local cur_clkmux2=$(($(devmem 0x1fa201e0)))
|
||||
devmem 0x1fa201e0 32 $(printf "0x%08X" $((cur_clkmux2 | 0x1)))
|
||||
|
||||
# Step 8: MCUCFG switch back to ARMPLL
|
||||
local cur_u2=$(($(devmem 0x1efbe640)))
|
||||
devmem 0x1efbe640 32 $(printf "0x%08X" $(((cur_u2 & 0xFFFFFFE0) | 0x12)))
|
||||
local cur_s2=$(($(devmem 0x1efbe7c0)))
|
||||
devmem 0x1efbe7c0 32 $(printf "0x%08X" $(((cur_s2 & 0xFFFFF9FF) | (1 << 9))))
|
||||
|
||||
# Step 9: Clear PLL2 path in CLK_MUX
|
||||
local cur_clkmux3=$(($(devmem 0x1fa201e0)))
|
||||
devmem 0x1fa201e0 32 $(printf "0x%08X" $((cur_clkmux3 & 0xFFFFFFFB)))
|
||||
|
||||
# Step 10: Re-lock PLL registers
|
||||
local cur_p2=$(($(devmem 0x1fa20268)))
|
||||
devmem 0x1fa20268 32 $(printf "0x%08X" $((cur_p2 & 0xFFFFFF00)))
|
||||
|
||||
# Verify
|
||||
local final_pcw=$(($(devmem 0x1fa202b4)))
|
||||
local final_chg=$(($(devmem 0x1fa202b8)))
|
||||
local read_pcw=$(( (final_pcw >> 24) & 0x7F ))
|
||||
local read_posdiv=$(( (final_chg >> 4) & 7 ))
|
||||
local read_freq
|
||||
if [ "$read_posdiv" -eq 0 ]; then
|
||||
read_freq=$((read_pcw * 50))
|
||||
else
|
||||
read_freq=$((read_pcw * 50 / (1 << read_posdiv)))
|
||||
fi
|
||||
|
||||
printf '{"result":"ok","target_mhz":%d,"actual_mhz":%d,"pcw":%d,"posdiv":%d}' \
|
||||
"$target_mhz" "$read_freq" "$read_pcw" "$read_posdiv"
|
||||
}
|
||||
|
||||
get_frame_engine() {
|
||||
if ! command -v devmem > /dev/null 2>&1; then
|
||||
echo '{"error":"devmem not available"}'
|
||||
return
|
||||
fi
|
||||
|
||||
# PSE queue status (IQ=hi16, OQ=lo16) and drop counts for each port
|
||||
local i reg_q reg_d pse_ports=""
|
||||
for i in 0 1 2 3 4 5 6 7 8 9; do
|
||||
reg_q=$(printf "0x%x" $((0x1fb50150 + i * 4)))
|
||||
reg_d=$(printf "0x%x" $((0x1fb50120 + i * 4)))
|
||||
local qval=$(($(devmem $reg_q)))
|
||||
local dval=$(($(devmem $reg_d)))
|
||||
local iq=$(( (qval >> 16) & 0xFFFF ))
|
||||
local oq=$(( qval & 0xFFFF ))
|
||||
[ -n "$pse_ports" ] && pse_ports="${pse_ports},"
|
||||
pse_ports="${pse_ports}{\"port\":${i},\"iq\":${iq},\"oq\":${oq},\"drops\":${dval}}"
|
||||
done
|
||||
|
||||
# PSE shared buffer
|
||||
local psb=$(($(devmem 0x1fb50104)))
|
||||
local pse_used=$(( (psb >> 16) & 0xFFFF ))
|
||||
local pse_free=$(( psb & 0x7FFF ))
|
||||
|
||||
# GDM counters (TX_OK, TX_DROP, RX_OK, RX_FC_DROP, RX_OVER_DROP)
|
||||
# Read as hex to handle large 32-bit counters
|
||||
local gdm1_tx=$(($(devmem 0x1fb50604)))
|
||||
local gdm1_txd=$(($(devmem 0x1fb50608)))
|
||||
local gdm1_rx=$(($(devmem 0x1fb50648)))
|
||||
local gdm1_rxd=$(($(devmem 0x1fb5064c)))
|
||||
|
||||
local gdm2_tx=$(($(devmem 0x1fb51604)))
|
||||
local gdm2_txd=$(($(devmem 0x1fb51608)))
|
||||
local gdm2_rx=$(($(devmem 0x1fb51648)))
|
||||
local gdm2_rxd=$(($(devmem 0x1fb5164c)))
|
||||
|
||||
local gdm4_tx=$(($(devmem 0x1fb52604)))
|
||||
local gdm4_txd=$(($(devmem 0x1fb52608)))
|
||||
local gdm4_rx=$(($(devmem 0x1fb52648)))
|
||||
local gdm4_rxd=$(($(devmem 0x1fb5264c)))
|
||||
|
||||
# CDM counters (TX_OK, RXCPU_OK, RXHWF_OK, RXCPU_DROP, RXHWF_DROP)
|
||||
local cdm1_tx=$(($(devmem 0x1fb50580)))
|
||||
local cdm1_rxcpu=$(($(devmem 0x1fb50590)))
|
||||
local cdm1_rxhwf=$(($(devmem 0x1fb50594)))
|
||||
local cdm1_rxcpud=$(($(devmem 0x1fb505a0)))
|
||||
local cdm1_rxhwfd=$(($(devmem 0x1fb505a4)))
|
||||
|
||||
local cdm2_tx=$(($(devmem 0x1fb51580)))
|
||||
local cdm2_rxcpu=$(($(devmem 0x1fb51590)))
|
||||
local cdm2_rxhwf=$(($(devmem 0x1fb51594)))
|
||||
local cdm2_rxcpud=$(($(devmem 0x1fb515a0)))
|
||||
local cdm2_rxhwfd=$(($(devmem 0x1fb515a4)))
|
||||
|
||||
printf '{"pse_ports":[%s],"pse_used":%d,"pse_free":%d,' "$pse_ports" "$pse_used" "$pse_free"
|
||||
printf '"gdm1":{"tx":%d,"tx_drop":%d,"rx":%d,"rx_drop":%d},' "$gdm1_tx" "$gdm1_txd" "$gdm1_rx" "$gdm1_rxd"
|
||||
printf '"gdm2":{"tx":%d,"tx_drop":%d,"rx":%d,"rx_drop":%d},' "$gdm2_tx" "$gdm2_txd" "$gdm2_rx" "$gdm2_rxd"
|
||||
printf '"gdm4":{"tx":%d,"tx_drop":%d,"rx":%d,"rx_drop":%d},' "$gdm4_tx" "$gdm4_txd" "$gdm4_rx" "$gdm4_rxd"
|
||||
printf '"cdm1":{"tx":%d,"rx_cpu":%d,"rx_hwf":%d,"rx_cpu_drop":%d,"rx_hwf_drop":%d},' "$cdm1_tx" "$cdm1_rxcpu" "$cdm1_rxhwf" "$cdm1_rxcpud" "$cdm1_rxhwfd"
|
||||
printf '"cdm2":{"tx":%d,"rx_cpu":%d,"rx_hwf":%d,"rx_cpu_drop":%d,"rx_hwf_drop":%d}}' "$cdm2_tx" "$cdm2_rxcpu" "$cdm2_rxhwf" "$cdm2_rxcpud" "$cdm2_rxhwfd"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
list)
|
||||
echo '{"getStatus":{},"getPpeEntries":{},"getTokenInfo":{},"getFrameEngine":{},"setGovernor":{"governor":"str"},"setMaxFreq":{"freq":"int"},"setOverclock":{"freq_mhz":"int"}}'
|
||||
;;
|
||||
call)
|
||||
case "$2" in
|
||||
getStatus)
|
||||
get_status
|
||||
;;
|
||||
getPpeEntries)
|
||||
get_ppe_entries
|
||||
;;
|
||||
getTokenInfo)
|
||||
get_token_info
|
||||
;;
|
||||
setGovernor)
|
||||
read input
|
||||
set_governor "$input"
|
||||
;;
|
||||
setMaxFreq)
|
||||
read input
|
||||
set_max_freq "$input"
|
||||
;;
|
||||
setOverclock)
|
||||
read input
|
||||
set_overclock "$input"
|
||||
;;
|
||||
getFrameEngine)
|
||||
get_frame_engine
|
||||
;;
|
||||
*)
|
||||
echo '{"error":"Invalid method"}'
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
@ -0,0 +1,13 @@
|
||||
{
|
||||
"admin/status/npu": {
|
||||
"title": "SoC Status",
|
||||
"order": 15,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "airoha_npu/status"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-app-airoha-npu" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
{
|
||||
"luci-app-airoha-npu": {
|
||||
"description": "Grant access to Airoha SoC status (NPU + CPU + Overclock)",
|
||||
"read": {
|
||||
"ubus": {
|
||||
"luci.airoha_npu": [ "getStatus", "getPpeEntries", "getTokenInfo", "getFrameEngine" ]
|
||||
},
|
||||
"file": {
|
||||
"/sys/kernel/debug/ppe/entries": [ "read" ],
|
||||
"/sys/kernel/debug/ppe/bind": [ "read" ],
|
||||
"/sys/devices/system/cpu/cpufreq/policy0/*": [ "read" ],
|
||||
"/sys/kernel/debug/ieee80211/*/mt76/token_info": [ "read" ]
|
||||
}
|
||||
},
|
||||
"write": {
|
||||
"ubus": {
|
||||
"luci.airoha_npu": [ "setGovernor", "setMaxFreq", "setOverclock" ]
|
||||
},
|
||||
"file": {
|
||||
"/sys/devices/system/cpu/cpufreq/policy0/scaling_governor": [ "write" ],
|
||||
"/sys/devices/system/cpu/cpufreq/policy0/scaling_max_freq": [ "write" ],
|
||||
"/dev/mem": [ "read", "write" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
luci-app-airoha-npu/screenshots/cpu-frequency.png
Normal file
BIN
luci-app-airoha-npu/screenshots/cpu-frequency.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 952 KiB |
BIN
luci-app-airoha-npu/screenshots/npu-offload-engine.png
Normal file
BIN
luci-app-airoha-npu/screenshots/npu-offload-engine.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 910 KiB |
BIN
luci-app-airoha-npu/screenshots/ppe-flow-table.png
Normal file
BIN
luci-app-airoha-npu/screenshots/ppe-flow-table.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-passwall
|
||||
PKG_VERSION:=26.6.2
|
||||
PKG_RELEASE:=163
|
||||
PKG_RELEASE:=164
|
||||
PKG_PO_VERSION:=$(PKG_VERSION)
|
||||
|
||||
PKG_CONFIG_DEPENDS:= \
|
||||
@ -84,7 +84,7 @@ config PACKAGE_$(PKG_NAME)_Nftables_Transparent_Proxy
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Geoview
|
||||
bool "Include Geoview"
|
||||
select PACKAGE_geoview
|
||||
default y if aarch64||arm||i386||x86_64
|
||||
default y if aarch64||i386||x86_64
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy
|
||||
bool "Include Haproxy"
|
||||
@ -104,7 +104,7 @@ config PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Client
|
||||
bool "Include Shadowsocks Rust Client"
|
||||
depends on !i386
|
||||
default y if aarch64||x86_64
|
||||
default y if x86_64
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Server
|
||||
bool "Include Shadowsocks Rust Server"
|
||||
@ -134,7 +134,7 @@ config PACKAGE_$(PKG_NAME)_INCLUDE_Simple_Obfs
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_SingBox
|
||||
bool "Include Sing-Box"
|
||||
select PACKAGE_sing-box
|
||||
default y if aarch64||i386||x86_64
|
||||
default y
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Geodata
|
||||
bool "Include V2ray_Geodata"
|
||||
@ -144,11 +144,11 @@ config PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Geodata
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Plugin
|
||||
bool "Include V2ray-Plugin (Shadowsocks Plugin)"
|
||||
default y if aarch64||i386||x86_64
|
||||
default y if i386||x86_64
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Xray
|
||||
bool "Include Xray"
|
||||
default y
|
||||
default n
|
||||
|
||||
config PACKAGE_$(PKG_NAME)_INCLUDE_Xray_Plugin
|
||||
bool "Include Xray-Plugin (Shadowsocks Plugin)"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user