Tuesday, April 30, 2019

USB Flash Media on Linux

USB flash media is one of the most useful devices to come along in recent computer history.  Gone are the days of floppy diskettes, CD-R media, and DVD-R media.  While those were fine, USB flash media has just made things much easier.  Our best interim solution in the 90s was the Iomega Zip drive (full disclosure: one still sits on my desk and I use it for sneaker-netting to vintage systems that have SCSI and a Zip drive).

OK, so there are some things to know when using USB flash media on Linux.  For me, the two common things I do are boot ISO images and copy files between systems.  Usually old Windows systems that I use for programming old two-way radio systems.

In the case of booting ISO images, you just need to dd the file to the block device.  Let's say I insert flash media and see in dmesg that it's /dev/sdc.  I would write out the ISO image using this command:

dd if=FUN_OPERATING_SYSTEM.iso of=/dev/sdc status=progress oflag=sync bs=4096

You may need to use the mkhybrid command on some Linux ISO images to make sure it loads from USB media.  Do that on the ISO file before running dd.  So what do the options do?
  • if is the "input file".  This is what I want to write to USB.
  • of is the "output file".  Where is it going?  In this case, a block device called /dev/sdc.
  • status=progress is a GNU feature that shows some status information during the transfer.
  • oflag=sync means "really write it to the block device".
  • bs=4096 means write 4k at a time.  By default, dd will just do one byte at a time.  For an 8GB ISO, that can take a while.  I have successfully set a 32k block size and dd performs just fine, so I do bs=32768
Great, now you can write out an ISO.  How about file transfer?  In my case, I stick with the FAT32 filesystem.  Even non-DOS operating systems can read and write it.  First, you need to partition the USB device and create a single partition as partition 1.  Alternatively you can format the device on Windows and it will do all of that for you.  Create a FAT32 filesystem on the first partition of the device with a command like:

mkfs.dos -F 32 /dev/sde1

Now mount it:

mount -o flush /dev/sde1 /mnt

What is "-o flush"?  This tells the kernel to sync data to the device when it's idle.  Other filesystems tend to call this "-o sync", but the distinction usually for FAT32 is that it will simply sync data earlier.  Usually the "sync" option for the filesystem means sync during the write which can cause it to take a long time to write.

The "-o flush" option also means when you unmount the device it will most likely be ready to remove.  This is also way faster than running sync before unmounting.  I've also heard urban legends of the sync command destroying the life of USB flash media, so I guess there's that.

If you have ever waited a long time to dd something to a USB flash device, try some of the above options for dd and mounting.  It makes the devices much nicer to work with on Linux.

Oh Geez Windows - Part 2

I never could get the ThinkPad X1 Carbon 6th generation laptop to install and run Windows 7.  USB 3.0 and NVMe devices were just too new for it.  What I ended up doing was buying a ThinkPad X230 off eBay and setting it up with Windows 7.  I them proceeded to try and get both versions of Motorola CPS working on that system.

First, MOTOTRBO CPS.  This was simple and worked very easily.  I did have to reinstall and add the wideband entitlement key after installing, but otherwise I am now able to program the XPR7550 radios just fine.

Second, Waris CPS.  This installed and ran fine, surprisingly.  However the X230 lacks a real serial port.  What I found that works is the StarTech ICUSB2321F.  This device uses the FTDI chipset for the serial port, which is reliable enough to get CPS to work.  So no more T23 for Waris programming.

I disabled all of the automatic updating in Windows since I don't care and I don't want to be surprised by this thing suddenly upgrading itself to Windows 10.

What I've learned through all this is that it's just easiest to use the platform Motorola recommends for CPS and not fight it.  A used laptop that meets the requirements is far less than the cost of any Motorola product you're trying to program.

Sunday, April 7, 2019

Oh Geez Windows

I don't use Microsoft Windows.  The last time I had a serious installation of Windows that I actually used was Windows NT 4.0.  I have no relevant Windows knowledge.

But I need Windows to run the Motorola CPS software to program two-way radios.  There's no alternative and Motorola Solutions is not a great software house.  Their software is generally quite terrible and extremely picky about its runtime environment.  I have two main versions of CPS that I need to run.  Because Motorola doesn't just make one version of CPS for all radios, the version you need is tied to your radio series.  Great.

The first version I need is for the Waris series radios, which are now discontinued.  This is the HT750 and HT1250 (among others).  This is a Windows program and uses the RIB device to speak to the radio through an actual no-shit serial port.  The versions of Windows it can run on are limited, but I am successfully using it on Windows XP.  Now, since it needs a real actual no-shit serial port (no USB adapters!), I need a system that (a) can run this version of Windows and (b) has a real serial port.  For this version of CPS, I settled on a ThinkPad T23 off eBay.  It runs CPS effectively enough to program these radios.

The second version I need is for the MOTOTRBO series radios.  I have the XPR7550, for instance.  Fortunately this requires an entirely different programming cable (this time it's USB!) and different software.  The recommended version of Windows is 7.

The T23 is out of the question for Windows 7, so I have drafted a spare work laptop in to temporary duty just for programming the radios.  After I program them, I plan to wipe the laptop and find a more permanent solution.  Unfortunately this has become much more difficult than I wanted.

The laptop I'm trying to install Windows 7 on is a 6th generation ThinkPad X1 Carbon.  It has USB 3.0 and NVMe storage.  These are things Windows 7 knows nothing about natively, which means that even though I have been able to figure out a way to create USB boot media for Windows 7 and boot it, it can't see what it booted from nor can it load any drivers from anywhere because the only way to sneakernet drivers in this early is through USB 3.0.

Ugh.

Googling around reveals some methods to update your Windows 7 install media and add drivers.  I tried this from the T23 on Windows XP, but the updating steps have to run from Windows 7.  Come on.

I have wasted a lot of time on this, but the plan at the moment is:
  • Install a virtual guest on my Linux workstation and install Windows 7.
  • Install OpenSSH and rsync in that virtual guest.
  • rsync over the Windows 7 install ISO to the Windows 7 guest.
  • Follow the steps to use the DISM program to modify the boot media to add in the USB 3.0 drivers and the NVMe drivers.  I have no idea if this will work.
  • Save the resulting changes back to the ISO.
  • rsync the ISO back to the Linux host.
  • dd out the modified ISO to flash media.
  • Boot the new ISO on the ThinkPad X1 Carbon and proceed with installation.
  • Install the MOTOTRBO software and program the radios.
At this point I am on the 3rd item.

Saturday, March 2, 2019

Memory Allocation in C - Followup

My previous post talked about memory management in C and I described how I like to use the assert() function.  What I did not note is that assert() functionality can be disabled by defining NDEBUG in the program or passing -DNDEBUG to the compiler.  That's a problem if you embed a function call in the expression that you wrap in assert.  Let's look at an example.

Take this silly example:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>

int main(int argc, char **argv) {
    char *s = NULL;
    char *data = "this is a string";

    assert((s = calloc(1, BUFSIZ)) != NULL);
    assert((s = strncpy(s, data, strlen(data))) != NULL);
    printf("s: |%s|\n", s);
    free(s);

    return EXIT_SUCCESS;
}


When compiled, it should probably just print "s: |this is a string|", right?  Obviously in a completely unnecessary and complicated way.  But that's still what it will do.  Let's try (I have saved this code to a file called foop.c):

$ gcc -O0 -g -Wall foop.c
$ ./a.out
s: |this is a string|

It's a miracle!  It worked.  But what happens if we compile it with -DNDEBUG?

$ gcc -O0 -g -Wall -DNDEBUG foop.c
foop.c: In function main:
foop.c:9:11: warning: unused variable data [-Wunused-variable]
     char *data = "this is a string";
           ^~~~


Uh oh.  What happens when we run it?

$ ./a.out
s: |(null)|

Yeah, that's wrong.  So we need to make sure the same thing happens whether or not we compile with -DNDEBUG, so let's rewrite it a bit:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>

int main(int argc, char **argv) {
    char *s = NULL;
    char *data = "this is a string";

    s = calloc(1, BUFSIZ);
    assert(s != NULL);

    s = strncpy(s, data, strlen(data));
    assert(s != NULL);

    printf("s: |%s|\n", s);
    free(s);

    return EXIT_SUCCESS;
}


Now let's try to compile it:

$ gcc -O0 -g -Wall foop.c        
$ ./a.out
s: |this is a string|


That checks out.  Now with -DNDEBUG:

$ gcc -O0 -g -Wall -DNDEBUG foop.c
$ ./a.out                        
s: |this is a string|


That's more like it.  assert() is useful for developers and I try to make use of it a lot during development, but remember to avoid embedding expressions in what you wrap in assert().  Relying on side effects leads to problems and assert() can't help with that.