Adding Wi-Fi to the Macintosh Portable

Over the past year or so, I've been working with other BlueSCSI developers to add Wi-Fi functionality to their open-hardware SCSI device, enabling Wi-Fi support for old Macs and other vintage computers going back some 36 years.

This is my Macintosh Portable M5126. It's very Macintosh and barely portable. For some reason I'm using it on my lawn reading the Wi-Fi Wikipedia article over Wi-Fi through my Wikipedia application for System 6, with my Wi-Fi Desk Accessory showing it connected to my "!" network with meager signal strength.

This article was originally supposed to be titled "Adding Wi-Fi to a PowerBook 100" since that laptop is much more reasonable to lug around, but its logic board died while working on this and I'm in the process of bringing it back to life.

SCSI2SD, RaSCSI, PiSCSI, ZuluSCSI, and BlueSCSI

For some background, there's a handful of hardware devices that simulate SCSI disks for vintage Macintosh, Amiga, and Sun computers (and synthesizers/samplers) and can replace old, loud, power-hungry, unreliable, low-capacity spinning hard drives with solid-state SD card media similar to CompactFlash IDE adapters for old PCs.

There's a bit of history surrounding these projects, but the first one created in 2013, SCSI2SD, is a commercial product coming in various form factors such as internal 50-pin, PowerBook, and external DB25. SCSI2SD requires proprietary software to configure the disks it presents, and then requires a low-level tool like dd to write a disk image at a particular offset on the SD card.

In 2017, RaSCSI, since renamed to PiSCSI, did this with a board attached to a Raspberry Pi or Pi Zero, but it requires a full Linux OS to serve up everything which makes it kind of overkill in my opinion, as it takes a while to boot all of that to serve a computer that wants its hard drive available within a few seconds.

In 2018, ArdSCSino-stm32, later forked to BlueSCSI, did this using a Blue Pill STM32 Arduino, and improved on SCSI2SD by using a regular FAT filesystem on its SD card so disk images could be configured just by copying them to the card and giving them particular filenames (much like the isostick did back in 2012) such as HD4-macplus.hda to present that file as a hard disk on SCSI ID 4.

In response to the global chip shortage in 2022, the company that created SCSI2SD created a new product called ZuluSCSI which forked BlueSCSI's firmware but was then entirely rewritten. Then in 2023 in a round-about fashion, BlueSCSI forked the rewritten ZuluSCSI firmware and created a new BlueSCSI v2 board which uses the $4 Raspberry Pi Pico RP2040 microcontroller.

DaynaPORT Ethernet

In addition to SCSI hard disks and Zip drives, on my Macintosh Plus workstation and BBS computers I use DaynaPORT SCSI/Link-3 SCSI ethernet devices which add 10baseT networking over SCSI. I also use a Dayna Pocket SCSI/Link for my PowerBook 100/180 which works the same way and is powered through the ADB port.

TCP/IP applications on System 6 on the Mac are usually written to use the MacTCP driver. MacTCP then talks to another driver for the particular network hardware to actually exchange packets, such as MacPPP which talks to serial modems, or in this case, the DaynaPORT SCSI driver.

Two DaynaPORT SCSI/Link-3 devices
DaynaPORT SCSI/Link Pocket

In 2021, the RaSCSI/PiSCSI project gained emulation of these Dayna ethernet devices which use a simple SCSI command set for exchanging packets. Since PiSCSI runs on a full Raspberry Pi, it's easy to use its native ethernet or Wi-Fi interface to deal with network traffic and then send it over SCSI through the PiSCSI software (or at least as easy as anything is on Linux).

Last year I implemented similar emulation in the PCE emulator. I don't usually use classic Mac OS in an emulator, but I found it useful to have a System 6 environment on my laptop when I was away from my Mac Plus workstation and wanted proper networking.

VCFMW

Around this time last year, I started talking to Eric Helgeson of the BlueSCSI project who was looking to make a classic Mac app that could transfer files from a classic Mac to the BlueSCSI itself which would then transfer to the FAT filesystem on the SD card, rather than into the virtual disk contained on the SD card. This way a new hard disk or CD-ROM image could be uploaded from a Mac which would then show up as a new drive to Mac OS without having to potentially disassemble the computer the BlueSCSI is in to remove its SD card.

After helping Eric with some classic Mac OS programming, we met at the Vintage Computer Festival Midwest last September and I learned about BlueSCSI's upcoming v2 design which was set to use the Raspberry Pi Pico. I told him that if it could use the newly released Pico W, I would be interested in writing the BlueSCSI firmware code to emulate a DaynaPORT ethernet device via its Wi-Fi.

To be clear, this wouldn't be like many ESP8266 projects that handle all of the TCP/IP and present some simplified interface to their host computers, but would be more of a media converter just connecting to Wi-Fi and presenting an ethernet interface to the host computer which would handle TCP/IP itself.

Once the BlueSCSI v2 design was finalized, Eric and Androda sent me a prototype board and I got to work.

picoprobe

After fumbling around with the PlatformIO environment in Visual Studio Code (two things new to me) needed for BlueSCSI, I was able to build and test the firmware on my test device but was limited to printf-style debugging. I then learned about picoprobe which is special firmware running on another Pico that communicates with the target Pico over its debug pins, which then allows stepping through code and inspecting variables inside of the PlatformIO interface. (I later learned there is a Pico WH model that breaks out these debug pins in a nicer way.)

picoprobe (left) connected to BlueSCSI

Pico PIO and SPI

To perform SDIO traffic as fast as possible, the BlueSCSI firmware offloads SPI traffic to the RP2040's Programmable I/O (PIO). Unfortunately the Wi-Fi chip of the Pico W also uses the same PIO instance so once Wi-Fi was activated, SDIO broke. Getting everything cooperating together required Androda to do some extensive debugging and while waiting for that, I started on the SCSI code in the firmware with the SD card support disabled so at least Wi-Fi and SCSI could work together.

Since I had already written code in PCE for the DaynaPORT, it was just a matter of adapting it to BlueSCSI's code and making it configurable like hard disks are by creating a file on the SD card like NE3.txt to add a network interface on SCSI id 3. By May of this year, I had a virtual device presenting to the Mac and recognized by Danya's System 6 driver and utilities:

DaynaPORT Diagnostics Utility showing a BlueSCSI

One of the commands that the Dayna driver sends to the DaynaPORT early on is to ask what its MAC address is, and this has to have Dayna's vendor prefix of 00:80:19. Since the Pico W would just be converting layer 2 traffic from Wi-Fi to SCSI, this meant either changing the Pico's MAC address to match the virtual Dayna device, or rewrite every outbound Wi-Fi packet to have the Pico's MAC and rewrite inbound packets to replace it with the Dayna MAC. Since I wasn't sure where that Dayna MAC might show up inside of any packets, I chose the former. Unfortunately being able to change the Pico's MAC address required recompiling the upstream driver library, which meant BlueSCSI now has to maintain our own Arduino-Pico and Pico-SDK trees with this pre-compiled library change.

SCSI Debugging

Once packets started flowing in from Wi-Fi and in from the Mac, I ran into an issue where packets going out to the Mac through SCSI were getting dropped, either by the DaynaPORT driver or MacTCP. This took a while to track down because everything I was seeing was showing the bytes being sent over SCSI were identical to what I received over the air, but the Mac was just ignoring them. I used PCE to show that the packets being sent to an emulated system was identical as well, and it was being processed by the virtual Mac just fine.

I eventually purchased this old Ancot DSC-202F SCSI analyzer on eBay which has a command-line interface over a serial port.

Ancot DSC-202F SCSI Analyzer hooked up to a BlueSCSI

The Ancot allowed me to watch the SCSI bus traffic and dump the commands and data to verify that things were leaving the BlueSCSI properly. I compared traffic from the BlueSCSI to what my real DaynaPORT device sent and it looked identical, which was helpful but also frustrating.

00B47: Bus free
00B48:    Arbitration   /80 (7)
00B4B:    Select        /88 (3,7)
00B4F:       Command    /08 (Read/Receive)00 00 05 F4 C0
00B57:          Data-In /00 66 00 00 00 00 00 80 19 10 0B E9 00 E0 67 1F
00B67:                   CB 67 08 00 45 00 00 54 DF D9 00 00 FF 01 58 75
00B77:                   C0 A8 01 01 C0 A8 01 08 08 00 DC FD 80 46 00 01
00B87:                   A6 27 78 A7 EC 66 29 B3 39 EE 62 17 68 24 21 CC
00B97:                   0C 02 FD 06 E7 9B DC B3 18 19 1A 1B 1C 1D 1E 1F
00BA7:                   20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
00BB7:                   30 31 32 33 34 35 36 37 84 32 6C 6C
00BC4:       Status     /00 (Good)
00BC6:       Message-In /00 (Cmd Cmplt)
00BC8: Bus free

Eventually Androda figured out what was required to get the PIO for SCSI, SDIO, and Wi-Fi all working at the same time and just when I was growing weary of debugging the SCSI issue, Eric pointed out that the PiSCSI code had something my new BlueSCSI code didn't:

[10:29] erichelgeson: i wonder if it's sending the reponse too fast, piscsi has a send delay
[10:30] erichelgeson: [...]/scsi_daynaport.cpp#L599
[10:30] Androda: Looks like that's set to 2 + 4 (6)
[10:33] jcs: i'll give it a shot
[10:41] jcs: dammit
[10:41] jcs: that was it

Apparently the BlueSCSI was sending traffic to the Mac too fast and the driver needed time to process the incoming packet after receiving its size, but before receiving the actual packet data. My code for PCE didn't have any delay and seemed to work fine, which was strange.

With all of that in place, I was finally able to join my #cyberpals IRC channel from my Macintosh Plus with its native TCP/IP stack, over Wi-Fi, through my Wallops IRC client. Though of note, this was booted with its SCSI hard disk on a SCSI2SD, not (yet) on the BlueSCSI which was providing Wi-Fi.

Wallops IRC client on a Macintosh Plus

Something I noticed while briefly testing it was that during large Fetch downloads, it would pause once in a while but then catch up. I didn't give it much thought, but it ended up being another show-stopper.

Wi-Fi Debugging

By this point in June, I needed a break and the project got "paged out" of my desk since I generally only have room for one big project at a time. The other BlueSCSI developers were testing it on newer, faster Macs and seeing the same pauses in traffic but it was much more frequent there.

In August, Eric wanted to release a beta BlueSCSI firmware with the new Wi-Fi code, in hopes that someone could devote more time to finding the cause of the traffic issues. This was enough motivation for me to "page the project back in" and try to figure out the cause.

[02:00] jcs: i spent some more time debugging the wifi situation and found something, are you still set on releasing it on friday @erichelgeson?
[08:00] erichelgeson: I can hold, what did you find?
[08:06] jcs: packets are dropping during wifi processing
[08:07] jcs: not the scsi injection
[08:53] Androda: Is it a raspberry pi framework issue?
[15:59] jcs: ugh, found it
[15:59] jcs: and now i get sustained 60kb/sec via fetch

While using the excellent Hex Packet Decoder on packets printed out from the BlueSCSI's console, I noticed that packets were coming in through Wi-Fi properly, getting added to the ring buffer, but then getting corrupted before they were being sent out through SCSI. This turned out to be a dumb mistake I made when cleaning up the code to use #define constants which ended up reversing the dimensions of the ring buffer so it became an array of packets[packet_size][queue_size] instead of packets[queue_size][packet_size].

When storing packets, instead of properly writing to packet[1] which should have been packet_size (1520) bytes offset from packet[0], it was writing at offset queue_size (30), in the middle of packet[0]. This didn't matter if packet[0] had already been transmitted to the Mac, but on larger transfers where the queue has multiple incoming packets to transfer, the current packet has the next packet written to the middle of it. The delay and restart seen was the TCP stream not getting an expected packet because it was corrupted, the sending side not getting an ACK for it, and then having to timeout and re-transmitting it.

Mac Plus and Portable

Once transfers were working at proper speeds, Eric announced a beta build of the BlueSCSI firmware and we got some initial feedback on it. Notably it broke booting hard disks on the Macintosh Plus, which I hadn't been testing with because I was using mine for other things and testing meant constant reboots. The Macintosh Plus and Macintosh Portable have notoriously "special" SCSI booting code in their ROM due to them being released before the SCSI spec was formalized and the SCSI2SD/ZuluSCSI/BlueSCSI already need workarounds to handle booting on these machines.

Since I couldn't leave my favorite classic Mac out in the cold, I dug into what broke in the BlueSCSI code between the last release and adding my new Wi-Fi code. While my firmware changes didn't directly change any of the disk handling code, it did bring in newer Arduino, Pico, and cyw43 driver code that could have broken something.

After wading through a recursive maze of dozens of Git submodules and many hours of trying to pick apart commits in each one, I finally figured out what broke the Mac Plus.

[16:37] jcs: ok, the "PlatformIO Setup" commit builds and boots, which moved to a different raspberrypi tree
[16:38] jcs: hard to pick apart the pico-sdk and later changes
[21:17] jcs: i can compile and boot with "Missed a few steps", "build: change board and core to upstream", "don't hard-code watchdog timer id", "uart_init to 115200"
[21:19] jcs: this leaves out "Booting from SD, and comms with CYW43 working"
[22:06] jcs: getting there...
[22:39] jcs: my mac plus is booted with wifi
[22:40] erichelgeson: Wow! What was the culprit?
[22:43] jcs: this change broke it:
-#define LED_ON()     sio_hw->gpio_set = 1 << LED_PIN
-#define LED_OFF()    sio_hw->gpio_clr = 1 << LED_PIN
+#define LED_ON()     cyw43_gpio_set(&cyw43_state, 0, true)  //sio_hw->gpio_set = 1 << LED_PIN
+#define LED_OFF()    cyw43_gpio_set(&cyw43_state, 0, false)  //sio_hw->gpio_clr = 1 << LED_PIN
[22:45] erichelgeson: Led logic. The first place I'd look when debugging boot issues
[22:46] jcs: right?

Wi-Fi DA

With the Mac Plus now booting, I started writing a Wi-Fi Desk Accessory which lives in the Apple menu once installed. Just like a modern computer with its own Wi-Fi card, I wanted to be able to scan for networks, change to a different one, and see the current signal strength.

I created the Desk Accessory in THINK C 5 and when launched, it scans the SCSI bus to find the BlueSCSI, then gets the current network information such as SSID and RSSI. It then launches a background Wi-Fi AP scan and when the results are ready, it populates a menu with the AP list. When a different network is selected, it prompts for the password and then issues a connection command to the BlueSCSI.

DA Menu
List of nearby networks
Prompting for a password

EtherTalk

AppleTalk is the common protocol that Macs use for file and printer sharing. I use it on my local network with Netatalk 2.x to host a file server on a Linux VM that my Mac Plus running System 6 can access, while also supporting my M2 MacBook Air at the same time which makes it very easy to shuffle files between the two. I also use Timbuktu over AppleTalk to be able to remotely control my BBS Server Mac Plus from my workstation Mac Plus, just like using VNC.

Historically AppleTalk ran over LocalTalk, which used cables daisy chained between Macs and printers. AppleTalk eventually gained the ability to send packets over Ethernet, which became EtherTalk. There are also devices that help bridge LocalTalk to EtherTalk.

I had tried getting EtherTalk working in PCE but was never able to receive anything over my laptop's Wi-Fi connection, so I just assumed it was not going to work well over Wi-Fi for BlueSCSI.

I read about an informal LocalTalk over UDP specification and thought about possibly integrating it into the BlueSCSI firmware where it could convert EtherTalk packets it receives from the Mac into UDP packets, enabling EtherTalk over Wi-Fi. After looking at the code of multitalk, I realized that EtherTalk was just multicast traffic and was likely being received over Wi-Fi just fine, but that the CYW43439 Wi-Fi chip on the Pico W was filtering it out.

After updating the cyw43 driver we are using to include newer multicast configuration functionality, I was able to configure the Wi-Fi chip to allow the 08:00:07 prefix used by AppleTalk. Once that filter was modified, I started to receive EtherTalk traffic over the Wi-Fi interface and could connect to my Netatalk share.

File server appearing over EtherTalk over Wi-Fi
File server share

Performance

The Pico W is not a terribly fast device, but neither are the vintage computers it's talking to so it doesn't really matter. On a faster Mac, it can do a few hundred kilobits/second which is about on par with the real DaynaPORT ethernet devices. Since the packets are all going over SCSI, doing an upload or download of a file that has to be read or written to the SCSI disk can put quite a bit of contention on the SCSI bus.

The biggest issue with this project is probably the tiny integrated antenna on the Pico W which limits Wi-Fi reception and throughput. Especially once it's been put into a laptop or other metal cage, its signal strength can suffer quite a bit. Unfortunately the Pico W has no provision for an external antenna and I'm not sure how easy it is to hack one in, but this would certainly be helpful for portable machines like the Macintosh Portable or 68k PowerBooks.

Release

The pull request for the BlueSCSI firmware implementation is still pending as more people test out the beta firmware and report bugs. The next major firmware release will have full Wi-Fi support and BlueSCSI distributors are already shipping v2 units with Pico W boards as an option.

BlueSCSI v2 in a Macintosh Portable
BlueSCSI v2 in a PowerBook 180

The Wi-Fi Desk Accessory for System 6 and 7 will be released shortly as well once I do a bit more testing. Without it, Wi-Fi support still works with the SSID and password configured in the bluescsi.ini file on the SD card.

By the way, I'll have a table with some of this Macintosh stuff at the Vintage Computer Festival Midwest September 9th and 10th so if you see me there, say hello.

Questions or comments?
Reply on Mastodon or e-mail me.