Showing posts with label linux. Show all posts
Showing posts with label linux. Show all posts

Sunday, February 4, 2024

A User-Space File System for Fun and History

I got my start with computers as a kid with an Atari 800 way back when.  When I'm feeling nostalgic, I still enjoy pulling it out and playing with it.  These days, I'm more likely to pull out an emulator than the real thing, as they're now so precise that it's very difficult to find a difference, and it eliminates all the hassles involved with disks that were slow and may have failed with age.  That means I need tools for manipulating disk images.

So there I was...  I was posting at AtariAge about a tool I wrote to extract the files from a disk image, and another to create an image containing a set of files, and someone remarked that they were surprised that nobody had created a Linux kernel module to mount the disk image files as a file system.  That was a dangerous comment.  Writing a Linux kernel module to support Atari file systems is certainly possible, but would be a lot of work.  But there's another option, which is to write a file system in user space using a package called "FUSE."

I've written a file system using FUSE before, and it's far less complicated than writing one in kernel space.  Besides making debugging vastly simpler, you can also just skip lots of things and the library will take care of it for you.  And with Atari file systems, there are, of course, many modern features that just aren't supported.  And another big advantage of FUSE is that it works on MacOS as well as Linux, so it makes the project available to a wider audience.  (It also runs on the Windows Subsystem for Linux.)

But note that I said Atari file systems, not file system.  Depending on how you count, I found at least seven or perhaps ten different file systems.  (Several are variations, so they can share most of the same code.)  This means the first task is to detect which file system a disk image is using, if any (as many programs just read disk sectors directly and didn't use a file system at all).  

So the first step is to have sanity checking routines to determine if a disk image is consistent for each file system.  Then implement the key file system features for each DOS version.  The key functions that had to be implemented are reading a directory, getting file attributes, reading a file, and writing a file.  Most other features are trivial or unsupported for most file systems, but I implemented as much as possible.  For example, files could be locked, which I interpreted as the write-permission bit for the file owner.  Some file systems did support time stamps on files, but for most, I just copied the time stamp on the disk image file.

But once it was working, it was quite simple to add more features.  So I made files like ".sector361" that would contain the raw bytes of sector 361.  The file doesn't have to exist in the directory, but as long as it's supported by the get attributes, read, and possibly write functions, it will work just fine.  This feature was also very helpful in debugging the file systems, as I could look at a hexdump of raw sectors when something wasn't behaving as expected, or when I had to reverse engineer a file system with incomplete specifications.

Of course, every time I finished a file system or new feature, someone on AtariAge would find yet another for me to look at.  That was really half the fun of it.  I discovered a number of file systems used on the Atari that I hadn't encountered before, and there were some weird ones.  The oddities of those file systems probably deserves its own blog post.

But once I had it pretty solid, there was another wrinkle.  There's a special "Atari Partition Table" format for hard disks that's used with some newer add-ons, including flash card readers that people now use with their old computers.  This presented two major problems.  First, the documentation for the APT images, while detailed, had points that were confusing or ambiguitiies, and included many options that aren't actually used.  This required getting sample images from users, as I didn't use it myself.  Second, this meant having a number of different file systems mounted in subdirectories.  That required refactoring the code, including adding a middle layer to call into.  Sometimes adding an extra layer of indirection lets you do magic.

Unfortunately, this doesn't work for all file systems in an APT partition, as the file system code uses a memory map of the image file, and with some APT options, the disk image is no longer a straight linear stream of bytes.  This has to do with fitting smaller sector sizes into 512-byte sectors when sometimes you care a lot more about efficient code than efficient space.  But since I can export the image of the contents of each partition, even if the bytes have to be scrambled by the read and write functions, that image can then be opened separately by a new instance of my program, creating a new mount point.  A bit awkward, but it works.

So that was a fun project.  While this had no commercial value, it was interesting to look at ancient file systems and see how they did things, as well as to play around with FUSE.

So here is the code if you're interested:

https://github.com/pcrow/atari_8bit_utils/tree/main/atrfs

Now I suppose what's needed next is a wrapper to take the code for a kernel file system, and run it under FUSE.  That would allow you to develop a kernel file system with all the convenience of user-space tools, but you could then build the code as a kernel module once it's working.  That's a project for another day.

Friday, August 11, 2017

Installing Gentoo Linux on a Dell Precision 5510

From time to time I realize just how slow and outdated my computer is.  Usually that's long after the corporate policy says I can order a new one, and this time was no different.  The engineering laptop is a Dell (no surprise if you know where I work), and the default model is the Precision 5510.  New computers always come with Windows installed, so my first task is to install Linux.  I figured other people with this or similar models will want to hear about what issues I encountered, so I'm documenting the process here.

The first thing I did was update the BIOS.  There have been some low-level security issues in the press recently, so I figured I should start with the latest base.  This was easy: I booted into the installed Windows, logged in, and went go to http://dell.com/support.  From there I entered my service tag, clicked on downloads, and selected the BIOS.  As I expected, the installed BIOS was a few months older than the latest one.  I'm glad I didn't try to install this under Linux, though I assume there is a way to do so.  It was a simple process of clicking the obvious choices, and then it rebooted and completed flashing.

Now the challenges began.

This is my first laptop without a CD/DVD drive (unless you go back to the floppy days).  So to boot Linux, I needed a bootable USB flash.  I had followed some very complicated instructions to set up a flash drive, but then found that I had much better results by simply taking the Gentoo live CD and using 'dd if=source_file.iso of=/dev/sdb bs=8192k' to copy it.

So it should be simple to boot from the flash drive, right?  Wrong!  Windows set up a "fast boot" where it bypasses most of the BIOS, including the part that watches for holding down F12 for the boot menu.  After some searching, I found that you need to enter "control panel" in the Windows search thing that replaces the Start menu (this is my first time to ever run Windows 10).  From there I used one of the control panels to disable fast boot.  Even with that, it took several tries to get it to recognize the F12 for the boot menu--it checks it very early in the process and moves past it very quickly.

So I booted the Gentoo USB, and started following the installation guide.  I've done this before, so I wasn't expecting too much trouble, but I did have some surprises.  First, I wiped the partition table and set it up for Gentoo.  I chose to use 1GB VFAT for the EFI partition that is also used as my /boot partition.  When everything is good, I'll only mount it to copy over new kernels when updating.  Most of the rest of the drive is my root partition, for which I selected ext4.  The last 64GB I used as a swap partition, which is excessive, but I hate running out of memory and having the OOM killer sacrifice the one thing I wanted to keep alive.

At this point, I found that I had no network.  This is my first modern laptop without an ethernet port.  It came with three different USB-C dongles that I could use, one is just ethernet, one is ethernet, video, and USB, and one is essentially a docking port with lots of ports on it.  What I quickly learned is that USB-C isn't like traditional USB.  USB-C is the connector, but it can use several different protocols over that connector, including PCI, in which case the marketing name is Thunderbolt.  None of that was working in Linux.  More on this in a bit...

So for the time being, I tried copying over stuff using a second flash stick, but that didn't get me very far.  I found an old regular USB ethernet dongle, and that worked fine.  Once I set up my kernel to boot from EFI, everything should have been mostly good, but of course it wasn't.  I had to go back and forth between booting from USB and failing with my kernel.  This was a challenge because I couldn't get the F12 menu to come up once I had wiped the Windows partition.  I ended up switching back and forth between legacy and EFI booting in the BIOS; legacy for USB and EFI for my attempt at my installed kernel.  This probably wasn't necessary, but it worked to get me past the problem.  (I needed to go into the BIOS anyway to tell it to boot the grub loader from EFI instead of Windows.)

Getting the system to boot natively was a huge pain.  Part of the problem is that after the boot completes, Gentoo clears the screen for the login prompt, so you lose the boot messages.  I decided to fix this by commenting out the getty command for tty1 in /etc/inittab.  Now I have to switch consoles to log in, but I can always see the last screenful of the boot.  I eventually got my installed kernel to work.  It turned out that I had failed to configure tmpfs, and Gentoo fails the boot process miserably if it can't put a tmpfs file system on /run.  This is what I get for configuring my own kernels--a bit of pain, but I always learn things.

So I finally dived into the problem of the Thunderbolt devices simply not working.  The main problem was again the BIOS.  It was configured with security options for Thunderbolt.  This is a good idea--without security, any device plugged into that port can do anything it wants with your computer.  But for setting up Linux, it's not a good idea, so I turned off all the security options.  I would like to turn them back on at some point, but I'll have to do some research to see if and how Linux handles Thunderbolt security.

Issues

Now I'm able to get the system up and running for the most part, but I'm still having some issues.  I'll document each one here:


Issue:

Switching from X back to a console works, but then moving back to X doesn't

Solution:

Need to load firmware as mentioned in https://wiki.gentoo.org/wiki/Intel
Switch acceleration from Sandybridge New Architecture (SNA) to UXA
Setting 'Option "DRI" "False"' works--switching is consistent, though there's an 8-10 second black screen when switching back to X.

Issue:

The trackpad works fine in the console as long as you don't want to do a right or middle click.  If you're used to using gpm, you can't do cut-and-paste operations or anything else requiring a middle or right mouse button.

Solutions:

I have not found any solution to this problem.

Issue:

X has no middle-click on the trackpad.

Solutions:

You can set the logical location of the middle button to be in the middle of the bottom of the trackpad with the synclient command.  Unfortunately, this doesn't help with gpm on the console.  I added the following script to my X startup script:

#!/bin/bash
eval $(synclient -l | egrep '(Left|Right)Edge|RightButtonAreaTop|MiddleButtonAreaRight' | sed -e 's/ //g')
if [ "${MiddleButtonAreaRight}" != 0 ]; then
    echo Middle mouse button already set up
    exit 0
fi
# The order here is critical or it will be rejected:
synclient RightButtonAreaLeft=$(( RightEdge - (RightEdge - LeftEdge) / 3 + 1 ))
synclient MiddleButtonAreaRight=$(( RightEdge - (RightEdge - LeftEdge) / 3 ))
synclient MiddleButtonAreaTop=${RightButtonAreaTop}
synclient MiddleButtonAreaLeft=$(( LeftEdge + (RightEdge - LeftEdge) / 3 ))

You can play with that to get the buttons to work however you like.  I chose to have three equal buttons, and I didn't play with the other settings, but you can look at the output of 'synclient -l' to see what all you can fiddle with.  Do note my comment about the order.  You can't have overlapping buttons, so I had to be sure to shrink the right button before defining the middle button (the left button is implicit, so you don't have to adjust it).

Issue:

The Thunderbolt devices work fine if they're connected when the system boots.  If you unplug and replug them, the system sometimes needs a kick to rescan the PCI bus:
echo 1 > /sys/bus/pci/rescan
Takes care of it usually.  However, after several repeated disconnect/connect cycles, the PCI scan fails, and it can't use the device.

Solution:

I don't do a lot of disconnecting/reconnecting, so I haven't dug any further into this.

Kernel Config

Getting the kernel config right for all the devices can be tricky.  I'm old fashioned enough that I configure my kernel with support for exactly the devices I use, avoiding modules as much as possible.  I'm noting here some of the drivers you'll want to be sure to include:

Trackpad: From dmesg, I see it's a SynPS/2 Synaptics TouchPad.

Ethernet: The dock uses a Realtek 8153 USB ethernet chip.

Sound: The WD-15 dock uses a 0bda:4014 Realtek USB audio device

Touch Screen: It has an Elan touchscreen.  Select the "HID Multitouch panels" driver under "Special HID drivers" in the kernel configuration.  It's working, but I haven't tuned it.  At some point I may want to investigate, and this looks like a promising guide:  https://wiki.archlinux.org/index.php/Calibrating_Touchscreen

WiFi: It has an Intel 8260 chip, which uses the iwlwifi driver  It also requires firmware.  I used this guide:  https://wiki.gentoo.org/wiki/Iwlwifi

Bluetooth: It has an Intel 8087:0a2b USB Bluetooth device.  I configured it so that the kernel recognizes it and is happy, but I don't have a wireless keyboard or mouse, so I may not use it anytime soon, but I'm thinking about using it for audio.  The only catch was that before it would connect to my Amazon Echo, I had to tell it, "Alexa, pair with my phone."  A volume control shows up with "alsamixer -D bluealsa."  The guide I used is: https://wiki.gentoo.org/wiki/Bluetooth

Video: It has both Intel and Nvidia graphics, but unlike some laptops, you can't just ignore the Intel and use the Nvidia graphics--you have to access the Nvidia through the Intel GPU.  So you can't install the Nvidia drivers without first installing the Intel drivers.

Flash Reader: It's a Realtek RTS525A PCI device.  It requires the kernel to be built with CONFIG_MFD_RTSX_PCI and CONFIG_MMC_REALTEK_PCI.  Unlike my old laptop, the card sticks out when inserted, so I can't leave an empty microSD adapter in the laptop.

WebCam: I haven't looked into using the webcam.

Tuesday, February 16, 2016

Linux with LIRC Remote Controls

On May 11, 2000, I ordered our first DVR.  It was  a ReplayTV 2020, capable of recording 20 hours of TV on its 20GB hard drive.  We had previously used a VCR for recording several shows, but the DVR experience was so much better that we never even got around to watching some shows that we had previously recorded on VHS.  Five years later, we replaced the ReplayTV with MythTV.  I've refreshed the hardware, but the database has carried over, reporting that our first recording was on February 20, 2004 (and episode of CSI).

I could go on and on about how wonderful MythTV is, though I wouldn't recommend it unless you like to tinker with all sorts of Linux oddities to keep everything working just right.  One of those oddities is dealing with a remote control.  While you can use a wireless keyboard (which we do), it's often much nicer to use a standard universal remote.  Which brings us to the point of this post.

To receive IR signals, you need an IR receiver.  Today there are several USB-based solutions, including a programmable FLIRC device that remembers IR signals and converts them into the keystrokes of your choice.  While I would probably get one of those if I were starting fresh today, I have an old PVR-250 card that included an IR receiver.  I keep the card for digitizing VHS tapes, and take advantage of the IR receiver.

All was good, until my remote control suddenly died.  I tried new batteries.  No luck.  And of course the remote is no longer being made, as even something as standard as a universal remote has to be changed every few years, so I ended up ordering a new universal remote.

Programming a universal remote for use with MythTV should be fairly simple.  It doesn't really matter what codes the remote sends, as long as the computer can receive them, and as long as each button sends a different key.  Making this a little trickier, the receiver I have only parses the RC-5 protocol used by Philips.  So I set the remote to a code set for a Philips TV that sent signals on most of the buttons.  I managed to find another universal remote, and I programmed it to a different Philips code set, then used the learning mode on the new remote to program the remaining buttons so that each button sent a different signal.

To program the Linux side, there are three places to set up codes.  The kernel has a mapping of codes to key codes in the input layer.  LIRC has a mapping in /etc/lirc/lircd.conf, and applications have a mapping in ~/.lircrc.

Fortunately, the lircd.conf mapping can be ignored once it's set up, as the key code to symbol mapping is standard regardless of the remote.  While you still need the file, its purpose was to do the mapping now in the input layer for older drivers that didn't use that layer.

The ~/.lircrc file is where the real magic of using LIRC comes in.  If the remote worked like a keyboard, then all applications would see the same key for each button.  But with LIRC, you can set up different actions for a given button for each  program.  For example, you can tell MythTV that the PLAY button has the action of 'P' on the keyboard, while for Xine the same PLAY button has the action of the space bar.

The tricky part is setting up the initial mapping from remote codes to key symbols in the kernel.  For this, there's the input-utils package.  First, the 'lsinput' command told me that my remote was input 4.  Knowing this, I used 'input-events 4' to watch the raw scan codes for each button on the remote.  I found that a few were duplicates, so I had to use the learning mode to learn different codes for those buttons.

Then it should have been a simple matter of using 'input-kbd' to program a new set of mappings.  This program takes a mapping from scan codes to keyboard codes (which LIRC then passes on to the programs).  I had a file that mapped the codes for my previous remote, and even the remote from before that (which we went back to using briefly, despite some buttons not working on it).  I was able to add the codes for the new remote.  But that's when things fell apart.

Somehow, after sending the new mapping file to the kernel using input-kbd, displaying the active map would show that some buttons reverted back to a previous value.  I was absolutely convinced that I was doing everything correctly, so I was going to report this as a kernel bug.  In preparing to send the report, I ran input-kbd under strace so that I could cite the exact ioctls that were misbehaving, and much to my surprise, I saw that input-kbd was sending my mapping, followed by a bunch of old mappings.

So it was time to look at the source for input-kbd.

Since I use Gentoo Linux, I had the source already downloaded.  Looking at the source, and knowing the behavior I was seeing, the bug was fairly obvious.  The program was written assuming that anyone submitting a new mapping would want to remap all the same codes as the old map, so it read the old map, then overwrote it with the new map, but when sending the map back to the kernel, used the size of the original map.

So I wrote a patch to fix that bug.  I also took the time to allow comments in the input file (saving my init script having to strip them out with sed and grep before loading them).  Again, as a Gentoo user, I was able to put the patches in /etc/portage/patches/sys-apps/input-utils/, and now the patches are automatically applied whenever I install the package.

But being a responsible person, I also looked for a place to report the bug back to the developer.  The project is on Git Hub, but there is no issue tracker there or anywhere else that I could find.  So I resorted to emailing the developer, hoping that the published address was still valid.  I'm pleased to report that the patches got through and were immediately applied (with a few tweaks).  I've sent one more iteration of improvements, but hopefully when input-utils-1.2 is released, this bug will be gone.

So having done all that, I decided to take the remote apart and see what was wrong with it.  I pried it apart with a screwdriver.  It's just one circuit board with a single chip.  I couldn't see any indication of cracked solder or anything like that, so I put it back together.  I put the batteries back in, and...

The old remote works just fine.

Well, I learned a bit about how the mapping of remotes works in Linux, and I rather like the new remote.  In any case, I'll have something to switch to when the old one dies again.