Compare commits

..
Author SHA1 Message Date
Leency 1772558259 drivers/usbrndis: fix throughput and link-up on phones
Test PR / Build (es_ES) (pull_request) Successful in 2m4s
Test PR / Build (ru_RU) (pull_request) Successful in 2m7s
Test PR / Build (en_US) (pull_request) Successful in 2m11s
- MaxTransferSize 2048 like Linux: phones no longer split messages
  across transfers.
- 32 RX buffers in flight instead of one (~1 Mbit/s cap).
- TX slots from a preallocated pool of 192, not Kmalloc.
- Link up on received frames despite MEDIA_DISCONNECT; 1 s fallback
  timer; wait for MEDIA_CONNECT before DHCP.
- Random host MAC per boot instead of the phone's fixed one.
- QUERY carries an input buffer (QEMU usb-net init works).
- Stop re-arming the notify pipe on endless errors.
- DEBUG_STATS counters and frame log, off by default.
2026-09-30 09:17:45 +03:00
Leency bd071d588b kernel/net: fix use-after-free on refused TCP connections (#622)
Build system / Build (en_US) (push) Successful in 3m2s
Build system / Build (es_ES) (push) Successful in 3m9s
Build system / Build (ru_RU) (push) Successful in 3m15s
Build system / Publish Images (push) Successful in 11m5s
tcp_input: on RST+ACK in SYN_SENT, unlock the socket mutex before
tcp_drop and leave through .drop_no_socket. tcp_drop frees the socket
and locks that mutex itself, so the network thread hung on it.

Assisted-by: Claude:claude-opus-5
Reviewed-on: #622
Reviewed-by: hidnplayr <hidnplayr@gmail.com>
Reviewed-by: Burer <burer@kolibrios.org>
Co-authored-by: Kiril Lipatov <lipatov.kiril@gmail.com>
2026-09-29 19:12:30 +00:00
Leency 9ec66b0eba drivers/i8255x: get to work on a real device (#725)
Build system / Build (en_US) (push) Successful in 3m23s
Build system / Build (es_ES) (push) Successful in 3m27s
Build system / Build (ru_RU) (push) Successful in 3m31s
Build system / Publish Images (push) Successful in 11m54s
- the TX reclaim loop in int_handler multiplied sizeof.txfd by itself  (mul eax) instead of by last_tx, so it looked 1024 bytes past tx_ring and never freed a descriptor
- it also left txfd.status set, and transmit treats a non-zero status as busy, so once the 16-entry ring wrapped every send ended in "TX overrun": DHCP fell back to link-local, ping showed "Socket error", while the link still read as connected
- reclaim only descriptors the device completed (TXFD_STATUS_C) and zero their status; transmit now checks for an unreclaimed buffer (virt_addr)

Seen on an EtherExpress Pro/100 in a Pentium II. Reproduced in QEMU (-cpu pentium2, i82557b): ping stopped at the 12th reply every time; with the fix it runs indefinitely, also under a UDP flood.

Reviewed-on: #725
Reviewed-by: hidnplayr <hidnplayr@gmail.com>
Reviewed-by: Burer <burer@kolibrios.org>
Co-authored-by: leency <lipatov.kiril@gmail.com>
2026-09-29 19:03:24 +00:00
Leency 95bcf78537 kernel/ahci: support CD/DVD drives (ATAPI) (#694)
Build system / Build (es_ES) (push) Successful in 3m34s
Build system / Build (en_US) (push) Successful in 3m39s
Build system / Build (ru_RU) (push) Successful in 3m46s
Build system / Publish Images (push) Successful in 10m4s
Summary: an optical drive on an AHCI port was detected and then ignored,
so CD/DVD only worked with the controller switched to IDE mode. It now
shows up as /srN and ISO9660 discs are readable, including disc changes.

Details:
- ahci_atapi_cmd sends a SCSI command through the ATA PACKET command
  (A bit in the command header, CDB in acmd, DMA when IDENTIFY PACKET
  word 49 allows it, DMADIR when word 62 requires it). Built on top of it:
  TEST UNIT READY, REQUEST SENSE, READ CAPACITY(10), READ(10), READ TOC
  (start of the last session for multisession discs) and START STOP UNIT
  for DISKFUNC.LoadTray.
- Drives are registered as sr0, sr1, ... with DISK_NO_INSERT_NOTIFICATION,
  2048-byte sectors and read-only media; iso9660 mounts them as /srN/1.
  They cannot be named cd*: fs_lfn.inc sends every path starting with "cd"
  to the old ATAPI-over-IDE code.
- Disc change: the kernel lets go of a mounted medium only when querymedia
  fails, so after a UNIT ATTENTION querymedia fails once and the kernel
  immediately mounts the new disc. A READ(10) that hits UNIT ATTENTION
  fails instead of silently reading the new disc.
- One ATAPI transfer is capped at (PRDT_MAX_ENTRIES - 1) * 4096 bytes, so
  the PRDT always covers the whole request. The PRDT builder moved out of
  ahci_rw_sectors into ahci_build_prdt and is shared by both paths.
- A command the drive refuses (an empty tray does that on every TEST UNIT
  READY) restarts the port quietly instead of running the full recovery.
- Optical ports never busy-poll a long command and never switch the whole
  controller to polling: missing interrupts are counted per port and only
  that port's PxIE is turned off.
- Diagnostic output (command statistics, interrupt wakeups, register dumps
  during init) is now behind AHCI_DBGLVL, off by default. Errors and one
  line per detected device are still printed.

Tested: QEMU, VirtualBox and VMware (detection, listings and reads checked
against host checksums; eject/insert in QEMU and VirtualBox, a direct disc
swap in VirtualBox, an empty drive in VMware), SATA disk read/write on the
same controller in QEMU, and a GA-D525TUD (NM10) with an ASUS DRW-24B3ST:
detection, reading, disc change. The debug level change was build-tested
only.

Assisted-by: Claude Opus 5
Reviewed-on: #694
Reviewed-by: Burer <burer@kolibrios.org>
Reviewed-by: Ivan B <1+dunkaist@noreply.localhost>
Co-authored-by: leency <lipatov.kiril@gmail.com>
2026-09-29 15:44:06 +00:00
Leency 6f3cae4e0c kernel/network: fix window-update check in tcp_output (#711)
Build system / Build (en_US) (push) Successful in 4m8s
Build system / Build (es_ES) (push) Successful in 3m58s
Build system / Build (ru_RU) (push) Successful in 4m9s
Build system / Publish Images (push) Successful in 11m38s
`tcp_output`: the window-update check computed `min(free, max_win - advertised)` instead of BSD's `min(free, max_win) - advertised`. With unread data in the receive buffer, every `send()` produced an empty ACK and never reached the persist state: no zero-window probes, and the connection hung.

- Clamp to the free space first, then subtract the advertised window.
- Compare signed: the result is negative when the window shrank.

Tested in QEMU with pcap: ~11000 empty ACKs in 0.6 s and no probe before the fix; 12 ACKs and a 1-byte persist probe after it.

Reviewed-on: #711
Reviewed-by: Burer <burer@kolibrios.org>
Reviewed-by: hidnplayr <hidnplayr@gmail.com>
Co-authored-by: leency <lipatov.kiril@gmail.com>
2026-09-29 13:38:57 +00:00
Leency b59e371c0e kernel/network: forget a network device's state when it is removed (#719)
Build system / Build (en_US) (push) Successful in 5m45s
Build system / Build (es_ES) (push) Successful in 5m55s
Build system / Build (ru_RU) (push) Successful in 6m4s
Build system / Publish Images (push) Failing after 11m50s
- `net_remove_device`: clear the slot's IPv4 settings, ARP table and counters, and drop its frames from the ethernet input queue.
- `ipv4_route`:
  - skip slots without a device;
  - an interface without an address routes broadcasts only;
  - off-link without a gateway fails instead of resolving 0.0.0.0.
- `ipv4_output_raw`: check the route before resolving it.
- `eth_output`, `arp_output_request`: refuse a null device.
- `eth_output`: count a too-large frame on the device, not on `eax`.

Reviewed-on: #719
Reviewed-by: hidnplayr <hidnplayr@gmail.com>
Reviewed-by: Burer <burer@kolibrios.org>
Co-authored-by: leency <lipatov.kiril@gmail.com>
2026-09-29 13:30:00 +00:00
Leency 939505198f http.obj: close the socket when connect() fails; kernel: unlock on tcp_connect errors
Check kernel codestyle / Check kernel codestyle (pull_request) Successful in 25s
Test PR / Build (ru_RU) (pull_request) Successful in 2m2s
Test PR / Build (en_US) (pull_request) Successful in 2m11s
Test PR / Build (es_ES) (pull_request) Successful in 2m18s
Build system / Build (en_US) (push) Successful in 3m50s
Build system / Build (es_ES) (push) Successful in 3m52s
Build system / Build (ru_RU) (push) Successful in 3m55s
Build system / Publish Images (push) Successful in 11m8s
open_connection in http.obj left the socket open whenever connect()
failed. The kernel never reclaims sockets of a finished process
(socket_process_end is a stub), so every failed connect leaked a socket
with its two SOCKET_BUFFER_SIZE rings for good, and a late SYN+ACK could
still connect the orphan. WebView with a few dead image hosts ran the
kernel heap dry ("SOCKET_ring_create: Out of memory!"), after which every
application lost the network. Close the socket on the way out.

The socket() error check compared against 0, but the syscall returns -1
on failure; compare with -1.

The close() above exposed a second bug: tcp_connect took SOCKET.mutex
before creating the rings and returned from .nomem and .enoroute without
releasing it. Any later socket_free on that socket - close() from the
application, once http.obj does it - then waited for the mutex forever
and the thread became unkillable. Unlock on both error exits.

Assisted-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 14:38:01 +03:00
Burer 75cb80fd33 ci: compact older builds on the storage server (#697)
Build system / Build (es_ES) (push) Successful in 3m23s
Build system / Build (en_US) (push) Successful in 3m27s
Build system / Build (ru_RU) (push) Successful in 3m28s
Build system / Publish Images (push) Successful in 3m14s
Every push to main publishes 4 images per language plus the unpacked build tree, and nothing on the storage server ever shrinks. `kolibri.raw` is a fixed 128 MiB regardless of content, and `kolibri.img` ends up stored three times per language.

This adds a compaction step at the end of the deploy playbook. It runs on the storage server itself, so nothing extra crosses the network.

- The build that was just published stays as it is: bare images and a browsable tree.
- Every older build keeps its images zipped and loses the unpacked tree, which is rebuildable from the commit anyway.

`*.distr.zip`, `sha256sums.txt` and `build.log` are never touched.

Reviewed-on: #697
Reviewed-by: Gleb Zaharov <risdeveau@lair.moe>
Reviewed-by: Mikhail Frolov <mixa.frolov2003@gmail.com>
2026-09-28 07:32:37 +00:00
Burer 6ba901606d ci: open submodule bump PRs as the bot, not as Actions
Test PR / Build (es_ES) (pull_request) Successful in 4m48s
Test PR / Build (en_US) (pull_request) Successful in 5m3s
Test PR / Build (ru_RU) (pull_request) Successful in 2m18s
Build system / Build (ru_RU) (push) Successful in 4m52s
Build system / Build (es_ES) (push) Successful in 5m11s
Build system / Build (en_US) (push) Successful in 5m16s
Build system / Publish Images (push) Successful in 4m8s
A branch pushed and a pull request opened with the built-in GITEA_TOKEN
come from the Actions user, and events it creates start no workflow run.
Test PR never ran on a bump, so the required check stayed missing and
the PRs could not be merged however many approvals they collected.

Put BOT_TOKEN back in both places: checkout persists it for the push,
and the API call needs it to open the PR under the bot account. This
reverts 882f1f024.

Assisted-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-28 07:17:58 +00:00
kolibrios-bot Bot b1d329da2a chore/submodules: bump cmm to 4aeaee3386dac12fc4ef57ad531b632890df5425
Test PR / Build (es_ES) (pull_request) Successful in 2m22s
Test PR / Build (ru_RU) (pull_request) Successful in 2m25s
Test PR / Build (en_US) (pull_request) Successful in 2m27s
Build system / Build (es_ES) (push) Successful in 6m5s
Build system / Build (en_US) (push) Successful in 6m31s
Build system / Build (ru_RU) (push) Successful in 6m35s
Build system / Publish Images (push) Successful in 5m18s
2026-09-28 07:14:00 +00:00
Leency df9946fe80 libs/libimg: make the grayscale JPEG palette opaque
Test PR / Build (en_US) (pull_request) Successful in 1m55s
Test PR / Build (ru_RU) (pull_request) Successful in 2m1s
Test PR / Build (es_ES) (pull_request) Successful in 2m5s
Build system / Build (es_ES) (push) Successful in 2m59s
Build system / Build (en_US) (push) Successful in 3m5s
Build system / Build (ru_RU) (push) Successful in 3m10s
Build system / Publish Images (push) Successful in 3m27s
A grayscale JPEG is decoded as Image.bpp8i with a 256-level gray
palette built from xor eax, eax, so every entry had alpha 0. After
img.convert to Image.bpp32 the picture was fully transparent: programs
that blend by alpha (WebView) reserved its room and drew nothing.
The palette now starts at 0xFF000000, as the PNG decoder fills its own.

Assisted-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-27 20:13:13 +03:00
22 changed files with 2406 additions and 812 deletions

No files matched your search

+24
View File
@@ -54,3 +54,27 @@
src: "{{ stable_artifact_path }}/"
dest: "{{ storage_path }}/../"
mode: preserve
# older builds shrink to compressed images and lose their unpacked tree; the newest few keep both
- name: Looking for zstd on the storage server
ansible.builtin.shell: command -v zstd
register: zstd_present
changed_when: false
failed_when: false
- name: Requiring zstd for compaction
ansible.builtin.assert:
that: zstd_present.rc == 0
fail_msg: >-
zstd is not installed on the storage server, so older builds cannot
be compacted. Install it and re-run this build.
- name: Compacting older builds
ansible.builtin.script:
cmd: >-
../../utils/compact-builds.sh
{{ storage_path | quote }}
{{ compact_keep | default(5) | int }}
{{ compact_max | default(10) | int }}
register: compaction
changed_when: "'compacted 0 of' not in compaction.stdout"
+74
View File
@@ -0,0 +1,74 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0-only
# SPDX-FileCopyrightText: 2026 KolibriOS team
# Shrink older builds on the storage server: compress their images and drop
# their unpacked tree, which is rebuildable from the commit. sha256sums.txt
# keeps naming the bare images, so a compacted build has to be decompressed
# before it can be verified.
#
# Layout: <root>/<version>/<lang>/kolibrios-<descr>-<lang>.{img,iso,raw} -> .zst
# <root>/<version>/<lang>/data/ -> removed
#
# Usage: compact-builds.sh <storage-root> [keep] [max-per-run]
# DRY_RUN=1 walks the same selection and changes nothing
set -euo pipefail
root=${1:?storage root required}
# the build just published is always among these, so an upload in flight is safe
keep=${2:-5}
# a build is hundreds of MiB to re-compress: cap the first pass over the backlog
max=${3:-10}
dry=${DRY_RUN:-0}
verb=compacted
[ "$dry" = 1 ] && verb="would compact"
# --rm drops the source only once the archive is written; the box serves the
# site over NFS, so compaction yields to it
compress_image() {
nice -n 10 ionice -c3 zstd -12 -T1 -q --rm "$1"
}
export -f compress_image
leftovers() {
local build=$1
find "$build" -mindepth 2 -maxdepth 2 \
\( -type d -name data \
-o -type f \( -name '*.img' -o -name '*.iso' -o -name '*.raw' \) \) \
-print -quit
}
compacted=0
pending=0
while IFS= read -r version; do
dir="$root/$version"
[ -n "$(leftovers "$dir")" ] || continue
pending=$((pending + 1))
[ "$compacted" -lt "$max" ] || continue
if [ "$dry" = 1 ]; then
compacted=$((compacted + 1))
echo "$verb $version"
continue
fi
# a build holds nine images: one per core, each zstd kept to one thread so
# the two levels of parallelism do not fight. Archive before dropping the
# tree, so a failure here leaves the build merely uncompacted and the next
# run picks it up again
find "$dir" -mindepth 2 -maxdepth 2 -type f \
\( -name '*.img' -o -name '*.iso' -o -name '*.raw' \) -print0 \
| xargs -0 -r -P "$(nproc)" -I{} bash -c 'compress_image "$@"' _ {}
find "$dir" -mindepth 2 -maxdepth 2 -type d -name data -exec rm -rf {} +
compacted=$((compacted + 1))
echo "$verb $version"
# <tag>-<commit count>-g<sha>: order by the count, never by mtime
done < <(find "$root" -mindepth 1 -maxdepth 1 -type d -name '*-*-g*' -printf '%f\n' \
| sort -t- -k2,2n | head -n "-$keep")
echo "$verb $compacted of $pending older build(s), $((pending - compacted)) left for the next run"
+2 -1
View File
@@ -34,10 +34,11 @@ jobs:
uses: actions/checkout@v4
with:
submodules: true
token: ${{ secrets.BOT_TOKEN }}
- name: Bump ${{ matrix.name }} and open PR
env:
TOKEN: ${{ secrets.GITEA_TOKEN }}
TOKEN: ${{ secrets.BOT_TOKEN }}
API: ${{ github.api_url }}
REPO: ${{ github.repository }}
NAME: ${{ matrix.name }}
+1
View File
@@ -427,6 +427,7 @@ tup.append_table(img_files, {
{"DEFAULT.SKN", VAR_SKINS .. "/../skins/Leency/Shkvorka/Shkvorka.skn"},
{"DISPTEST", VAR_PROGS .. "/testing/disptest/disptest"},
{"END", VAR_PROGS .. "/system/end/light/end"},
{"ESKIN", VAR_PROGS .. "/system/eskin/eskin"},
{"GMON", VAR_PROGS .. "/system/gmon/gmon"},
{"HDD_INFO", VAR_PROGS .. "/system/hdd_info/hdd_info"},
{"KBD", VAR_PROGS .. "/testing/kbd/kbd"},
+1
View File
@@ -10,6 +10,7 @@
/SYS/SETUP BOOT 0 # Load device settings
/SYS/LOADDRV I2CHID 0 # Start I2C touchpad driver
/SYS/@TASKBAR "" 0 # Start taskbar
/SYS/ESKIN "" 0 # Set style
/SYS/@ICON "" 0 # Multithread desktop icons
/SYS/TMPDISK A0 0 # Add virtual RAM disk /tmp0/1
/SYS/@RESHARE "" 0 # Shared resources
+1 -1
View File
@@ -3,6 +3,7 @@ language=en
font file=/sys/Fonts/Tahoma.kf
font smoothing=sbp
font height=9
speaker mute=off
[touchpad]
speed=100
@@ -28,7 +29,6 @@ double_click_delay=64
[low-level]
LBA=off
PCI=on
speaker=on
;timeout in minutes
;program needs a full path
@@ -20,6 +20,7 @@ disptest;
docpack;Документация / Блок документов;asm;http://git.kolibrios.org/KolibriOS/kolibrios/src/branch/main/programs/system/docpack;;http://wiki.kolibrios.org/wiki/Docpack/ru
easyshot;Простой скриншотер;c--;http://git.kolibrios.org/KolibriOS/kolibrios/src/branch/main/programs/cmm/misc/easyshot.c;http://board.kolibrios.org/viewtopic.php?t=3695;
end;Программа завершения работы;asm;http://git.kolibrios.org/KolibriOS/kolibrios/src/branch/main/programs/system/end;http://board.kolibrios.org/viewtopic.php?t=2442;http://wiki.kolibrios.org/wiki/End/ru
eskin;Установщик фона и скина при старте системы;asm;http://git.kolibrios.org/KolibriOS/kolibrios/src/branch/main/programs/system/eskin;http://board.kolibrios.org/viewtopic.php?t=2424;
fb2read;Читалка FB2;oberon07;http://git.kolibrios.org/KolibriOS/kolibrios/src/branch/main/programs/other/fb2reader;http://board.kolibrios.org/viewtopic.php?t=3128;
fspeed;Тестирование скорости диска;asm;http://git.kolibrios.org/KolibriOS/kolibrios/src/branch/main/programs/testing/fspeed;http://board.kolibrios.org/viewtopic.php?t=2660;
gmon;Информация о загрузке системы;asm;http://git.kolibrios.org/KolibriOS/kolibrios/src/branch/main/programs/system/gmon;http://board.kolibrios.org/viewtopic.php?t=470;http://wiki.kolibrios.org/wiki/Gmon/ru
1 Имя программы Описание Lang Gitea Forum Wiki
20 docpack Документация / Блок документов asm http://git.kolibrios.org/KolibriOS/kolibrios/src/branch/main/programs/system/docpack http://wiki.kolibrios.org/wiki/Docpack/ru
21 easyshot Простой скриншотер c-- http://git.kolibrios.org/KolibriOS/kolibrios/src/branch/main/programs/cmm/misc/easyshot.c http://board.kolibrios.org/viewtopic.php?t=3695
22 end Программа завершения работы asm http://git.kolibrios.org/KolibriOS/kolibrios/src/branch/main/programs/system/end http://board.kolibrios.org/viewtopic.php?t=2442 http://wiki.kolibrios.org/wiki/End/ru
23 eskin Установщик фона и скина при старте системы asm http://git.kolibrios.org/KolibriOS/kolibrios/src/branch/main/programs/system/eskin http://board.kolibrios.org/viewtopic.php?t=2424
24 fb2read Читалка FB2 oberon07 http://git.kolibrios.org/KolibriOS/kolibrios/src/branch/main/programs/other/fb2reader http://board.kolibrios.org/viewtopic.php?t=3128
25 fspeed Тестирование скорости диска asm http://git.kolibrios.org/KolibriOS/kolibrios/src/branch/main/programs/testing/fspeed http://board.kolibrios.org/viewtopic.php?t=2660
26 gmon Информация о загрузке системы asm http://git.kolibrios.org/KolibriOS/kolibrios/src/branch/main/programs/system/gmon http://board.kolibrios.org/viewtopic.php?t=470 http://wiki.kolibrios.org/wiki/Gmon/ru
+100 -14
View File
@@ -130,6 +130,8 @@ RU_STATUS_IDLE = 0000b shl 2
RU_STATUS_SUSPENDED = 0001b shl 2
RU_STATUS_NO_RESOURCES = 0010b shl 2
RU_STATUS_READY = 0100b shl 2
SCB_STATUS_CUS = 11000000b ; CU Status
CU_STATUS_ACTIVE = 10b shl 6
SCB_STATUS_FCP = 1 shl 8 ; Flow Control Pause
SCB_STATUS_SWI = 1 shl 10 ; Software Interrupt
SCB_STATUS_MDI = 1 shl 11 ; MDI read/write complete
@@ -183,6 +185,8 @@ struct txfd
ends
TXFD_STATUS_C = 1 shl 15
TXFD_CMD_IA = 1 shl 0
TXFD_CMD_CFG = 1 shl 1
TXFD_CMD_TX = 1 shl 2
@@ -628,6 +632,9 @@ reset:
mov ax, CU_START or INT_MASK
out dx, ax
call cmd_wait
; cmd_wait only means the SCB accepted the command. The Configure below
; reuses confcmd, so a slow chip would read it instead of our MAC.
call confcmd_wait
;-------------
; Configure CU
@@ -652,6 +659,7 @@ reset:
mov ax, CU_START ; expect Interrupts from now on
out dx, ax
call cmd_wait
call confcmd_wait
; Start media check timer
mov [ebx + device.state], ETH_LINK_DOWN
@@ -678,6 +686,11 @@ init_rx_ring:
;---------------------
; build rxfd structure
cmp [ebx + device.rx_desc], 0 ; the RFD of an earlier reset
je @f
invoke NetFree, [ebx + device.rx_desc]
mov [ebx + device.rx_desc], 0
@@:
invoke NetAlloc, 2000
test eax, eax
jz .out_of_mem
@@ -689,6 +702,7 @@ init_rx_ring:
mov [esi + sizeof.NET_BUFF + rxfd.command], RXFD_CMD_EL or RXFD_CMD_SUSPEND
mov [esi + sizeof.NET_BUFF + rxfd.link], eax
mov [esi + sizeof.NET_BUFF + rxfd.count], 0
mov [esi + sizeof.NET_BUFF + rxfd.rx_buf_addr], 0xffffffff ; simplified mode, no RBD
mov [esi + sizeof.NET_BUFF + rxfd.size], 1528
ret
@@ -709,6 +723,15 @@ init_tx_ring:
invoke GetPhysAddr
mov ecx, TX_RING_SIZE
.next_desc:
; after a reset with frames still unreclaimed: free them, or transmit sees the
; descriptor busy (virt_addr) and reports TX overrun forever
cmp [esi + txfd.virt_addr], 0
je @f
push eax ecx
invoke NetFree, [esi + txfd.virt_addr]
pop ecx eax
mov [esi + txfd.virt_addr], 0
@@:
mov [esi + txfd.status], 0
mov [esi + txfd.command], 0
lea edx, [eax + txfd.buf_addr]
@@ -763,7 +786,7 @@ proc transmit stdcall bufferptr
lea edi, [ebx + device.tx_ring + eax]
; Check if current descriptor is free or still in use
cmp [edi + txfd.status], 0
cmp [edi + txfd.virt_addr], 0 ; buffer not reclaimed yet
jne .overrun
; Fill in status and command values
@@ -786,6 +809,22 @@ proc transmit stdcall bufferptr
mov eax, edi
invoke GetPhysAddr
set_io [ebx + device.io_addr], 0
; CU_START is only valid while the CU is idle or suspended:
; wait for the previous frame to leave (1.2 ms at 10 Mbit)
push eax ecx
set_io [ebx + device.io_addr], REG_SCB_STATUS
mov ecx, 100000
.cu_busy:
in al, dx
and al, SCB_STATUS_CUS
cmp al, CU_STATUS_ACTIVE
jne .cu_free
dec ecx
jnz .cu_busy
DEBUGF 2, "CU still active, starting anyway\n"
.cu_free:
pop ecx eax
set_io [ebx + device.io_addr], REG_SCB_PTR
out dx, eax
@@ -854,7 +893,7 @@ int_handler:
DEBUGF 1,"Status: %x\n", ax
test ax, SCB_STATUS_FR ; did we receive a frame?
test ax, SCB_STATUS_FR or SCB_STATUS_RNR ; frame received or receiver stopped?
jz .no_rx
push ax
@@ -901,6 +940,7 @@ int_handler:
mov [esi + sizeof.NET_BUFF + rxfd.command], RXFD_CMD_EL or RXFD_CMD_SUSPEND
mov [esi + sizeof.NET_BUFF + rxfd.link], eax
mov [esi + sizeof.NET_BUFF + rxfd.count], 0
mov [esi + sizeof.NET_BUFF + rxfd.rx_buf_addr], 0xffffffff ; simplified mode, no RBD
mov [esi + sizeof.NET_BUFF + rxfd.size], 1528
; restart RX
@@ -916,11 +956,22 @@ int_handler:
mov ax, RX_START
out dx, ax
call cmd_wait
.out_of_mem:
; Hand the frame over to the kernel
jmp [EthInput]
.out_of_mem:
; The buffer is still our only RFD: drop the frame and reuse it
mov esi, [esp] ; the buffer
mov ecx, [esi + NET_BUFF.length]
dec [ebx + device.packets_rx] ; counted above, before the allocation
sub dword [ebx + device.bytes_rx], ecx
sbb dword [ebx + device.bytes_rx + 4], 0
inc [ebx + device.packets_rx_drop]
add esp, 12 ; buffer, .rx_loop, ebx
mov esi, [ebx + device.rx_desc]
jmp .not_ok
.not_ok:
; Reset the FD
mov [esi + sizeof.NET_BUFF + rxfd.status], 0
@@ -945,6 +996,27 @@ int_handler:
.no_rx_:
DEBUGF 1, "no more data\n"
; With a single RFD the receiver sits in No Resources/Suspended whenever a
; frame arrived before we re-armed it. Nothing restarts it but us.
set_io [ebx + device.io_addr], 0
set_io [ebx + device.io_addr], REG_SCB_STATUS
in al, dx
and al, SCB_STATUS_RUS
cmp al, RU_STATUS_READY
je .ru_ready
test byte[esp], 1 ; bit 0 of the saved status is reserved,
jnz .ru_ready ; we mark it: one restart per IRQ
or byte[esp], 1
movzx eax, al
DEBUGF 1, "Restarting receiver, RU status %x\n", eax:2
mov esi, [ebx + device.rx_desc]
push ebx
test [esi + sizeof.NET_BUFF + rxfd.status], RXFD_STATUS_C
jnz .rx_loop ; a frame completed meanwhile
add esp, 4
jmp .not_ok ; re-arm the empty RFD
.ru_ready:
pop ax
.no_rx:
@@ -955,13 +1027,13 @@ int_handler:
push eax
.loop_tx:
mov edi, [ebx + device.last_tx]
mov eax, sizeof.txfd
mul eax
mov eax, [ebx + device.last_tx]
mov edx, sizeof.txfd
mul edx
lea edi, [ebx + device.tx_ring + eax]
cmp [edi + txfd.status], 0
je .tx_done
test [edi + txfd.status], TXFD_STATUS_C ; sent by the device?
jz .tx_done
cmp [edi + txfd.virt_addr], 0
je .tx_done
@@ -970,6 +1042,7 @@ int_handler:
push [edi + txfd.virt_addr]
mov [edi + txfd.virt_addr], 0
mov [edi + txfd.status], 0 ; free for transmit again
invoke NetFree
inc [ebx + device.last_tx]
@@ -980,12 +1053,6 @@ int_handler:
pop eax
.no_tx:
test ax, RU_STATUS_NO_RESOURCES
jz .not_out_of_resources
DEBUGF 2, "Out of resources!\n"
.not_out_of_resources:
pop edi esi ebx
xor eax, eax
inc eax
@@ -1079,6 +1146,25 @@ proc check_media_mii stdcall dev:dword
endp
; Wait (up to 1 s) until the device has executed the command in confcmd.
align 4
confcmd_wait:
mov ecx, 1000 ; udelay = Sleep(1), 1 ms
.loop:
test [ebx + device.confcmd.status], TXFD_STATUS_C
jnz .done
call udelay
dec ecx
jnz .loop
movzx eax, [ebx + device.confcmd.command]
DEBUGF 2, "Command 0x%x not completed\n", eax:4
ret
.done:
movzx eax, [ebx + device.confcmd.command]
movzx ecx, [ebx + device.confcmd.status]
DEBUGF 1, "Command 0x%x done, status 0x%x\n", eax:4, ecx:4
ret
align 4
cmd_wait:
+604 -74
View File
@@ -38,10 +38,7 @@ DEBUG = 1
__DEBUG__ = 1
__DEBUG_LEVEL__ = 2 ; 1 = verbose, 2 = errors/events only
; 1 = print per-second RX/TX rate statistics to the debug board while
; traffic flows (cb/fr = transfers/frames, B = bytes, tx = frames sent,
; wnd = advertised TCP window). Costs a few counters per frame and one
; line of board output per second; invaluable for throughput debugging.
; 1 = per-second RX/TX counters and a frame log on the debug board
DEBUG_STATS = 0
include '../../struct.inc'
@@ -84,6 +81,9 @@ OID_GEN_CURRENT_PACKET_FILTER = 0x0001010E
; NDIS packet filter: directed | multicast | all-multicast | broadcast
RNDIS_PACKET_FILTER = 0x0000000F
; With a host address of our own choosing (see random_mac) also
; promiscuous, as Linux always sets: the device never learns that address.
RNDIS_FILTER_PROMISC = 0x00000020
RNDIS_STATUS_MEDIA_CONNECT = 0x4001000B
RNDIS_STATUS_MEDIA_DISCONNECT = 0x4001000C
@@ -98,13 +98,32 @@ STATE_RUN = 4 ; registered, data path live
ETH_FRAME_MIN = 60 ; minimal ethernet frame (without FCS)
ETH_FRAME_MAX = 1514 ; maximal ethernet frame (without FCS)
RNDIS_HDR_SIZE = 44 ; REMOTE_NDIS_PACKET_MSG header
RNDIS_RX_BUF = 8192 ; our max transfer, advertised in INIT
RNDIS_RX_BUFFERS = 2 ; ping-pong pair for the bulk IN pipe
; Our MaxTransferSize and the RX buffer size: one frame, as in Linux.
; With 8192 phones split messages across transfers and we lost them.
RNDIS_RX_BUF = 2048
; Bulk IN transfers kept in flight: up to 32 frames per 10 ms wakeup
; of the USB thread (~38 Mbit/s). See the note in rndis_start.
RNDIS_RX_BUFFERS = 32
RNDIS_RX_MAX_ERRORS = 32 ; consecutive RX errors before receive
; stops (anti retry-storm)
; The same for the interrupt IN pipe (was an endless retry storm).
RNDIS_NOTIFY_MAX_ERRORS = 16
RNDIS_RSP_BUF = 1024 ; encapsulated response buffer
RNDIS_TX_MAX_PENDING = 32 ; max messages in flight on bulk OUT
RNDIS_QUERY_LEN = 28 + 48 ; QUERY_MSG with a 48-byte input buffer,
; what Linux rndis_host sends; also the
; size of CmdBuf, the longest command
; Bulk OUT messages in flight, each in a slot of a preallocated pool
; (Kmalloc's 128 KB heap ran out). TX completions wait up to 10 ms for
; the USB thread, and 96 slots were still exhausted during uploads.
; A 2 KB slot never crosses a page, so 192 TRBs fit the 256-entry ring.
RNDIS_TX_MAX_PENDING = 192 ; slots, a multiple of 32 (free bitmap)
RNDIS_TX_SLOT = 2048 ; [context ptr][message <= 1559 bytes]
RNDIS_FLOWS = 8 ; TCP flows tracked by DEBUG_STATS (power of 2)
RNDIS_LOG_BURST = 40 ; frame-log lines allowed per second (DEBUG_STATS)
RNDIS_GET_RETRIES = 200 ; response polls per command (see below)
; 1/100 s to wait for MEDIA_CONNECT before taking the link as up.
; A phone just switched to tethering drops the first DHCP DISCOVER.
RNDIS_LINK_WAIT = 100
; USB structures
struct usb_descr
@@ -177,6 +196,8 @@ struct rndis_dev ETH_DEVICE
InitState dd ? ; STATE_* of the init chain
DevMaxTx dd ? ; device's max transfer (from INIT_C)
LinkDown dd ? ; a MEDIA_DISCONNECT was indicated
LinkKnown dd ? ; the device has indicated its media state
LinkTimer dd ? ; TimerHS handle of the link-up fallback
TxPending dd ? ; messages in flight on bulk OUT
Dead dd ? ; set on disconnect/fatal error
@@ -187,9 +208,16 @@ struct rndis_dev ETH_DEVICE
KaPending dd ? ; a keepalive reply is in flight
GetRetries dd ? ; polls left for the current command
RxBufs rd RNDIS_RX_BUFFERS ; bulk IN buffers (KernelAlloc)
RxArea dd ? ; one KernelAlloc holding all RX buffers
RxBufs rd RNDIS_RX_BUFFERS ; bulk IN buffers, slices of RxArea
RxSize dd ? ; size of one buffer
TxArea dd ? ; KernelAlloc'ed pool of TX slots
TxFree rd RNDIS_TX_MAX_PENDING/32 ; bit set = slot free
RxErrRun dd ? ; consecutive failed RX transfers
NotifyErrRun dd ? ; consecutive failed notification polls
RxArmed dd ? ; transfers outstanding on the bulk IN
; pipe; zero means receive has died
LenErrNoted dd ? ; refused-frame lengths already logged
; per-second statistics, only maintained when DEBUG_STATS = 1
StatTicks dd ? ; last print time, timer ticks
@@ -199,11 +227,24 @@ struct rndis_dev ETH_DEVICE
StatTxPkts dd ? ; packets_tx snapshot at last print
StatWnd dd ? ; TCP receive window we advertised
; in the last outgoing TCP frame
StatTxHigh dd ? ; deepest the bulk OUT queue has been
StatTail dd ? ; received bytes no frame was made of
StatRxUni dd ? ; frames received addressed to our MAC
StatRxBc dd ? ; frames received as broadcast/multicast
LogTicks dd ? ; start of the current frame-log second
LogCount dd ? ; frame-log lines printed in it
StatLogDrop dd ? ; frame-log lines suppressed by the limit
StatGap dd ? ; TCP segments that arrived beyond a
; hole in their flow (loss/reorder)
StatRetx dd ? ; TCP segments with data already seen
; (the peer retransmitting)
FlowNext dd ? ; next slot of Flows to recycle
Flows rd RNDIS_FLOWS*2 ; [key][next expected seq]
SetupCmd rb 8 ; setup packet: SEND_ENCAPSULATED_CMD
SetupRsp rb 8 ; setup packet: GET_ENCAPSULATED_RSP
SetupKa rb 8 ; setup packet: keepalive reply
CmdBuf rb 64 ; init chain command messages
CmdBuf rb RNDIS_QUERY_LEN ; init chain command messages
KaBuf rb 16 ; keepalive reply message
NotifBuf rb 16 ; interrupt notification buffer
RspBuf rb RNDIS_RSP_BUF ; encapsulated responses
@@ -239,6 +280,18 @@ proc START c, reason:dword, cmdline:dword
jne .nothing
DEBUGF 2,"loading (RNDIS host network driver)\n"
; one random host address per boot (see random_mac)
rdtsc
xor eax, edx
imul eax, eax, 0x9E3779B1
mov dword [random_mac], eax
imul eax, eax, 0x85EBCA6B
rol eax, 13
mov word [random_mac+4], ax
and byte [random_mac], 0xFE ; unicast
or byte [random_mac], 0x02 ; locally administered
invoke RegUSBDriver, my_service, service_proc, usb_functions
.nothing:
@@ -693,7 +746,16 @@ proc rndis_notify_callback stdcall uses ebx esi edi, .pipe:dword, .status:dword,
cmp [.status], USB_STATUS_STALL
je .ret ; don't hammer a stalled pipe
cmp [.status], 0
jne .rearm ; transient error: retry
je .status_ok
; transient error: retry, but not for ever
inc [ebx+rndis_dev.NotifyErrRun]
cmp [ebx+rndis_dev.NotifyErrRun], RNDIS_NOTIFY_MAX_ERRORS
jb .rearm
DEBUGF 2,"notification pipe stopped after %u errors (status %u)\n", \
[ebx+rndis_dev.NotifyErrRun], [.status]
ret
.status_ok:
mov [ebx+rndis_dev.NotifyErrRun], 0
cmp [.length], 8
jb .rearm
@@ -763,17 +825,23 @@ proc rndis_response_callback stdcall uses ebx esi edi, .pipe:dword, .status:dwor
mov [ebx+rndis_dev.DevMaxTx], eax
DEBUGF 2,"initialized, device max transfer %u\n", eax
; query the permanent MAC address
; query the permanent MAC address, with an unused input buffer as Linux
; sends it: QEMU's usb-net stalls a query without one
lea edi, [ebx+rndis_dev.CmdBuf]
push edi
xor eax, eax
mov ecx, RNDIS_QUERY_LEN / 4
rep stosd
pop edi
mov dword [edi], RNDIS_MSG_QUERY
mov dword [edi+4], 28 ; MessageLength
mov dword [edi+4], RNDIS_QUERY_LEN ; MessageLength
mov dword [edi+8], 2 ; RequestID
mov dword [edi+12], OID_802_3_PERMANENT_ADDRESS
mov dword [edi+16], 0 ; InformationBufferLength
mov dword [edi+16], RNDIS_QUERY_LEN - 28 ; InformationBufferLength
mov dword [edi+20], 20 ; InformationBufferOffset
mov dword [edi+24], 0 ; DeviceVcHandle
mov [ebx+rndis_dev.InitState], STATE_MAC
mov eax, 28
mov eax, RNDIS_QUERY_LEN
call rndis_send_command
test eax, eax
jz .fail
@@ -804,6 +872,24 @@ proc rndis_response_callback stdcall uses ebx esi edi, .pipe:dword, .status:dwor
[ebx+rndis_dev.mac+0]:2,[ebx+rndis_dev.mac+1]:2,[ebx+rndis_dev.mac+2]:2,\
[ebx+rndis_dev.mac+3]:2,[ebx+rndis_dev.mac+4]:2,[ebx+rndis_dev.mac+5]:2
; A locally administered address is the same on every plug-in, so each
; boot repeated the previous boot's connections into stale phone NAT
; state. Replace it with a random one per boot, as Linux does.
mov edx, RNDIS_PACKET_FILTER
test byte [ebx+rndis_dev.mac], 0x02
jz .mac_done
mov eax, dword [random_mac]
mov dword [ebx+rndis_dev.mac], eax
mov ax, word [random_mac+4]
mov word [ebx+rndis_dev.mac+4], ax
or edx, RNDIS_FILTER_PROMISC
push edx
DEBUGF 2,"using random MAC %x-%x-%x-%x-%x-%x\n",\
[ebx+rndis_dev.mac+0]:2,[ebx+rndis_dev.mac+1]:2,[ebx+rndis_dev.mac+2]:2,\
[ebx+rndis_dev.mac+3]:2,[ebx+rndis_dev.mac+4]:2,[ebx+rndis_dev.mac+5]:2
pop edx
.mac_done:
; enable reception: set the packet filter
lea edi, [ebx+rndis_dev.CmdBuf]
mov dword [edi], RNDIS_MSG_SET
@@ -813,7 +899,7 @@ proc rndis_response_callback stdcall uses ebx esi edi, .pipe:dword, .status:dwor
mov dword [edi+16], 4 ; InformationBufferLength
mov dword [edi+20], 20 ; InformationBufferOffset
mov dword [edi+24], 0 ; DeviceVcHandle
mov dword [edi+28], RNDIS_PACKET_FILTER
mov dword [edi+28], edx
mov [ebx+rndis_dev.InitState], STATE_FILTER
mov eax, 32
call rndis_send_command
@@ -864,11 +950,13 @@ proc rndis_response_callback stdcall uses ebx esi edi, .pipe:dword, .status:dwor
je .media_up
cmp eax, RNDIS_STATUS_MEDIA_DISCONNECT
jne .done
mov [ebx+rndis_dev.LinkKnown], 1
mov [ebx+rndis_dev.LinkDown], 1
mov [ebx+rndis_dev.state], ETH_LINK_DOWN
DEBUGF 2,"link down\n"
jmp .media_changed
.media_up:
mov [ebx+rndis_dev.LinkKnown], 1
mov [ebx+rndis_dev.LinkDown], 0
mov [ebx+rndis_dev.state], ETH_LINK_SPEED_10M or ETH_LINK_FULL_DUPLEX
DEBUGF 2,"link up\n"
@@ -926,6 +1014,28 @@ proc rndis_response_callback stdcall uses ebx esi edi, .pipe:dword, .status:dwor
endp
; The link-up fallback (see RNDIS_LINK_WAIT): the device said nothing
; about its media in time, take the link as up. Runs from the kernel's
; timer list, the context is the one given to TimerHS.
proc rndis_link_timer stdcall uses ebx, .ctx:dword
mov ebx, [.ctx]
mov [ebx+rndis_dev.LinkTimer], 0
cmp [ebx+rndis_dev.Dead], 0
jne .ret
cmp [ebx+rndis_dev.LinkKnown], 0
jne .ret ; the device spoke meanwhile
mov [ebx+rndis_dev.LinkKnown], 1
DEBUGF 2,"no media indication, assuming link up\n"
mov [ebx+rndis_dev.state], ETH_LINK_SPEED_10M or ETH_LINK_FULL_DUPLEX
push ebx
invoke NetLinkChanged
pop ebx
.ret:
ret
endp
; Init chain complete: allocate the receive buffers, register in the
; network stack, bring the link up and start receiving.
; IN: ebx = device context
@@ -934,15 +1044,32 @@ proc rndis_start
mov [ebx+rndis_dev.InitState], STATE_RUN
mov [ebx+rndis_dev.RxSize], RNDIS_RX_BUF
xor edi, edi
.alloc_loop:
invoke KernelAlloc, RNDIS_RX_BUF
; All receive buffers come from one page-aligned block: a 2 KB slice of
; it never crosses a page, so every transfer is a single TRB.
invoke KernelAlloc, RNDIS_RX_BUF * RNDIS_RX_BUFFERS
test eax, eax
jz .fail
mov [ebx+rndis_dev.RxArea], eax
xor edi, edi
.slice_loop:
mov [ebx+rndis_dev.RxBufs+edi*4], eax
add eax, RNDIS_RX_BUF
inc edi
cmp edi, RNDIS_RX_BUFFERS
jb .alloc_loop
jb .slice_loop
; The pool of transmit slots, all free. It must exist before NetRegDev:
; the stack may transmit as soon as the device is registered.
invoke KernelAlloc, RNDIS_TX_SLOT * RNDIS_TX_MAX_PENDING
test eax, eax
jz .fail
mov [ebx+rndis_dev.TxArea], eax
xor edi, edi
.free_loop:
or [ebx+rndis_dev.TxFree+edi*4], -1
inc edi
cmp edi, RNDIS_TX_MAX_PENDING/32
jb .free_loop
push ebx
invoke NetRegDev
@@ -952,8 +1079,9 @@ proc rndis_start
mov [ebx+rndis_dev.Registered], 1
DEBUGF 2,"registered as network device %u\n", eax
; An LTE router bridge is up unless the device indicated otherwise
; before we finished initializing.
; the link follows the media indication, or RNDIS_LINK_WAIT expires
cmp [ebx+rndis_dev.LinkKnown], 0
je .link_wait
cmp [ebx+rndis_dev.LinkDown], 0
jne @f
mov [ebx+rndis_dev.state], ETH_LINK_SPEED_10M or ETH_LINK_FULL_DUPLEX
@@ -961,18 +1089,40 @@ proc rndis_start
push ebx
invoke NetLinkChanged
pop ebx
jmp .link_done
.link_wait:
invoke TimerHS, RNDIS_LINK_WAIT, 0, rndis_link_timer, ebx
mov [ebx+rndis_dev.LinkTimer], eax
test eax, eax
jnz .link_done
DEBUGF 2,"no timer for the link, assuming it is up\n"
mov [ebx+rndis_dev.LinkKnown], 1
mov [ebx+rndis_dev.state], ETH_LINK_SPEED_10M or ETH_LINK_FULL_DUPLEX
push ebx
invoke NetLinkChanged
pop ebx
.link_done:
; Arm the first receive. From here on the RX callback ping-pongs between
; the two buffers: it re-arms the pipe with the other buffer BEFORE
; parsing the completed one, so the device streams the next frames while
; the host walks the current buffer. Exactly one transfer is outstanding
; at any time - queueing several short-packet-terminated bulk IN
; transfers breaks the EHCI stack (see the note in usbcdc.asm).
; Arm every receive buffer at once. Callbacks run in the USB thread,
; woken up to 10 ms after the IRQ; with one transfer in flight a device
; sending one frame per transfer is capped at ~1 Mbit/s. Each callback
; re-arms its own buffer at the ring tail, so frame order is kept.
; NB: on EHCI this used to hurt (see usbcdc.asm); if so, use 2 buffers.
xor edi, edi
.arm_loop:
invoke USBNormalTransferAsync, [ebx+rndis_dev.InPipe], \
[ebx+rndis_dev.RxBufs], [ebx+rndis_dev.RxSize], \
[ebx+rndis_dev.RxBufs+edi*4], [ebx+rndis_dev.RxSize], \
rndis_rx_callback, ebx, 1
test eax, eax
jz .fail
jz .armed
inc [ebx+rndis_dev.RxArmed]
inc edi
cmp edi, RNDIS_RX_BUFFERS
jb .arm_loop
.armed:
cmp [ebx+rndis_dev.RxArmed], 0
je .fail
DEBUGF 2,"receive queue depth %u\n", [ebx+rndis_dev.RxArmed]
ret
.fail:
@@ -983,10 +1133,8 @@ proc rndis_start
endp
if DEBUG_STATS
; Print transfer-rate statistics to the debug board about once a second
; while traffic flows (called from the RX callback). Shows where the
; bottleneck is: cb/fr = USB transfers and frames per interval (RX),
; tx = frames sent per interval (the TCP ACK clock on downloads).
; Print RX/TX counters about once a second while traffic flows.
; cb/fr = transfers/frames received, tx = frames sent.
; IN: ebx = device context
proc rndis_stats
@@ -1001,10 +1149,16 @@ proc rndis_stats
jb .done
mov ecx, [ebx+rndis_dev.packets_tx]
sub ecx, [ebx+rndis_dev.StatTxPkts]
DEBUGF 2,"stat %ut: cb %u fr %u B %u tx %u wnd %u txovr %u rxdrop %u\n", \
DEBUGF 2,"stat %ut: cb %u fr %u B %u tx %u wnd %u txovr %u txerr %u rxdrop %u rxovr %u gap %u retx %u txq %u tail %u rxu %u rxb %u logdrop %u\n", \
edx, [ebx+rndis_dev.StatCb], [ebx+rndis_dev.StatFrames], \
[ebx+rndis_dev.StatBytes], ecx, [ebx+rndis_dev.StatWnd], \
[ebx+rndis_dev.packets_tx_ovr], [ebx+rndis_dev.packets_rx_drop]
[ebx+rndis_dev.packets_tx_ovr], [ebx+rndis_dev.packets_tx_err], \
[ebx+rndis_dev.packets_rx_drop], [ebx+rndis_dev.packets_rx_ovr], \
[ebx+rndis_dev.StatGap], [ebx+rndis_dev.StatRetx], \
[ebx+rndis_dev.StatTxHigh], [ebx+rndis_dev.StatTail], \
[ebx+rndis_dev.StatRxUni], [ebx+rndis_dev.StatRxBc], \
[ebx+rndis_dev.StatLogDrop]
mov [ebx+rndis_dev.StatTxHigh], 0
mov [ebx+rndis_dev.StatCb], 0
mov [ebx+rndis_dev.StatFrames], 0
mov [ebx+rndis_dev.StatBytes], 0
@@ -1016,6 +1170,281 @@ proc rndis_stats
pop edx ecx eax
ret
endp
; Log ARP, ICMP, DNS, DHCP and TCP SYN/FIN/RST in both directions, at
; most RNDIS_LOG_BURST lines a second (the rest counted in logdrop).
; Also counts arrivals to our MAC (rxu) and to a group (rxb).
; IN: ebx = device context, esi -> ethernet frame, ecx = frame length,
; edi = 0 received, 1 sent. Destroys everything, the caller saves it.
align 4
rndis_log_frame:
mov ebp, edi ; direction
test ebp, ebp
jnz .classified
test byte [esi], 1 ; group bit of destination MAC
jz .unicast
inc [ebx+rndis_dev.StatRxBc]
jmp .classified
.unicast:
mov eax, [esi]
cmp eax, dword [ebx+rndis_dev.mac]
jne .classified
mov ax, [esi+4]
cmp ax, word [ebx+rndis_dev.mac+4]
jne .classified
inc [ebx+rndis_dev.StatRxUni]
.classified:
; Pick out the frames worth a line; edx -> transport header.
cmp word [esi+12], 0x0608 ; EtherType ARP (BE 08 06)
je .arp
cmp word [esi+12], 0x0008 ; EtherType IPv4
jne .done
cmp ecx, 14+20
jb .done
movzx eax, byte [esi+14]
and eax, 15
shl eax, 2 ; IP header length
cmp eax, 20
jb .done
test word [esi+20], 0xFF1F ; not the first fragment
jnz .done
lea edx, [esi+14+eax]
add eax, 14+8 ; room for an ICMP/UDP header
cmp ecx, eax
jb .done
cmp byte [esi+23], 1 ; ICMP
je .limit
cmp byte [esi+23], 17 ; UDP
je .udp
cmp byte [esi+23], 6 ; TCP
jne .done
add eax, 20-8
cmp ecx, eax
jb .done
test byte [edx+13], 7 ; FIN, SYN or RST
jz .done
jmp .limit
.udp:
mov eax, [edx] ; both ports, big-endian
mov ecx, 2
.udp_port:
cmp ax, 53 shl 8 ; DNS
je .limit
cmp ax, 67 shl 8 ; DHCP server
je .limit
cmp ax, 68 shl 8 ; DHCP client
je .limit
shr eax, 16
dec ecx
jnz .udp_port
jmp .done
.arp:
cmp ecx, 42
jb .done
; Rate limit: RNDIS_LOG_BURST lines per 100 ticks.
.limit:
push edx
invoke GetTimerTicks
pop edx
mov ecx, eax
sub ecx, [ebx+rndis_dev.LogTicks]
cmp ecx, 100
jb @f
mov [ebx+rndis_dev.LogTicks], eax
mov [ebx+rndis_dev.LogCount], 0
@@:
cmp [ebx+rndis_dev.LogCount], RNDIS_LOG_BURST
jb @f
inc [ebx+rndis_dev.StatLogDrop]
ret
@@:
inc [ebx+rndis_dev.LogCount]
; eax = ticks, ebp = direction, esi -> frame, edx -> transport header
cmp word [esi+12], 0x0608
je .print_arp
cmp byte [esi+23], 1
je .print_icmp
cmp byte [esi+23], 17
je .print_udp
;--------------------------------- TCP
push ebp ; direction, tested below
movzx ecx, word [esi+16]
xchg cl, ch ; IP total length
movzx ebx, byte [esi+14]
and ebx, 15
shl ebx, 2
sub ecx, ebx ; minus the IP header
movzx ebx, byte [edx+12]
shr ebx, 4
shl ebx, 2
sub ecx, ebx ; minus the TCP header
jns @f
xor ecx, ecx
@@:
mov ebp, ecx ; payload length
movzx ecx, word [edx]
xchg cl, ch ; source port
mov di, [edx+2]
rol di, 8
movzx edi, di ; destination port
movzx ebx, word [edx+14]
xchg bl, bh ; window (unscaled)
cmp dword [esp], 0
jne .tcp_out
DEBUGF 2,"t%u in TCP %u.%u.%u.%u:%u > %u.%u.%u.%u:%u flags %x win %u len %u\n", eax, \
[esi+26]:1, [esi+27]:1, [esi+28]:1, [esi+29]:1, ecx, \
[esi+30]:1, [esi+31]:1, [esi+32]:1, [esi+33]:1, edi, \
[edx+13]:2, ebx, ebp
pop eax
ret
.tcp_out:
DEBUGF 2,"t%u out TCP %u.%u.%u.%u:%u > %u.%u.%u.%u:%u flags %x win %u len %u\n", eax, \
[esi+26]:1, [esi+27]:1, [esi+28]:1, [esi+29]:1, ecx, \
[esi+30]:1, [esi+31]:1, [esi+32]:1, [esi+33]:1, edi, \
[edx+13]:2, ebx, ebp
pop eax
ret
;--------------------------------- UDP (DNS, DHCP)
.print_udp:
movzx ecx, word [edx]
xchg cl, ch ; source port
mov di, [edx+2]
rol di, 8
movzx edi, di ; destination port
movzx ebx, word [edx+4]
xchg bl, bh
sub ebx, 8 ; payload length
test ebp, ebp
jnz .udp_out
DEBUGF 2,"t%u in UDP %u.%u.%u.%u:%u > %u.%u.%u.%u:%u len %u\n", eax, \
[esi+26]:1, [esi+27]:1, [esi+28]:1, [esi+29]:1, ecx, \
[esi+30]:1, [esi+31]:1, [esi+32]:1, [esi+33]:1, edi, ebx
ret
.udp_out:
DEBUGF 2,"t%u out UDP %u.%u.%u.%u:%u > %u.%u.%u.%u:%u len %u\n", eax, \
[esi+26]:1, [esi+27]:1, [esi+28]:1, [esi+29]:1, ecx, \
[esi+30]:1, [esi+31]:1, [esi+32]:1, [esi+33]:1, edi, ebx
ret
;--------------------------------- ICMP
.print_icmp:
test ebp, ebp
jnz .icmp_out
DEBUGF 2,"t%u in ICMP %u.%u.%u.%u > %u.%u.%u.%u type %u code %u\n", eax, \
[esi+26]:1, [esi+27]:1, [esi+28]:1, [esi+29]:1, \
[esi+30]:1, [esi+31]:1, [esi+32]:1, [esi+33]:1, \
[edx]:1, [edx+1]:1
ret
.icmp_out:
DEBUGF 2,"t%u out ICMP %u.%u.%u.%u > %u.%u.%u.%u type %u code %u\n", eax, \
[esi+26]:1, [esi+27]:1, [esi+28]:1, [esi+29]:1, \
[esi+30]:1, [esi+31]:1, [esi+32]:1, [esi+33]:1, \
[edx]:1, [edx+1]:1
ret
;--------------------------------- ARP
; a request names who asks; a reply names the MAC, which tells a stale
; entry (the phone's address changed) from a missing one
.print_arp:
cmp byte [esi+21], 2 ; opcode: 1 request, 2 reply
je .arp_reply
test ebp, ebp
jnz .arp_req_out
DEBUGF 2,"t%u in ARP who-has %u.%u.%u.%u tell %u.%u.%u.%u\n", eax, \
[esi+38]:1, [esi+39]:1, [esi+40]:1, [esi+41]:1, \
[esi+28]:1, [esi+29]:1, [esi+30]:1, [esi+31]:1
ret
.arp_req_out:
DEBUGF 2,"t%u out ARP who-has %u.%u.%u.%u tell %u.%u.%u.%u\n", eax, \
[esi+38]:1, [esi+39]:1, [esi+40]:1, [esi+41]:1, \
[esi+28]:1, [esi+29]:1, [esi+30]:1, [esi+31]:1
ret
.arp_reply:
test ebp, ebp
jnz .arp_rep_out
DEBUGF 2,"t%u in ARP %u.%u.%u.%u is-at %x-%x-%x-%x-%x-%x\n", eax, \
[esi+28]:1, [esi+29]:1, [esi+30]:1, [esi+31]:1, \
[esi+22]:2, [esi+23]:2, [esi+24]:2, [esi+25]:2, [esi+26]:2, [esi+27]:2
ret
.arp_rep_out:
DEBUGF 2,"t%u out ARP %u.%u.%u.%u is-at %x-%x-%x-%x-%x-%x\n", eax, \
[esi+28]:1, [esi+29]:1, [esi+30]:1, [esi+31]:1, \
[esi+22]:2, [esi+23]:2, [esi+24]:2, [esi+25]:2, [esi+26]:2, [esi+27]:2
.done:
ret
; Track incoming TCP sequence numbers per flow: gap counts segments
; past a hole, retx segments with data already received (cumulative).
; IN: ebx = device context, esi -> ethernet frame, ecx = frame length
; Destroys everything but ebx and ebp.
proc rndis_track_tcp
cmp ecx, 54 ; eth+min IP+min TCP
jb .done
cmp word [esi+12], 0x0008 ; EtherType IPv4
jne .done
cmp byte [esi+23], 6 ; IP protocol TCP
jne .done
movzx edx, byte [esi+14]
and edx, 15
shl edx, 2 ; IP header length
movzx eax, word [esi+16]
xchg al, ah ; IP total length
sub eax, edx
jbe .done
lea edi, [esi+14+edx] ; TCP header
movzx edx, byte [edi+12]
shr edx, 4
shl edx, 2 ; TCP header length
sub eax, edx ; payload length
jbe .done ; pure ACK: no sequence space
mov ecx, eax
mov edx, [esi+26] ; source IP
xor edx, [edi] ; ^ source/destination ports
mov eax, [edi+4]
bswap eax ; sequence number
lea esi, [ebx+rndis_dev.Flows]
mov edi, RNDIS_FLOWS
.find:
cmp [esi], edx
je .found
add esi, 8
dec edi
jnz .find
; a new flow: take over the oldest slot
mov edi, [ebx+rndis_dev.FlowNext]
inc [ebx+rndis_dev.FlowNext]
and edi, RNDIS_FLOWS-1
lea esi, [ebx+rndis_dev.Flows+edi*8]
mov [esi], edx
jmp .advance
.found:
mov edx, eax
sub edx, [esi+4] ; seq - expected
jz .advance
js .old
inc [ebx+rndis_dev.StatGap]
jmp .advance
.old:
inc [ebx+rndis_dev.StatRetx]
lea edx, [eax+ecx]
sub edx, [esi+4]
jle .done ; nothing new in it
.advance:
add eax, ecx
mov [esi+4], eax
.done:
ret
endp
end if ; DEBUG_STATS
@@ -1031,11 +1460,12 @@ proc rndis_rx_callback stdcall uses ebx esi edi, .pipe:dword, .status:dword, .bu
locals
msg_off dd ? ; offset of the current message
rearmed dd ? ; the other buffer is already armed
endl
mov ebx, [.calldata]
DEBUGF 1,"RX cb st %u len %u\n", [.status], [.length]
; this transfer has left the queue, whatever its outcome
dec [ebx+rndis_dev.RxArmed]
cmp [ebx+rndis_dev.Dead], 0
jne .ret
cmp [.status], USB_STATUS_CLOSED
@@ -1059,28 +1489,13 @@ end if
jae .rx_dead
invoke USBNormalTransferAsync, [ebx+rndis_dev.InPipe], [.buffer], \
[ebx+rndis_dev.RxSize], rndis_rx_callback, ebx, 1
test eax, eax
jz .check_dry
inc [ebx+rndis_dev.RxArmed]
jmp .ret
.status_ok:
mov [ebx+rndis_dev.RxErrRun], 0 ; the error run is broken
; Re-arm the pipe with the other buffer BEFORE parsing this one: the
; device streams the next frames while the host walks the current
; buffer. The completed transfer is already off the queue, so still at
; most one transfer is outstanding (a hard requirement of the EHCI
; stack, see the note in usbcdc.asm).
mov [rearmed], 0
mov eax, [ebx+rndis_dev.RxBufs]
cmp eax, [.buffer]
jne @f
mov eax, [ebx+rndis_dev.RxBufs+4]
@@:
invoke USBNormalTransferAsync, [ebx+rndis_dev.InPipe], eax, \
[ebx+rndis_dev.RxSize], rndis_rx_callback, ebx, 1
test eax, eax
jz @f
mov [rearmed], 1
@@:
mov [msg_off], 0
.msg_loop:
@@ -1151,9 +1566,30 @@ end if
if DEBUG_STATS
inc [ebx+rndis_dev.StatFrames]
add [ebx+rndis_dev.StatBytes], ecx
pushad
lea esi, [edi+NET_BUFF.data]
xor edi, edi
call rndis_log_frame
popad
pushad
lea esi, [edi+NET_BUFF.data]
call rndis_track_tcp
popad
end if
DEBUGF 1,"->host frame %u bytes\n", [edi+NET_BUFF.length]
; Frames arrive, so the link is up despite MEDIA_DISCONNECT (a phone
; tethering at cold boot never sends MEDIA_CONNECT).
cmp [ebx+rndis_dev.LinkDown], 0
je .link_ok
mov [ebx+rndis_dev.LinkDown], 0
mov [ebx+rndis_dev.state], ETH_LINK_SPEED_10M or ETH_LINK_FULL_DUPLEX
DEBUGF 2,"frames arrive while the link is down, taking it as up\n"
pushad
invoke NetLinkChanged
popad
.link_ok:
; hand the buffer to the network stack. EthInput consumes
; [esp] = buffer and returns to the address at [esp+4].
push ebp ebx esi
@@ -1168,19 +1604,31 @@ end if
inc [ebx+rndis_dev.packets_rx_drop]
jmp .msg_loop
; End of processing. If arming the other buffer failed at entry, fall
; back to re-submitting the just-parsed buffer so receive does not stop.
; put this buffer back at the tail of the queue
.rearm:
if DEBUG_STATS
; bytes of the transfer not turned into frames; must stay 0
mov eax, [.length]
sub eax, [msg_off]
jbe @f
add [ebx+rndis_dev.StatTail], eax
@@:
call rndis_stats
end if
cmp [rearmed], 0
jne .ret
invoke USBNormalTransferAsync, [ebx+rndis_dev.InPipe], [.buffer], \
[ebx+rndis_dev.RxSize], rndis_rx_callback, ebx, 1
test eax, eax
jnz .ret
jz .rearm_failed
inc [ebx+rndis_dev.RxArmed]
ret
.rearm_failed:
DEBUGF 2,"failed to re-arm RX\n"
; Losing one buffer of the queue is survivable as long as something is
; still outstanding; if nothing is, receive has stopped for good.
.check_dry:
cmp [ebx+rndis_dev.RxArmed], 0
jne .ret
DEBUGF 2,"receive queue ran dry, receive stopped\n"
.ret:
ret
@@ -1188,7 +1636,11 @@ end if
DEBUGF 2,"bulk IN stalled, receive stopped\n"
ret
; Every buffer still in the queue arrives here in turn; report the death
; of the pipe only once.
.rx_dead:
cmp [ebx+rndis_dev.state], ETH_LINK_DOWN
je .ret
DEBUGF 2,"RX stopped after repeated transfer errors\n"
mov [ebx+rndis_dev.state], ETH_LINK_DOWN
push ebx
@@ -1213,7 +1665,50 @@ proc rndis_tx_callback stdcall uses ebx esi edi, .pipe:dword, .status:dword, .bu
DEBUGF 1,"TX error %u\n", [.status]
@@:
mov eax, [.calldata]
invoke Kfree
call rndis_tx_release
ret
endp
; Take a free transmit slot.
; IN: ebx = device context. OUT: eax = slot, 0 if none. Destroys ecx, edx.
; The stack transmits on its own thread while completions run on the USB
; thread, possibly on another CPU, hence the locked bit operations.
if RNDIS_TX_SLOT <> 2048
err "rndis_tx_claim/rndis_tx_release shift by 11 for 2 KB slots"
end if
proc rndis_tx_claim
xor edx, edx
.scan:
mov eax, [ebx+rndis_dev.TxFree+edx*4]
test eax, eax
jz .next
bsf ecx, eax
lock btr [ebx+rndis_dev.TxFree+edx*4], ecx
jnc .scan ; taken meanwhile: look again
shl edx, 5
add edx, ecx ; slot number
shl edx, 11
mov eax, [ebx+rndis_dev.TxArea]
add eax, edx
ret
.next:
inc edx
cmp edx, RNDIS_TX_MAX_PENDING/32
jb .scan
xor eax, eax
ret
endp
; Give a transmit slot back.
; IN: ebx = device context, eax = slot. Destroys eax.
proc rndis_tx_release
sub eax, [ebx+rndis_dev.TxArea]
shr eax, 11 ; slot number
lock bts [ebx+rndis_dev.TxFree], eax
ret
endp
@@ -1227,8 +1722,8 @@ endp
;; ;;
;; The frame is prepended with a 44-byte REMOTE_NDIS_PACKET_MSG. ;;
;; If the transfer would be a multiple of the endpoint packet ;;
;; size, one extra zero byte is sent (not counted in the message ;;
;; length) so that the transfer ends in a short packet. ;;
;; size, one extra zero byte is sent so that the transfer ends in ;;
;; a short packet; it is counted in MessageLength (QEMU needs it). ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -1251,13 +1746,22 @@ endl
je .drop
mov ecx, [esi+NET_BUFF.length]
cmp ecx, 14
jb .err
jb .badlen
cmp ecx, ETH_FRAME_MAX
ja .err
ja .badlen
DEBUGF 1,"host-> frame %u bytes\n", ecx
; limit the number of messages in flight
lock inc [ebx+rndis_dev.TxPending]
if DEBUG_STATS
; remember how deep the queue actually gets: the margin left before
; frames start being refused
mov eax, [ebx+rndis_dev.TxPending]
cmp eax, [ebx+rndis_dev.StatTxHigh]
jbe @f
mov [ebx+rndis_dev.StatTxHigh], eax
@@:
end if
cmp [ebx+rndis_dev.TxPending], RNDIS_TX_MAX_PENDING
ja .overrun
@@ -1276,6 +1780,7 @@ endl
test eax, edx
jnz @f
inc eax
mov [msglen], eax ; the pad byte is part of it
@@:
mov [xferlen], eax
; never exceed what the device declared it can receive
@@ -1285,9 +1790,8 @@ endl
cmp eax, edx
ja .toobig
@@:
; allocate [context dword][message]
add eax, 4
invoke Kmalloc
; take a slot for [context dword][message]
call rndis_tx_claim
test eax, eax
jz .no_memory
mov [alloc], eax
@@ -1321,6 +1825,15 @@ endl
rep stosb
if DEBUG_STATS
; stats: spell out the control frames we send
pushad
mov esi, [alloc]
add esi, 4+RNDIS_HDR_SIZE ; frame start
mov ecx, edx ; frame length
movi edi, 1
call rndis_log_frame
popad
; stats: remember the TCP receive window we advertise (rwnd health on
; downloads; parsed from the outgoing frame: eth+IPv4+TCP)
cmp edx, 54 ; eth+min IP+min TCP
@@ -1359,7 +1872,7 @@ end if
.submit_failed:
mov eax, [alloc]
invoke Kfree
call rndis_tx_release
lock dec [ebx+rndis_dev.TxPending]
jmp .err
@@ -1376,6 +1889,16 @@ end if
inc [ebx+rndis_dev.packets_tx_ovr]
jmp .drop
; A frame the stack handed down does not fit an ethernet frame at all.
; Report the first few: the length says at once whether the stack built
; an oversized segment (t_maxseg not clamped to our mtu, which shows up
; as an endlessly retransmitting connection) or something else.
.badlen:
cmp [ebx+rndis_dev.LenErrNoted], 4
jae .err
inc [ebx+rndis_dev.LenErrNoted]
DEBUGF 2,"refusing a frame of %u bytes\n", ecx
.err:
inc [ebx+rndis_dev.packets_tx_err]
.drop:
@@ -1410,6 +1933,11 @@ proc DeviceDisconnected stdcall uses ebx esi edi, .devdata:dword
mov ebx, [.devdata]
DEBUGF 2,"device disconnected\n"
mov [ebx+rndis_dev.Dead], 1
mov eax, [ebx+rndis_dev.LinkTimer]
test eax, eax
jz @f
invoke CancelTimerHS, eax ; must not fire on a freed context
@@:
cmp [ebx+rndis_dev.Registered], 0
je @f
mov [ebx+rndis_dev.state], ETH_LINK_DOWN
@@ -1421,16 +1949,17 @@ proc DeviceDisconnected stdcall uses ebx esi edi, .devdata:dword
pop ebx
mov [ebx+rndis_dev.Registered], 0
@@:
xor esi, esi
.free_loop:
mov eax, [ebx+rndis_dev.RxBufs+esi*4]
; every transfer callback has run by now, so no slot or buffer is in use
mov eax, [ebx+rndis_dev.RxArea]
test eax, eax
jz @f
invoke KernelFree, eax
@@:
mov eax, [ebx+rndis_dev.TxArea]
test eax, eax
jz @f
invoke KernelFree, eax
@@:
inc esi
cmp esi, RNDIS_RX_BUFFERS
jb .free_loop
mov eax, ebx
invoke Kfree
ret
@@ -1480,5 +2009,6 @@ end data
align 4
parse_scratch rndis_parse
random_mac rb 6 ; host address used instead of a made-up one
include_debug_strings
File diff suppressed because it is too large. Load diff
+28
View File
@@ -83,6 +83,31 @@ macro arp_init {
}
;-----------------------------------------------------------------;
; ;
; arp_clear_device: Empty the ARP table and counters of a removed ;
; device. ;
; ;
; IN: edi = device number * 4 ;
; ;
;-----------------------------------------------------------------;
macro arp_clear_device {
xor eax, eax
mov [ARP_entries + edi], eax
mov [ARP_packets_tx + edi], eax
mov [ARP_packets_rx + edi], eax
mov [ARP_conflicts + edi], eax
push edi
imul edi, (ARP_TABLE_SIZE * sizeof.ARP_entry)/4
add edi, ARP_table
mov ecx, (ARP_TABLE_SIZE * sizeof.ARP_entry)/2
rep stosw
pop edi
}
;-----------------------------------------------------------------;
; ;
; arp_decrease_entry_ttls ;
@@ -313,6 +338,9 @@ arp_output_request:
DEBUGF DEBUG_NETWORK_VERBOSE, "ARP_output_request: ip=%u.%u.%u.%u device=0x%x\n",\
[esp]:1, [esp + 1]:1, [esp + 2]:1, [esp + 3]:1, ebx
test ebx, ebx ; device is gone
jz .exit
mov ax, ETHER_PROTO_ARP
mov ecx, sizeof.ARP_header
mov edx, ETH_BROADCAST ; broadcast mac
+40 -1
View File
@@ -105,6 +105,29 @@ macro ipv4_init {
}
;-----------------------------------------------------------------;
; ;
; ipv4_clear_device: Reset the IPv4 settings of a removed device, ;
; so no route points to it anymore. ;
; ;
; IN: edi = device number * 4 ;
; ;
;-----------------------------------------------------------------;
macro ipv4_clear_device {
xor eax, eax
mov [IPv4_address + edi], eax
mov [IPv4_subnet + edi], eax
mov [IPv4_nameserver + edi], eax
mov [IPv4_gateway + edi], eax
mov [IPv4_broadcast + edi], eax
mov [IPv4_packets_tx + edi], eax
mov [IPv4_packets_rx + edi], eax
mov [IPv4_packets_dumped + edi], eax
}
;-----------------------------------------------------------------;
; ;
; Decrease TimeToLive of all fragment slots ;
@@ -757,6 +780,8 @@ ipv4_output_raw:
push esi eax
call ipv4_route
test eax, eax
jz .arp_error
call arp_ip_to_mac
test eax, 0xffff0000 ; error bits
@@ -967,6 +992,8 @@ ipv4_route:
; Check for on-link
xor edi, edi
.loop:
cmp [net_device_list + edi], 0 ; skip slots without a device
je .next
mov ebx, [IPv4_address + edi]
and ebx, [IPv4_subnet + edi]
jz .next
@@ -983,7 +1010,10 @@ ipv4_route:
mov edi, 4 ; skip loopback device
.loop_gw:
cmp [IPv4_gateway + edi], 0
je .next_gw
cmp [net_device_list + edi], 0
jne .found_gw
.next_gw:
add edi, 4
cmp edi, 4*NET_DEVICES_MAX
jb .loop_gw
@@ -1049,14 +1079,23 @@ ipv4_route:
cmp eax, 0xffffffff
je @f
; An interface without an address (DHCP not done yet, or the lease
; dropped when the link went down) can send nothing but broadcasts: with
; address and mask both zero every destination compared as on-link, and
; the stack went asking ARP for internet addresses on behalf of 0.0.0.0.
test edx, edx
jz .fail
; Check if we should route to gateway or not
mov ebx, [IPv4_address + edi]
mov ebx, edx
and ebx, [IPv4_subnet + edi]
mov ecx, eax
and ecx, [IPv4_subnet + edi]
cmp ecx, ebx
je @f
mov eax, [IPv4_gateway + edi]
test eax, eax
jz .fail ; off-link and no gateway
@@:
DEBUGF DEBUG_NETWORK_VERBOSE, "IPv4_route: %u\n", edi
ret
+44 -1
View File
@@ -61,6 +61,41 @@ macro eth_init {
}
;-----------------------------------------------------------------;
; ;
; eth_clear_device: Drop the frames of a removed device that are ;
; still waiting in the input queue, their ;
; NET_BUFF.device will not be valid anymore. ;
; ;
; IN: ebx = device ptr ;
; ;
;-----------------------------------------------------------------;
macro eth_clear_device {
local .loop, .done
spin_lock_irqsave
mov esi, [ETH_frame_head]
.loop:
cmp esi, ETH_frame_head
je .done
mov eax, esi
mov esi, [esi + NET_BUFF.NextPtr]
cmp [eax + NET_BUFF.device], ebx
jne .loop
; unlink it, ETH_frame_head/tail double as the NextPtr/PrevPtr of the list head
mov ecx, [eax + NET_BUFF.PrevPtr]
mov [ecx + NET_BUFF.NextPtr], esi
mov [esi + NET_BUFF.PrevPtr], ecx
dec [ETH_frame_queued]
stdcall net_buff_free, eax
jmp .loop
.done:
spin_unlock_irqrestore
}
align 4
; This function is called by ethernet drivers.
; Push the received ethernet packet onto the ethernet input queue.
@@ -245,6 +280,9 @@ eth_output:
DEBUGF DEBUG_NETWORK_VERBOSE, "ETH_output: size=%u device=%x\n", ecx, ebx
test ebx, ebx
jz .no_device
cmp ecx, [ebx + ETH_DEVICE.mtu]
ja .too_large
@@ -295,11 +333,16 @@ eth_output:
ret
.too_large:
inc [eax + NET_DEVICE.packets_tx_err]
inc [ebx + NET_DEVICE.packets_tx_err]
DEBUGF DEBUG_NETWORK_VERBOSE, "ETH_output: Packet too large!\n"
xor eax, eax
ret
.no_device:
DEBUGF DEBUG_NETWORK_ERROR, "ETH_output: no device!\n"
xor eax, eax
ret
;-----------------------------------------------------------------;
+10
View File
@@ -597,6 +597,16 @@ net_remove_device:
mov dword [edi-4], eax
dec [net_device_count]
;-----------------------------------------------------------------
; Forget everything the protocols still know about this interface.
; Otherwise its IP address keeps matching in ipv4_route, and output
; ends up calling through the now empty net_device_list slot.
sub edi, net_device_list + 4 ; device number * 4
ipv4_clear_device
arp_clear_device
eth_clear_device
call net_send_event
xor eax, eax
+6 -2
View File
@@ -1422,10 +1422,14 @@ endl
test [edx + TCP_header.Flags], TH_ACK
jz .drop
mov eax, ebx
push ebx
lea ecx, [ebx + SOCKET.mutex]
call mutex_unlock
pop eax
mov ebx, ECONNREFUSED
call tcp_drop
jmp .drop
jmp .drop_no_socket
@@:
;-----------------------------------------------------------------------------------
+8 -6
View File
@@ -234,23 +234,25 @@ endl
mov ebx, TCP_max_win
shl ebx, cl
pop ecx
sub ebx, [eax + TCP_SOCKET.RCV_ADV]
add ebx, [eax + TCP_SOCKET.RCV_NXT]
; Clamp to available window first, then subtract what the peer already knows (as in BSD)
cmp ebx, ecx
jl @f
jbe @f
mov ebx, ecx
@@:
sub ebx, [eax + TCP_SOCKET.RCV_ADV]
add ebx, [eax + TCP_SOCKET.RCV_NXT]
DEBUGF DEBUG_NETWORK_VERBOSE, "TCP_output: we can increase window by %d bytes\n", ebx
mov edi, [eax + TCP_SOCKET.t_maxseg]
shl edi, 1
cmp ebx, edi
jae .send
cmp ebx, edi ; signed: may be negative if window shrank
jge .send
cmp ebx, SOCKET_BUFFER_SIZE/2
jae .send
jge .send
.no_window:
+4
View File
@@ -180,6 +180,8 @@ tcp_connect:
.nomem:
pop edx eax
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
xor eax, eax
dec eax
mov ebx, ENOMEM
@@ -194,6 +196,8 @@ tcp_connect:
.enoroute:
pop eax
popa
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
xor eax, eax
dec eax
mov ebx, EADDRNOTAVAIL
+5 -3
View File
@@ -1618,21 +1618,23 @@ endl
; Open a new TCP socket
mcall socket, AF_INET4, SOCK_STREAM, 0
test eax, eax
jz .error3
cmp eax, -1
je .error3
mov [socketnum], eax
DEBUGF 1, "Socket: 0x%x\n", eax
; Connect to the server
mcall connect, [socketnum], [sockaddr], 18
test eax, eax
jnz .error3
jnz .error4
DEBUGF 1, "Socket is now connected.\n"
invoke freeaddrinfo ; Free allocated memory
mov eax, [socketnum]
ret
.error4:
mcall close, [socketnum]
.error3:
DEBUGF 2, "Could not connect to the remote server\n"
invoke freeaddrinfo ; Free allocated memory
@@ -501,7 +501,8 @@ img.decode.jpg:
jnz .no_create_palette
push ecx edi
mov edi, [eax + Image.Palette]
xor eax, eax
mov eax, 0xFF000000 ; opaque, like the palettes of the other decoders:
; converted to 32 bpp, alpha 0 made the picture invisible
mov ecx, 256
@@:
stosd
-256
View File
@@ -1,256 +0,0 @@
; SPDX-License-Identifier: NOASSERTION
;
; System settings: read them from the kernel or from system.ini,
; apply them to the kernel and save them to system.ini.
;---------------------------------------------------------------
; Take the current values from the kernel, the mouse ones at system start only
settings_read_boot:
mcall 18,19,0
mov [mouseSpeed],eax
mcall 18,,2
mov [mouseAccel],eax
mcall 18,,6
mov [mouseDelay],eax
settings_read_system:
mcall 26,5
dec eax ; the kernel counts languages from 1
cmp eax,LANGUAGES
jb @f
xor eax,eax
@@:
mov [language],eax
mcall 26,11
mov [lba],eax
mcall 26,12
mov [pci],eax
mcall 18,8,1
mov [speakerMute],eax
mcall 48,9
mov [fontSmoothing],eax
mcall 48,11
mov [fontHeight],eax
; keep the font height within what the window shows and steps through
font_height_limits:
mov eax,[fontHeight]
cmp eax,FONT_H_MIN
jge @f
mov eax,FONT_H_MIN
@@:
cmp eax,FONT_H_MAX
jle @f
mov eax,FONT_H_MAX
@@:
mov [fontHeight],eax
ret
;---------------------------------------------------------------
; Override the values with the ones stored in system.ini.
; A missing or unknown value keeps the current one.
settings_read_ini:
mov esi,iniSettings
.next:
mov edi,[esi+12]
test edi,edi
jnz .word
mov eax,[esi+8]
invoke ini.get_int, sz_ini, [esi], [esi+4], [eax]
jmp .store
.word:
and [iniWord],0
invoke ini.get_str, sz_ini, [esi], [esi+4], iniWord, 4, 0
mov eax,[iniWord]
or eax,0x20202020 ; lowercase, zeroes become spaces
xor edx,edx
@@:
cmp eax,[edi+4+edx*4]
jz .found
inc edx
cmp edx,[edi]
jb @b
jmp .skip
.found:
mov eax,edx
.store:
mov edx,[esi+8]
mov [edx],eax
.skip:
add esi,16
cmp esi,iniSettings.end
jb .next
jmp font_height_limits
;---------------------------------------------------------------
; Store the values to system.ini (all but the mouse ones).
; Not ebx for the loop: ini_set_str returns with ebx = 13.
settings_save_ini:
mov esi,iniSettings
.next:
mov eax,[esi+8]
mov eax,[eax]
mov edi,[esi+12]
test edi,edi
jnz .word
invoke ini.set_int, sz_ini, [esi], [esi+4], eax
jmp .done
.word:
mov eax,[edi+4+eax*4]
mov [iniWord],eax
xor ecx,ecx ; length of the word
@@:
cmp byte[iniWord+ecx],' '
jz @f
inc ecx
cmp ecx,4
jb @b
@@:
invoke ini.set_str, sz_ini, [esi], [esi+4], iniWord, ecx
.done:
add esi,16
cmp esi,iniSettings.saved
jb .next
ret
;---------------------------------------------------------------
; Pass the values to the kernel, the mouse ones at system start only
settings_apply_boot:
mcall 18,19,1,[mouseSpeed]
mcall 18,,3,[mouseAccel]
mcall 18,,7,[mouseDelay]
settings_apply:
mov ecx,[language]
inc ecx
mcall 21,5
mcall 21,11,[lba]
mcall 21,12,[pci]
mcall 48,10,[fontSmoothing]
mcall 48,12,[fontHeight]
; the speaker can only be toggled
mcall 18,8,1
cmp eax,[speakerMute]
jz @f
mcall 18,,2
@@:
ret
;---------------------------------------------------------------
; Button style, desktop background and skin from the [style] section.
; Used at system start only.
style_apply:
invoke ini.get_int, sz_ini, sz_style, sz_buttonStyle, 0
mcall 48,1,eax
; the background is set by a program: bg_program with bg_param
invoke ini.get_str, sz_ini, sz_style, sz_bgProgram, bgProgram, PATH_MAX, 0
cmp byte[bgProgram],0
jz .skin
invoke ini.get_str, sz_ini, sz_style, sz_bgParam, bgParam, PATH_MAX, 0
cmp byte[bgParam],'\' ; KIV param like '\S__/path/to/img'
jnz @f
mov [fileInfo.path],bgParam+4
call wait_for_file
@@:
mcall 70,runBgProgram
.skin:
invoke ini.get_str, sz_ini, sz_style, sz_skin, skinPath, PATH_MAX, 0
cmp byte[skinPath],0
jz @f
mov [fileInfo.path],skinPath
call wait_for_file
mcall 48,8,skinPath
@@:
ret
; Files on /kolibrios and /usbhd may appear a few seconds after boot:
; try to read the file for up to 5 seconds
wait_for_file:
mov edi,5
@@:
mcall 70,fileInfo
test eax,eax
jz @f
mcall 5,100
dec edi
jnz @b
@@:
ret
;---------------------------------------------------------------
; DATA
; The languages of the kernel
LANGUAGES = 8
FONT_H_MIN = 9
FONT_H_MAX = 99 ; two digits in the window
PATH_MAX = 256
; Every value stored in system.ini: section, key, variable, words.
; Words = 0 for a number, otherwise the list of words for 0, 1, 2...
align 4
iniSettings:
dd sz_system, sz_language, language, wordsLanguage
dd sz_system, sz_fontHeight, fontHeight, 0
dd sz_system, sz_fontSmooth, fontSmoothing, wordsSmoothing
dd sz_lowLevel, sz_lba, lba, wordsOnOff
dd sz_lowLevel, sz_pci, pci, wordsOnOff
dd sz_lowLevel, sz_speaker, speakerMute, wordsSpeaker
.saved: ; the mouse is configured by its own application
dd sz_mouse, sz_speed, mouseSpeed, 0
dd sz_mouse, sz_accel, mouseAccel, 0
dd sz_mouse, sz_dblClick, mouseDelay, 0
.end:
wordsOnOff dd 2, 'off ','on '
wordsSpeaker dd 2, 'on ','off ' ; the ini keeps 'speaker on', not 'mute'
wordsSmoothing dd 3, 'off ','on ','sbp '
wordsLanguage dd LANGUAGES, 'en ','fi ','de ','ru ','fr ','et ','es ','it '
runBgProgram:
dd 7, 0, bgParam, 0, 0
db 0
dd bgProgram
fileInfo:
dd 0, 0, 0, 4, fileBuf
db 0
.path dd ?
sz_ini db "/sys/settings/system.ini",0
sz_system db "system",0
sz_language db "language",0
sz_fontHeight db "font height",0
sz_fontSmooth db "font smoothing",0
sz_speaker db "speaker",0
sz_lowLevel db "low-level",0
sz_lba db "LBA",0
sz_pci db "PCI",0
sz_mouse db "mouse",0
sz_speed db "speed",0
sz_accel db "acceleration",0
sz_dblClick db "double_click_delay",0
sz_style db "style",0
sz_buttonStyle db "buttons_gradient",0
sz_bgProgram db "bg_program",0
sz_bgParam db "bg_param",0
sz_skin db "skin",0
; not stored in the program file
language dd ?
lba dd ?
pci dd ?
speakerMute dd ?
fontSmoothing dd ?
fontHeight dd ?
mouseSpeed dd ?
mouseAccel dd ?
mouseDelay dd ?
iniWord dd ? ; a word of system.ini
fileBuf dd ?
bgProgram rb PATH_MAX
bgParam rb PATH_MAX
skinPath rb PATH_MAX
+480 -346
View File
@@ -1,316 +1,462 @@
; SPDX-License-Identifier: NOASSERTION
;
; System settings.
; SETUP BOOT - apply the settings from system.ini (run from AUTORUN.DAT)
; SETUP - window to change and save them
;
; Texts are UTF-8
include '../../macros.inc'
format meos 01
entry START
stack 4096
; Text encoded with Code Page 866 - Cyrillic
;;;;;;;;;;;;;;;;;;;;;;;
;; SYSTEM SETTINGS ;;
;;;;;;;;;;;;;;;;;;;;;;;
format binary as ""
use32
org 0
db 'MENUET01'
dd 1
dd START
dd IM_END
dd I_END
dd I_END
dd param
dd 0
include '../../proc32.inc'
include '../../macros.inc'
include '../../dll.inc'
;---------------------------------------------------------------
BootSettings:
; Set system language
mov word[param],0
invoke ini.get_str, sz_ini, sz_system, sz_language, param, 2, 0
mov ax, [param]
or ax, 0x2020 ; convert to lowercase
mov ecx,langMarks.size/2
mov edi,langMarks
repnz scasw
jnz @f
neg ecx
add ecx,langMarks.size/2
mcall 21,5
@@:
; Set font smoothing
mov dword[param],0
invoke ini.get_str, sz_ini, sz_system, sz_fontSmooth, param, 4, 0
xor ecx,ecx
mov eax,[param]
or eax,0x20202020
cmp eax,'off '
jz @f
inc ecx
cmp eax,'on '
jz @f
cmp eax,'sbp '
jnz .skipFont
inc ecx
@@:
mcall 48,10
.skipFont:
; Enable/disable system speaker
mov dword[param],0
invoke ini.get_str, sz_ini, sz_system, sz_speaker, param, 4, 0
mov eax,[param]
or eax,0x20202020
cmp eax,'off '
jz @f
cmp eax,'on '
jnz .skipSpeaker
inc [speaker_mute]
@@:
call _speaker_mute
.skipSpeaker:
; Set font size
invoke ini.get_int, sz_ini, sz_system, sz_fontSize, 9
mov ecx,eax
mcall 48,12
; Set mouse speed
invoke ini.get_int, sz_ini, sz_mouse, sz_speed, 1
mov edx,eax
mcall 18,19,1
; Set mouse acceleration
invoke ini.get_int, sz_ini, sz_mouse, sz_acceleration, 1
mov edx,eax
mcall 18,19,3
; Set mouse double click delay
invoke ini.get_int, sz_ini, sz_mouse, sz_double_click_delay, 1
mov edx,eax
mcall 18,19,7
; Enable/disable LBA access for applications
mov dword[param],0
invoke ini.get_str, sz_ini, sz_low_level, sz_lba, param, 4, 0
xor ecx,ecx
mov eax,[param]
or eax,0x20202020
cmp eax,'off '
jz @f
cmp eax,'on '
jnz .skipLBA
inc ecx
@@:
mcall 21,11
.skipLBA:
; Enable/disable PCI access for applications
mov dword[param],0
invoke ini.get_str, sz_ini, sz_low_level, sz_pci, param, 4, 0
xor ecx,ecx
mov eax,[param]
or eax,0x20202020
cmp eax,'off '
jz @f
cmp eax,'on '
jnz close
inc ecx
@@:
mcall 21,12
jmp close
;---------------------------------------------------------------
START:
mcall 68,11
stdcall dll.Load, @IMPORT
push eax
test eax,eax
jnz quit
jnz close
GetCommandLine eax
cmp dword[eax],'BOOT'
jnz @f
cmp [param],dword 'BOOT'
jz BootSettings
pop eax
call settings_read_boot
call settings_read_ini
call settings_apply_boot
call style_apply
quit:
mcall -1
; get current settings
mcall 26,5
dec eax
mov [syslang],eax
@@:
call settings_read_system
; the checkbox image is shared by @RESHARE, 0 if it is not running
mcall 68,22,sz_checkbox,0,0
mov [checkboxImage],eax
mcall 26,11
mov [lba_read],eax
mcall 26,12
mov [pci_acc],eax
mcall 18,8,1
mov [speaker_mute],eax
mcall 48,9
mov [fontSmoothing],eax
mcall 48,11
mov [fontSize],eax
loadtxt:
cmp [syslang],3
jz .ru
cmp [syslang],5
jz .et
mov [text],texteng
jmp draw_window
.et:
mov [text],textet
jmp draw_window
.ru:
mov [text],textrus
;---------------------------------------------------------------
draw_window:
mov eax,[language]
mov eax,[texts+eax*4]
mov [text],eax
mcall 12,1
mcall 48,3,sc,sizeof.system_colors
mov byte[sc.work_text+3],FONT
mov byte[sc.work_button_text+3],FONT
mcall 48,4
lea ecx,[eax+(50 shl 16)+CLIENT_H+4]
mov edx,[sc.work]
or edx,0x34000000
mcall 0,<50,CLIENT_W+9>,,,0,title
; a row: label, value (drawn by draw_values), '-' and '+' buttons
xor ebp,ebp
.row:
imul edi,ebp,ROW_STEP
add edi,TOP ; y of the row
mov ecx,edi
shl ecx,16
add ecx,ROW_H-1 ; the button is 1 pixel bigger than given
lea edx,[FIRST_ROW_BUTTON+ebp*2]
mcall 8,<BUTTON1_X,ROW_H-1>,,,[sc.work_button]
lea edx,[FIRST_ROW_BUTTON+1+ebp*2]
mcall 8,<BUTTON2_X,ROW_H-1>
; the kernel leaves the corners of a button out: one frame for both,
; its right corners stay out
mov edx,[sc.work_button]
shr edx,1
and edx,0x7F7F7F ; the border colour of a button
mov cx,1
mcall 13,<BUTTON1_X,BUTTON2_X+ROW_H-1-BUTTON1_X>
add ecx,(ROW_H-1) shl 16
mcall 13
lea ebx,[edi+TEXT_DY+(LABEL_X shl 16)]
mov ecx,ebp
call get_text
mcall 4,,[sc.work_text]
lea ebx,[edi+TEXT_DY+((BUTTON1_X+8) shl 16)]
lea edx,[glyphs+ebp*4]
mcall 4,,[sc.work_button_text]
lea ebx,[edi+TEXT_DY+((BUTTON2_X+8) shl 16)]
lea edx,[glyphs+2+ebp*4]
mcall 4
inc ebp
cmp ebp,ROWS
jb .row
; group box of the checkboxes: a frame, its title over it on the work colour
mcall 13,<GROUP_X,GROUP_W>,<GROUP_Y,GROUP_H>,[sc.work_graph]
mcall 13,<GROUP_X+1,GROUP_W-2>,<GROUP_Y+1,GROUP_H-2>,[sc.work]
mov ecx,T_GROUP
call get_text
mov ecx,[sc.work_text]
or ecx,0x40000000
mcall 4,<GROUP_TITLE_X,GROUP_TITLE_Y>,,,,[sc.work]
; The checkboxes are spread over the group, the last one ends at its end.
; LBA and PCI are fixed, so the step only depends on the speaker label.
mov ecx,T_SPEAKER
call get_text
mov eax,ITEM_END-ITEM_X-CHECK_SIZE-8
.length:
mov cl,[edx]
mov ecx,50*65536+32*(4+stringsAmount)
mcall 0,<50,700>,,0xB4111199,0,title
; Main buttons
mov eax,8
mov ecx,6*65536+26
mov edx,4
mov esi,0x5580c0
mov ebp,stringsAmount
@@:
mcall ,<490,24>
inc edx
test cl,cl
jz .step
and cl,0xC0
cmp cl,0x80 ; UTF-8 continuation byte
jz .length
sub eax,8
jmp .length
.step:
shr eax,1
mov [checkStep],eax
; a checkbox: the box (drawn by draw_values), then the label, both under
; one button up to the end of the group. The next button covers the rest.
xor ebp,ebp
.check:
mov edi,[checkStep]
imul edi,ebp
add edi,ITEM_X ; x of the box
lea ebx,[edi+CHECK_SIZE+8]
shl ebx,16
add ebx,CHECK_Y+TEXT_DY
call check_label
mcall 4,,[sc.work_text]
mov ebx,edi
shl ebx,16
add ebx,ITEM_END
sub ebx,edi
lea edx,[FIRST_CHECK_BUTTON+BT_HIDE+BT_NOFRAME+ebp]
mcall 8,,<CHECK_Y,ROW_H-1>
inc ebp
cmp ebp,CHECKS
jb .check
mcall ,<526,24>
inc edx
mcall ,<562,120>
inc edx
add ecx,32*65536
dec ebp
jnz @b
; APPLY ALL
add ecx,32*65536
mcall ,<514,168>,,3,0x005588dd
; SAVE ALL
add ecx,32*65536
dec edx
mcall
mcall 12,2
;---------------------------------------------------------------
draw_values:
mov edx,languageNames
mov ecx,[language]
call nth_string
mov [valueText],edx
draw_infotext:
mov eax,[syslang]
mov edi,[text]
lea esi,[eax*8+langs]
add edi,28
movsd
movsd
add edi,LLL-8
mov edx,sz_subpixel
mov ecx,[fontSmoothing]
cmp ecx,2
jae @f
add ecx,T_OFF
call get_text
mov eax,[lba_read]
call onoff
mov [edi],ebx
mov eax,[pci_acc]
call onoff
mov [edi+LLL],ebx
mov eax,[speaker_mute]
call onoff
mov [edi+LLL*2],ebx
mov ebx,'SUBP'
mov ecx,'IXEL'
cmp [fontSmoothing],2
jz @f
mov eax,[fontSmoothing]
call onoff
mov ecx,' '
@@:
mov [valueText+4],edx
mov [edi+LLL*3],ebx
mov [edi+LLL*3+4],ecx
mov eax,[fontHeight]
mov bl,10
mov eax,[fontSize]
mov bl, 10
div bl
add ax,'00'
mov word[heightText],ax
mov [valueText+8],heightText
add ax, '00'
mov [edi+LLL*4],ax
; draw text
mcall 13,<342,96>,32*stringsAmount,80111199h
mov eax,4
mov ebx,6*65536+11
mov ecx,1ffffffh
mov edx,[text]
mov esi,LLL
mov ebp,stringsAmount
newline:
mcall
add ebx,32
add edx,esi
dec ebp
jnz newline
; each value in a light box framed on three sides, the button is the fourth,
; the left corners are cut off
xor ebp,ebp
.row:
imul edi,ebp,ROW_STEP
add edi,TOP
mov ecx,edi
shl ecx,16
add ecx,ROW_H
mcall 13,<VALUE_X+1,VALUE_W-1>,,[sc.work_graph]
add ecx,(1 shl 16)-2
mcall 13,<VALUE_X,VALUE_W>
; sunken, the inner lines of a button reversed: three boxes, each 1 pixel
; smaller, leave darker lines left and top and lighter right and bottom
mov edx,[sc.work_light]
mov eax,edx
shr eax,3
and eax,0x1F1F1F
sub edx,eax ; 1/8 darker
mcall 13,<VALUE_X+1,VALUE_W-1>
mov edx,[sc.work_light]
or edx,0x1F1F1F ; lighter
add ecx,(1 shl 16)-1
mcall 13,<VALUE_X+2,VALUE_W-2>
dec ecx
mcall 13,<VALUE_X+2,VALUE_W-3>,,[sc.work_light]
lea ebx,[edi+TEXT_DY+((VALUE_X+VALUE_PAD) shl 16)]
mov edx,[valueText+ebp*4]
mcall 4,,[sc.work_text]
inc ebp
cmp ebp,ROWS
jb .row
mov ebp,2
add ebx,32
@@:
mcall
add ebx,32
add edx,esi
dec ebp
jnz @b
; checkboxes: a frame, light inside, the @RESHARE image when set
xor ebp,ebp
.check:
mov ebx,[checkStep]
imul ebx,ebp
add ebx,ITEM_X
shl ebx,16
add ebx,CHECK_SIZE
mcall 13,,<CHECK_BOX_Y,CHECK_SIZE>,[sc.work_graph]
add ebx,(1 shl 16)-2
mcall 13,,<CHECK_BOX_Y+1,CHECK_SIZE-2>,[sc.work_light]
mov eax,[checks+ebp*8]
mov eax,[eax]
xor eax,[checks+ebp*8+4]
jz .next
mov edx,ebx
mov dx,CHECK_BOX_Y+1
mov ebx,[checkboxImage]
test ebx,ebx ; no @RESHARE: the box stays empty
jz .next
mcall 7,,<CHECK_SIZE-2,CHECK_SIZE-2>
.next:
inc ebp
cmp ebp,CHECKS
jb .check
;---------------------------------------------------------------
still:
mcall 10
cmp eax,1
jz draw_window
cmp eax,2
jz .key
jz key
cmp eax,3
jnz still
jz button
mcall 17
shr eax,8
cmp eax,1
jz quit
sub eax,FIRST_ROW_BUTTON
jb still ; 0: no button after all
cmp eax,FIRST_CHECK_BUTTON-FIRST_ROW_BUTTON
jae .check
; '-' or '+' of a row: step the value within the limits of the row
shr eax,1 ; eax = row, CF = '+'
lea esi,[eax*3]
lea esi,[rows+esi*4]
mov edi,[esi]
mov ecx,[edi]
jc .plus
cmp ecx,[esi+4]
jbe still
dec ecx
jmp .set
.plus:
cmp ecx,[esi+8]
jae still
inc ecx
.set:
mov [edi],ecx
push eax
call apply_and_save
pop eax
cmp eax,1 ; the language and the font smoothing
jbe draw_window ; change all the texts: repaint the window
jmp draw_values
.check:
mov eax,[checks-(FIRST_CHECK_BUTTON-FIRST_ROW_BUTTON)*8+eax*8]
xor dword[eax],1
call apply_and_save
jmp draw_values
.key:
jmp still
;---------------------------------------------------------------
key:
mcall 2
jmp still
;---------------------------------------------------------------
; every change takes effect at once and is kept in system.ini
apply_and_save:
call settings_apply
jmp settings_save_ini
; edx = label of the checkbox ebp
check_label:
mov edx,[checkLabels+ebp*4]
test edx,edx
jnz @f
mov ecx,T_SPEAKER
call get_text
button:
mcall 17
shr eax,8
call dword[eax*4+buttonTab-4]
jmp draw_infotext
close:
pop eax
mcall -1
language1:
dec [syslang]
jns @f
mov [syslang],7
jmp @f
language2:
inc [syslang]
cmp [syslang],8
jc @f
mov [syslang],0
@@:
pop eax
jmp loadtxt
LBA1:
btr [lba_read],0
ret
LBA2:
bts [lba_read],0
ret
PCI1:
btr [pci_acc],0
ret
PCI2:
bts [pci_acc],0
ret
SPEAKER1:
btr [speaker_mute],0
ret
SPEAKER2:
bts [speaker_mute],0
ret
font1:
cmp [fontSmoothing],0
jz @f
dec [fontSmoothing]
ret
font2:
cmp [fontSmoothing],2
jz @f
inc [fontSmoothing]
ret
fontSize1:
cmp [fontSize],10
jc @f
dec [fontSize]
@@:
ret
; edx = string number ecx of the window language
get_text:
mov edx,[text]
; edx = string number ecx of the zero separated list at edx
nth_string:
jecxz .done
@@:
inc edx
cmp byte[edx-1],0
jnz @b
loop @b
.done:
fontSize2:
inc [fontSize]
ret
apply_all:
call _lba_read
call _pci_acc
call _speaker_mute
call fontApply
call fontSizeApply
_syslang:
mov ecx,[syslang]
inc ecx
mcall 21,5
ret
_lba_read:
mcall 21,11,[lba_read]
ret
_pci_acc:
mcall 21,12,[pci_acc]
ret
fontApply:
mcall 48,10,[fontSmoothing]
ret
fontSizeApply:
mcall 48,12,[fontSize]
ret
_speaker_mute:
mcall 18,8,1
cmp [speaker_mute],eax
jz @f
inc ecx
mcall 18
@@:
ret
;---------------------------------------------------------------
onoff:
cmp [syslang],3
jz .ru
cmp [syslang],5
jz .et
mov ebx,'OFF '
test eax,eax
jz @f
mov ebx,'ON '
ret
.ru:
mov ebx,'НЕТ '
test eax,eax
jz @f
mov ebx,'ДА '
ret
.et:
mov ebx,'V─L.'
test eax,eax
jz @f
mov ebx,'SEES'
@@:
ret
;---------------------------------------------------------------
saveAll:
; system language
mov eax,[syslang]
mov ax, word[eax*2+langMarks]
mov [param],eax
invoke ini.set_str, sz_ini, sz_system, sz_language, param, 2
; font size
invoke ini.set_int, sz_ini, sz_system, sz_fontSize, [fontSize]
; font smoothing
mov dword[param],'off'
cmp [fontSmoothing],0
jz @f
mov dword[param],'on '
cmp [fontSmoothing],1
jz @f
mov dword[param],'sbp'
@@:
invoke ini.set_str, sz_ini, sz_system, sz_fontSmooth, param, 3
; system speaker
mov dword[param],'off'
cmp [speaker_mute],0
jz @f
mov dword[param],'on '
@@:
invoke ini.set_str, sz_ini, sz_system, sz_speaker, param, 3
; LBA access for applications
mov dword[param],'off'
cmp [lba_read],0
jz @f
mov dword[param],'on '
@@:
invoke ini.set_str, sz_ini, sz_low_level, sz_lba, param, 3
; PCI access for applications
mov dword[param],'off'
cmp [pci_acc],0
jz @f
mov dword[param],'on '
@@:
invoke ini.set_str, sz_ini, sz_low_level, sz_pci, param, 3
ret
;---------------------------------------------------------------
; DATA
align 4
buttonTab: ; button handler pointers: -,+,apply
dd close
dd saveAll
dd apply_all
dd language1
dd language2
dd _syslang
dd LBA1
dd LBA2
dd _lba_read
dd PCI1
dd PCI2
dd _pci_acc
dd SPEAKER1
dd SPEAKER2
dd _speaker_mute
dd font1
dd font2
dd fontApply
dd fontSize1
dd fontSize2
dd fontSizeApply
@IMPORT:
library libini, 'libini.obj'
import libini, \
@@ -319,88 +465,76 @@ import libini, \
ini.set_str, 'ini_set_str',\
ini.set_int, 'ini_set_int'
ROWS = 3
CHECKS = 3
FIRST_ROW_BUTTON = 10 ; row N has '-' = 10+N*2 and '+' = 11+N*2
FIRST_CHECK_BUTTON = 20
FONT = 0xB0 ; text flags: zero terminated, 8x16 UTF-8
; layout of the client area: controls ROW_H high, GAP empty pixels apart,
; EDGE to the window border, GAP at the top
LABEL_CHARS = 20 ; the longest label, Spanish
GAP = 12
EDGE = 8
TOP = GAP
ROW_H = 24
ROW_STEP = ROW_H + GAP
TEXT_DY = 5 ; the capitals, rows 2..11 of the font, centred in ROW_H
LABEL_X = EDGE - 1 ; column 0 of a letter is empty: aligned with the group frame
VALUE_X = LABEL_X + LABEL_CHARS*8 + GAP
VALUE_PAD = GAP
VALUE_W = 8*8 + VALUE_PAD*2
BUTTON1_X = VALUE_X + VALUE_W ; right after the value box
BUTTON2_X = BUTTON1_X + ROW_H - 1 ; the buttons share a border
CLIENT_W = BUTTON2_X + ROW_H + EDGE
GROUP_X = EDGE
GROUP_W = CLIENT_W - EDGE*2
GROUP_Y = TOP + ROWS*ROW_STEP + 5 ; GAP to the capitals of the title
GROUP_TITLE_Y = GROUP_Y - 7 ; the frame crosses the capitals
CHECK_Y = GROUP_Y + 1 + GAP ; the row of the checkboxes
GROUP_H = 1 + GAP + ROW_H + GAP + 1
CHECK_SIZE = 15
CHECK_BOX_Y = CHECK_Y + (ROW_H-CHECK_SIZE)/2
ITEM_X = GROUP_X + 1 + GAP
ITEM_END = GROUP_X + GROUP_W - GAP ; the last column of a letter is empty
GROUP_TITLE_X = ITEM_X - 8 - 1 ; a space, then a letter from its column 1
CLIENT_H = GROUP_Y + GROUP_H + EDGE
; variable, min, max of every row
align 4
rows:
dd language, 0, LANGUAGES-1
dd fontSmoothing, 0, 2
dd fontHeight, FONT_H_MIN, FONT_H_MAX
; variable and the value of it that means 'not checked'
checks dd lba,0, pci,0, speakerMute,1
checkLabels dd sz_lba, sz_pci, 0 ; 0: the speaker label of the language
glyphs db '<',0,'>',0,'<',0,'>',0,'-',0,'+',0
sz_subpixel db 'Subpixel',0
languageNames db 'English',0,'Finnish',0,'German',0,'Russian',0
db 'French',0,'Estonian',0,'Spanish',0,'Italian',0
title db "System settings",0
sz_checkbox db "CHECKBOX",0
sz_ini db "/sys/settings/system.ini",0
; Texts of the window languages: zero terminated strings in this order
; (row labels, group title, speaker label, 'off', 'on')
T_GROUP = ROWS
T_SPEAKER = ROWS + 1
T_OFF = ROWS + 2
sz_system db "system",0
sz_language db "language",0
sz_fontSize db "font height",0
sz_fontSmooth db "font smoothing",0
sz_speaker db "speaker mute",0
; the texts of every kernel language, English where there is no translation
texts dd texteng, texteng, texteng, textrus, texteng, texteng, textspa, texteng
sz_mouse db "mouse",0
sz_speed db "speed",0
sz_acceleration db "acceleration",0
sz_double_click_delay db "double_click_delay",0
texteng db 'System language',0, 'Font smoothing',0, 'Font height',0
db ' Access settings ',0, 'Speaker',0
db 'Off',0, 'On',0
sz_low_level db "low-level",0
sz_lba db "LBA",0
sz_pci db "PCI",0
textspa db 'Idioma del sistema',0, 'Suavizado de fuentes',0, 'Altura de fuente',0
db ' Ajustes de acceso ',0, 'Altavoz',0
db 'No',0, 'Sí',0
LLL = 56
stringsAmount = 6
textrus db 'Язык системы',0, 'Сглаживание шрифтов',0, 'Высота шрифтов',0
db ' Настройки доступа ',0, 'Динамик',0
db 'Нет',0, 'Да',0
align 4
langs:
db 'ENGLISH FINNISH GERMAN RUSSIAN FRENCH ESTONIANSPANISH ITALIAN '
sz langMarks, 'en','fi','de','ru','fr','et','es','it'
;---------------------------------------------------------------
include 'settings.inc'
textrus:
db 'Язык системы : < > Применить'
db 'Включить LBA : - + Применить'
db 'Доступ к шине PCI : - + Применить'
db 'Выключить SPEAKER : - + Применить'
db 'Сглаживание шрифтов : - + Применить'
db 'Высота шрифтов : - + Применить'
db 'ВНИМАНИЕ: Применить все'
db 'НЕ ЗАБУДЬТЕ СОХРАНИТЬ НАСТРОЙКИ Сохранить все'
text dd ? ; texts of the window language
checkboxImage dd ?
checkStep dd ? ; x distance of two checkboxes
sc system_colors
valueText rd ROWS ; the strings shown in the value boxes
heightText rb 4
texteng:
db 'System language : < > Apply '
db 'Allow LBA access : - + Apply '
db 'Allow PCI access : - + Apply '
db 'Disable SPEAKER : - + Apply '
db 'Font smoothing : - + Apply '
db 'Font height : - + Apply '
db 'NOTE: Apply all '
db 'SAVE YOUR SETTINGS BEFORE QUITING KOLIBRI Save all '
textet:
db 'S№steemi keel : < > Kinnita '
db 'LBA lugemine lubatud : - + Kinnita '
db 'PCI juurdepффs programm. : - + Kinnita '
db 'Disable SPEAKER : - + Kinnita '
db 'Font smoothing : - + Kinnita '
db 'Font height : - + Kinnita '
db 'M─RKUS: Kinnita kїik '
db 'SALVESTA SEADED ENNE KOLIBRIST V─LJUMIST Salvesta kїik'
IM_END:
text dd ?
syslang dd ?
lba_read dd ?
pci_acc dd ?
speaker_mute dd ?
fontSmoothing dd ?
fontSize dd ?
param:
rb 1024
I_END: