FT232RL USB Serial sound card hack

Did you know that you could play sound using an usb to uart/serial converter , by just using an FT232RL usbserial and an external speaker with amplifier?
Let’s take a look at the software side first, the basic idea is to send bytes purposedly crafted to create some sort of PWM.
So we have 8 bits each byte sent, which mean 8 levels, kinda crappy if you like hi-fi sound , but that’s not the purpose of this hack obviously.

So the easiest way is to send these bytes for each level

Level Bits
-4 00000000
-3 10000000
-2 11000000
-1 11100000
0 11110000
1 11111000
2 11111100
3 11111110
4 11111111

A simple python script with pyserial can easily do it, by taking bytes from stdin coming from a raw mono s8 pcm file , to the usbserial

To create the raw file use the following command:

The 58000 sample rate value is obtained experimentally by looking at the script output , to see how much bytes/sec are actually sent

To play it:

Connect the GND pin of the serial converter to the ground of the amplifier cable, and the TX pin to the signal pin.

If you get strange interference because your amplifier is class D and it is not filtered, you need to add a lowpass filter with cutoff frequency of 22 Khz


Thanks to Valerio “Gurzo” Morgante ( latanadelgurzo.blogspot.it ) who was working on doing the opposite ( Emulating a serial port using a sound card ) and that gave me the idea of obtaining sound output using an inexpensive FT232RL usb-serial

Ubiquiti Nanostation M5 Loco Repairs

Since very few information is available on repairing UBNT Nanostation M5 Loco ( probably the same for M2 ), i am going to post here and keep updating that post with the main kinds of failure encountered with their eventual repair

To do these repairs since the board contains big ground planes, you absolutely need to preheat it to at least 120 C° before using soldering iron or smd rework station.

UBNT Nanostation loco m5 PCB

Types of failure

PoE shorted, power supply led blinks as a consequence of that

This can be caused by shorted D5 diode ( look for the big black square diode on the left of the ethernet port ), replace it with another one or if you are in an hurry and willing to risk, just remove it.

Ethernet port goes only at 10 Mbit ( but usually won’t work at all even if it says that ) , or no link is detected

This is the trickiest damage, i’ve tried replacing ethernet transformer where it was evidently burned ( one winding open ), but even after that i only got from “no link” to “10Mbit” , so either other components shorted or in that case the SoC is damaged and no repair is worth to do.

The nanostation will only go into firmware recovery mode ( 4 leds blinking )

That kind of failure can be caused by remote reset NPN transistor damaged, replace or remove ( if you don’t need remote reset ) Q503 , you can find it on the right of the reset switch, beware that this kind of failure can be also caused by broken switch, so check first with a multimeter if the switch is working properly, also check that the one on the PoE injector is not broken.


High speed photography: popping soap bubble

Have you ever wondered how a bubble looks like while popping? if you think it just “disappears”, you are wrong, and provided you have a decent flash and a camera that allows you to use something like 10sec shutter speed, then you can take such photos with just a relay module and an Arduino.
The idea to trigger the flash at the right moment is to form with the end of a wire an “O” shape and then with another wire, when the bubble touches the wire, it will shortly conduct some current that can be used to trigger the flash.


But as you can imagine it’s not that easy doing it, the bubble is very thin and it will conduct only for a fraction of second before poppin, its resistance will be around 10 megaohms, so either you need to build a GOOD low noise amplifier or just use the trick i’m going to explain.
If you have some experience with electronics you already know that a wire has a certain capacitance caused by surrounding stuff and air as dielectric, otherwise, well, now you do.

To be able to detect the bubble touching both wires at the same time, the trick is charging the “wire capacitor” to +5V, then switch the pin to high impedence input and measure how much it takes to discharge because of leakage current of your arduino.
Since that parameter may vary for each board, wire length, wire insulation and other stuff, you have to find the open circuit value experimentally.

Untitled Sketch_bb

If you have it, you can use an SCR too instead of the relay, but be sure to add a delay in the arduino sketch to get the correct timing on photo ( a relay typically takes 6-15ms to trigger the flash ).


Below is my setup with a flash set to the lowest power ( lowest power means less duration too, so more sharp photos of moving objects ) and triggered by the arduino with a relay


For these photos i’ve used SIGMA 70-300mm lens set at f/16 together with a Nikon d40 body with iso set to 400, the procedure to take a photo is like that, start the exposure using an IR remote, blow a bubble and make it pop while triggering the flash, wait for the remaining exposure time to finish, and enjoy the result.
Needless to say, you need to do that outside at night or in some very dark room otherwise.

Now it’s time for some results




Sitecom WL-326 OpenWrt update

Earlier i’ve written a post on how to install OpenWrt on that router, now it’s time to start fixing broken stuff, especially the reset button and the usb port, along with default switch/network configuration.
After some work i’ve managed to reverse engineer the GPIOs of that router, GPIO0 is used on the wps button on top of the case which is the only button the router has , so we’ll be using it as reset/failsafe button.
Also who has tried installing openwrt on that router may have noticed that the usb port has no power, turns out that GPIO6 is the one which enables the DC-DC converter on the board ( probably they did that to allow resetting the 3G modem without physically removing it from the port ).

So i’ve created a new dts file for that router finally, instead of using the WR5123ng image, below there’s the patch to apply on openwrt source tree

After that work, reset button works ok ( to get into failsafe mode wait for an udp packet from the router then shortly press the button once ), usb port power is tied to a dummy usbpower led, set brightness to 0 to power on, brightes to 1 to power off


Still usb is not working, on dmesg dwc2 driver is constantly reporting an overcurrent condition that i think it is caused by VBUS being supplied from an external power supply instead of the SoC, so it thinks that there’s a short circuit, probably some driver work is still needed, so more updates are likely to follow

A very simple internet of things system to control lights and other stuff


In these days i’ve worked on how to create a system that allows me to turn on and off lights or appliances at home with minimal cost and complexity, and there it is: https://github.com/tizbac/IoTManager

Each node (ESP-01 ESP8266) with nodemcu firmware and the init.lua script from esp8266 folder of my repository has two outputs and can control two appliances.
A node has commands to retrieve name , unique identifier and current state, and to set the state, all that happens via UDP packet.

At first i tried, especially for discovery purposes to use UDP broadcast packets, but it seems that the module has some bug that makes the reception of broadcasts very unreliable, so at last i resorted to try to send a command to query status for each ip address specified in the subnet, like for, it would be to

The server which runs on an ARM board like a raspberry or a beaglebone takes care of the authentication of the clients from the internet ( the ESP8266 modules have no authentication , they rely on the safety of the network, so avoid passwords like “password” or “0123456789” ).

When first started the server creates a self signed certificate to use with HTTPS and a random password, then when the user connects to the webserver from a local ip address a qrcode is displayed to configure the android application.

The QR Code contains the public ip address , the port , the password and the sha1 fingerprint of the certificate, so that even if it is self-signed, it can be verified by the application to prevent man in the middle attacks.

The servers also takes care of enforcing state on the nodes, especially when a packet is lost or when the node for some reason loses power, at each discovery the state is compared and if not equal it will be resent again until the node status matches.

That’s it, with barely 200 lines of python and a trivial android app you can safely control your house from where you want.

Quick test of NVA3 series reclocking with nouveau and comparison with NVIDIA drivers

These months there has been a lot of work on reclocking support for NVA3 nvidia cards by Roy Spliet on the opensource nouveau drivers.

Since i own a Geforce 210 GT i’ve decided to give it a try with some benchmark and the results are quite good

The reclocking of these cards is still very unstable, to have higher chance of success in reclocking, use an fbconsole and not the terminal from some composited environment like gnome-shell.

First game i’ve tested is tesseract, which is an graphically upgraded version of sauerbraten

Nouveau , when reclocked to highest performance level, can get very near to NVIDIA Proprietary driver, maybe with bleeding edge mesa , it could actually reach it.
Second game tested is Openarena :
Also on this one, performance suffered a great boost from reclocking, on that card 07 corresponds to boot frequencies, so before RSpliet’s work, only 07 was available.

Gpu test also , except some tests gave same results

Pixmark Volposition and GiMark , both failed and caused nouveau to crash

Meantime i’m downloading other games and i’m going to test also a geforce GT330 m from my sony vaio notebook , once it is reclockable to highest performance level.

Alcor UFD Controller Hacking update 2


In these days between some exercises for a microeconomics exam , i’ve continued to work on reverse engineering the Alcor 698x UFD microcontroller, and i’ve got another poor quality Alcor based flash drive from a friend , so now i’ve an 8 GB one , and a 4 GB one.

I’ve started , with the help of wireshark , usbmon and a virtual machine running windows with USB forwarding by SPICE , reverse engineering the format of the various vendor specific commands that are sent to the flash drive.

But while working on it i’ve encountered a serious problem , the Linux kernel scsi implementation removes the 3 MSBs from the 2nd byte of the SCSI commands , which in our case , breaks various commands , for example , 0x51 directed at LUN 0 , would become 0x11.

The interesting commands now are two: 0x82 and 0x81 , they are used to download and upload configurations to the flash drive.

The 0x81 command is used with that specific CDB

0x81 , 0x00 0xff , 0x00 , 0x00 , 0x00 , 0x00, 0x00 ,0x00 ,0x00 ,0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 

Then it takes 512 bytes of data that contains the actual configuration, below there’s an example of that configuration:

Let’s look at what i’ve found so far:
Going from the start of the file to the end
99 07 : This seems to be some sort of signature i have yet to find a flash drive that does not have it
10 : This is the vendor string length ( including zeroes , the length field itself and the type field )
26: This is the product string length ( including zeroes , the length field itself and the type field )
12 01 10 01 00 00 00 08: These bytes are still unknown , 12 could be the SCSI vendor string length , but still not tested
8F 05: This is the little endian representation of the VID ( Vendor ID ) that the usb device will show to the PC
87 63: This is the little endian representation of the PID ( Product ID ) that the usb device will show to the computer
05 01: These are the bcdDevice field of the usb descriptor
01:  Manufacter index ( not confirmed )
02: Product index , ( not confirmed )
00 01: Unknown
10: Again vendor string length
03: Descriptor type ( String)
10 03 48 00 61 00 63 00 6B 00 69 00 6E 00 67 00: Actual Vendor String
26: Product string length ( Again )
03: Descriptor type ( String )
50 00 52 00 4F 00 56 00 41 00 50 00 52 00 4F 00 44 00 4F 00 54 00 54 00 4F 00 50 00 52 00 4F 00 56 00 41 00: The actual product string
47 65 6E 65 72 69 63 20 46 6C 61 73 68 20 44 69 73 6B 20 20 20 20 20 20 38 2E 30 30: SCSI Identification string (Generic Flash Disk      8.00)
02: This byte is a checksum of the all bytes before it , if you set it incorrectly , the flash drive will refuse to use the settings, to calculate it , sum all bytes values ( unsigned ) , and then do &0xFF to use only the first eight bits.
AA 55 : Unknown 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 : Padding
3C C3 : Unknown
2A : Serial string length
03: Descriptor type (String )
32 00 30 00 31 00 34 00 30 00 37 00 31 00 39 00 31 00 34 00 31 00 34 00 32 00 37 00 30 00 38 00 35 00 46 00 30 00 37 00 42: The actual serial number string
After that there are @ offset 0xc3 an 0xc7 two 02 which are unknown
And @ 0xd4 there’s 88 50 51 49 which is unknown too
Once the settings have been uploaded , the flash drive has to be unplugged and plugged in again

The 0x82 command

This command instead, is used to read current configuration, it can be used in two ways

82 51 01 00 00 00 00 00 00 00

In that way you will get the current usb configuration with the same format as 0x81 command, 512 bytes too

82 51 00 00 00 00 00 00 00 00 

With this one instead you get another configuration(maybe) that still has to be investigated, and i have to figure out how it is uploaded too
I’ve created a git repository on github.com containing the kernel patch and the current code for the alcorhack tool
Again , if someone wants to help, contact me on G+ or comment here 

Alcor UFD Controller Reverse engineering part 1 , querying info and decrypting the flash list shipped in AlcorMP tool


I’ve started that work because i want to port AlcorMP utility to Linux and to be able to use custom flash chips with these UFD chips, this utility called AlcorMP allows to do a lot of stuff , from checking flash integrity to programming the usb flash drive into a CD-Rom emulator.

For who is new to that field, these flash drives store configuration data and badblocks on an hidden sector of the flash memowy which normally is not visible by the end user.

To program that sector you have to issue vendor specific SCSI commands, the ones that i’ve found are:
0x9a: Seems to return 0x200 bytes of data still not reverse engineered
0xfa00: Seems to return 0x200 bytes of data too, but this one returns the Flash Chip identification as the first 6 bytes , so it’s something useful.
There’s also 0xf5 that is still unknown

Now the hard part, first thing you will see when you try to figure out where the program takes the flash part.no and vendor, is that there’s no plaintext list with that data, and there’s no compressed data either.
Analyzing it with binwalk gives a very discouraging entropy graph as shown below

flashlist.afl entropy plot using binwalk -E

After some work on the UfdComLib.dll , it turns out that the file flashlist.afl has been encrypted with a block cipher on purpose.

Lucky the program itself ( except LLF.dll that is encrypted too ), is not obfuscated , so it has been relatively easy extracting the encryption algorithm from the program and use it to decode the flashlist.afl .

The function on UfdComLib.dll that gives an huge help locating the decryption code with IDA is the one at 0x100022F0

.text:100022F0 ; =============== S U B R O U T I N E =======================================
.text:100022F0 ; Attributes: bp-based frame
.text:100022F0 ; int __cdecl sub_100022F0(LPCSTR lpFileName)
.text:100022F0 sub_100022F0 proc near ; CODE XREF: sub_10001D80+292p
.text:100022F0 ReturnedString = byte ptr -14Ch
.text:100022F0 var_44 = byte ptr -44h
.text:100022F0 var_40 = byte ptr -40h
.text:100022F0 var_3C = byte ptr -3Ch
.text:100022F0 var_38 = byte ptr -38h
.text:100022F0 var_36 = byte ptr -36h
.text:100022F0 var_34 = byte ptr -34h
.text:100022F0 var_32 = byte ptr -32h
.text:100022F0 var_30 = byte ptr -30h
.text:100022F0 var_2E = byte ptr -2Eh
.text:100022F0 AppName = byte ptr -2Ch
.text:100022F0 var_24 = dword ptr -24h
.text:100022F0 var_20 = dword ptr -20h
.text:100022F0 var_1C = dword ptr -1Ch
.text:100022F0 var_18 = dword ptr -18h
.text:100022F0 var_14 = dword ptr -14h
.text:100022F0 var_10 = dword ptr -10h
.text:100022F0 var_C = dword ptr -0Ch
.text:100022F0 var_4 = dword ptr -4
.text:100022F0 hFile = dword ptr 8
.text:100022F0 push ebp
.text:100022F1 mov ebp, esp
.text:100022F3 push 0FFFFFFFFh
.text:100022F5 push offset SEH_100022F0
.text:100022FA mov eax, large fs:0
.text:10002300 push eax
.text:10002301 mov large fs:0, esp
.text:10002308 sub esp, 140h
.text:1000230E push ebx
.text:1000230F push esi
.text:10002310 push edi
.text:10002311 mov edi, [ebp+hFile]
.text:10002314 xor esi, esi
.text:10002316 mov [ebp+var_10], esp
.text:10002319 push esi ; hTemplateFile
.text:1000231A push 80h ; dwFlagsAndAttributes
.text:1000231F push 3 ; dwCreationDisposition
.text:10002321 push esi ; lpSecurityAttributes
.text:10002322 push 1 ; dwShareMode
.text:10002324 push 80000000h ; dwDesiredAccess
.text:10002329 push edi ; lpFileName
.text:1000232A mov [ebp+var_14], esi
.text:1000232D mov [ebp+var_4], esi
.text:10002330 call ds:CreateFileA
.text:10002336 cmp eax, 0FFFFFFFFh
.text:10002339 jz short loc_10002371
.text:1000233B push eax ; hObject
.text:1000233C call ds:CloseHandle
.text:10002342 push 4 ; int
.text:10002344 push edi ; lpFileName
.text:10002345 call sub_10004220
.text:1000234A mov ebx, eax
.text:1000234C add esp, 8
.text:1000234F cmp ebx, esi
.text:10002351 mov [ebp+var_24], ebx
.text:10002354 ja short loc_100023BD
.text:10002356 push offset aError_flash_it ; “ERROR_FLASH_ITEM_COUNT”
.text:1000235B lea ecx, [ebp+var_40]
.text:1000235E call ??0CString@@QAE@PBD@Z ; CString::CString(char const *)
.text:10002363 lea eax, [ebp+var_40]
.text:10002366 push offset unk_10006950
.text:1000236B push eax
.text:1000236C call _CxxThrowException
.text:10002371 ; —————————————————————————
.text:10002371 loc_10002371: ; CODE XREF: sub_100022F0+49j
.text:10002371 push offset asc_100081E4 ; “rn”
.text:10002376 lea ecx, [ebp+hFile]
.text:10002379 call ??0CString@@QAE@PBD@Z ; CString::CString(char const *)
.text:1000237E mov esi, eax
.text:10002380 push offset aError_file_no_ ; “ERROR_FILE_NO_EXISTS: ”
.text:10002385 lea ecx, [ebp+var_24]
.text:10002388 mov byte ptr [ebp+var_4], 1
.text:1000238C call ??0CString@@QAE@PBD@Z ; CString::CString(char const *)
.text:10002391 push edi
.text:10002392 push eax
.text:10002393 lea eax, [ebp+var_18]
.text:10002396 mov byte ptr [ebp+var_4], 2
.text:1000239A push eax
.text:1000239B call ??H@YG?AVCString@@ABV0@PBD@Z ; operator+(CString const &,char const *)
.text:100023A0 push esi
.text:100023A1 lea ecx, [ebp+var_3C]
.text:100023A4 push eax
.text:100023A5 push ecx
.text:100023A6 mov byte ptr [ebp+var_4], 3
.text:100023AA call ??H@YG?AVCString@@ABV0@0@Z ; operator+(CString const &,CString const &)
.text:100023AF lea edx, [ebp+var_3C]
.text:100023B2 push offset unk_10006950
.text:100023B7 push edx
.text:100023B8 call _CxxThrowException
.text:100023BD ; —————————————————————————
.text:100023BD loc_100023BD: ; CODE XREF: sub_100022F0+64j
.text:100023BD mov eax, ebx
.text:100023BF shl eax, 5
.text:100023C2 add eax, ebx
.text:100023C4 lea ecx, [eax+eax*4]
.text:100023C7 lea edx, [ebx+ecx*2]
.text:100023CA shl edx, 1
.text:100023CC push edx ; unsigned int
.text:100023CD call ??2@YAPAXI@Z ; operator new(uint)
.text:100023D2 add esp, 4
.text:100023D5 cmp eax, esi
.text:100023D7 mov [ebp+var_18], eax
.text:100023DA jz short loc_1000240A
.text:100023DC mov [ebp+var_14], ebx
.text:100023DF mov esi, 296h
.text:100023E4 mov edx, eax
.text:100023E6 loc_100023E6: ; CODE XREF: sub_100022F0+114j
.text:100023E6 mov ecx, [ebp+var_14]
.text:100023E9 dec ecx
.text:100023EA mov [ebp+var_14], ecx
.text:100023ED js short loc_10002406
.text:100023EF mov ecx, 0A5h
.text:100023F4 xor eax, eax
.text:100023F6 mov edi, edx
.text:100023F8 add edx, esi
.text:100023FA rep stosd
.text:100023FC stosw
.text:100023FE mov edi, [ebp+hFile]
.text:10002401 mov eax, [ebp+var_18]
.text:10002404 jmp short loc_100023E6
.text:10002406 ; —————————————————————————
.text:10002406 loc_10002406: ; CODE XREF: sub_100022F0+FDj
.text:10002406 xor esi, esi
.text:10002408 jmp short loc_1000240C
.text:1000240A ; —————————————————————————
.text:1000240A loc_1000240A: ; CODE XREF: sub_100022F0+EAj
.text:1000240A xor eax, eax
.text:1000240C loc_1000240C: ; CODE XREF: sub_100022F0+118j
.text:1000240C push ebx ; int
.text:1000240D push esi ; int
.text:1000240E push eax ; int
.text:1000240F push 4 ; int
.text:10002411 push edi ; hFile
.text:10002412 mov [ebp+var_14], eax
.text:10002415 call sub_100043D0
.text:1000241A add esp, 14h
.text:1000241D cmp eax, esi
.text:1000241F jnz short loc_1000243C
.text:10002421 push offset aError_flashlis ; “ERROR_FlashListApi_ReadItemrn”
.text:10002426 lea ecx, [ebp+var_44]
.text:10002429 call ??0CString@@QAE@PBD@Z ; CString::CString(char const *)
.text:1000242E lea eax, [ebp+var_44]
.text:10002431 push offset unk_10006950
.text:10002436 push eax
.text:10002437 call _CxxThrowException
.text:1000243C ; —————————————————————————
.text:1000243C loc_1000243C: ; CODE XREF: sub_100022F0+12Fj
.text:1000243C push 0FFFFFFFFh
.text:1000243E push esi
.text:1000243F mov ecx, offset unk_10008430
.text:10002444 call sub_10003060
.text:10002449 cmp ebx, esi
.text:1000244B jnz short loc_1000249E
.text:1000244D mov edi, dword_10008434
.text:10002453 cmp edi, esi
.text:10002455 jz short loc_1000248D
.text:10002457 mov esi, dword_10008438
.text:1000245D loc_1000245D: ; CODE XREF: sub_100022F0+180j
.text:1000245D mov ecx, esi
.text:1000245F dec esi
.text:10002460 test ecx, ecx
.text:10002462 jz short loc_10002472
.text:10002464 push 0 ; char
.text:10002466 mov ecx, edi ; void *
.text:10002468 call sub_100040E0
.text:1000246D add edi, 10h
.text:10002470 jmp short loc_1000245D
.text:10002472 ; —————————————————————————
.text:10002472 loc_10002472: ; CODE XREF: sub_100022F0+172j
.text:10002472 mov edx, dword_10008434
.text:10002478 push edx ; void *
.text:10002479 call ??3@YAXPAX@Z ; operator delete(void *)
.text:1000247E add esp, 4
.text:10002481 mov dword_10008434, 0
.text:1000248B xor esi, esi
.text:1000248D loc_1000248D: ; CODE XREF: sub_100022F0+165j
.text:1000248D mov dword_1000843C, esi
.text:10002493 mov dword_10008438, esi
.text:10002499 jmp loc_100025C7
.text:1000249E ; —————————————————————————
.text:1000249E loc_1000249E: ; CODE XREF: sub_100022F0+15Bj
.text:1000249E mov edx, dword_10008434
.text:100024A4 cmp edx, esi
.text:100024A6 jnz short loc_100024D3
.text:100024A8 mov eax, ebx
.text:100024AA shl eax, 4
.text:100024AD push eax ; unsigned int
.text:100024AE call ??2@YAPAXI@Z ; operator new(uint)
.text:100024B3 add esp, 4
.text:100024B6 mov dword_10008434, eax
.text:100024BB push ebx
.text:100024BC push eax
.text:100024BD call sub_10003DF0
.text:100024C2 mov dword_1000843C, ebx
.text:100024C8 loc_100024C8: ; CODE XREF: sub_100022F0:loc_10002501j
.text:100024C8 ; sub_100022F0+223j
.text:100024C8 mov dword_10008438, ebx
.text:100024CE jmp loc_100025C7
.text:100024D3 ; —————————————————————————
.text:100024D3 loc_100024D3: ; CODE XREF: sub_100022F0+1B6j
.text:100024D3 mov ecx, dword_1000843C
.text:100024D9 cmp ebx, ecx
.text:100024DB jg short loc_10002523
.text:100024DD mov eax, dword_10008438
.text:100024E2 cmp eax, ebx
.text:100024E4 jge short loc_10002501
.text:100024E6 mov ecx, ebx
.text:100024E8 sub ecx, eax
.text:100024EA shl eax, 4
.text:100024ED add eax, edx
.text:100024EF push ecx
.text:100024F0 push eax
.text:100024F1 call sub_10003DF0
.text:100024F6 mov dword_10008438, ebx
.text:100024FC jmp loc_100025C7

This function uses another function to read the header and get the number of entries on the flash list file , sub_10004220

.text:10004220 ; =============== S U B R O U T I N E =======================================
.text:10004220 ; int __cdecl sub_10004220(LPCSTR lpFileName, int)
.text:10004220 sub_10004220 proc near ; CODE XREF: sub_100022F0+55p
.text:10004220 var_100 = dword ptr -100h
.text:10004220 var_DC = dword ptr -0DCh
.text:10004220 var_D4 = dword ptr -0D4h
.text:10004220 lpFileName = dword ptr 4
.text:10004220 arg_4 = dword ptr 8
.text:10004220 mov ecx, [esp+lpFileName]
.text:10004224 sub esp, 100h
.text:1000422A lea eax, [esp+100h+var_100]
.text:1000422E push esi
.text:1000422F push eax ; int
.text:10004230 push ecx ; lpFileName
.text:10004231 xor esi, esi
.text:10004233 call sub_10004270
.text:10004238 add esp, 8
.text:1000423B test eax, eax
.text:1000423D jnz short loc_10004247
.text:1000423F pop esi
.text:10004240 add esp, 100h
.text:10004246 retn
.text:10004247 ; —————————————————————————
.text:10004247 loc_10004247: ; CODE XREF: sub_10004220+1Dj
.text:10004247 mov edx, [esp+104h+var_DC]

This one above checks if the file has been read successfully and if yes, it returns the number of entries of the flash list file.
.text:10004270 ; =============== S U B R O U T I N E =======================================
.text:10004270 ; Attributes: bp-based frame
.text:10004270 ; int __cdecl sub_10004270(LPCSTR lpFileName, int)
.text:10004270 sub_10004270 proc near ; CODE XREF: sub_10004220+13p
.text:10004270 Buffer = byte ptr -328h
.text:10004270 var_228 = byte ptr -228h
.text:10004270 var_128 = byte ptr -128h
.text:10004270 dwErrCode = dword ptr -28h
.text:10004270 NumberOfBytesRead= dword ptr -24h
.text:10004270 var_20 = dword ptr -20h
.text:10004270 var_1C = dword ptr -1Ch
.text:10004270 var_18 = dword ptr -18h
.text:10004270 var_14 = dword ptr -14h
.text:10004270 var_10 = dword ptr -10h
.text:10004270 var_C = dword ptr -0Ch
.text:10004270 var_4 = dword ptr -4
.text:10004270 lpFileName = dword ptr 8
.text:10004270 arg_4 = dword ptr 0Ch
.text:10004270 push ebp
.text:10004271 mov ebp, esp
.text:10004273 push 0FFFFFFFFh
.text:10004275 push offset SEH_10004270
.text:1000427A mov eax, large fs:0
.text:10004280 push eax
.text:10004281 mov large fs:0, esp
.text:10004288 sub esp, 31Ch
.text:1000428E push ebx
.text:1000428F push esi
.text:10004290 mov eax, [ebp+lpFileName]
.text:10004293 push edi
.text:10004294 xor esi, esi
.text:10004296 mov [ebp+var_10], esp
.text:10004299 push esi ; hTemplateFile
.text:1000429A push 80h ; dwFlagsAndAttributes
.text:1000429F mov edi, 1
.text:100042A4 push 3 ; dwCreationDisposition
.text:100042A6 push esi ; lpSecurityAttributes
.text:100042A7 push edi ; dwShareMode
.text:100042A8 push 80000000h ; dwDesiredAccess
.text:100042AD push eax ; lpFileName
.text:100042AE mov [ebp+var_14], edi
.text:100042B1 mov [ebp+var_4], esi
.text:100042B4 call ds:CreateFileA
.text:100042BA mov ebx, eax
.text:100042BC cmp ebx, 0FFFFFFFFh
.text:100042BF mov [ebp+lpFileName], ebx
.text:100042C2 jnz short loc_100042D5
.text:100042C4 lea ecx, [ebp+var_20]
.text:100042C7 push offset unk_100069F0
.text:100042CC push ecx
.text:100042CD mov [ebp+var_20], esi
.text:100042D0 call _CxxThrowException
.text:100042D5 ; —————————————————————————
.text:100042D5 loc_100042D5: ; CODE XREF: sub_10004270+52j
.text:100042D5 lea edx, [ebp+NumberOfBytesRead]
.text:100042D8 push esi ; lpOverlapped
.text:100042D9 push edx ; lpNumberOfBytesRead
.text:100042DA lea eax, [ebp+Buffer]
.text:100042E0 push 200h ; nNumberOfBytesToRead
.text:100042E5 push eax ; lpBuffer
.text:100042E6 push ebx ; hFile
.text:100042E7 call ds:ReadFile
.text:100042ED cmp [ebp+NumberOfBytesRead], 200h
.text:100042F4 jz short loc_10004307
.text:100042F6 lea ecx, [ebp+var_18]
.text:100042F9 push offset unk_100069F0
.text:100042FE push ecx
.text:100042FF mov [ebp+var_18], edi
.text:10004302 call _CxxThrowException
.text:10004307 ; —————————————————————————
.text:10004307 loc_10004307: ; CODE XREF: sub_10004270+84j
.text:10004307 lea edx, [ebp+var_128]
.text:1000430D lea eax, [ebp+Buffer]
.text:10004313 push edx
.text:10004314 push 10h
.text:10004316 push offset unk_10006840
.text:1000431B push 100h
.text:10004320 push eax
.text:10004321 call sub_10004650
.text:10004326 mov esi, [ebp+arg_4]
.text:10004329 add esp, 14h
.text:1000432C lea ecx, [ebp+var_128]
.text:10004332 lea edx, [ebp+var_228]
.text:10004338 push esi
.text:10004339 push 100h
.text:1000433E push ecx
.text:1000433F push 100h
.text:10004344 push edx
.text:10004345 call sub_10004650
.text:1000434A add esp, 14h
.text:1000434D mov ecx, 4
.text:10004352 mov edi, offset unk_10006840
.text:10004357 xor eax, eax
.text:10004359 repe cmpsd
.text:1000435B jz short loc_100043A4
.text:1000435D lea ecx, [ebp+var_1C]
.text:10004360 push offset unk_100069F0
.text:10004365 push ecx
.text:10004366 mov [ebp+var_1C], 2
.text:1000436D call _CxxThrowException
.text:10004372 ; —————————————————————————
.text:10004372 loc_10004372: ; DATA XREF: .rdata:stru_10006E88o
.text:10004372 mov edx, [ebp+dwErrCode]
.text:10004375 mov [ebp+var_14], 0
.text:1000437C push edx ; dwErrCode
.text:1000437D call ds:SetLastError
.text:10004383 mov eax, offset loc_100043A1
.text:10004388 retn
.text:10004389 ; —————————————————————————
.text:10004389 loc_10004389: ; DATA XREF: .rdata:stru_10006E88o
.text:10004389 push 0FFh ; dwErrCode
.text:1000438E mov [ebp+var_14], 0
.text:10004395 call ds:SetLastError
.text:1000439B mov eax, offset loc_100043A1
.text:100043A0 retn
.text:100043A1 ; —————————————————————————
.text:100043A1 loc_100043A1: ; DATA XREF: sub_10004270+113o
.text:100043A1 ; sub_10004270+12Bo
.text:100043A1 mov ebx, [ebp+lpFileName]
.text:100043A4 loc_100043A4: ; CODE XREF: sub_10004270+EBj
.text:100043A4 cmp ebx, 0FFFFFFFFh
.text:100043A7 jz short loc_100043B0
.text:100043A9 push ebx ; hObject
.text:100043AA call ds:CloseHandle
.text:100043B0 loc_100043B0: ; CODE XREF: sub_10004270+137j
.text:100043B0 mov ecx, [ebp+var_C]
.text:100043B3 mov eax, [ebp+var_14]
.text:100043B6 pop edi
.text:100043B7 pop esi
.text:100043B8 mov large fs:0, ecx
.text:100043BF pop ebx
.text:100043C0 mov esp, ebp
.text:100043C2 pop ebp
.text:100043C3 retn
.text:100043C3 sub_10004270 endp ; sp-analysis failed
.text:100043C3 ; —————————————————————————
sub_10004270 is interesting, it actually opens the file and reads a 512 byte block, then a function is used multiple times on the data read from the file: sub_10004650
sub_10004650 is just a wrapper around sub_10004680 , and it does only dereference some char ** to char* , so let’s checkout sub_10004680
.text:10004680 ; =============== S U B R O U T I N E =======================================
.text:10004680 ; int __cdecl sub_10004680(char *, int, char *, int, char *)
.text:10004680 sub_10004680 proc near ; CODE XREF: sub_10004650+19p
.text:10004680 arg_0 = dword ptr 4
.text:10004680 arg_4 = dword ptr 8
.text:10004680 arg_8 = dword ptr 0Ch
.text:10004680 arg_C = dword ptr 10h
.text:10004680 arg_10 = dword ptr 14h
.text:10004680 push ebx
.text:10004681 push ebp
.text:10004682 mov ebp, [esp+8+arg_0]
.text:10004686 push edi
.text:10004687 test ebp, ebp
.text:10004689 jz loc_10004750
.text:1000468F mov ebx, [esp+0Ch+arg_8]
.text:10004693 test ebx, ebx
.text:10004695 jz loc_10004750
.text:1000469B mov edi, [esp+0Ch+arg_10]
.text:1000469F test edi, edi
.text:100046A1 jz loc_10004750
.text:100046A7 push esi
.text:100046A8 push 100h ; unsigned int
.text:100046AD call ??2@YAPAXI@Z ; operator new(uint)
.text:100046B2 mov esi, eax
.text:100046B4 mov eax, [esp+14h+arg_C]
.text:100046B8 push esi
.text:100046B9 push eax
.text:100046BA push ebx
.text:100046BB call sub_10004760
.text:100046C0 mov edx, [esp+20h+arg_4]
.text:100046C4 add esp, 10h
.text:100046C7 xor eax, eax
.text:100046C9 xor ecx, ecx
.text:100046CB test edx, edx
.text:100046CD jbe short loc_10004740
.text:100046CF sub ebp, edi
.text:100046D1 mov [esp+10h+arg_0], edx
.text:100046D5 loc_100046D5: ; CODE XREF: sub_10004680+BAj
.text:100046D5 inc eax
.text:100046D6 and eax, 0FFh
.text:100046DB mov dl, [eax+esi]
.text:100046DE mov bl, [eax+esi]
.text:100046E1 add edx, ecx
.text:100046E3 and edx, 0FFh
.text:100046E9 mov ecx, edx
.text:100046EB mov dl, [ecx+esi]
.text:100046EE xor bl, dl
.text:100046F0 mov [eax+esi], bl
.text:100046F3 mov dl, bl
.text:100046F5 mov bl, [ecx+esi]
.text:100046F8 xor bl, dl
.text:100046FA mov [ecx+esi], bl
.text:100046FD mov dl, bl
.text:100046FF mov bl, [eax+esi]
.text:10004702 xor bl, dl
.text:10004704 mov [eax+esi], bl
.text:10004707 mov dl, bl
.text:10004709 xor ebx, ebx
.text:1000470B and edx, 0FFh
.text:10004711 mov bl, [ecx+esi]
.text:10004714 add edx, ebx
.text:10004716 and edx, 800000FFh
.text:1000471C jns short loc_10004726
.text:1000471E dec edx
.text:1000471F or edx, 0FFFFFF00h
.text:10004725 inc edx
.text:10004726 loc_10004726: ; CODE XREF: sub_10004680+9Cj
.text:10004726 mov dl, [edx+esi]
.text:10004729 mov bl, [edi+ebp]
.text:1000472C xor dl, bl
.text:1000472E mov [edi], dl
.text:10004730 mov edx, [esp+10h+arg_0]
.text:10004734 inc edi
.text:10004735 dec edx
.text:10004736 mov [esp+10h+arg_0], edx
.text:1000473A jnz short loc_100046D5
.text:1000473C mov edi, [esp+10h+arg_10]
.text:10004740 loc_10004740: ; CODE XREF: sub_10004680+4Dj
.text:10004740 push esi ; void *
.text:10004741 call ??3@YAXPAX@Z ; operator delete(void *)
.text:10004746 add esp, 4
.text:10004749 mov eax, edi
.text:1000474B pop esi
.text:1000474C pop edi
.text:1000474D pop ebp
.text:1000474E pop ebx
.text:1000474F retn
.text:10004750 ; —————————————————————————
.text:10004750 loc_10004750: ; CODE XREF: sub_10004680+9j
.text:10004750 ; sub_10004680+15j …
.text:10004750 pop edi
.text:10004751 pop ebp
.text:10004752 xor eax, eax
.text:10004754 pop ebx
.text:10004755 retn
.text:10004755 sub_10004680 endp
.text:10004755 ; —————————————————————————

This one is clearly an encryption algorithm, but before having a complete view on the problem there’s also another function: sub_10004760

.text:10004760 ; =============== S U B R O U T I N E =======================================
.text:10004760 sub_10004760 proc near ; CODE XREF: sub_10004680+3Bp
.text:10004760 arg_0 = dword ptr 4
.text:10004760 arg_4 = dword ptr 8
.text:10004760 arg_8 = dword ptr 0Ch
.text:10004760 push ebx
.text:10004761 mov ebx, [esp+4+arg_0]
.text:10004765 test ebx, ebx
.text:10004767 jz short loc_100047CC
.text:10004769 mov ecx, [esp+4+arg_8]
.text:1000476D test ecx, ecx
.text:1000476F jz short loc_100047CC
.text:10004771 push ebp
.text:10004772 push esi
.text:10004773 push edi
.text:10004774 xor edi, edi
.text:10004776 xor eax, eax
.text:10004778 loc_10004778: ; CODE XREF: sub_10004760+21j
.text:10004778 mov [eax+ecx], al
.text:1000477B inc eax
.text:1000477C cmp eax, 100h
.text:10004781 jb short loc_10004778
.text:10004783 mov ebp, [esp+10h+arg_4]
.text:10004787 xor esi, esi
.text:10004789 loc_10004789: ; CODE XREF: sub_10004760+63j
.text:10004789 mov eax, esi
.text:1000478B xor edx, edx
.text:1000478D div ebp
.text:1000478F mov al, [edx+ebx]
.text:10004792 mov dl, [esi+ecx]
.text:10004795 add edi, edx
.text:10004797 add eax, edi
.text:10004799 and eax, 0FFh
.text:1000479E mov edi, eax
.text:100047A0 mov al, [edi+ecx]
.text:100047A3 xor dl, al
.text:100047A5 mov [esi+ecx], dl
.text:100047A8 mov al, dl
.text:100047AA mov dl, [edi+ecx]
.text:100047AD xor dl, al
.text:100047AF mov [edi+ecx], dl
.text:100047B2 mov al, dl
.text:100047B4 mov dl, [esi+ecx]
.text:100047B7 xor dl, al
.text:100047B9 mov [esi+ecx], dl
.text:100047BC inc esi
.text:100047BD cmp esi, 100h
.text:100047C3 jb short loc_10004789
.text:100047C5 pop edi
.text:100047C6 pop esi
.text:100047C7 pop ebp
.text:100047C8 mov eax, ecx
.text:100047CA pop ebx
.text:100047CB retn
After studying on it some hours i’ve figured out that sub_10004760 is a function used to inizialize a vector of length 256 ( 0x100 ) with the encryption key that is later used on the caller function.
With HexRays decompiler it’s fairly easy to generate proper C code of these functions and so re-use them.
The file is made of a first 256 byte block , that encrypted using the algorithm above and “ALCORFLASHCFG_SZ” as the key yields other 256 bytes that have to be used as the key for the next 256 byte block inside the file that is the header.
The header contains some useful info like the size of each record , the number of records and what i think to be the version ( 4 ) as follows:

The data after the header is organized in entry_size sized blocks and each one is decrypted using the same key obtained to decrypt the header, but , the first keybyte has to be the record number starting from 0 and the last keybyte has to be the bitwise negation of the record number
This gives us records on which i’m still working to figure out the exact format , but what i’ve defined so far:

Other fields still have to be reverse engineered, and they are mandatory to be able to write a program that can reprogram these flash drives.
The complete program that can read the flash list is the following:

If someone is interested in helping me on that work , comment here, or circle me on Google Plus

Resurrecting QBell 39″ LCD LED TV as VGA display using an MT6820 board


I’ve got that TV to be trashed, because after some hours it won’t turn on anymore, first thing i’ve checked is the power supply which was fine.
The original board is labeled “T.MSD309.B21B”, and apparently it is a chinese universal LVDS LCD driver board.

So i’ve checked on ebay and that board is available , but it costs a lot of money ( 80+ € excl. shipping ) , and since the old one broke almost in new condition, and i’m pretty sure it is software-broken, it’s reasonable to think that the board is flawed and it will break again and again.
Also i couldn’t be sure that the panel was in working condition , so 100 € is too much risky considering that and what said above.

I’ve choosen then, to use an MT6820 board which can be bought from ebay for as low as 6€, it has only VGA input , but it is fine for what i want to use that LCD for.

This board has various options to support most LCD panels, so i’ve tried all of them, and the one that is closest to perfection is the no. 1.
But it isn’t perfect, you will get swapped columns.
While going through various LVDS pinouts i’ve noticed that a group of signals is marked “EVEN” and another group “ODD”, since the number of pixels horizontally is even, it clearly appears to be caused by swapped EVEN and ODD connections.

The glitch caused by swapped EVEN and ODD

When swapping the signals according to the MT6820 pinout, i was very tired and screwed up all the ordering , so i had to find again the correct pinout.
Since the Part.NO of the panel gives 0 results on google , literally, i’ve spotted the lvds controller board chip manufacter on the panel , which is “CMO” , CMO stands for ChiMei Optoelectronics, but it does not longer exist , however , after some more hard search , i’ve figured out that it has been acquired by Innolux, and then i’ve picked the first datasheet of a LED backlit panel 39 inches wide.

After having the right pinout i’ve reassembled the cable and i’ve got to the starting point where columns were swapped.

So the next day, after some rest , i’ve tried again to swap the connections , and it finally works.

Now let get deeper into the details, if you order an MT6820 , you will likely get an LVDS cable that is useless for our work, and a button board that we need instead.

The button board must be connected with the supplied cable even if the pin count does not match, that is because K5 and K6 on the input side are unused.
Make sure that the other input pins and led pins match on the button board.

On the QBell LVDS cable you have to swap all the EVEN pins with ODD pins, to do that , do not cut the wires or do some s**t like that , instead just use some thin object like a needle to extract the contacts from the plastic of the connector on the MT6820 side and swap them all.

Now that you have ( hopefully ) correct LVDS connection, you need to do one more thing , the MT6820 supports only 3.3V and 5V panel , instead at least on this QBell , the panel voltage is 12V , so you have to cut the Red wires ( pins 1,2,3 ) and connect them with the +12V from the power supply( you can see output voltages from the PCB of the power supply , since they are labeled ).

The backlight is not electrically compatible with the MT6820 so you have to just connect both BLON and ADJ pins of the power supply to +5VSB .
The +5V of the MT6820 instead has to be connected on the +5V of the power supply.
To make the power supply power on as soon you plug the TV to the outlet, connect PSON and +5VSB together.

The MT6820 board “installed”

If nothing is wrong you should see “No signal” written in chinese and it should work correctly as a regular PC VGA display.

LCD Panel pinout

LCD Panel pinout
MT6820 pinout

[AVANZATO] Installare OpenWRT sul router DSL2740B Revisione A1 , Utile anche per debrick


Premetto subito che le informazioni qui riportate sono per chi ha già una buona esperienza.
Altra premessa è il fatto che non è detto che si possa far ritornare il router al suo stato di fabbrica dopo le modifiche descritte in questo articolo, se si vuole avere un punto di ripristino, eseguire prima di flashmem

readmem 0x1f000000 0x20000 /percorso/al/file/di/backup

Questo comando salva i primi 128kb della memoria flash, che comprendono la nvram ed il cfe della D-Link

Per eseguire l’installazione di OpenWRT è necessario come prima cosa sostituire il bootloader, per fare ciò è necessario utilizzare la porta JTAG , che contrariamente a quanto riportato dalla maggior parte degli articoli che trattano questo router, è presente.

La porta JTAG è accessibile sulle piazzole con le seguenti etichette

Testpoint JTAG
Per eseguire il collegamento tramite JTAG è possibile utilizzare un cavo DLC5 sulla porta parallela come descritto qui http://wiki.openwrt.org/doc/hardware/port.jtag.cable.unbuffered , oppure un adattatore Altera USB Blaster , di quest’ultimo anche un clone cinese va bene.
Per programmare la memoria flash , il programma che mi ha funzionato senza problemi è urjtag (http://urjtag.org), di questo programma non va utilizzata la versione 0.10 ,  ma la versione SVN
Una volta eseguiti tutti i collegamenti ed avviato urjtag come root da riga di comando digitando “jtag”, è necessario specificare che tipo di cavo si usa, nel caso della porta parallela

cable parallel 0x378 DLC5

mentre nel caso di Altera USB Blaster
cable usbblaster
Poi è necessario identificare il processore, per fare ciò, digitare detect
Dovrebbe apparire un output simile:
jtag> detect
IR length: 5
Chain length: 1
Device Id: 00000110001101011000000101111111 (0x0635817F)
  Manufacturer: Broadcom (0x17F)
  Part(0):      BCM6358 (0x6358)
  Stepping:     V1
  Filename:     /usr/share/urjtag/broadcom/bcm6358/bcm6358
Se dovesse comparire come device Id una stringa di 0 , oppure altri errori , come TDO seems to be stuck at , o roba simile, ricontrollare i collegamenti e che il router sia acceso.
Poi è necessario inizializzare EJTAG in modalità DMA , per fare ciò:
jtag> initbus ejtag_dma 
EJTAG version: <= 2.0
EJTAG Implementation flags: R4k MIPS16 DMA MIPS32
Clear memory protection bit in DCR
Clear Watchdog
Potential flash base address: [0x0], [0x1f00008c]
Processor successfully switched in debug mode.
Se dovesse bloccarsi urjtag in questo passo oppure dovesse dire che non riesce a mettere in modalità debug il processore, ritentare e se il problema persiste , provare ad entrare nel prompt CFE utilizzando la porta seriale e ritentare da capo la parte inerente ad urjtag.
È di fondamentale importanza , essendo JTAG stateful , che quando qualcosa vada storto o si vuole ricominciare , sia chiuso e riaperto urjtag e spento e riacceso il router.

Una volta inizializzato EJTAG , si può passare ad inizializzare la flash CFI con il seguente comando

jtag> detectflash 0x1f000000

Dovrebbero essere restituite tutte le informazioni sulla memoria flash , in caso contrario , ritentare più volte e se continua a non funzionare , chiedere aiuto nei commenti di questo post
Un altro passo fondamentale è eseguire il comando
jtag> endian big
Questo comando, imposta che anche il file è bigendian come il processore e quindi urjtag non andrà ad invertire le word
Se tutto è andato a buon fine , si è pronti a scrivere il nuovo CFE sulla memoria flash, per fare ciò , va innanzitutto scaricato il nuovo CFE https://drive.google.com/file/d/0B5uKF4o3fPSWYzZya2paWWdOd2c/edit?usp=sharing e poi va scritto sulla memoria flash con il comando flashmem come segue:
flashmem 0x1f000000 /percorso/completo/a/cfe6358.bin
Se tutto è corretto inizierà a cancellare e programmare i settori della memoria flash con i contenuti del file, una voltè che è completata la procedura , e la fase di verifica non ha dato errori, si è pronti a riavviare il router e tramite la porta seriale configurare i parametri della scheda
Verranno chiesti alcuni parametri come segue:
*** Board is not initialized properly ***

Press:  <enter> to use current value
       ‘-‘ to go previous parameter
       ‘.’ to clear the current value
       ‘x’ to exit this command
Board Id Name (0-4)
96358VW          ——-  0
96358VW-16       ——-  1
96358GW          ——-  2
96358GW-16       ——-  3
96358M           ——-  4       :  0
Number of MAC Addresses (1-32)    :  0  4
Base MAC Address                  :  24:0a:64:xx:xx:xx
Press any key to reset the board:
Sostituire ovviamente le xx del mac address con dei codici esadecimali validi e che non siano un indirizzo di broadcast.
È possibile anche modificare i primi 3 byte del mac address, quelli che ho messo io sono del produttore della mia scheda madre ( ASUS ).
Il reset della scheda al termine dell’operazione non è detto che funzionerà , in tal caso trascorsi almeno 5 secondi , rimuovere e riapplicare l’alimentazione
Una volta completato questo passo, rientrando nuovamente nel prompt del CFE , dovrebbero essersi abilitate le porte ethernet con indirizzo ip
Openwrt deve essere scaricato da http://downloads.openwrt.org/attitude_adjustment/12.09/brcm63xx/generic/openwrt-96358VW-generic-squashfs-cfe.bin o nel caso questa versione sia vecchia in futuro , scaricare comunque una versione che sia squahfs e sia 96358VW.
Una volta scaricato openwrt , connettere il PC con un cavo ethernet allo switch del router, e visitare la pagina
In questa pagina , non serve fare altro che selezionare il file precedentemente scaricato e dare OK, in pochi minuti dovrebbe avviarsi il router con openwrt.

Oltre al modem ADSL che come su tutti i router brcm63xx a causa del fatto che broadcom non rilascia la documentazione. non funziona , potrebbe non funzionare anche la scheda wireless essendo anche essa broadcom, in questo caso, è necessario procurarsi una scheda ath5k od una scheda ath9k minipci ed installarla al posto della broadcom.
Installando una scheda atheros, è possibile utilizzare questo router come access point multi-ssid e multi-vlan ( cose fattibili solo con Cisco che costano minimo 150 € ), ed è possibile anche lavorare sulla banda 5 ghz , quindi utilizzare il router come semplice access point 802.11a oppure utilizzarlo per connettersi ad una rete outdoor 5 ghz , come quella di ninux.org.
Preciso un’ultima cosa inoltre, questa guida probabilmente è applicabile anche ad altri router basati su chip bcm6358 anche se non sono dichiarati compatibili con openwrt ( neanche questo lo è )