Thursday, October 1, 2026

Aruba Instant On AP11: Access Point'i Çalıştırmak İçin Windows ICS Kurmak

Yemi bir access point satın alırken beklentim oldukça basitti: cihazı Ethernet'e takacağım, bir IP alacak, web arayüzüne gireceğim, SSID ve şifreyi belirleyip hayatıma devam edeceğim.

Sonuçta ürünün adı Instant On.

Fakat HPE Aruba Instant On AP11 ile yaşadığım kurulum süreci, "instant" kelimesinin network dünyasında ne kadar esnek yorumlanabileceğini gösterdi. Kutudan çıkan cihazı çalıştırabilmek için DHCP Option 6, Windows Internet Connection Sharing, yerel debug arayüzü, cloud onboarding ve firmware partition değişimiyle uğraştım.

İşin daha komik tarafı ise cihaz güncellendikten sonra bütün problemlerin ortadan kalkmasıydı.

Başlangıç: AP11 ve 2020'den Gelen Firmware

Cihaz HPE Networking Instant On AP11. İlk açılışta ZTE H3600 router'a bağladım. Router cihazı düzgün şekilde gördü ve AP11'e:

IP      : 192.168.1.119
Gateway : 192.168.1.1

verdi.

Fakat AP'nin yerel debug ekranında ilginç bir şey vardı:

DNS server address : 0.0.0.0
Device onboarding  : Error
Firmware           : 1.4.1.0 (74478.1)

Firmware sürümü özellikle dikkat çekiciydi. Cihaz kutudan 1.4.1.0 ile çıktı ve build/tarih bilgileri 2020 dönemini işaret ediyordu.

Yerel web arayüzü de oldukça sınırlıydı. IP yapılandırmasında yalnızca:

  • DHCP
  • PPPoE

seçenekleri vardı. Statik IP veya elle DNS verme seçeneği yoktu.

ZTE DNS Vermiyor mu?

İlk şüpheli doğal olarak router oldu. ZTE H3600'ün DHCP ayarlarında DNS sunucuları zaten açıkça tanımlıydı:

Primary DNS   : 1.1.1.1
Secondary DNS : 8.8.8.8

Daha sonra aynı DHCP sunucusundan IP alan Windows PC'yi kontrol ettim:

DHCP Server : 192.168.1.1
DNS Servers : 1.1.1.1
              8.8.8.8

Yani ZTE, DHCP üzerinden DNS bilgisini gerçekten dağıtıyordu.

AP11 ise aynı DHCP sunucusundan IP adresi ve gateway alıyor, fakat DNS bilgisini 0.0.0.0 olarak gösteriyordu.

Router ve AP birkaç kez cold boot edildi. DHCP lease sıfırlandı. Sonuç değişmedi.

"Instant On" Fakat Önce Cloud'a Ulaşmanız Gerekiyor

Instant On serisinin başka bir sürprizi de klasik Aruba Instant / IAP cihazlarından farklı olarak tam yerel yönetim arayüzünün bulunmaması.

SSID, güvenlik, radyo ayarları ve esas konfigürasyon cloud portalı veya mobil uygulama üzerinden yapılıyor. Yerel IP'deki arayüz esas olarak bağlantı ve troubleshooting amaçlı.

Dolayısıyla ortaya güzel bir tavuk-yumurta problemi çıktı:

AP'nin firmware'ini güncellemek için cloud'a bağlanması gerekiyor.
Cloud'a bağlanmak için DNS gerekiyor.
Fakat kutudan çıkan firmware ZTE'nin DHCP'sinden DNS'i düzgün alamıyor.
Üstelik o firmware'de DNS'i elle girebileceğiniz Static seçeneği de yok.

Gerçek anlamda bir bootstrap deadlock.

Yerel Setup SSID'sini Çıkartmak Bile Bir Macera

AP Ethernet bağlantısı olmadan açıldığında bir süre sonra yeşil/turuncu LED kombinasyonuna geçiyordu, fakat setup SSID'si çıkmıyordu.

Sonunda AP'yi Ethernet linki olan fakat DHCP sunucusu bulunmayan bir bağlantıya taktım. Bir süre sonra LED sabit turuncuya geçti ve:

InstantOn-CA:...

şeklinde setup SSID'si ortaya çıktı.

Bu SSID'ye bağlandıktan sonra:

https://connect.arubainstanton.com

üzerinden yerel arayüze erişebildim.

Ancak burada da eski firmware yüzünden yalnızca DHCP ve PPPoE vardı. Statik IP/DNS seçeneği hâlâ yoktu.

Çözüm: Windows ICS ile AP'ye Başka Bir DHCP Sunucusu Vermek

Bu noktada problemi izole etmek için AP'yi doğrudan PC'nin ikinci Ethernet portuna bağladım.

PC'de iki adet Intel 82575EB Gigabit Ethernet portu vardı. İnternet bağlantısını taşıyan Hyper-V virtual interface üzerinden, ikinci fiziksel Ethernet portuna Windows Internet Connection Sharing (ICS) açtım.

Windows ICS otomatik olarak:

PC / Gateway : 192.168.137.1
DHCP         : Windows ICS
DNS          : 192.168.137.1

şeklinde küçük bir NAT ağı oluşturdu.

AP11 yeniden başlatıldığında bu kez Windows ICS'den düzgün IP, gateway ve DNS aldı:

IP      : 192.168.137.75
Gateway : 192.168.137.1
DNS     : 192.168.137.1

Yani cihaz başka bir DHCP implementasyonundan DNS'i düzgün alabiliyordu.

Bu aynı zamanda problemin genel bir "AP11 DNS alamıyor" problemi olmadığını, eski firmware ile ZTE H3600 DHCP arasında belirli bir interoperability problemi olduğunu düşündüren en güçlü test oldu.

Cloud Nihayet Cihazı Gördü

DNS geldikten sonra AP11 Instant On cloud'a ulaşabildi. Mobil uygulama üzerinden cihaz seri numarasıyla bulundu ve durum bir süre:

Updating

olarak göründü.

AP firmware'i cloud üzerinden indirip birkaç kez reboot etti. Sonunda cihaz online oldu ve SSID oluşturulabildi.

Telefon yeni SSID üzerinden internete çıktı. MacBook ile yaptığım ilk hızlı testte ise yaklaşık 300 Mbit/s gördüm. Yani cihaz çalışmaya başladıktan sonra donanım tarafında söyleyecek pek kötü bir şey yoktu.

Firmware 1.4.1.0 → 3.4.2.0

Firmware güncellemesinden sonra yerel arayüze tekrar girdiğimde tasarımın ve bağlantı seçeneklerinin ciddi biçimde değiştiğini gördüm.

Yeni firmware:

3.4.2.0

idi.

Ve artık IP yapılandırma bölümünde:

  • Automatic
  • Static
  • PPPoE

seçenekleri bulunuyordu.

Ayrıca Primary ve Secondary DNS sunucuları elle girilebiliyordu.

Aynı ekranda:

Portal status     : Connected
Onboarding status : Onboarded

bilgileri de görünüyordu.

Restart sebebinde ise:

CLOUD cmd ... switch-partition-reboot

ifadesi yer alıyordu. Yani firmware güncellemesi cloud üzerinden gerçekleştirilmiş ve cihaz yeni firmware partition'ına geçirilmişti.

Final Test: Tekrar ZTE H3600

Asıl önemli test cihazı Windows ICS'den çıkarıp tekrar ZTE H3600'e bağlamaktı.

İlk bağlantıda AP, Windows ICS'den aldığı eski 192.168.137.78 lease'ine bir süre tutundu.

Daha da ilginci, ZTE H3600 cihaz listesinde AP'nin MAC adresiyle birlikte bu eski 192.168.137.78 adresini görebiliyordu.

Bu, ZTE'nin 192.168.137.0/24 ağına route ettiği anlamına gelmiyordu. AP eski DHCP lease bilgisini kullanmaya devam ediyor ve ağ üzerinde o adresle kendisini duyuruyordu.

Tam bir cold boot sonrasında ise eski lease bırakıldı ve AP yeni DHCP isteği gönderdi.

ZTE'de AP'nin MAC adresine daha önce:

192.168.1.119

rezervasyonu vermiştim.

Yeni firmware ile AP artık ZTE'den her şeyi doğru aldı:

IP            : 192.168.1.119
Subnet        : 255.255.255.0
Gateway       : 192.168.1.1
Primary DNS   : 1.1.1.1
Secondary DNS : 8.8.8.8

Portal status     : Connected
Onboarding status : Onboarded
Firmware          : 3.4.2.0

Peki Problem Neydi?

Elimde paket seviyesinde DHCP capture olmadığı için "1.4.1 firmware'de şu satırda bug var" demek doğru olmaz.

Fakat yapılan testlerin sonucu oldukça güçlü:

  1. ZTE H3600 DHCP üzerinden 1.1.1.1 ve 8.8.8.8 DNS adreslerini düzgün dağıtıyordu.
  2. Aynı DHCP sunucusundan Windows PC DNS adreslerini sorunsuz aldı.
  3. AP11 firmware 1.4.1.0 ile IP ve gateway almasına rağmen DNS'i 0.0.0.0 gösterdi.
  4. AP11 Windows ICS DHCP'sinden DNS'i düzgün aldı.
  5. Bu bağlantı üzerinden firmware 3.4.2.0'a güncellendi.
  6. Aynı AP, aynı ZTE ve aynı DHCP ayarlarıyla 3.4.2.0 üzerinde DNS'i artık sorunsuz aldı.

Dolayısıyla en mantıklı sonuç, AP11'in eski 1.4.1.0 firmware'i ile ZTE H3600 DHCP arasında DNS / DHCP Option 6 seviyesinde bir uyumsuzluk olduğu.

Firmware güncellemesinden sonra aynı donanım, aynı router ve aynı DHCP yapılandırmasıyla problemin tamamen ortadan kalkması bunu oldukça güçlü biçimde destekliyor.

"Instant On"

Donanım çalışmaya başladıktan sonra AP11 gayet iyi. 5 GHz performansı güzel, bağlantı stabil ve cihaz küçük. İlk testte MacBook üzerinden yaklaşık 300 Mbit/s görmek de gayet tatmin ediciydi.

Ama kurulum deneyimi için aynı şeyi söylemek zor.

Bir access point'i devreye almak için:

  • cloud hesabı açmak,
  • site oluşturmak,
  • seri numarasıyla onboarding yapmak,
  • LED renklerini yorumlamak,
  • yerel debug arayüzüne girmek,
  • DHCP ve DNS problemini teşhis etmek,
  • Windows ICS kurmak,
  • cloud üzerinden firmware güncellemek

zorunda kaldım.

Ve bütün bunlar üzerinde Instant On yazan bir cihazda oldu.

En azından maceranın sonunda AP11 gerçekten "On" oldu.

"Instant" kısmı ise biraz tartışmalı.

Ben Asrı

Yazar: Kaan Karsan

Adam Curtis’in 2002 yapımı BBC belgeseli “Ben Asrı” (The Century Of The Self), tüketim toplumunun nasıl virajlar aşılarak ya da nasıl engeller konularak yaratıldığını anlatan ufuk açıcı bir belgesel. Dört saatlik süresi boyunca izleyicisini ‘defalarca’ baştan yaratıyor.

Sigmund Freud’un harflerden oluşan siyah beyaz portresi
Sigmund Freud — tipografik portre.

Her şey Sigmund Freud’la başlıyor: “İnsan rasyonel değildir. Bu sebeple rasyonel kararlar vermeye yardıma muhtaçtır. İçgüdülerinin kölesi olarak yaşayan insanoğlunun kontrol edilmesi gerekir.” Psikanalizin dünyanın en mühim çalışma alanlarından biri haline geldiği erken 1900’lerin Freud çağında artık ‘özgürleşmeye’ yönelmiş dünya, zıtlıkların doğurduğu bir kaos tarafından yönetiliyor. Ringin bir köşesinde Freud gibi düşünceler, yani, insanın sadece içgüdülerine bağımlı bir şekilde karar veren, büyük bir felakete sürüklenen ve mutlak suretle gizli bir denetim ağına bağlı olarak ‘kontrol’ altına alınması gereken bir tür olduğu düşüncesi var. Diğer köşede ise ‘liberal’ ütopyaya sınırsızca biat edilmesi gerekliliğini şart koşan psikanalist paradigma... Adam Curtis’in dört saat süresince kafa karıştırdığı kadar zihin billurlaştıran belgeseli “Ben Asrı”, mevzubahis fikirler karşıtlığından yola çıkarak ‘ben olmak’ın tarihini anlatıyor. Elbette ki bu ‘benleşme’ mevzusunun tüm izleği temelde ‘kimlik’ kavramının en yüce belirleyicisi olan kapitalist kitle yönetim metodolojisine dayanıyor. “Ben Asrı”, insanın hikayesini anlattığı kadar insanı kendi ihtiyaçları doğrultusunda biçimlendiren, ‘çok bilinmeyenli’ düzenin tarihini de anlatmaya soyunuyor.

Spot ışıklarının altında değil, kontrol odasında bir adam var. Bu adamın adı Edward Bernays. Bernays bir ‘Freud’cu. Olmaması için hiçbir sebep yok, zira kendisi aynı zamanda Sigmund Freud’un yeğeni... Freud’un çalışmalarının İngilizceye çevrilip alanını genişletmesinde de büyük bir payı var. Lakin Bernays’in ismi psikanaliz alanına yaptığı bu yararsal katkılardan ziyade başka bir atılımıyla anılıyor. Artık her şirkette bir departman olarak rastladığımız ‘Halkla İlişkiler’ biliminin mucidi Bernays. Başka bir deyişle ‘arzu yaratma’ mesleğinin, yani, kapitalizmin direğinin...

Bernays’in böyle bir fikirle çıkagelmesinin ardında Freud’un ideaları var. Sadece ihtiyaçları peşinde koşan insanların o güne kadar asla düşünmedikleri şeyleri arzulamaları gerekiyor. Bu, hayatın dizginini katiyen elinde bulundurmaması gereken insanın dikkatini dağıtmak için geliştirilen, bir nevi bir kontrol mekanizması... Hatta bir açıdan da yeni bir demokrasi anlayışı... Kendi bilinçaltıyla rasyonel kararlar veremeyecek olan, gizli kimliği denetim altında tutulması elzem insan türünün ve aşina hale geldiği demokrasi kavramının ‘rıza mühendisliği’ refakatinde dönüştürülmesi süreci... Böylece yaklaşık yüz yıldır ‘tüketici’ adını verdiğimiz, yani, tahmin edilebilir ve hesaplanmış insan türü üretilecekti.

Edward Bernays, tıpkı şu an tanıdığımız birçok lider gibi demokrasinin muhteşem bir kavram olduğunu, lakin insanların demokrasiye güdümlü olarak kesinlikle ‘mantıklı’ kararlar veremeyeceğini düşünüyor. Mimarı olduğu yeni meslek alanı olan ‘Halkla İlişkiler’ de bu muhtemel irrasyonel kararların dikkatini dağıtacak bir politika üretmeye yönleniyor: Kitleleri yönetenler, insanların doğal ahlaksızlıklarını türlü yöntemlerle ‘uzaklaştırmalılar’. Bu noktada belgeselde çok sembolik bir yer ve önem tutan ‘sigara’ örneğinden bahsetmemiz şart. Tütün şirketleri, kadınların sigara kullanmamasından kaynaklanan bir zarar etme dönemine giriyorlar ve bu gidişatı değiştirmesi için Bernays’in yöntem dağarcığına başvuruyorlar. Dönem toplumunda kadınların sigara içmesini ‘uygun’ görmeyen bir inanış var. Mesleki hayatını Freud’un tespitleri üzerine kuran Bernays, acilen Freud’cu psikanalistlere başvuruyor. Psikanalistler de sigaranın çok derinlerde cinsel bir sembol olduğunu ve eğer ki kadınlar doğru şekilde manipüle edilirse sigara kullanmaya başlayabileceklerini söylüyorlar. Böylece Bernays, sigara ve özgürlük arasında bir bağ kurarak gazetelere tam sayfa ilanlar veriyor; kadınların sigara içerek özgürleşebileceklerini telkin eden metinler yayımlatıyor. Bu sayede erkeklerin dünyasında ‘ayrıcalıklı’ bir yeri olan sigara, kadınların dünyasına bir özgürlük belirteci olarak giriveriyor. Curtis’in belgeseli benzer örneklerle, eşsiz arşiv görüntüleriyle ve tanık göstermelerle özellikle ilk iki bölümü boyunca kitle yönetim formüllerini gözler önüne seriyor.

Belgeselin son iki saatinde ise Freud’cu paradigma, Marilyn Monroe’nun ölümü gibi sembolik olaylarla alaşağı edildikten sonra türeyen ‘ikinci yeni’ tüketici modelinin peşinden gidiyor. Artık dünya insanın baskılanamayacağına ve özellikle cinsel açıdan özgür bırakılması gerektiğine ikna olmuş durumda. Böylece cinsel devrim başlıyor; yeni insan kendi katmanlarını birer birer yok ederek özüne ulaşıyor. Tüketicisinin nasıl davranacağını öngöremeyen sermaye de yeni bir oyun planı geliştirmeye başlıyor. Kabuğunu kıran insan, sonradan ‘tek kişilik sosyalizm’ olarak adlandırılacak bir psikolojinin içine giriyor. Bir şekilde bu ‘öz’ ve ‘kendini gerçekleştirmiş’ insanı kazanmak zorunda olan politik dünya da sıradaki adımlarını bu insanların ışığında atmaya koyuluyor. Adam Curtis, bu yeni vaziyeti iki çok net örnekle, Reagan ve Thatcher iktidarıyla örnekliyor.

Curtis’in iki başyapıtından biri olan “Ben Asrı”, hem insan kimliğine hem de bilincine anlam kazandırıyor. Ziyadesiyle enformatif, ziyadesiyle çarpıcı... İzledikten sonra hiçbir şeyin ‘eskisi gibi’ olmayacağı belgesellerden...


Kaynak: Kaan Karsan, “Ben Asrı”, paylaşılan dergi sayfası. Metin görselden aktarılmıştır.

Anahtar kelimeler: Adam Curtis, Ben Asrı, The Century of the Self, Sigmund Freud, Edward Bernays, belgesel, tüketim toplumu, halkla ilişkiler.

Friday, September 25, 2026

MS 3d Viewer'ı modlamaktan bıkıp sıfırdan STL Viewer yazmak.



Windows üzerinde STL dosyalarını hızlı şekilde kontrol etmek için kullandığım Microsoft 3D Viewer'ın desteği sonlandırıldı. Uygulama çalışmaya devam ediyordu, ancak her açılışta örnek Bee modeli yükleniyor ve arayüzün üst bölümünde 3D Viewer'ın artık desteklenmediğini bildiren bir banner gösteriliyordu.

İlk amaç yeni bir program yazmak değildi. Bee modelini ve banner'ı mevcut uygulamadan kaldırmak yeterli olacaktı.

1. Bee.glb nereden geliyor?

İlk olarak uygulamanın kullanıcı profili altındaki geçici dosyaları incelendi. Başlangıç modeli burada görünüyordu:

C:\Users\...\AppData\Local\Packages\
Microsoft.Microsoft3DViewer_8wekyb3d8bbwe\
TempState\Assets\Bee.glb

Dosya yaklaşık 20 MB büyüklüğündeydi. Bee.glb silinip uygulama yeniden başlatıldığında dosya tekrar oluşturuldu. Aynı davranış ağ bağlantısı olmadan da devam etti.

Bu nedenle modelin internetten indirilmediği, uygulama paketinin içerisinde başka bir kopyasının bulunduğu açıktı.

Paket içerisindeki Assets\Archive.zip incelendiğinde kaynak bulundu:

Assets\Archive.zip
└── ModelShelf
    └── Bee.glb

Archive içerisindeki Bee.glb ile TempState altında oluşturulan dosyanın büyüklüğü aynıydı:

Bee.glb
Length           : 20177864 bytes
CompressedLength : 16013449 bytes

2. WindowsApps paketini doğrudan değiştirmemek

Kurulu uygulama şu dizindeydi:

C:\Program Files\WindowsApps\
Microsoft.Microsoft3DViewer_7.2602.8012.0_x64__8wekyb3d8bbwe

WindowsApps altındaki paket üzerinde doğrudan değişiklik yapmak yerine uygulamanın tamamı ayrı bir çalışma dizinine kopyalandı:

C:\Users\...\Desktop\3DViewerMod

Store paketine ait imza ve metadata dosyaları çalışma kopyasından çıkarıldı:

AppxSignature.p7x
AppxBlockMap.xml
AppxMetadata\
microsoft.system.package.metadata

Daha sonra AppxManifest.xml içerisindeki package identity değiştirildi:

Microsoft.Microsoft3DViewer
        ↓
Kadir.3DViewerMod

Developer Mode altında paket yeniden register edildi:

Add-AppxPackage -Register ".\AppxManifest.xml"

Sonuçta Microsoft Store tarafından kurulan orijinal uygulama yerinde kalırken, dosyaları değiştirilebilen bağımsız bir development package elde edildi.

3. Bee'yi boş bir GLB ile değiştirmek

İlk denemede Archive.zip içerisindeki Bee.glb yerine sözdizimsel olarak geçerli 112 byte'lık boş bir GLB yerleştirildi.

Bee kayboldu ancak uygulama bu kez model yükleme hatası verdi:

Couldn't load 3D model
Try again later.


Daha sonra tek üçgenden oluşan geçerli bir GLB oluşturuldu. Sonuç değişmedi. Bu test uygulamanın yalnızca GLB parser'ından geçen herhangi bir dosyayı başlangıç modeli olarak kabul etmediğini gösteriyordu.

Bu noktada yeni bir GLB üretmek yerine orijinal Bee.glb'nin yapısını koruyup yalnızca geometrisini etkisiz hale getirmek daha güvenliydi.

4. GLB dosyasının iç yapısı

GLB 2.0 dosyası temel olarak bir header, JSON chunk ve binary BIN chunk'tan oluşuyor:

+-----------------------+
| GLB Header            |
| magic / version / len |
+-----------------------+
| JSON Chunk Header     |
+-----------------------+
| JSON                  |
+-----------------------+
| BIN Chunk Header      |
+-----------------------+
| Binary vertex data    |
| indices               |
| animation data        |
| ...                   |
+-----------------------+

Orijinal Bee modeli analiz edildiğinde aşağıdaki yapı elde edildi:

Magic       : 0x46546C67
Version     : 2
TotalLength : 20177864

Meshes      : 1
Nodes       : 112
Accessors   : 1934
BufferViews : 1938
Materials   : 1
Animations  : 3

Mesh'in POSITION verisini kullanan accessor bulundu:

POSITION accessor : 1
ComponentType     : 5126 (FLOAT)
Type              : VEC3
Count             : 29614

BufferView        : 1
ViewByteOffset    : 310388
ViewByteLength    : 355368

Boyut da beklenen değerle tam olarak eşleşiyordu:

29614 vertices
× 3 coordinates
× 4 bytes / float
-----------------
355368 bytes

5. Bee geometrisini tek noktaya çökertmek

GLB içerisindeki BIN chunk'ın başlangıç adresi hesaplandı:

jsonLength      = *(uint32_t *)(GLB + 12)
binHeaderOffset = 20 + jsonLength
binDataOffset   = binHeaderOffset + 8

POSITION verisinin gerçek dosya offset'i ise:

positionStart =
    binDataOffset
    + bufferView.byteOffset
    + accessor.byteOffset

Bu dosyada hesaplanan değerler:

BIN data starts : 397920
POSITION starts : 708308
POSITION bytes  : 355368

JSON, node yapısı, material, animation, index buffer ve diğer bütün veriler korundu. Yalnızca POSITION accessor'a ait 355368 byte sıfırlandı.

[Array]::Clear(
    $bytes,
    $positionStart,
    $positionBytes
)

Böylece 29614 vertex'in tamamı:

(0.0, 0.0, 0.0)

koordinatına taşındı. Dosya hâlâ orijinal Bee modelinin bütün yapısına sahipti, ancak mesh geometrisi tek bir noktaya çökmüştü.

Bee geometrisi kaldırılmış Microsoft 3D Viewer

Değiştirilmiş GLB tekrar Archive.zip içerisine yerleştirildiğinde başlangıç modeli görünmedi ve uygulama herhangi bir model yükleme hatası da üretmedi.

6. Deprecation banner

Bee problemi çözüldükten sonra ikinci hedef destek sonlandırma banner'ıydı.



resources.pri içerisinde banner'a ait localization resource'ları bulundu:
InfoBannerPromptAfterText
InfoBannerPromptBeforeText
InfoBannerDialogTitleAfter
InfoBannerDialogTitleBefore
InfoBannerDialogBodyAfter
InfoBannerDialogBodyBefore
InfoBannerDialogCloseButtonText

Mevcut banner metni InfoBannerPromptAfterText tarafından sağlanıyordu. Ancak resource string'i silmek yalnızca metni ortadan kaldıracaktı. Banner'ın kapladığı alan ve container ekranda kalacaktı.

7. 3DViewer.dll içerisinde banner kodu

Resource isimlerinin UTF-16 referansları 3DViewer.dll içerisinde bulundu. Banner metnini hazırlayan fonksiyonlardan biri 0x180EB59E0 adresindeydi.

180eb59e0  push   ...
...
180eb5a10  lea    0x1807cecb8,%rcx
           ; InfoBannerPromptBeforeText

           call   0x1809f4880
           mov    %rax,%rsi

180eb5a25  lea    0x1807ced08,%rcx
           ; InfoBannerPromptAfterText

           call   0x1809f4880
           mov    %rax,%rdi

...

180eb5a5d  test   %al,%al
180eb5a5f  cmove  %rdi,%rsi

180eb5a63  mov    $0x1,%r8b
180eb5a66  mov    %rsi,%rdx
180eb5a69  call   0x180dce7b0

180eb5a71  mov    0x3a0(%r14),%rdx
...
180eb5a9d  call   0x180ad3d40
           ret

Kod hem Before hem de After metnini yüklüyor, çalışma zamanındaki duruma göre birini seçiyor ve this + 0x3A0 alanındaki XAML nesnesine uyguluyordu.

Üst seviye initializer içerisindeki çağrı da bulundu:

180edd9e9  call  0x180e97aa0
180edd9f1  call  0x180eb5be0
180edd9f9  call  0x180eb5970
180edda01  call  0x180eb59e0

Son çağrı geçici olarak NOP ile değiştirildi:

VA          : 0x180EDDA01
File offset : 0xEDAA01

Original:
E8 DA 7F FD FF

Patched:
90 90 90 90 90

Test sonucu fonksiyonun görevi konusunda doğrudan doğrulama sağladı: banner metni kayboldu ancak kırmızı container ekranda kaldı.

8. Generated XAML

Bir sonraki adım compiled XAML tarafına indi. Uygulamada fiziksel XAML dosyaları bulunmuyordu. UI bağlantıları 3DViewer.dll içerisinde generated code olarak bulunuyordu.

İlgili Connect fonksiyonu 0x180EB4C40 adresindeydi:

180eb4c40  push   ...
180eb4c4b  mov    %rcx,%rdi
180eb4c4e  lea    -0x1(%rdx),%eax

           ; connectionId - 1

180eb4c51  cmp    $0x2b,%eax

           ; 43 connection IDs

180eb4c54  jae    0x180eb54aa
180eb4c5a  movslq %eax,%rdx

180eb4c5d  lea    0x18032e220,%rax

           ; jump table

180eb4c64  movslq (%rax,%rdx,4),%rdx
180eb4c68  add    %rax,%rdx
180eb4c6b  jmp    *%rdx

Bu klasik generated-XAML Connect(connectionId, target) dispatch yapısıydı.

Banner metninde kullanılan this + 0x3A0 member'ı connection ID 41 tarafından atanıyordu:

180eb5349  mov    %r8,%rcx
180eb534c  lea    0x18001f968,%rdx
180eb5353  call   *0x180749b20
180eb5359  mov    %rax,%rdx

180eb535c  lea    0x3a0(%rdi),%rcx
180eb5363  call   *0x180749af0

180eb5369  jmp    0x180eb54aa

Komşu XAML member'ları da aynı dispatch içerisinde görünüyordu:

Connection ID 39  → this + 0x390
Connection ID 40  → this + 0x398
Connection ID 41  → this + 0x3A0
Connection ID 42  → event-connected control
Connection ID 43  → event-connected control

Örneğin ID 40:

180eb5324  mov    %r8,%rcx
180eb5327  lea    0x18005e630,%rdx
180eb532e  call   *0x180749b20
180eb5334  mov    %rax,%rdx

180eb5337  lea    0x398(%rdi),%rcx
180eb533e  call   *0x180749af0

Bu aşamada banner'ın text element'i ile surrounding XAML controls arasındaki bağlantılar incelenebiliyordu. Ancak yapılmakta olan iş artık STL görüntülemek için gereken işin çok ötesine geçmişti.

9. Burada reverse engineering'i bırakmak

Başlangıç modeli için GLB vertex buffer değiştirilmiş, Store paketinin development copy'si oluşturulmuş, resources.pri incelenmiş, PE section adresleri hesaplanmış, 3DViewer.dll disassemble edilmiş ve generated XAML connection table'a kadar inilmiştir.

Ama ihtiyaç değişmemişti:

Open STL.
Rotate model.
Inspect geometry.

Bu nedenle 3D Viewer üzerinde çalışmaya devam etmek yerine aynı işi yapan küçük bir native Windows programı yazmak daha uygun hale geldi.

10. Native STL Viewer

Yeni uygulama C++17, Win32 API, Windows Common Controls ve OpenGL kullanılarak yazıldı.

STLViewer.exe
│
├── Win32
│   ├── HWND
│   ├── HMENU
│   └── message loop
│
├── comctl32.dll
│   ├── ToolbarWindow32
│   ├── SysTreeView32
│   ├── SysListView32
│   └── msctls_statusbar32
│
├── uxtheme.dll
│
├── WGL
│   └── OpenGL context
│
└── STL loader
    ├── Binary STL
    └── ASCII STL

Qt, Electron, CEF, WinUI, HTML/CSS veya .NET UI katmanı kullanılmadı. Viewport da ayrı bir native child HWND üzerinde çalışan WGL context'tir.

11. Binary STL parser

Binary STL formatı yeterince basit olduğu için ayrı bir model import framework'ü kullanılmadı.

80 bytes  header
 4 bytes  uint32 triangle count

for each triangle:

12 bytes  normal       (3 × float32)
12 bytes  vertex 1     (3 × float32)
12 bytes  vertex 2     (3 × float32)
12 bytes  vertex 3     (3 × float32)
 2 bytes  attribute

---------------------------------
50 bytes per triangle

Binary STL kontrolünde beklenen dosya büyüklüğü:

expected_size =
    84 + triangle_count * 50

ile gerçek dosya boyutu karşılaştırılıyor. Eşleşme yoksa parser ASCII STL okumayı deniyor.

12. İlk build

İlk build'de iki küçük Win32/CMake problemi çıktı.

Mouse koordinatlarında kullanılan:

GET_X_LPARAM
GET_Y_LPARAM

makroları için windowsx.h eklenmesi gerekiyordu:

#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>

İkinci problem manifest'in hem resource script hem de Visual Studio tarafından eklenmesiydi:

CVTRES : fatal error CVT1100:
duplicate resource. type:MANIFEST, name:1

LINK : fatal error LNK1123:
failure during conversion to COFF

Tekrarlanan manifest resource kaldırıldıktan sonra executable oluşturuldu.

STL Viewer ilk çalışan build

13. İlk STL testi

İlk gerçek test dosyası:

sadboys_mirror_battery_cover_mod.STL

Dosya binary STL olarak algılandı ve 8712 triangle sorunsuz yüklendi. Hesaplanan bounding dimensions:

X : 43.501 mm
Y : 25.244 mm
Z : 49.200 mm

Triangles : 8712
Format    : Binary STL
File size : 425 KB
STL Viewer v0.1 gerçek STL modeli

Version 0.1 bu aşamada binary ve ASCII STL açabiliyor, modeli OpenGL ile çizebiliyor, orbit ve zoom yapabiliyor, shaded/wireframe görünüm sunuyor, grid ve XYZ axes gösterebiliyor ve model boyutlarıyla triangle sayısını hesaplayabiliyor.

14. Arayüz

Arayüz için yeni bir UI framework kullanılmadı. Program geleneksel Windows desktop uygulaması olarak tutuldu.

Ana pencere ve bütün yardımcı kontroller Win32/Common Controls. Bu nedenle menü, TreeView, ListView, status bar ve pencere davranışı Windows tarafından çiziliyor.

Windows desktop UI tasarım referansı

Geliştirme sırasında hedeflenen arayüz yoğunluğu Windows 7 / Office 2010 dönemi desktop utility uygulamalarına yakın tutuldu: küçük toolbar ikonları, standart menüler, sol model/property paneli, büyük viewport ve status bar.

15. Mevcut durum

Proje şu anda ilk çalışan prototip aşamasında. Renderer bilinçli olarak legacy OpenGL immediate mode kullanıyor. Bu, ilk aşamada STL parser ve Win32 shell'i renderer optimizasyonundan bağımsız geliştirmeyi kolaylaştırıyor.

Sonraki adımlar arasında gerçek fit-to-view hesabı, pan, daha kontrollü orbit, splitter, toolbar image list ve daha sonra gerekirse VBO/VAO tabanlı renderer bulunuyor.

Programın kapsamı değişmeyecek:

No cloud.
No account.
No AI.
No telemetry.
No bees.

Opens STL files.

Sunday, September 20, 2026

Tüketici Elektroniğinde Kaliteye Ne Oldu?

Bu yazı aslında yeni bir pil şarj cihazı ararken başladı. Elimde uzun zamandır kullandığım bir Energizer Universal CHEUF vardı. Cihaz bir süre kullanılmadan durduktan sonra tekrar elime aldığımda garip davranmaya başlamıştı. Şarj edilebilir AA pilleri takıp adaptörü bağladığımda ekran açılıyor, kapanıyor, tekrar açılıyor ve cihaz sürekli reset atıyormuş gibi davranıyordu.

İlk bakışta arıza şarj cihazının kendisindeymiş gibi görünüyordu. Fakat birkaç ölçüm sonunda bizi 470 µF'lik sıradan bir elektrolitik kondansatörden 1950'lerin Japon kalite devrimine kadar götüren güzel bir hikâye çıktı.

Boşta 12 volt, yükte kaos

CHEUF'un harici adaptörü 12 V / 600 mA. İlk yapılacak şey doğal olarak adaptörü ölçmekti. Multimetreyle boşta ölçtüğümde çıkış gayet normaldi:

Adaptör nominal çıkışı : 12 V / 600 mA
Boşta ölçülen çıkış    : ~12 V

Dolayısıyla adaptör ilk bakışta sağlam görünüyordu. Fakat anahtarlamalı güç kaynaklarında boşta doğru gerilim görmek pek bir şey kanıtlamıyor. Kurumuş veya ESR değeri yükselmiş bir çıkış kondansatörü nedeniyle güç kaynağı yüksüzken 12 V gösterebilir, fakat birkaç yüz miliamper çekildiği anda regülasyon bozulabilir.

Bunu ayırmanın en kolay yolu şarj cihazını laboratuvar güç kaynağıyla beslemekti. 12 V verdiğimde CHEUF tamamen normal çalıştı.

2 adet pil ile giriş akımı : ~250 mA
4 adet pil ile giriş akımı : ~550 mA
Besleme                    : 12 V

Bu ölçüm önemliydi. Dört pil takıldığında cihaz yaklaşık 550 mA çekiyordu; yani 600 mA nominal adaptör kapasitesinin oldukça yakınına çıkıyordu. Laboratuvar kaynağında cihazın düzgün çalışması, şarj elektroniğini büyük ölçüde temize çıkarırken adaptörü doğrudan şüpheli durumuna getiriyordu.



Energizer Universal CHEUF. Arıza ilk bakışta şarj cihazındaymış gibi görünüyordu.

Adaptörü açınca suçlu zaten elini kaldırmıştı

Adaptörü açınca fazla ölçüm yapmaya gerek kalmadı. Sekonder taraftaki 470 µF elektrolitik kondansatör gözle görülecek kadar şişmişti. Üstteki emniyet yarığı dışarı doğru kubbeleşmişti. Kondansatör resmen "buradayım" diyordu.



Adaptörün sekonder tarafındaki şişmiş 470 µF elektrolitik kondansatör.

Arızanın davranışı da buna tam uyuyordu. Kondansatörün kapasitesi düşmüş veya ESR değeri yükselmişse çıkış filtresi artık yük değişimlerini karşılayamaz. CHEUF çalışmaya başlar, şarj devresi akım çekmeye başlar, 12 V hattı çöker, kontrol elektroniği reset olur. Reset olunca yük ortadan kalkar, adaptörün çıkışı tekrar yükselir ve cihaz yeniden çalışmaya teşebbüs eder.

             CHEUF çalışır
                   │
                   ▼
             Akım yükselir
                   │
                   ▼
          Adaptör çıkışı çöker
                   │
                   ▼
              MCU reset olur
                   │
                   ▼
               Yük azalır
                   │
                   ▼
            12 V geri gelir
                   │
                   └──────────────► tekrar

Ekranın sürekli açılıp kapanmasının nedeni buydu.

470 µF / 25 V ve cihaz tekrar hayatta

Eski kondansatörün yerine 470 µF / 25 V bir elektrolitik taktım. Kapasiteyi değiştirmedim; gerilim dayanımını ise 25 V seçtim. Fiziksel ölçüler uygunsa 12 V'luk bir hatta 25 V sınıfı kondansatör kullanmanın herhangi bir sakıncası yok.

Sonuç oldukça antiklimaktikti: adaptör düzeldi, CHEUF dört pille normal şekilde çalışmaya başladı.

Yani LCD, mikrodenetleyici, şarj kontrol elektroniği, güç transistörleri ve cihazın geri kalanı sağlamdı. Koca ürünün çalışmasını engelleyen şey tek bir elektrolitik kondansatördü.

İşte hikâyenin daha ilginç kısmı burada başlıyor.

Birkaç sentlik komponent neden bütün ürünü öldürüyor?

Bir tüketici elektroniği ürününü açıp üzerinde tanınmayan bir üreticinin elektrolitik kondansatörünü görmek artık şaşırtıcı değil. Fakat ürünün üzerinde Energizer gibi dünya çapında bilinen bir marka olduğunda insan ister istemez şu soruyu soruyor:

470 µF'lik kondansatörde birkaç sent tasarruf etmek gerçekten buna değer mi?

Burada hemen "planlı eskitme" sonucuna atlamak doğru olmaz. Bir üreticinin cihazı belirli bir tarihte bozulsun diye bilinçli olarak zayıf komponent seçtiğini söylemek için bundan çok daha fazla kanıt gerekir.

Fakat modern seri üretimde başka ve daha sıradan bir mekanizma var: minimum şartnameyi minimum maliyetle karşılayan BOM.

Büyük marka ürünü doğrudan kendi fabrikasında üretmek zorunda değil. Tasarım ve üretim bir OEM veya ODM firmasına yaptırılabilir. Şartnamede çıkış gerilimi, akım, güvenlik standartları, sıcaklık sınırları, garanti hedefi ve maliyet belirtilir. Eğer marka özellikle "şu üreticinin şu serisi kullanılacak" demiyorsa, fason üretici doğal olarak teknik şartları geçen daha ucuz komponentlere yönelebilir.

Bir kondansatörde 10 veya 20 cent önemsiz görünebilir. Bir milyon cihazda ise:

$0.10 × 1.000.000 = $100.000
$0.20 × 1.000.000 = $200.000

Satın alma departmanının Excel tablosunda bu tasarruf gayet görünürdür. Buna karşılık daha iyi kondansatör sayesinde cihazların sekiz veya on yıl sonra daha az bozulmasının şirkete sağlayacağı değer aynı tabloda kolay kolay görünmez.

Problem tam olarak burada ortaya çıkıyor. Mühendislik açısından soru:

Bu devreyi ne kadar güvenilir yapabiliriz?

iken finansal optimizasyon açısından soru kolaylıkla şuna dönüşebiliyor:

Şartnameyi ve garanti süresini geçen en ucuz tasarım hangisi?

İki soru birbirine benziyor ama aynı şey değil.

Peki 1980'lerin Japon cihazları neden farklı görünüyor?

1980'lerden veya 1990'ların başından kalma bir Sony, Panasonic, Pioneer, JVC, NEC veya Toshiba cihazını açınca elektronikle ilgilenen insanların sık fark ettiği bir şey var: kullanılan parçaların önemli bölümü bugün "premium" diye ayrıca aradığımız üreticilerden geliyor.

Nichicon, Nippon Chemi-Con, Rubycon, ELNA ve Matsushita elektrolitikler; Alps potansiyometreler ve anahtarlar; Omron röleler; Murata ve TDK pasifler... Bunlar dönemin Japon elektronik endüstrisinin kendi tedarik ekosisteminin parçalarıydı.



1980'ler Japon tüketici elektroniğinde kaliteli yerli komponent tedarik zinciri önemli bir avantajdı.

Bunun nedenlerinden biri Japon elektronik sektörünün son derece güçlü ve kısmen dikey entegre olmasıydı. Matsushita yalnızca televizyon veya müzik seti yapan bir şirket değildi; komponentlerden pillere kadar geniş bir üretim ekosistemine sahipti. NEC, Toshiba, Sony, Sanyo ve diğer büyük üreticiler de yarıiletken ve komponent tarafında önemli kabiliyetlere sahipti.

Dolayısıyla kaliteli Japon komponenti kullanmak, bugünkü bir ODM'nin katalogdan pahalı bir "premium alternatif" seçmesine her zaman benzemiyordu. Bu parçalar zaten yerel endüstriyel ekosistemin içindeydi.

Derating: 16 V çalışıyorsa neden 25 V kullanalım?

Eski cihazlarda görülen bir başka tasarım alışkanlığı da derating, yani komponenti katalogdaki maksimum sınırına dayamadan kullanmaktır.

Örneğin 12 V'luk bir hatta 16 V elektrolitik teknik olarak kullanılabilir:

12 V / 16 V = 0,75

Komponent nominal gerilim sınırının %75'inde çalışıyor.

Bu tek başına kötü tasarım anlamına gelmez. Elektrolitik kondansatörün ömrünü gerilimden başka sıcaklık, ripple akımı, ESR, seri özellikleri ve güç kaynağının gerçek çalışma koşulları da belirler. Ancak maliyet ve fiziksel hacim izin veriyorsa 25 V sınıfı kullanmak daha fazla gerilim marjı sağlar.

Aynı düşünce dirençte, transistörde, diyotta ve diğer komponentlerde de uygulanabilir. 0,25 W'lık direnci sürekli 0,24 W'ta çalıştırmamak; 100 V'luk transistöre sürekli 98 V darbe bindirmemek gibi.

Eski Japon cihazlarında bazen devreyi incelerken "Bunun burada bu kadar yüksek değerli olmasına ne gerek var?" dedirten parçaların bir kısmı tam olarak bu mühendislik marjının sonucudur.

Ve burada W. Edwards Deming çıkıyor karşımıza

Japon kalite kültürünün hikâyesinde ilginç bir isim var: William Edwards Deming. Daha da ilginci, Deming Japon değil Amerikalı bir istatistikçiydi.



W. Edwards Deming (1900–1993). Savaş sonrası Japonya'daki kalite yönetimi dönüşümünün önemli isimlerinden biri.

Deming 1950 yılında Japanese Union of Scientists and Engineers, yani JUSE tarafından Japonya'ya davet edildi. Mühendislere, yöneticilere ve araştırmacılara istatistiksel kalite kontrol yöntemleri üzerine dersler verdi. 1951'de JUSE onun katkılarını onurlandırmak ve kalite çalışmalarını teşvik etmek amacıyla Deming Prize'ı kurdu.

Buradaki temel fikirlerden biri son derece güçlüydü: kalite, üretim hattının sonunda kötü ürünleri ayıklamak değildir.

Diyelim ki bir fabrika 100.000 kondansatör üretiyor ve son kontrolde 2.000 tanesinin hatalı olduğunu buluyor. Klasik yaklaşım bu 2.000 parçayı ayırıp kalanları göndermektir.

İstatistiksel proses kontrolünün sorduğu daha önemli soru ise şudur:

Bu 2.000 parçayı hatalı üreten süreç neden böyle davranıyor?

Sıcaklık mı değişiyor? Elektrolit miktarı mı dağılıyor? Makine ayarı mı kayıyor? Hammadde partileri arasında fark mı var? Operasyonun varyansı nereden geliyor?

Amaç yalnızca hatayı yakalamak değil, hatayı üreten sistemi değiştirmek.

Kalite kontrol departmanının işi değildir

Deming'in yaklaşımının daha radikal tarafı kaliteyi yalnızca üretim hattındaki QC departmanına bırakmamasıydı. Tasarım, yönetim, tedarikçi ilişkileri, proses ve organizasyon birlikte ele alınmalıydı.

Japonya'da bu yaklaşım zamanla TQC/TQM uygulamaları ve kalite çemberleri gibi yöntemlerle daha geniş bir endüstriyel kültüre dönüştü. JUSE'nin kalite çemberleri tarihçesine göre üretim sahasındaki QC-circle hareketi 1962'de kurumsal biçimde örgütlenmeye başladı.

Bu bakış açısından maliyet azaltmanın yolu yalnızca:

470 µF kondansatör → 12 cent daha ucuz olanı kullan

değildir.

Mühendislik yoluyla maliyet azaltmak çok daha geniş bir alan açar:

PCB'deki parça sayısını azalt
Montaj operasyonlarını azalt
Fire oranını düşür
Test süresini kısalt
Ortak komponent kullanımını artır
Mekanik parçaları sadeleştir
Proses varyasyonunu azalt
Arıza ve garanti dönüşlerini azalt

Yani maliyeti düşürürken ürünün kritik komponentlerini aşağı doğru tıraşlamak zorunda değilsiniz. Hatta iyi mühendisliğin ilginç tarafı, bazen ürünü hem daha ucuz hem daha güvenilir yapabilmesidir.

Japonları da romantikleştirmemek gerekiyor

Buradan "1980'lerde Japonlar kusursuz cihaz yapıyordu" sonucu çıkarmak da doğru değil. Eski Japon elektroniklerini tamir eden herkes bazı kronik problemleri bilir.

Özellikle bazı dönemlerin SMD elektrolitik kondansatörleri bugün ciddi sızıntı problemleri çıkarabiliyor. Bazı cihazlarda kullanılan yapıştırıcılar yaşlandıkça korozif veya iletken hale gelebiliyor. Plastik dişliler çatlıyor, kayışlar eriyor, optik mekanizmalar yaşlanıyor.

Yani mesele Japon mühendislerinin hata yapmaması değildi.

Asıl farklardan biri, ürünün uzun vadeli güvenilirliğinin marka değerinin bir parçası olarak görülmesiydi.

On yıl çalışan cihaz reklamın kendisiydi

1980'lerde Sony, Panasonic veya Pioneer gibi markaların rekabet avantajlarından biri kalite algısıydı. Bir tüketici 1984'te aldığı televizyonu 1994'te hâlâ kullanıyorsa bunun zihnindeki karşılığı basitti:

Bu marka sağlam yapıyor.

Bu deneyim bir sonraki televizyonun, müzik setinin veya video cihazının satışına katkıda bulunuyordu. Uzun ürün ömrü yalnızca tüketiciye verilmiş fazladan bir hediye değildi; markanın itibarı için çalışan uzun vadeli bir reklamdı.

Bugünkü şirket yönetiminde ise BOM'dan çıkarılan 20 cent hemen ölçülebilirken, on yıl sonra hâlâ çalışan bir cihazın marka değerine katkısını aynı kesinlikle Excel hücresine yazmak çok daha zordur.

Satın alma masasında biraz mühendislik gerekebilir

Bu yüzden teknik satın alma yalnızca fiyat karşılaştırması olmamalı. Satın alma kararında en azından şu soruların cevabı bilinmeli:

Bu komponent devrede ne yapıyor?
Çalışma sıcaklığı ne?
Ripple akımı ne kadar?
ESR sınırı önemli mi?
Arızalandığında yalnız kendisi mi gider?
Arızası bütün ürünü kullanılamaz mı yapar?
Daha iyi parçanın gerçek maliyet farkı ne?
Bu fark ürünün toplam maliyetinin yüzde kaçı?

Her yere en pahalı komponenti koymak mühendislik değildir. Gereksiz overengineering de maliyet, hacim ve enerji açısından kötü tasarım olabilir.

Ama kritik noktadaki birkaç sentlik komponentin bütün cihazın ömrünü belirlemesine izin vermek de iyi optimizasyon değildir.

Tekrar o 470 µF'ye dönelim

Masadaki Energizer CHEUF şimdi tekrar çalışıyor. Adaptörün içindeki tek bir kondansatörü değiştirmek yeterli oldu:

Arıza       : Yük altında adaptörün çökmesi
Belirti      : LCD'nin sürekli açılıp kapanması
Adaptör      : 12 V / 600 mA
CHEUF akımı  : 2 pilde ~250 mA
               4 pilde ~550 mA
Arızalı parça: 470 µF elektrolitik
Yeni parça   : 470 µF / 25 V
Sonuç        : Normal çalışma

Elektronik tamirinin sevdiğim taraflarından biri de bu. Bazen küçük bir arızayı takip etmeye başlıyorsunuz ve devre size ürünün nasıl tasarlandığını, hangi maliyet kararlarının verildiğini ve hatta onu üreten endüstrinin kültürünü anlatmaya başlıyor.

Bu kez hikâye AA pil şarj cihazıyla başladı. Sonunda 1950'de Japonya'ya gidip istatistiksel kalite kontrolü anlatan Amerikalı bir istatistikçiye kadar ulaştı.

Ve bütün yolu açan şey, adaptörün köşesinde şişip bizi bekleyen 470 µF'lik bir kondansatördü.


Kaynak notu: W. Edwards Deming'in 1950'de JUSE davetiyle Japonya'da kalite kontrol dersleri vermesi ve Deming Prize'ın 1951'de kurulmasına ilişkin tarihsel bilgiler JUSE ve W. Edwards Deming Institute arşivleri esas alınarak kontrol edilmiştir.

Kaynaklar:
JUSE — Deming Prize / History of the Deming Prize
W. Edwards Deming Institute — Deming Timeline
JUSE — QC Circle History

Saturday, September 19, 2026

Çakılan access point’i loglamak: bölüm 2.

Bir önceki bölümün sonunda ASUS RP-N12'ye bir çeşit kara kutu takmıştık.

Cihazın kendi syslog'u RAM üzerinde tutulduğu için RP-N12 kilitlendiğinde veya yeniden başladığında asıl görmek istediğimiz kayıtların kaybolma ihtimali vardı. Bu nedenle Windows bilgisayarda UDP/514 dinleyen küçük bir Python programı kurduk. RP-N12 bütün kernel ve sistem mesajlarını ağ üzerinden bu bilgisayara göndermeye başladı.

Task Scheduler sayesinde logger Windows açıldığında otomatik başlıyor, bilgisayarın kendi zaman damgasını da her satıra ekliyordu.

Sonra yapılacak pek bir şey kalmamıştı.

RP-N12 çalışmaya devam edecekti.

Biz de bozulmasını bekleyecektik.

Kara kutunun ilk ciddi kaydı için fazla beklemek gerekmedi.


04:36 — Kernel Bellek Ayıramıyor

18 Eylül 2026, saat 04:36:45.

Syslog dosyasına bir anda uzun bir kernel dump'ı düşmeye başladı.

İçindeki en önemli satırlardan biri şuydu:

SLUB: Unable to allocate memory on node -1 (gfp=0x20)

cache: kmalloc-8192
object size: 8192
buffer size: 8192
default order: 3
min order: 1

node 0:
slabs: 95
objs: 269
free: 0

Ardından kernel mevcut belleğin durumunu da döktü:

free:50
slab_reclaimable:133
slab_unreclaimable:1435

Normal free:200kB
min:508kB
low:632kB
high:760kB

slab_reclaimable:532kB
slab_unreclaimable:5740kB

Ve buddy allocator'ın elindeki fiziksel sayfalar:

Normal:

26*4kB
0*8kB
4*16kB
1*32kB
0*64kB
0*128kB
0*256kB
0*512kB
0*1024kB
0*2048kB
0*4096kB

= 200kB

Bu artık RP-N12'nin “arada bir saçmalıyor” seviyesinde bir belirti değildi.

Linux kernel gerçekten bir bellek tahsis isteğini karşılayamamıştı.

Fakat burada hemen “RAM bitti” veya “memory leak bulduk” demek de doğru değildi.

Olay biraz daha ilginçti.


8 KB Ayıramayan Linux

Hatanın merkezinde şu cache vardı:

kmalloc-8192

Linux kernel'deki kmalloc() fiziksel kernel belleğinden küçük nesneler ayırmak için kullanılan temel mekanizmalardan biri.

kmalloc-8192 ise 8192 byte, yani 8 KiB büyüklüğündeki nesneler için kullanılan slab cache.

Kernel o anda 8 KiB'lık bir tahsis yapmaya çalışmış ve başarısız olmuştu.

8 KiB bugün kullandığımız bilgisayarlar açısından komik derecede küçük bir miktar.

Ama RP-N12'nin dünyasında durum biraz farklı.

Kernel dump'ının ilerleyen satırlarında şunu görüyoruz:

4096 pages RAM
646 pages reserved

MIPS kernel'imizde sayfa boyutu 4096 byte.

Dolayısıyla fiziksel RAM:

4096 × 4096
= 16,777,216 byte
= 16 MiB

RP-N12'nin bütün fiziksel belleği yalnızca 16 MiB.

Bunun da tamamını Linux kullanamıyor.

Canlı sistemde /proc/meminfo:

MemTotal: 13864 kB

gösteriyordu.

Yani kernel, ayrılmış fiziksel bölgeler ve diğer düşük seviye ihtiyaçlardan sonra Linux'un hesapladığı kullanılabilir RAM yaklaşık 13.5 MiB.

Ve bu küçücük alanın içinde Linux kernel, Wi-Fi sürücüsü, Ethernet, network stack, web arayüzü, dnsmasq, watchdog, syslog, telnet ve geri kalan her şey birlikte yaşıyor.


MemFree Neden Tek Başına Yeterli Değil?

Olaydan önce yaptığımız ölçümlerde şuna benzer değerler görüyorduk:

MemTotal:       13864 kB
MemFree:         1244 kB
Buffers:         1184 kB
Cached:          2556 kB
Slab:            4892 kB
SReclaimable:     532 kB
SUnreclaim:      4360 kB

İlk bakışta:

“Sadece 1.2 MB RAM kalmış.”

demek mümkün.

Ama Linux belleği böyle okunmuyor.

Boş RAM'in bir kısmını cache olarak kullanmak Linux için normal. Gerektiğinde bazı cache'ler geri alınabilir.

Bu nedenle bizi asıl ilgilendiren başka bir alan vardı:

Slab

Slab allocator kernel'in sürekli oluşturup yok ettiği küçük nesneleri verimli biçimde yönetmek için kullandığı bellek sistemi.

Örneğin dosya sistemi yapıları, network yapıları, socket'ler, çeşitli kernel objeleri ve kmalloc cache'leri burada bulunabiliyor.

Slab belleğinin iki önemli kısmı var:

SReclaimable
SUnreclaim

SReclaimable, kernel'in gerektiğinde geri kazanabileceği slab belleği.

SUnreclaim ise kolayca geri alınamayan aktif kernel nesneleri.

Bizim ilk baseline ölçümlerimiz:

SUnreclaim: 4332 kB
SUnreclaim: 4360 kB

civarındaydı.

Bellek tahsis hatası anında kernel'in verdiği değer ise:

slab_unreclaimable:5740kB

oldu.

Yaklaşık 1.4 MiB artış.

16 MiB fiziksel RAM'li bir sistem için 1.4 MiB küçük bir değişiklik değil.

Üstelik hata anında toplam unreclaimable slab yaklaşık 5.7 MiB olmuştu.

Fiziksel RAM'in üçte birinden fazlası.


Memory Leak mi Bulduk?

Henüz hayır.

Bu noktada ilk güçlü şüphemiz doğal olarak kernel veya Wi-Fi driver tarafında bir slab leak olmasıydı.

Fakat birkaç saat sonra tekrar /proc/meminfo aldığımızda:

MemFree:             948 kB
Buffers:            1172 kB
Cached:             2760 kB

Slab:               5016 kB
SReclaimable:        544 kB
SUnreclaim:         4472 kB

gördük.

Yani hata anında:

5740 kB

olan SUnreclaim, daha sonra:

4472 kB

seviyesine geri inmişti.

Bu önemli.

Eğer bütün artış kalıcı bir leak olsaydı belleğin geri dönmemesini beklerdik.

Dolayısıyla şu aşamada daha doğru ifade:

RP-N12 ciddi bir kernel memory-pressure olayı yaşadı. Unreclaimable slab belirgin biçimde yükseldi ve kernel bir bellek tahsisini gerçekleştiremedi. Ancak elimizde henüz monoton biçimde büyüyen kalıcı bir memory leak kanıtı yok.

Fragmentation da olayın bir parçası olabilir.


RAM Var Ama Uygun RAM Yok

Kernel dump'ındaki en öğretici satırlardan biri buydu:

Normal: 26*4kB 0*8kB 4*16kB 1*32kB ...

Burada Linux buddy allocator'ın o anda elindeki serbest fiziksel blokları görüyoruz.

Toplam:

200 kB

serbest alan bulunuyor.

Ama dağılıma dikkat:

26 × 4 KB
0  × 8 KB
4  × 16 KB
1  × 32 KB

Bellek yalnızca toplam miktardan ibaret değil.

Kernel bazı tahsislerde belirli büyüklükte fiziksel olarak uygun ve contiguous sayfalara ihtiyaç duyuyor.

Logda ayrıca:

page allocation failure. order:1

mesajı da bulunuyordu.

4 KiB sayfalı sistemde order:1 iki ardışık sayfa, yani 8 KiB'lık fiziksel blok anlamına gelir.

Tam da kmalloc-8192 olayımızın büyüklüğü.

Bu yüzden:

“200 KB boş RAM vardı; 8 KB'ı nasıl ayıramadı?”

sorusu ilk bakışta mantıklı görünse de doğru soru değil.

Doğru soru:

“İstenen biçimde uygun fiziksel sayfalar mevcut muydu?”

Kernel'in cevabı o anda hayırdı.


16 MB RAM'den Nasıl Gigabaytlarca Veri Geçiyor?

Bu noktada başka bir soru ortaya çıktı.

RP-N12'nin yalnızca 16 MiB RAM'i varsa televizyona saatlerce video nasıl taşıyabiliyor?

Canlı /proc/net/dev sayacı bize Ethernet tarafında şunu göstermişti:

eth2 RX bytes:
2427196843

Yaklaşık 2.43 GB veri yalnızca o uptime sırasında Ethernet'ten geçmişti.

Aynı zamanda Wi-Fi tarafında:

ra0 TX bytes:
2368736948

yaklaşık 2.37 GB gönderilmişti.

Bu değerler AP'nin yaptığı işi çok güzel gösteriyor:

Ethernet
   |
   | ~2.4 GB
   v
RP-N12
   |
   | ~2.37 GB
   v
Wi-Fi

Peki 16 MB RAM'den 2.4 GB nasıl geçiyor?

Çünkü RAM bir depo değil.

Bir çalışma alanı.


Paket Fabrikası

Klasik Ethernet MTU'su çoğu ağda:

1500 byte

civarında.

Bir byte 8 bit olduğuna göre:

1500 × 8 = 12000 bit

Örneğin 20 Mbit/s trafik için kaba hesap:

20,000,000 / 12,000
≈ 1667 paket/saniye

Gerçek TCP/IP/Ethernet hesabında header'lar, Wi-Fi overhead'i, ACK'ler, retransmission, aggregation ve başka ayrıntılar var; bu yalnızca ölçeği görmek için kaba bir hesap.

Ama temel fikir değişmiyor.

Paket geliyor.

Bir RX buffer'a giriyor.

Network stack paketi işliyor.

Bridge paketi Wi-Fi arayüzüne yönlendiriyor.

TX descriptor hazırlanıyor.

Wi-Fi donanımı paketi gönderiyor.

Buffer serbest bırakılıyor veya tekrar kullanılıyor.

Sonra aynı bellek başka paket için kullanılıyor.

          +-------- RAM --------+
          |                     |
Ethernet -> RX -> bridge -> TX -+-> Wi-Fi
          |                     |
          +---- buffer reuse ---+

RP-N12'nin RAM'i videonun tamamını saklamıyor.

Verinin çok küçük bir bölümünü herhangi bir anda taşıyor.

Bir otoyoldan günde on bin otomobil geçmesi için otoyolun aynı anda on bin otomobili tutabilmesi gerekmediği gibi.


Buffer Gerçekten Büyümeye Başlarsa?

Asıl sorun giriş ve çıkış hızları birbirinden ayrıldığında oluşuyor.

Kabaca:

Queue growth = Rin - Rout

Örneğin Ethernet tarafından:

80 Mbit/s

geliyor fakat Wi-Fi o anda yalnızca:

30 Mbit/s

aktarabiliyorsa fark:

50 Mbit/s

olur.

Bu:

6.25 MB/s

buffer büyüme hızı demek.

Sadece 100 ms böyle devam etse:

625 KB

queue oluşabilir.

16 MiB RAM'li bir cihazda bu artık ihmal edilebilir bir miktar değil.

Gerçekte network stack, queue limitleri, packet drop ve TCP congestion control gibi mekanizmalar bu büyümenin sonsuza kadar sürmesine izin vermez.

Ama bu hesap küçük RAM'li bir AP'de network buffer yönetiminin neden önemli olduğunu gösteriyor.


Netflix 4K Çalışıyor Ama Remote Desktop Sürünüyor

RP-N12 üzerinde yaptığımız başka bir deney de bu noktada anlam kazandı.

LG OLED televizyon üzerinden bilgisayara Remote Desktop bağlandığımda performans son derece kötüydü.

Fakat aynı televizyon aynı AP üzerinden yüksek kaliteli video streaming yapabiliyordu.

İlk bakışta:

“4K video çalışıyorsa masaüstü neden çalışmıyor?”

sorusu oldukça mantıklı.

Fakat ağ açısından bu iki uygulama çok farklı.

Streaming uygulaması gelecekte kullanacağı veriyi önceden indirebilir.

Kabaca:

NETWORK ------------> BUFFER -----------> VIDEO
                       ###########
                             ^
                          playback

Wi-Fi 200 ms kötüleşirse televizyon buffer'daki videoyu oynatmaya devam eder.

Kullanıcı hiçbir şey fark etmeyebilir.

Remote Desktop'ta ise gelecek önceden indirilemez.

Mouse'u hareket ettirmeden PC gelecekte hangi ekranı üretmesi gerektiğini bilmiyor.

Akış şöyle:

INPUT
  |
  v
TV
  |
  v
Wi-Fi
  |
  v
PC
  |
  +-- masaüstünü değiştir
  +-- görüntüyü üret
  +-- encode et
  |
  v
NETWORK
  |
  v
Wi-Fi
  |
  v
TV
  |
  +-- decode
  +-- display

Bu zincirdeki her gecikme kullanıcının parmağı ile ekrandaki tepki arasına ekleniyor.

Bu nedenle streaming için esas sorunlardan biri yeterli ortalama throughput iken interaktif Remote Desktop için:

latency, jitter, retransmission ve packet loss

çok daha görünür hale geliyor.

Dolayısıyla paradoksal görünen şu durum gayet mümkün:

20 Mbit/s'lik buffered bir video akışı, 5 Mbit/s'lik interaktif Remote Desktop oturumundan kullanıcı açısından çok daha sorunsuz olabilir.

Bilgisayar grafikleri tarafından düşünürsek bunu şöyle hayal etmek mümkün:

Streaming, önceden hazırlanmış karelerin oynatılmasına benziyor.

Remote Desktop ise her input'tan sonra yeni görüntünün gerçek zamanlı üretilmesini gerektiriyor.

Bu yüzden yeni bir access point'ten beklediğimiz kazanç yalnızca “daha fazla Mbps” olmayacak.

Asıl fark:

daha düşük latency
daha düşük jitter
daha az retransmission
daha verimli airtime
daha güçlü CPU/RAM

tarafında olacak.


16:12 — Kara Kutu Tekrar Konuşuyor

Memory allocation failure'dan yaklaşık on bir buçuk saat sonra syslog'a iki yeni kernel mesajı düştü.

2026-09-18 16:12:34.678
kernel: 0x60330030=0x92e8ff

2026-09-18 16:12:34.685
kernel: 0x6033001C=0xffff

Hepsi bu.

Öncesinde açıklayıcı bir hata mesajı yok.

Sonrasında stack trace yok.

İki register adresi.

İki hexadecimal değer.

İlk bakışta bunların hangi subsystem'e ait olduğunu bile bilmiyorduk.

Bu noktada RP-N12'nin firmware'ine inmeye karar verdik.


ASUS Hâlâ Kaynak Kodu Tutuyormuş

RP-N12 için ASUS'un yayınladığı GPL source paketini bulduk.

Paket firmware 1.0.1.0g dönemine ait.

Bizim cihazdaki daha yeni firmware'in birebir source'u değil; dolayısıyla bunu önemli bir sınırlama olarak akılda tutmak gerekiyor.

Arşiv açıldığında karşımıza klasik bir embedded Linux SDK ağacı çıktı:

linux-2.6.36.x/
user/
vendors/
tools/
lib/
config/
prebuild_romfs/

Kernel:

Linux 2.6.36

Board configuration ise kullandığımız platformu açıkça gösteriyordu:

CONFIG_RALINK_MT7628=y
CONFIG_MT7628_ASIC=y

CONFIG_FIRST_IF_MT7628=y
CONFIG_RT_FIRST_CARD=7628

CONFIG_RT2880_DRAM_16M=y
CONFIG_RALINK_RAM_SIZE=16

Böylece runtime'da hesapladığımız 16 MiB RAM, ASUS'un build configuration'ıyla da doğrulanmış oldu.

Fakat asıl aradığımız şey:

0x60330030
0x6033001C

idi.

Kaynak kod içerisinde bu adresler doğrudan bulunamadı.

Sebebi kısa süre sonra ortaya çıktı.


Wi-Fi Driver'ın Source'u Yok

ASUS GPL paketinde Ralink/MediaTek Wi-Fi driver'ının kaynak kodu bulunmuyordu.

Build sistemi ayrı bir dizine referans veriyordu:

../NonGPL/drivers

Dağıtılan pakette bu dizin yoktu.

Fakat firmware'in hazırlanabilmesi için önceden derlenmiş kernel modülü bırakılmıştı:

prebuild_romfs/usr/lib/mt_wifi.ko

Yaklaşık 1.6 MB'lık MIPS kernel modülü.

Ve şansımıza tamamen strip edilmemişti.

İçerisinde hâlâ:

function symbols
debug strings
relocations

bulunuyordu.

Dolayısıyla source code yoktu ama binary bize hâlâ oldukça fazla şey anlatabilirdi.


mt_wifi.ko'yu Disassemble Etmek

İlk olarak binary'nin string tablosunda gizemli adresleri aradık.

Ve birebir bulduk:

0x60330030=0x%x
0x6033001C=0x%x

Bu önemli bir dönüm noktasıydı.

Artık syslog'daki iki satırın:

MediaTek/Ralink Wi-Fi driver'ı mt_wifi.ko

tarafından üretildiğini biliyorduk.

ELF relocation bilgileri sayesinde bu stringleri kullanan fonksiyonu da bulabildik:

SetTxRxCr_Proc()

Fonksiyon bir string parametre alıyor.

İlgili kod yolu:

SetTxRxCr_Proc(pAd, "0")

şeklinde çağrılıyor.

Fonksiyon "0" modunda çalıştığında iki register'ı okuyup kernel loguna yazıyor:

0x60330030
0x6033001C

Tam olarak kara kutunun kaydettiği iki satır.


Peki SetTxRxCr_Proc() Neden Çağrılmış?

Bir sonraki adım çağıran fonksiyonu bulmaktı.

Relocation/call ilişkisini geriye doğru takip ettiğimizde karşımıza şu çıktı:

APCheckBcnQHandler()

İsim oldukça açıklayıcı.

Bcn, driver içerisinde beacon için kullanılan kısaltma.

Wi-Fi access point'ler düzenli aralıklarla beacon frame yayınlar. Bu frame'ler ağın varlığını, SSID'yi ve çeşitli kablosuz ağ parametrelerini duyuran temel 802.11 management frame'leridir.

Aynı binary içinde ilgili kod çevresinde şu debug stringleri bulunuyordu:

bcn_flush too long!

flush all stuck bcn too long!!

Ayrıca:

DumpBcnQMessage
PSEWatchDog
MonitorTxBcnRxPse

gibi semboller de aynı driver'da bulunuyor.

Artık iki hexadecimal register satırı çok daha anlamlı hale gelmişti.

Bunlar rastgele debug çıktıları değildi.

Access point'in beacon/TX queue sağlık kontrolü sırasında driver tarafından alınmış bir donanım register snapshot'ıydı.


Binary'den Kaynak Koda Doğru

APCheckBcnQHandler()'ın MIPS assembly'sini takip ettiğimizde içeride bir problem/recovery sayacı bulunduğunu gördük.

Tam kaynak kod elimizde olmadığı için değişken isimlerini bilmiyoruz; dolayısıyla aşağıdaki gerçek ASUS/MediaTek source'u değil, assembly'nin davranışını açıklamak için yazılmış pseudo-code:

if (beacon_problem_condition) {

    problem_counter++;

    ...

    if (problem_counter == 7) {
        SetTxRxCr_Proc(pAd, "0");
    }

    ...

    if (problem_counter > 10) {
        DumpBcnQMessage(...);
        problem_counter = 0;
    }
}

Buradaki en önemli bilinmeyen hâlâ:

beacon_problem_condition

Yani driver tam olarak ne gördüğünde bu sayacı artırıyor?

Beacon queue zamanında boşalmıyor mu?

PSE tarafında paket mi sıkışıyor?

DMA descriptor ilerlemiyor mu?

TX tarafındaki bir hardware counter mı değişmiyor?

Bunu henüz kesin olarak çözmedik.

Fakat 16:12 mesajının kaynağı artık oldukça iyi tanımlanmış durumda:

Wi-Fi subsystem
       |
       v
APCheckBcnQHandler()
       |
       v
problem/recovery counter
       |
       v
SetTxRxCr_Proc("0")
       |
       +-- 0x60330030
       |
       +-- 0x6033001C

Ve cihazımızın bıraktığı değerler:

0x60330030 = 0x92e8ff
0x6033001C = 0xffff

Driver'ın İçinde Daha Büyük Bir Diagnostic Mekanizması Var

SetTxRxCr_Proc() fonksiyonunu incelerken başka bir şey daha ortaya çıktı.

Fonksiyon yalnızca bu iki register'ı okuyabilmiyor.

Binary içerisinde:

RX Status Counter

0x6020410C
0x60204110
0x60204114
0x6020411C
0x60204120

ve:

CCA Status

0x60000024
0x60120118
0x60130110
0x60130114
0x60130118
0x60130120
0x60130124
0x60130128

gibi daha geniş diagnostic dump stringleri de bulunuyor.

Yani driver'ın kendi içinde TX/RX, RX status ve CCA tarafını incelemek için hazırlanmış bir debug mekanizması mevcut.

Bizim olayda kısa "0" modu çağrıldığı için yalnızca iki register görüyoruz.

Bu da ileride ilginç bir araştırma alanı açıyor:

Driver'ın mevcut diagnostic fonksiyonlarını firmware'i değiştirmeden tetikleyebilir miyiz?

Fakat şimdilik çalışan deney düzeneğine dokunmuyoruz.

Çünkü kara kutunun asıl amacı hâlâ aynı.

Gerçek kilitlenmeyi doğal haliyle yakalamak.


Sabahki Memory Failure ile Öğleden Sonraki Wi-Fi Olayı Bağlantılı mı?

Şu anda bilmiyoruz.

Ve bence bu deneyin en önemli taraflarından biri de burada.

Elimizde iki gerçek gözlem var:

04:36:45
|
+-- page allocation failure
+-- kmalloc-8192
+-- free = 200 kB
+-- SUnreclaim = 5740 kB


       yaklaşık 11.5 saat


16:12:34
|
+-- APCheckBcnQHandler
+-- SetTxRxCr_Proc("0")
+-- 0x60330030 = 0x92e8ff
+-- 0x6033001C = 0xffff

Bunlardan hareketle birkaç hipotez kurulabilir.

Birinci ihtimal: Tamamen bağımsız iki olay.

16 MB RAM'li eski cihaz zaman zaman memory pressure yaşıyor, MediaTek Wi-Fi driver'ı da bağımsız olarak beacon queue recovery mekanizmasını çalıştırıyor olabilir.

İkinci ihtimal: Bellek baskısı Wi-Fi driver'ın zamanlamasını veya queue yönetimini etkiliyor olabilir.

Üçüncü ihtimal ise daha ilginç: Wi-Fi driver'daki TX/RX/PSE/buffer davranışı kernel slab kullanımını artırıyor olabilir.

Örneğin network nesneleri veya driver buffer'ları zamanında serbest kalmıyorsa önce slab büyür, ardından allocator baskı altına girebilir.

Ama bunların hiçbiri şu anda sonuç değil.

Yalnızca test edilebilir hipotezler.

Bir sonraki olayda aynı anda:

SUnreclaim
kmalloc-8192
kmalloc-4096
kmalloc-2048
buddyinfo
Wi-Fi register dump

değerlerini yakalayabilirsek tablo çok daha netleşecek.


Küçük Bir Cihaz, Oldukça Büyük Bir Yazılım Yığını

RP-N12'ye dışarıdan bakınca yaptığı iş çok basit görünüyor.

Ethernet'ten paket al.

Wi-Fi'dan gönder.

Fakat cihazın içinde:

Linux kernel
      |
      +-- SLUB allocator
      +-- buddy allocator
      +-- network stack
      +-- bridge
      +-- Ethernet driver
      |
      +-- MediaTek Wi-Fi driver
               |
               +-- DMA
               +-- TX/RX rings
               +-- PSE
               +-- beacon queues
               +-- watchdog/recovery

çalışıyor.

Ve bunların tamamının paylaşabileceği fiziksel RAM:

16 MiB.

Belki RP-N12'nin yaşadığı problem tek bir “bug” bile değildir.

Eski kernel, küçük RAM, eski vendor driver'ı, yoğun network trafiği ve zaman zaman oluşan fragmentation birlikte cihazı sınırda çalıştırıyor olabilir.

Bunu henüz bilmiyoruz.

Ama ilk bölümde kurduğumuz kara kutu sayesinde artık tahmin etmek zorunda değiliz.

Cihaz bize kendi içinden veri veriyor.


Kara Kutu Çalışmaya Devam Ediyor

İlk ciddi kayıtta Linux'un bellek ayıramadığını gördük.

İkinci ilginç kayıtta Wi-Fi driver'ın beacon/TX tarafındaki recovery mekanizmasına ulaştık.

Sonra ASUS'un yıllar önce yayınladığı kaynak paketini açıp 1.6 MB'lık MIPS kernel modülünü disassemble ederek syslog'daki iki anlamsız hexadecimal satırın hangi fonksiyondan çıktığını bulduk.

Ama aradığımız esas olay henüz gerçekleşmedi.

RP-N12 henüz tamamen kilitlenmedi.

Dolayısıyla deney devam ediyor.

Bir gün ping cevap vermeyi bıraktığında asıl bakacağımız şey cihazın öldüğü an olmayacak.

Ondan önceki birkaç saniye ve dakika olacak.

Kernel belleği ne durumdaydı?

Slab büyüyor muydu?

Beacon queue tekrar takılmış mıydı?

MediaTek recovery mekanizması çalışmış mıydı?

Ethernet hâlâ paket geçiriyor muydu?

Watchdog bir şey fark etmiş miydi?

Kara kutuyu kurmamızın nedeni tam olarak buydu.

Part 1'de RP-N12'yi dinlemeye başladık.

Part 2'de cihaz ilk kez gerçekten konuştu.

Bir sonraki bölümde ise tornavidayı alıyoruz.


ASUS RP-N12 · Embedded Linux · Linux 2.6.36 · MediaTek/Ralink · MT7628 · SLUB · kmalloc · Memory Pressure · Wi-Fi · Beacon Queue · MIPS · Reverse Engineering · Remote Syslog

Friday, September 18, 2026

Çakılan access point’i loglamak.

Ev ağında LAN/Ethernet üzerinden ağa bağlı bir access point olarak ASUS RP-N12 kullanıyorum. 2.4 GHz 802.11n döneminden kalma, mütevazı bir cihaz. Bu AP'nin hızı mobil cihazlarıma ve 4k dahil Netflix gibi video streaminge yetiyor fakat sorunum hız değil stabilite.

Cihaz bazen günlerce hiçbir sorun çıkarmadan çalışıyor, bazen de ağ trafiği ve kendi arayüzüne ulaşılamayacak şekilde tam kilitleniyor. Kilitlenince kapatıp açmak (power cycle) gerekiyor, sırf bu sorun yüzünden yeni access point araştırırken AP'nin remote logunu PC'ye yazacak şekilde konfigüre edip bir sonraki kilitlenmeyi beklemeye karar verdim.


Önce İçeride Ne Çalıştığına Bakalım

RP-N12'nin web arayüzünde bir sistem günlüğü bulunuyor. İlk bakışta cihazın oldukça eski bir embedded Linux sistemi kullandığı görülüyor:

Jan  1 00:00:09 kernel: klogd started: BusyBox v1.12.1
init_r4k_clocksource...
Ralink gpio driver initialized
flash manufacture id c2 device 20 16
MX25L3205D ... 4096 Kbytes
Ralink APSoC Ethernet Driver Initialization v3.1
Raeth v3.1 (Tasklet)
RT305x_ESW: Link Status Changed

Firmware BusyBox 1.12.1 tabanlı. Ethernet tarafında Ralink'in Raeth sürücüsü ve RT305x_ESW switch sürücüsü kullanılıyor. Flash ise Macronix MX25L3205D; kapasitesi yalnızca 4 MB.

Elimizde kaynakların son derece sınırlı olduğu eski nesil bir embedded Linux sistem var. Bu da işi daha zevkli hale getiriyor açıkçası :P

Logda görülen MTD partition boundary uyarıları da dikkat çekici fakat tek başına bunları arızanın göstergesi saymak doğru olmaz. Firmware'in flash partition düzeninden kaynaklanan normal boot mesajları da olabilirler.

Problem: Cihaz Kilitlendiğinde Kendi Loguna da Ulaşılamıyor

Web arayüzündeki log normal çalışma sırasında faydalı. Fakat cihaz tamamen kilitlendiğinde web arayüzünün de erişilemez hale geliyor.

Dolayısıyla logu RP-N12'nin dışına çıkarmam gerekiyor.

Fakat önce cihazın içine biraz daha yakından bakmak istedim.


Telnet'i Açmak

RP-N12'nin Administration → System bölümünde Enable Telnet seçeneği bulunuyor.

Telnet'i geçici olarak açtıktan sonra cihazın IP adresine bağlandım:

192.168.1.100

İlk denemeyi Windows'un kendi Telnet istemcisiyle yaptım. Çalışıyor fakat scrollback ve terminal davranışı pek kullanışlı değil. Özellikle uzun çıktıları incelemek için kısa sürede PuTTY'ye geçtim.

PuTTY'de ayrıca All session output logging açarak yaptığım bütün işlemleri PC tarafında kaydetmeye başladım.

Side quest: NTP sorunu, Tarih 2011'de Kalmış

Shell'e girdikten sonra ilk dikkat çeken şey cihazın saatiydi:

# date
Sat Jan 1 00:39:08 UTC 2011

Web arayüzünde NTP sunucusu olarak pool.ntp.org tanımlıydı fakat cihaz saati bir türlü güncelleyemiyordu.

İlk şüpheli doğal olarak NTP idi. Fakat biraz kurcalayınca problemin NTP ile ilgili olmadığını gördüm.

# cat /etc/resolv.conf

# route -n
192.168.1.0  0.0.0.0  255.255.255.0  U  br0
127.0.0.0    0.0.0.0  255.0.0.0      U  lo
0.0.0.0      0.0.0.0  0.0.0.0        U  br0

/etc/resolv.conf tamamen boştu.

Daha önemlisi cihazın geçerli bir default gateway'i yoktu.

ping sonucu bunu doğruladı:

# ping -c 3 1.1.1.1
100% packet loss

# ping -c 3 pool.ntp.org
ping: bad address 'pool.ntp.org'

Burada ilginç bir ayrım var. RP-N12 access point/bridge olarak istemcilerin Ethernet trafiğini gayet güzel taşıyabiliyor. Wi-Fi istemcileri ana router üzerinden internete çıkabiliyor.

Fakat RP-N12'nin kendi Linux işletim sistemi internete çıkamıyor.

İlgili NVRAM değerlerine baktığımda tablo şöyleydi:

lan_gateway=
wan_pppoe_gateway=
wan_gateway=
dhcp_dns1_x=
wan_dns=

lan_netmask=255.255.255.0
lan_dhcp=0
lan_hostname=RP-N12
lan_ipaddr=192.168.1.100
lan_ifname=br0
lan_proto_x=0

RP-N12 192.168.1.100 statik IP adresiyle çalışıyor fakat cihazın kendi management plane'i için gateway ve DNS bilgileri oluşmamış.

NVRAM'i hemen değiştirmek istemedim. Sonuçta amacım cihazı tamir etmeden önce mevcut haliyle neden kilitlendiğini gözlemlemekti.

Bu nedenle yalnız RAM'de geçerli olacak geçici bir route ve DNS tanımladım:

Not: Bu iki değişikliği NVRAM'e yazmadım. Dolayısıyla RP-N12 reboot ederse default route ve /etc/resolv.conf değişiklikleri kaybolacak. Remote syslog bundan bağımsız; RP-N12 ile log bilgisayarı aynı 192.168.1.0/24 ağında olduğundan 192.168.1.108 adresine UDP/514 göndermek için gateway veya DNS gerekmiyor.

# route add default gw 192.168.1.1 br0
# echo "nameserver 192.168.1.1" > /etc/resolv.conf

Sonuç:

# ping -c 3 192.168.1.1
0% packet loss

# ping -c 3 1.1.1.1
0% packet loss

# ping -c 3 pool.ntp.org
0% packet loss

pool.ntp.org artık çözülüyor ve internete erişiliyordu.

Bir süre sonra firmware'in kendi NTP mekanizması da devreye girdi:

# date
Thu Sep 17 21:52:11 UTC 2026

Yani cihazın NTP sunucusuna ulaşacak yolu yoktu.

Bu arada eski firmware'in İstanbul zaman dilimi kaydı da güncel değildi. Türkiye artık yıl boyunca UTC+3 kullandığı için aynı UTC offset'ine sahip ve DST uygulamayan (GMT+03:00) Nairobi seçeneğini kullandım.


RAM Ne Durumda?

Kilitlenmenin nedenlerinden biri memory leak olabilir. Bu kadar az RAM'li bir sistemde küçük bir kernel/driver sızıntısı bile uzun uptime sonunda önemli hale gelebilir.

İlk ölçüm:

MemTotal:       13864 kB
MemFree:          976 kB
Buffers:         1104 kB
Cached:          2844 kB
Slab:            4884 kB
SReclaimable:     552 kB
SUnreclaim:      4332 kB
SwapTotal:          0 kB

Bir süre sonraki ikinci ölçüm:

MemTotal:       13864 kB
MemFree:         1244 kB
Buffers:         1184 kB
Cached:          2556 kB
Slab:            4892 kB
SReclaimable:     532 kB
SUnreclaim:      4360 kB
SwapTotal:          0 kB

Burada yalnızca MemFree değerine bakıp “RAM bitmiş” demek yanlış olur. Linux boş RAM'i cache için kullanır ve gerektiğinde bunun bir kısmını geri alabilir.

Daha ilginç değer SUnreclaim. Yaklaşık 14 MB kullanılabilir belleğin 4.3 MB kadarı reclaim edilemeyen slab durumunda.

Bu tek başına memory leak kanıtı değil.

Fakat cihaz haftalar boyunca çalışırken örneğin 4.3 MB → 5 MB → 6 MB → 8 MB şeklinde sürekli yükselen bir değer görürsek kernel veya driver tarafındaki bir sızıntı ciddi şüpheli haline gelir.

Ethernet ve Wi-Fi Sayaçları

Arıza Ethernet switch/PHY veya Wi-Fi sürücüsü tarafında da olabilir. Bunun için interface sayaçlarını da baseline olarak kaydettim.

eth2:
RX errors 0
TX errors 0
carrier 0
collisions 0

ra0:
RX errors 98
TX errors 0

Daha sonraki ölçümde Wi-Fi RX error sayısı 98'den 101'e çıktı. 2.4 GHz ortamında bu seviyedeki birkaç hata tek başına anlamlı değil.

Fakat cihaz kilitlenmeden hemen önce bu sayaç hızla yükselmeye başlarsa başka bir ipucu elde etmiş olacağız.

Kernel tarafında da özellikle şu Ralink thread'leri çalışıyor:

[RtmpCmdQTask]
[RtmpWscTask]
[RtmpMlmeTask]

Bunlar ileride Wi-Fi sürücüsü kilitlenmesi ihtimalini araştırırken işimize yarayabilir.


Asıl İş: RP-N12'ye Kara Kutu Takmak

Web arayüzünde Remote Log Server diye bir alan bulunuyor.

Plan basit:

RP-N12
192.168.1.100
     |
     | UDP / 514
     v
Windows PC
192.168.1.108
     |
     v
C:\Temp\rp-n12-syslog.log

RP-N12'nin kendi syslogd prosesine baktığımda remote logging parametresinin gerçekten kullanıldığını gördüm.

/sbin/syslogd -m 0 -t GMT-3 -O /tmp/syslog.log -R 192...

ps terminal genişliği nedeniyle satırı kesiyordu. Bunun üzerine prosesin gerçek command line'ını doğrudan /proc üzerinden okudum:

# cat /proc/4924/cmdline

/sbin/syslogd-m0-tGMT-3-O/tmp/syslog.log-R192.168.1.108-L

NUL karakterleri ekranda görünmediği için argümanlar birleşik görünüyor. Gerçek yapı kabaca şöyle:

/sbin/syslogd
-m 0
-t GMT-3
-O /tmp/syslog.log
-R 192.168.1.108
-L
ASUS RP-N12 syslogd remote hedef doğrulaması ve BLACK BOX ONLINE testi
RP-N12 tarafı: syslogd hedefinin 192.168.1.108 olduğu /proc üzerinden doğrulandı; hemen ardından kara kutu test mesajı gönderildi.

Ufak Bir Troubleshooting Klasiği: Yanlış IP

İlk denemede Windows tarafındaki collector çalışıyor fakat RP-N12'den hiçbir paket gelmiyordu.

UDP/514 için firewall kuralı açıldı. Python listener'ın gerçekten portu dinlediği doğrulandı. Hatta localhost üzerinden gönderilen test mesajı başarıyla kaydedildi:

2026-09-18 00:56:17.096 [127.0.0.1] LOCAL SYSLOG TEST

BusyBox üzerinde doğrudan UDP paketi göndermek için nc kullanmayı düşündüm.

# nc
-sh: nc: not found

# busybox nc
nc: applet not found

İş artık Wireshark ile paketin kabloya çıkıp çıkmadığını kontrol etme noktasına gelmişti.

Sonra çok daha temel bir şey ortaya çıktı.

Windows PC'nin IP adresi 192.168.1.109 değil, 192.168.1.108 idi.

Syslog paketlerini gayet düzgün şekilde yanlış bilgisayara gönderiyorduk.

Remote Log Server adresini 192.168.1.108 olarak değiştirince bütün problem bir anda ortadan kalktı.

BLACK BOX ONLINE

RP-N12 üzerinde:

# logger "RP-N12 BLACK BOX ONLINE"

Windows tarafında ise:

2026-09-18 01:05:04.149 [192.168.1.100]
<13>kadir: RP-N12 BLACK BOX ONLINE
Windows Python syslog collector üzerinde RP-N12 BLACK BOX ONLINE mesajı
PC tarafı: Python UDP/514 collector, RP-N12'nin gönderdiği BLACK BOX ONLINE mesajını gerçek zamanlı olarak aldı ve diske yazdı.

Artık RP-N12 kendi loglarını kendi RAM/flash alanında tutmakla kalmıyor; olayları gerçekleştiği anda başka bir bilgisayara gönderiyor.

Bir başka avantaj da PC'deki Python collector'ın her paketin başına Windows sistem saatini eklemesi. RP-N12'nin NTP'si tekrar bozulsa veya saati 2011'e dönse bile gerçek olay zamanını kaybetmeyeceğim.


Windows Tarafındaki Collector

İşin PC tarafında çok küçük bir Python programı yeterli:

import socket
from datetime import datetime

PORT = 514
LOG = r"C:\Temp\rp-n12-syslog.log"

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(("0.0.0.0", PORT))

print(f"RP-N12 syslog bekleniyor UDP/{PORT}")

with open(LOG, "a", encoding="utf-8", buffering=1) as f:
    while True:
        data, addr = sock.recvfrom(65535)
        now = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
        msg = data.decode("utf-8", errors="replace").rstrip()
        line = f"{now} [{addr[0]}] {msg}"
        print(line)
        f.write(line + "\n")

Program UDP/514'ü dinliyor, gelen paketin kaynak IP'sini ve PC'nin timestamp'ini ekleyip şu dosyaya yazıyor:

C:\Temp\rp-n12-syslog.log

Fakat burada yeni bir problem var. RP-N12 bazen haftalarca kilitlenmiyor. Her Windows açılışında gidip Python scriptini elle çalıştırmak anlamsız.

Task Scheduler ile Sessiz Autostart

Scripti kalıcı olarak:

C:\Scripts\rp-n12-syslog.py

altına taşıdım.

Windows Startup klasörüne normal bir kısayol atmak mümkün fakat bu yöntem hem daha hantal hem de konsol penceresinin görünmesine neden olabiliyor.

Bunun yerine Windows Task Scheduler kullandım.

Task adı:

RP-N12 Syslog Logger

Trigger:

At log on of WIN11\hicbi

Action:

Program/script:
C:\Python313\pythonw.exe

Add arguments:
"C:\Scripts\rp-n12-syslog.py"

Start in:
C:\Scripts

Burada python.exe yerine özellikle pythonw.exe kullanıyorum. Böylece login sırasında CMD/terminal penceresi açılmıyor; collector tamamen görünmez şekilde arka planda çalışıyor.

Task'ın üç gün sonra otomatik sonlandırılmasına neden olan varsayılan Stop the task if it runs longer than: 3 days seçeneğini de kapattım.

Ayrıca:

If the task fails:
Restart every 1 minute
Attempt to restart up to 3 times

If the task is already running:
Do not start a new instance

ayarlarını kullandım.

Gerçekten Arka Planda mı?

Task'ı elle başlattıktan sonra portu kontrol ettim:

C:\>netstat -ano | findstr "0.0.0.0:514 "

UDP    0.0.0.0:514    *:*    25904

PID'yi sorgulayınca:

C:\>tasklist /FI "PID eq 25904"

Image Name     PID
=========== =======
pythonw.exe   25904

Yani görünürde hiçbir pencere olmamasına rağmen Python collector gerçekten UDP/514'ü dinliyordu.

Son uçtan uca test:

# logger "RP-N12 AUTOSTART TEST OK"

Ve log dosyası:

2026-09-18 01:19:02.442 [192.168.1.100]
<13>kadir: RP-N12 AUTOSTART TEST OK
RP-N12 Task Scheduler autostart syslog testi
Final doğrulama: Task Scheduler ile sessiz çalışan pythonw.exe collector, AUTOSTART TEST OK mesajını ve hemen sonrasındaki klogd/syslogd restartını kaydetti.

Syslog Servisinin Restart'ını Bile Yakaladık

Bir ayar değişikliğinden sonra RP-N12 kendi logging servislerini yeniden başlattı. Kara kutu bunu da kaydetti:

2026-09-18 01:20:35.116 [192.168.1.100]
<13>kernel: klogd: exiting

2026-09-18 01:20:35.127 [192.168.1.100]
<46>System log daemon exiting.

2026-09-18 01:20:35.158 [192.168.1.100]
<13>kernel: klogd started: BusyBox v1.12.1

Bu da mekanizmanın istediğimiz şekilde çalıştığını gösteriyor. RP-N12'nin kendi syslog altyapısının kapanıp yeniden açılması bile dışarıdaki logda kalıyor.


Şimdi Ne Bekliyoruz?

Artık yapılacak en doğru şey aslında hiçbir şey yapmamak.

RP-N12 normal access point görevine devam edecek. Windows PC günde uzun süre açık olduğundan Task Scheduler collector'ı otomatik olarak başlatacak ve cihazın gönderdiği mesajlar:

C:\Temp\rp-n12-syslog.log

dosyasında birikecek.

Bir sonraki kilitlenmede hemen reboot etmek yerine önce şu ayrımı yapacağım:

192.168.1.1   → ana router ping?
192.168.1.100 → RP-N12 ping?
Wi-Fi         → çalışıyor mu?
Telnet        → cevap veriyor mu?
Syslog        → son mesaj ne?

Eğer RP-N12 ping'e cevap vermezken ana router cevap veriyorsa cihazın kendisi veya Ethernet tarafı kilitlenmiş olabilir.

İkisi birden kaybolursa RP-N12'nin Layer-2 ağı bozduğu ihtimali yeniden gündeme gelecek.

Her iki IP de cevap verirken yalnız Wi-Fi ölürse Ralink radio/driver tarafı çok daha kuvvetli bir şüpheli olacak.

Logda özellikle şu kelimeleri arayacağım:

watchdog
kernel panic
Oops
Call Trace
Out of memory
oom
Killed process
alloc
ra0
raeth
RT305x_ESW
link
reset
deauth
disassoc

Ya Cihaz Tamamen Hard-Lock Olursa?

Remote syslog'un doğal bir sınırı var.

Kernel veya SoC tamamen kilitlenirse son log mesajını gönderecek CPU zamanı bile kalmayabilir. Bu durumda dosya muhtemelen şöyle bitecek:

normal mesaj
normal mesaj
normal mesaj
son mesaj
...

ve ardından sessizlik.

Bu da aslında bilgi.

Fakat nedenini görmek için bir sonraki aşama cihazın PCB'sindeki UART hattını bulmak olacak. 3.3 V TTL seviyesinde TX/RX/GND üzerinden boot ve kernel konsoluna ulaşabilirsem Ethernet ve Wi-Fi çöktüğünde bile cihazın içeride ne yaptığını görebilirim.

UART da aynı anda tamamen susuyorsa güç, watchdog, kernel deadlock veya SoC seviyesinde hard-lock ihtimalleri daha ciddi hale gelir.

JTAG ise şimdilik gereksiz derecede ağır top.


Sonuç: Arızayı Gözlemlenebilir Hale Getirdik

Şu anda RP-N12'nin neden kilitlendiğini hâlâ bilmiyorum.

Memory leak olduğuna dair kanıt yok. Ethernet sayaçları temiz. Wi-Fi hata sayıları olağan seviyede. Cihaz bazen haftalarca problemsiz çalışabildiği için masanın başında bekleyerek arızayı yakalamak da gerçekçi değil.

Fakat başlangıçtaki durumla şimdiki durum arasında önemli bir fark var.

Başlangıçta cihaz kilitlendiğinde elimizde yalnızca:

"Wi-Fi yine gitti."

bilgisi vardı.

Şimdi ise RP-N12 kendi kernel ve sistem mesajlarını gerçek zamanlı olarak başka bir makineye gönderiyor; Windows bunlara bağımsız timestamp ekliyor ve disk üzerinde kalıcı olarak saklıyor.

Başka bir deyişle problemi henüz çözmedik.

Ama artık RP-N12 suç işlerse olay yerinde kara kutu var.


ASUS RP-N12 · BusyBox · Ralink · embedded Linux · syslog · remote syslog · UDP 514 · Task Scheduler · Python · access point · network troubleshooting