left header graphic The Network People
Solutions for Hosting Providers
right header graphic

home : computing : freebsd : Crystalfontz USB LCD Displays FreeBSD Systems Review

FreeBSD + Crystalfontz USB LCD Displays

Matt Simerson (matt@tnpi.biz)

v1.0, 17 Feb 2004


This document describes how to get a Crystalfontz USB LCD display to work with FreeBSD.


Introduction

There are a quite a variety of LCD displays now available. Many are serial versions and thus work quite well on any platform, assuming you can run some version of LCD software on your platform or plan to write your own. Considering that options are available ranging from client/server packages to simple Perl modules, it shouldn't be beyond the average sysadmin's abilities to add a LCD to his mod'ed system. However, the newer USB variants can present significant issues.

(Happily) knowing next to nothing about USB devices beyond plugging them in and using them, I accepted an offer to spend some time seeing if I could get a Crystalfontz USB LCD display to work with FreeBSD. I scoured the Internet for a while rummaging around to find a documented solution and turned up very little. According to rumors on the lcdproc mailing list, a few developer types had gotten it working but nobody bothered publishing HOW they did it.

I feel it's important to note that the Crystalfontz LCD family seems to have the best reputation among the developers that write software for interacting with LCD displays. I read a lot of posts while researching this project and the general consensus is that "Crystalfontz LCD's cost more but they just work". I have no affiliation with Crystalfontz beyond them contributing several LCD panels to me for testing. I have not used any other LCD panels.

My care package of LCD's arrived from Crystalfontz and I plugged the USB LCD 634 into my development server (FreeBSD 5.2, dual PIII, 2U) and it immediately registered the device:

  • ugen0: Crystalfontz Crystalfontz CFA-634 USB LCD rev 1.10/2.00 addr 2

This delighted me as I thought the project would be much easier than I had imagined. I needed a program that works with LCD interfaces and since lcdproc was in ports, it was the natural choice. I installed lcdproc:

  • cd /usr/ports/sysutils/lcdproc
  • make install clean
  • cd /usr/local/etc
  • cp LCDd.conf.sample LCDd.conf
  • vi LCDd.conf

I modified the following items in the CFontz driver section of the LCDd.conf file:

  • Driver=CFontz
  • Device=/dev/ugen0.2
  • Speed=19200
  • NewFirmware=yes

I then started up LCDd in the normal FreeBSD way:

  • /usr/local/etc/rc.d/LCDd.sh.sample start

LCDd promptly yielded up a "Device not configured" error trying to initialize the /dev/ugen0.2 device and failed to start up. Bummer. More research into the fundamentals of how USB worked uncovered that there are three layers of abstraction in the USB protocol. FreeBSD recognized the device but didn't have a specific driver for it, and as such, only had two of the three components necessary to actually use it. We had the physical interface, the basic software abstraction layer worked (the OS recognized the device), but the missing component was a software driver that knew how to communicate with the USB interface on the Crystalfontz LCD.

Some more research on the matter revealed that some Linux developers had written some kernel patches to interface with the USB LCDs. From reading through their documentation and source code, I was able to discover that the Crystalfontz USB displays I have (the 631-634 models) are nearly identical to their serial LCD brethren. The primary difference was a serial to USB converter made by FTDI (8U232AM). The only other difference I could ascertain (based on the docs) was that two jumpers are soldered shut.

My next test was to see if the USB device (which contains a serial port) would work as a serial device. According to the docs, it will work (minus the back light) via a RS-232 plug so I plugged in a serial cable. Nothing. I think with the right terminal command, I could raise the voltage on pin 5 and get it fired up but I simply plugged in a modified USB cable to provide it with 5v power. After that I could get scrambled text on the display via a term program and the RS-232 interface. That was to be expected as the jumpers that are soldered closed switch it from expecting a RS-232 to STI signal. I am quite confident that I could whip out my soldering iron and some solder wick and convert the LCD into a serial version. Alas, that wasn't the goal.

Knowing that my missing component was a USB driver for the FTDI USB to serial adapter, I set off on a new search to find that driver. Google revealed that the USB driver I was searching for is included in FreeBSD versions after 4.8. I then read all about the drivers via:

  • man ucom
  • man uftdi

Fortune was smiling upon me. The drivers were already written. The next thing I did was check to see if they were loaded:

  • kldstat
  • Id Refs Address Size Name
  • 1 8 0xc0400000 6570c4 kernel
  • 2 1 0xc6ca7000 6000 linprocfs.ko
  • 3 1 0xc6cad000 19000 linux.ko

The kernel modules that would support that device were not loaded. So, I loaded them with kldload and then verified them via klstat:

  • kldload uftdi
  • kldstat
  • Id Refs Address Size Name
  • 1 12 0xc0400000 6570c4 kernel
  • 2 1 0xc6ca7000 6000 linprocfs.ko
  • 3 1 0xc6cad000 19000 linux.ko
  • 4 1 0xccdf4000 3000 uftdi.ko
  • 5 1 0xccdf7000 4000 ucom.ko

Progress! I then unplugged the LCD and plugged it back in. It once again loaded exactly as it had the very first time I plugged it in. For some reason, the USB system wasn't recognizing the presence of the FTDI USB serial bridge. According to "man ucom", when that bridge was detected, FreeBSD would create a /dev/ucom device driver for it which could then be accessed as a normal serial device.

This is where it turns messy. I paid a visit to the USB source code in /usr/src/sys/dev/usb and spent some quality time reading through the source code. This is where reading that "C for Dummies" book came in handy. After reading through the code, I discovered that there is already support for the CFA-631, one of the four USB LCD's in my test arsenal.

I plugged in the 631, and:

  • ucom0: Crystalfontz Crystalfontz CFA-631 USB LCD, rev 1.10/2.00, addr 3

Notice the subtle difference between that line and the one from the 634.

  • ugen0: Crystalfontz Crystalfontz CFA-634 USB LCD, rev 1.10/2.00, addr 2

The 631 registers using the ucom driver (USB comm port) instead of the ugen (USB general purpose) driver. Now all I needed to do was figure out how FreeBSD determined the difference between them and get the 634 to register as a ucom as well. While perusing through the usbdevs.h file in /usr/src/sys/dev/usb, I found this line:

  • /* Crystalfontz products */
    • define USB_PRODUCT_FTDI_CFA_631 0xfc0c /* Crystalfontz CFA-631 USB LCD */

I copied the #define line four times as show below. I had determined that the 0xfc0c was the USB Vendor Product ID, a unique product ID that each USB device identifies itself to the system with. To determine the device ID's of the other products, I killed usbd and restarted it in verbose debug mode, and then plugged in each device, recording their product IDs.

  • /* Crystalfontz products */
    • define USB_PRODUCT_FTDI_CFA_631 0xfc0c /* Crystalfontz CFA-631 USB LCD */
    • define USB_PRODUCT_FTDI_CFA_632 0xfc08 /* Crystalfontz CFA-632 USB LCD */
    • define USB_PRODUCT_FTDI_CFA_633 0xfc0b /* Crystalfontz CFA-633 USB LCD */
    • define USB_PRODUCT_FTDI_CFA_634 0xfc09 /* Crystalfontz CFA-634 USB LCD */

I made similar changes adding the following lines to the /usr/src/sys/dev/usb/usbdevs file:

  • product FTDI CFA_632 0xfc08 Crystalfontz CFA-632 USB LCD
  • product FTDI CFA_633 0xfc0b Crystalfontz CFA-633 USB LCD
  • product FTDI CFA_634 0xfc09 Crystalfontz CFA-634 USB LCD

I made the following changes to the /usr/src/sys/dev/usb/uftdi.c file (in patch format):

  • *** uftdi.c.orig Wed Feb 18 16:25:07 2004
  • --- uftdi.c Wed Feb 18 15:47:27 2004
  • ***************
  • *** 153,159 ****
  • if (uaa->vendor == USB_VENDOR_FTDI &&
    • (uaa->product == USB_PRODUCT_FTDI_SERIAL_8U100AX ||
    • uaa->product == USB_PRODUCT_FTDI_SERIAL_8U232AM ||
    • ! uaa->product == USB_PRODUCT_FTDI_CFA_631))
    • return (UMATCH_VENDOR_PRODUCT);
  • .
  • return (UMATCH_NONE);
  • --- 153,162 ----
  • if (uaa->vendor == USB_VENDOR_FTDI &&
    • (uaa->product == USB_PRODUCT_FTDI_SERIAL_8U100AX ||
    • uaa->product == USB_PRODUCT_FTDI_SERIAL_8U232AM ||
    • ! uaa->product == USB_PRODUCT_FTDI_CFA_631 ||
    • ! uaa->product == USB_PRODUCT_FTDI_CFA_632 ||
    • ! uaa->product == USB_PRODUCT_FTDI_CFA_633 ||
    • ! uaa->product == USB_PRODUCT_FTDI_CFA_634))
    • return (UMATCH_VENDOR_PRODUCT);
  • .
  • return (UMATCH_NONE);
  • ***************
  • *** 207,212 ****
  • --- 210,227 ----
    • break;
  • case USB_PRODUCT_FTDI_SERIAL_8U232AM:
  • case USB_PRODUCT_FTDI_CFA_631:
    • + sc->sc_type = UFTDI_TYPE_8U232AM;
    • + sc->sc_hdrlen = 0;
    • + break;
  • + case USB_PRODUCT_FTDI_CFA_632:
    • + sc->sc_type = UFTDI_TYPE_8U232AM;
    • + sc->sc_hdrlen = 0;
    • + break;
  • + case USB_PRODUCT_FTDI_CFA_633:
    • + sc->sc_type = UFTDI_TYPE_8U232AM;
    • + sc->sc_hdrlen = 0;
    • + break;
  • + case USB_PRODUCT_FTDI_CFA_634:
    • sc->sc_type = UFTDI_TYPE_8U232AM;
    • sc->sc_hdrlen = 0;
    • break;

You can apply this patch by first copying and pasting the patch contents as shown above into the file uftdi.c.patch. Then, use patch to apply the patch file to the stock uftdi.c file as shown below:

  • cd /usr/src/sys/dev/usb
  • vi uftdi.c.patch
  • patch < uftdi.c.patch

I made the following additions to the /usr/src/sys/dev/usb/usb_devs_data.h file: (search for 631 and append the lines)

  • {
    • USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_632,
    • 0,
    • "Future Technology Devices",
    • "Crystalfontz CFA-632 USB LCD",
  • },
  • {
    • USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_633,
    • 0,
    • "Future Technology Devices",
    • "Crystalfontz CFA-633 USB LCD",
  • },
  • {
    • USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_634,
    • 0,
    • "Future Technology Devices",
    • "Crystalfontz CFA-634 USB LCD",
  • },

The next step is to compile and install the modified USB kernel modules:

  • cd /usr/src/sys/modules/ucom
  • make install
  • cd /usr/src/sys/modules/uftdi
  • make install
  • kldunload uftdi
  • kldload uftdi

After doing all that, each time a Crystalfontz 631, 632, 633, or 634 is is plugged in, you'll see it registered with a line as follows:

  • ucom0: Crystalfontz Crystalfontz CFA-631 USB LCD, rev 1.10/2.00, addr 2
  • ucom0: Crystalfontz Crystalfontz CFA-632 USB LCD, rev 1.10/2.00, addr 2
  • ucom0: Crystalfontz Crystalfontz CFA-633 USB LCD, rev 1.10/2.00, addr 2
  • ucom0: Crystalfontz Crystalfontz CFA-634 USB LCD, rev 1.10/2.00, addr 2

At this point we've got the device registered with the OS and FreeBSD has created a /dev/ucom[0-9] that we can access via a typical serial tool or any of the programs designed for this purpose such as lcdproc or perl-LCDd. I plugged in the 634 to the USB interface, adjusted the LCDd.conf file as show below, and then started up LCDd.

  • Driver=CFontz
  • Device=/dev/ucom0
  • Speed=19200
  • NewFirmware=yes

LCDd immediately updated the LCD with it's little splash screen. I opened another terminal and ran "lcdproc C" in it and poof, CPU info scrolled across the 634 USB LCD. Cool. I changed the LCDd.conf file to use a 16x2 LCD size, unplugged the 634 and plugged in the 632. It worked as expected. However, neither the 631 nor the 633 worked when I configured LCDd for them.

Having already experimented with the newer version of LCDproc from the LCDproc web site, I recalled it having a separate driver for use with the USB 633 LCD. I shut down LCDd, compiled the 0.4.4 version from the nightly builds page with the cfontz633 driver and fired it up. It worked.

The little orphan in the group is the USB 631. It plugs in, the screen lights up and it registers as a CFA-621 at ucom0 but no matter what I try sending it (tried all three versions of LCDproc including 0.5-current), it doesn't register a signal. Per the lcdproc web site (which doesn't mention it), I'd venture to say the problem is simply lack of support for it in lcdproc. Considering that the 633 just got added, I'd expect to see it in a future release.

If you want your LCD to work at boot time, there's a couple things you'll want to do. The first is adding the following to /boot/loader.conf:

  • uftdi_load="YES"

That tells FreeBSD to load the FTDI USB driver at boot time (before the already plugged in device gets registered with ugen). If you don't do this, you'll have to unplug the device, load the uftdi kernel module, and then plug it back in before it'll work properly.

The last thing to consider is having LCDd start up automatically, either at boot time or launched when a USB LCD is plugged in. If you want LCDd to run at boot time, it's as easy as:

  • cp /usr/local/etc/rc.d/LCDd.sh.sample /usr/local/etc/rc.d/LCDd.sh

To have LCDd launched when a LCD is plugged in, you'll need to modify /etc/usb.conf adding some lines that resemble the following:

  • # Crystalfontz 632 LCD display
  • device "Crystalfontz LCD"
    • devname "ucom[0-9]+"
    • product 0xfc08
    • attach "/usr/local/sbin/LCDd -p /dev/${DEVNAME} -c /usr/local/etc/LCDd.conf-632"
    • Crystalfontz 633 LCD display
  • device "Crystalfontz LCD"
    • devname "ucom[0-9]+"
    • product 0xfc0B
    • attach "/usr/local/sbin/LCDd633 -p /dev/${DEVNAME} -c /usr/local/etc/LCDd.conf-633"
    • Crystalfontz 634 LCD display
  • device "Crystalfontz LCD"
    • devname "ucom[0-9]+"
    • product 0xfc09
    • attach "/usr/local/sbin/LCDd -p /dev/${DEVNAME} -c /usr/local/etc/LCDd.conf"

The reason for having separate .conf files is there is not currently a way to pass the screen size to LCDd via the command line.

Now, when you plug in one of the LCD displays, LCDd gets run with the setting specific to that display, no matter which ucom device it happens to get. Very nifty indeed.


Copyright

Copyright (c) 2004, The Network People, Inc.

All rights reserved.

Redistribution and use in any forms, with or without modification, are permitted provided that the following conditions are met:

Redistributions must retain the above copyright notice, this list of conditions and the following disclaimer.

Neither the name of the The Network People, Inc. nor the names of its contributors may be used to endorse or promote products derived from this documentation without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


Credits

This would not have been possible without the countless volunteers who contribute code for the FreeBSD project, the good folks at Crystalfontz who generously donated the hardware to test with, and Randy Ricker who financed my interest in the project.


Getting Help

LCDproc is written for linux. You'll find more info about using it with Crystalfontz USB displays here.

If for some reason you have problems with these instructions and need help, technical support is available on a fee basis only. Further information is available on the Hire TNPI page.


Last modified on 4/25/05.