{"id":1768,"date":"2026-08-16T00:21:01","date_gmt":"2026-08-16T00:21:01","guid":{"rendered":"https:\/\/fatsquirrel.org\/oldfartsalmanac\/?p=1768"},"modified":"2026-08-16T01:47:29","modified_gmt":"2026-08-16T01:47:29","slug":"adventures-in-x-25-part-2-ai-zig-and-vintage-hardware","status":"publish","type":"post","link":"https:\/\/fatsquirrel.org\/oldfartsalmanac\/adventures-in-x-25-part-2-ai-zig-and-vintage-hardware\/","title":{"rendered":"Adventures in X.25: Part 2 \u2013 AI, Zig, and vintage hardware"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Following on from the <a href=\"https:\/\/fatsquirrel.org\/oldfartsalmanac\/adventures-in-x-25-part-1-the-obsession\/\">previous post<\/a>, my obsession with X.25 did not wane at all &#8211; in fact it may have strengthened. And, if that wasn&#8217;t bad enough, it has spawned a couple of new ones.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><a href=\"https:\/\/fatsquirrel.org\/oldfartsalmanac\/wp-content\/uploads\/2026\/08\/20260815_153735-scaled.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"744\" src=\"https:\/\/fatsquirrel.org\/oldfartsalmanac\/wp-content\/uploads\/2026\/08\/20260815_153735-1024x744.jpg\" alt=\"\" class=\"wp-image-1771\" style=\"aspect-ratio:1.376334726346347;width:474px;height:auto\" srcset=\"https:\/\/fatsquirrel.org\/oldfartsalmanac\/wp-content\/uploads\/2026\/08\/20260815_153735-1024x744.jpg 1024w, https:\/\/fatsquirrel.org\/oldfartsalmanac\/wp-content\/uploads\/2026\/08\/20260815_153735-300x218.jpg 300w, https:\/\/fatsquirrel.org\/oldfartsalmanac\/wp-content\/uploads\/2026\/08\/20260815_153735-768x558.jpg 768w, https:\/\/fatsquirrel.org\/oldfartsalmanac\/wp-content\/uploads\/2026\/08\/20260815_153735-1536x1116.jpg 1536w, https:\/\/fatsquirrel.org\/oldfartsalmanac\/wp-content\/uploads\/2026\/08\/20260815_153735-2048x1489.jpg 2048w, https:\/\/fatsquirrel.org\/oldfartsalmanac\/wp-content\/uploads\/2026\/08\/20260815_153735-624x454.jpg 624w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The venerable synchronous serial card needed a driver; these devices are substantially more complicated than the regular (and well supported) async adapters. Owing to the age of the device, the most &#8220;recent&#8221; driver for Linux targeted a 2.6 kernel, but at least it was open source and didn&#8217;t need any proprietary blobs. When I took a look at the code, it appeared to be surprisingly simple &#8211; just a wrapper around reading and writing registers, all of which were well documented on the datasheet. I&#8217;m no expert on device drivers but I have worked on them in the past, so I decided to see if I could port it up to a recent kernel. And this is where the spectre of AI comes into the picture.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">[The expression &#8220;AI&#8221; is totally inappropriate for what everybody currently uses it to mean. In case you don&#8217;t know what LLMs are, just read &#8220;LLMs&#8221; as &#8220;AI&#8221;]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Generally speaking, I&#8217;m wary of LLMs, but there&#8217;s no denying they can be exceptionally useful tools. Part of my wariness is that they can also be wildly wrong, with the same level of certain confidence you&#8217;d expect from a subject expert. Nonetheless, bringing the driver up to date sounded like more of a tedious task rather than something difficult, so I put free Claude on the case. The result was almost perfect and took a matter of minutes. To get it working I had to make a few tweaks and do some diagnostics, but all in all it did a great job. I forked the original Fastcom driver repo and <a href=\"https:\/\/github.com\/veghead\/escc-pci-335-linux-driver\">uploaded it to Github<\/a> (it&#8217;s OK, the original license was GPLv2).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Having a working driver yielded two character device nodes which do <em>not <\/em>behave like ttys. Well, you <em>can <\/em>read and write the data you want to transmit and receive to it &#8211; but without configuration this doesn&#8217;t really mean anything. The Cisco certainly wouldn&#8217;t know what to do with it, and besides, the whole point of this project was to run X.25 over it. The configuration involved calling a bunch of ioctls on the device which mapped quite nicely to the device registers. To initialise the card, the driver has a utility that reads an INI file, literally containing register names and values, then fires off a bunch of ioctls that set them. Crude but effective.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The link-layer protocol I was hoping to run over the serial link was LAPB: a bit-oriented protocol (i.e. the framing needn&#8217;t involve bytes) that is the layer 2 standard of X.25. It&#8217;s a massively over-engineered protocol that supports a ludicrous number of scenarios [<a href=\"https:\/\/www.youtube.com\/watch?v=aHy8KCvc864\">see my video on OSI<\/a> to see why this was fatal for X.25] and even after several months I&#8217;m still working out stuff about it. Fortunately, the serial card is smart enough to handle the majority of the donkey work of LAPB that helps establish the link and keep it alive. Despite this, I&#8217;ve had a bugger of a time getting it to work properly, even with ludicrous amounts of consultancy with various LLMs. But, even with most of LAPB taken care of, I still had to deal with the X.25 packet layer protocol (which Cisco refers to as &#8220;X.25&#8221;).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Before you explode with frustration that I&#8217;m not aware of Linux&#8217;s support for LAPB <em>and<\/em> &#8220;X.25&#8221;, you need to understand that I know it&#8217;s there, but I also know it&#8217;s soon to be removed, and I really want to deal with this in userland. That&#8217;s my rule, apparently.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The aforementioned XOTPAD had implemented X.25 in Rust, a language I like but am not fully convinced by, and so that was the logical place for me to start. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After having a crack at integrating this card&#8217;s interface into xotpad I found the implementation also over-engineered, and clunky. Another side note here &#8211; I&#8217;m still a C and Go guy, and I detest C++. Rust, to me, feels like another C++: stuffed full of features that do nothing but make certain kinds of developers feel like artists. I love the simplicity and flexibility of C, but also I appreciate that it has to go; even for seasoned developers, keeping the code secure is nigh on impossible in C.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I looked into Rust&#8217;s support for bit-fields and found the language supports them through some dude&#8217;s library. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So, wouldn&#8217;t this be a great opportunity to try another language that sounded like a safe and modern version of C? After agreeing with myself, I decided to learn Zig by implementing X.25&#8217;s layer 3 protocol (PLP). Zig has great support for structs and bit-fields right at its heart, with types including u1 to uN. Sounds like fun.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here we return to LLMs. Rather than go straight from &#8220;Hello World&#8221; to packet definitions, I asked an LLM to generate a starting point with the bit-fields all laid out for me, which it did quite well. But then I asked it to generate me a simple program that reads from a file and prints the bytes in hex. That code it gave me did not work. After a bit of Googling I discovered that Zig recently reengineered a lot of fundamental standard library features. When I asked the LLM about this, it apologised (of course) and rewrote the code for Zig v0.16+. That code didn&#8217;t work either. I tried a couple of different LLMs and they all produced broken code.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Searching for information was also particularly frustrating because Google decided to break its excellent search engine by turning it into the front end of an LLM. It gave me incorrect information!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Rather than give up and go back to C or Go, I developed another obsession to do this thing. So, I went back to traditional methods, like reading the docs [IMHO zig has crap documentation, especially for new features], and looking at the code. At this point, LLMs were pretty useless as advisors.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After a few days of anger and frustration, I started to feel like I was beginning to get it. LLMs again became useful by explaining why things didn&#8217;t work, which I found fascinating. They can give bad advice, but they can then explain why it was bad advice. They sound so human but this is very inhuman behaviour. This became a major source of bemusement which I will detail later.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With help from Gemini, I got a working program that read LAPB\/X.25 packets that the card wasn&#8217;t able to process by itself, and decode them. A bit. I even got it to respond to a few in a way that made the Cisco happy! But what I didn&#8217;t manage to do was produce the correct response to make the X.25 layer 3 protocol happy.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You would not believe how long I spent tying to sort this out; let&#8217;s just say it was multiple 8 hour days (weekends of course). At every step, I asked a couple of LLMs what could be going on and they gave great sounding answers, which turned out to be wrong.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Eventually, this evening, I figured it out by reading the datasheet really closely. Before LLMs, that is what I would have done in the first place.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It turned out that a single register in the serial card contained the wrong value.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now, I had sent all of the register details and connection logs to the LLMs, and they explained exactly what was going on, but they did not spot the error. In fact they confirmed everything was fine! This is what had messed me up.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After I had got it working, I asked the LLM if the error it had denied existed was, in fact, an error. It said yes, absolutely.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now, if I had asked a human Cisco expert what I was doing wrong, they would have been able to spot it right away. <br \/><br \/>If you&#8217;re a Cisco expert, see if you can spot the problem from this log segment<br \/><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>*Apr  5 01:02:17.110: Serial0\/1: LAPB O CONNECT (7) IFRAME 0 0\n*Apr  5 01:02:17.118: Serial0\/1: LAPB I CONNECT (2) IFRAME (R\/ERR) 0 1\n*Apr  5 01:02:17.118: Serial0\/1: LAPB O ERROR (5) FRMR 0x20 1 0 R W <\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">I&#8217;m guessing that you will. Feel free to <a href=\"https:\/\/fatsquirrel.org\/veghead\/contact\/\">let me know what you think<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><br \/>But, these LLMs, which appear to be experts, didn&#8217;t see it, and I&#8217;m pretty confident they would never have spotted it, because it was clearly not a scenario they&#8217;d been trained on.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now, imagine an LLM as a car mechanic. The equivalent would be something like a dead spark plug, and it checking everything from the tyre pressure to the seatbelts, finally putting forward suggestions about lack of gas, too much stuff in the trunk, a broken carburettor, but ultimately not working it out. A human mechanic would take one look (or listen) and realise a spark plug was out. If you tell the LLM what was wrong, it will immediately tell you why that&#8217;s obviously the reason, and then point out that this model of car frequently has problems with the spark plugs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This was not a one-off: in my day job we have access to <a href=\"https:\/\/cheekyweekly.blogspot.com\/2010\/09\/profile-posh-claude_25.html\">posh Claude<\/a>, and in the last few weeks I have been trying to get the money&#8217;s worth. My experiences are evidently different to every other developer in the world (if you believe people&#8217;s posts on <a href=\"https:\/\/www.reddit.com\/r\/mildlyinteresting\/comments\/pdxw6\/comment\/c3onmz6\/\">forums<\/a> and Linkedin) because they have been confidently imparting incorrect information. This is possibly down to working with firmware rather than the more common areas of development &#8211; the biggest issue seems to be with them making up registers, SDK functions, and failing to comprehend the intricacies of compilers. And this is where the fundamental flaws in LLMs become apparent: they can only produce output based on things that are commonly discussed online. Couple that with the fact they have been <a href=\"https:\/\/arxiv.org\/abs\/2409.05746\">mathematically proven to produce incorrect results, forever,<\/a> I&#8217;m genuinely concerned how seriously humanity is investing in them.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To be continued&#8230;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Following on from the previous post, my obsession with X.25 did not wane at all &#8211; in fact it may have strengthened. And, if that wasn&#8217;t bad enough, it has spawned a couple of new ones. The venerable synchronous serial card needed a driver; these devices are substantially more complicated than the regular (and well [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1771,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-1768","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-geek"],"_links":{"self":[{"href":"https:\/\/fatsquirrel.org\/oldfartsalmanac\/wp-json\/wp\/v2\/posts\/1768","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fatsquirrel.org\/oldfartsalmanac\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fatsquirrel.org\/oldfartsalmanac\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fatsquirrel.org\/oldfartsalmanac\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fatsquirrel.org\/oldfartsalmanac\/wp-json\/wp\/v2\/comments?post=1768"}],"version-history":[{"count":5,"href":"https:\/\/fatsquirrel.org\/oldfartsalmanac\/wp-json\/wp\/v2\/posts\/1768\/revisions"}],"predecessor-version":[{"id":1774,"href":"https:\/\/fatsquirrel.org\/oldfartsalmanac\/wp-json\/wp\/v2\/posts\/1768\/revisions\/1774"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/fatsquirrel.org\/oldfartsalmanac\/wp-json\/wp\/v2\/media\/1771"}],"wp:attachment":[{"href":"https:\/\/fatsquirrel.org\/oldfartsalmanac\/wp-json\/wp\/v2\/media?parent=1768"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fatsquirrel.org\/oldfartsalmanac\/wp-json\/wp\/v2\/categories?post=1768"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fatsquirrel.org\/oldfartsalmanac\/wp-json\/wp\/v2\/tags?post=1768"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}