The Story of the Network Driver

[Translated from Persian]

My friends wanted me to write about the network driver story...

It was around 2001. I was in first/second grade. Me and Roozbeh Pournader were working on the FarsiWeb project at the Sharif University of Technology’s Computing Center. One Thursday, they gave us a new computer that was much better than the ones we had. The only thing they said was to test it before breaking the seal, in case it was broken and needed to be sent back. We tested it; its floppy drive was broken, but we didn't care.

The important thing was that its network card needed a driver, which was on a floppy disk, and although it was also available on the internet, it wasn't installed on the machine and hence wasn't available. So, we couldn't connect to the network, test the network card, break the seal, install our hard drives, etc. We had to wait until Saturday (Friday is the weekend in Iran) to either send the machine back or find someone with a CD writer to copy the DLL file onto a CD so we could install it on the machine. It was frustrating. It was midnight, and we were itching to get the machine running by morning and enjoy working on it throughout Friday.

We wondered what to do. We remembered a technology from before the network card era: the link cable. We didn't have the cable itself, but decided to make one. A serial link cable doesn't need more than 4/5 wires. The details were in the Quick Basic manual! We stripped a piece of network cable and inserted it into the serial port holes of both machines, but we couldn't establish a connection. Probably because we had Windows 2000 or so, and we were trying to link through Norton Commander (nc) under DOS, which Windows 2000 probably didn't like, at least not without formalities. Anyway, we also gave up on the non-existent link cable.

We thought about typing it in. Both of us were touch typists, and the zipped file was only 40 kilobytes; nothing much. We did a test; first off, 40k becomes 80k in base 16 (hex), and 80k is 40 pages full of those DOS screenfulls. Second, with an average speed of 200 characters per minute, it would take 400 minutes, meaning 7 hours! And that's without rest and assuming the one reading doesn't choke! Third and most importantly, that 200 characters-per-minute speed is for typing English text, which is highly redundant, not practically random data (zip output)! So, this plan also hit a dead end.

There were no standard solutions left. It was midnight, and it was time to give up and sleep. But Ali Agha’s restaurant was also closed, and overall, we were not in the mood to give up. I checked the back of the machine to see what other ports it had! The only thing I found, but indeed significant, was the sound card!

I glanced across the room at the other peoples' table where there was one of those new speakers with a subwoofer. The sound input cable from the speaker to the subwoofer detached at both ends and was symmetric. I took the cable, plugged one end into the output of the neighboring machine, and the other end into the input of the target machine. So, 50% of the problem was solved, leaving the software! In other words, we needed a driver for our sound-card-turned-into-a-network-card, through which we could transfer the real network card driver. "We need to go deeper!"

First, we set up and tested the sound transfer capacity. We played a sound on one side with Windows Media Player, and recorded it on the other side with Windows Voice Recorder. It worked and played back correctly. We set the recording settings to 44100Hz mono 8bit. The output file was WAV, which has a simple header and raw data. That’s for that.

As a committed IOI participant, I always had a CD with Turbo Pascal and various tools with me. We booted the compiler on both systems.

The next part was converting the input data (the zipped network driver file) into an audio file for transmission, and of course, receiving and recovering it on the other side!

I did a quick Google search (back then, "googling" hadn't made it into the dictionary yet!) and wrote about 30 lines of code that would generate 100 output samples for each bit of the input file: if the input bit was zero, the output was all zeros, otherwise, it produced a reference "A" note, meaning a sinusoidal wave at 440 Hz frequency. That’s for that.

We tried a sentence, sent it over. Roozbeh was sitting on the other side behind Pascal, and we proceeded. We take a derivative first: calculate the difference between each sample and the previous one; then, if its absolute value was greater than 4, write one, otherwise write zero. Then take every hundred samples, take the "majority" and write it in the output. That would be our data, if everything was transmitted flawlessly (!!). If…

The first problem was figuring out when / where the data started. So, I added several 0 bits and a 1 at the beginning of the data as a start signal, and looked for this pattern in the decoder. The second problem was that after decoding a few correct bits, it would get messed up. To increase reliability, I changed it from 100 samples per bit to 200 samples, and for ease of segmentation (!!), I added 100 samples of zero on either side of each bit. On the other side, we decoded the same way as before, but now for each bit, four bits were coming through, expecting the first and last to be zero (separators), and the middle two to be our data, expecting them to be the same. In the code, however, if either of the middle ones were on, we declared the output to be on.

We tested it. A few words would go through, then it would get jumbled. To understand what was going on, before that last conversion, I wrote the output with 0s and 1s and looked at it. The combination that should have been like this:

01100000000001100110

01100110011001100000

00000110000001100110

00000110011000000110

...

Meaning from every four, the first and last should be zero, and the middle two the same, then after a few lines, it shifts, meaning, for example, it becomes:

01100000000001100110

01100110011001100000

00000110000001100110

00000110011000000110

00001100110011000000

11000000110000001100

...

This means the frequency of the sound card that's reading is not exactly 44100 Hz, nor is the one recording. In fact, none of them is exactly that, but anyway, this frequency difference needs to be considered in our driver.

Those separators we added, which unnecessarily doubled the sound time, now come into play: from every four characters, the first and last must be zero. I tweaked the decoder to adjust itself whenever it saw otherwise. That is, if the first character was unfortunately one, understand that it's rushed and actually read the second character, so read one less character, and vice versa for the fourth character.

With this adjustment, several test sentences went through flawlessly, so we went to transfer the network driver! A quick calculation:

40 * 1024 * 8 * 100 * 4 ÷ 44100 ÷ 60 ≈ 50

We took a fifty-minute nap while the first machine read to the second, which eagerly took notes and memorized. We got the file, unzipped it, and it failed, giving an error. Now what?!

We did a visual inspection of the file in nc, saw that it had mostly transferred correctly and read the beginning and end, so we sat down to correct its errors! We sat down on the two machines, One of us dictated, and we both typed: 20 lines of code to take a file and two numbers, start and end, and write the sum of the file's bytes from start to end.

Then we started binary searching for the bytes that differed between my file and Roozbeh's... That is, I would give a start and end, we would compare the sums, if they differed, we would split the range and start over (recurse).

In the end, we found three wrong bits; we corrected them manually, and the file unzipped. The network driver was installed, and the network card was tested! We returned the sound cable to the speaker it belonged to, turned off our new machine, had our last tea, and since it was already 7 AM, we left the center to go to sleep.

That was the story of the network driver. A few educational points from this story, in my opinion (after fifteen years), about solving problems:

  1. Neither my mom had ever written a modem driver, nor my dad, nor had I read or learned anything about it, and probably the same for Roozbeh. But with a systematic approach, we broke the problem into pieces that we knew how to solve.

  1. When debugging a problem, it helps tremendously if you can produce a proper visualization. In such cases, the power of the human visual system can lead to understanding in a fraction of a second, something that without this method could take hours to achieve.

  1. Perhaps even more fundamentally, it's important to learn how to proceed every time you say “it doesn't work.” Essentially, this always comes down to dividing the part that doesn't work into multiple parts and testing them one by one to narrow down the circle of suspicion until the problem becomes obvious.

  1. Every solution is a tradeoff. In this case, we balanced between the complexity and, consequently, the implementation time of the coder and decoder algorithms on one hand, and the length of the resulting audio file on the other. So when the solution reached a point where the audio file was fifty minutes long, instead of spending three hours to reduce it from fifty minutes to five, we accepted that it was faster to send it as is. It didn't need to be the shortest, just short enough!

    Let me also digress: We had a friend who preferred to hire graduates from Azad University in his company and not from Sharif University, we asked why, he said because when you give a Sharif University graduate a problem and tell them to solve it in two weeks, they come back in one week with the solution, after another week they've made it twice as fast, after another week they've made it twice as smaller, after another week they've made it twice as stronger, ... When you give an Azad University graduate a problem and tell them to solve it in two weeks, they come back in two weeks, hand it over, and move on to the next problem. :))

  1. Having the right tools makes the difference between success and failure. I see many people wanting to debug a heap of code, but they don't even have the tools, execution environment, and, in short, the prerequisites to break the problem into smaller pieces. They can only run the whole thing and say "it doesn't work."

    Note: If you deal with computers on a daily basis, tool writing should be one of your habits. Now, a tool can be an Excel macro, a shell script, Python code, or C code. It can be disposable or used for twenty years. It can remain just yours, or find thousands of users.

  1. Ultimately, typing it might have taken just as long. And it didn't really matter if we didn't find out whether the network card worked until Saturday! But the joy we experienced that night until morning, I haven't felt in solving many other problems to this day, and fifteen years later, I'm writing it down for you in detail.

  1. Whenever you want to say "it can't be done," think about why it can't be done. Can you prove it can't be done? One of the other things I've learned in these fifteen years is not to pay attention when others say "it can't be done." In most cases, I've shown that it can be done.

  1. I learned more from these kinds of games and challenges than in class!

About ten years after this story, Roozbeh sent me a T-shirt with the following design, bless him, those were the good days...

Behdad Esfahbod

26 September 2015

English translation: 2024

Tehran

[CC BY]