SourceForge Logo

Linux X10 universal device driver

with unified device interface

(aka Project Unidev)


Introduction

Let's start out with a short "why do I want this".  The normal /dev/x10/* interface to the X10 network uses up 2 major character devices out of the total of 256.  Some consider that excessive.  So, this version of the device interface gives you the same access with the tested line discipline and protocol translators with a new interface that minimizes the number of devices used up.  This interface accepts commands like "A1 ON" and "E ALL_UNITS_OFF" which are identical to what is logged by the standard interface to WiSH.  Logging is identical in form and function and x10logd works without modification.  All functionality provided in the native interface is provided through this interface with the exception of extended code, extended data, and simulated status of the X10 network.  And, if people like the unidev interface better, it may become the default.

Please read through this document completely before installing.  If you are in a terrible rush, at least read the sections on Installation and Usage.  This reads like bicycle instructions so it isn't for the faint of heart.  This document contains the installation instructions and supercedes INSTALL.txt and README.txt in the event that there is a discrepancy.

One final word on why this might prove useful:  If you think you have an idea that can improve the interface to the X10 network, this code provides a very straightforward example of how to write a new interface.  


Index


FAQ

Beyond the basic introduction to the project, this section is intended to explain more of how this project came about and why certain choices were made.  This is an extension of the standard WiSH FAQ.

  1. Q:  What the heck is x10_unidev and the unidev directory?
    A:
      One of the advantages of the way the project was designed is that the user interface is completely separate from the line discipline which is completely separate from the protocol translator.  The PowerLinc and CM11A prove the theory that you can reuse the line disciipline.  But, what about creating a completely new interface?  Unidev is short for Unified Devices and is a version of the device interface which utilizes a single /dev device. Why would I do that?  Quite simply, I have received a couple of suggestions that I need to stop using up so many device entries and move the state machine/status management to the userspace.  After putting some thought into it, I decided that it might be worthwhile to create a device interface that was as minimal as possible but which retained complete x10 control.  unidev is exactly that and it took me about 6 hours to write, test, and debug for all 4 transceivers.  To create it, not a line of code was changed in any other file except x10_unidev.c.  Read more about how to compile and use this in the Unidev guide.
  2. Q:  Can I write my own user interface for the project?
    A: 
    You most certainly can and I will consider adding it to the distribution.  But, you need to be prepared to answer questions and do fixing on it if there are any bugs.  Unidev is provided as an example of how easy it is to put a new front end on the drivers.  I still like the original interface better, but Unidev has some potential.
  3. Q:  What are the advantages of the Unidev interface over the standard interface?
    A:
      The unidev interface utilizes fewer major character devices.  This would make the interface applicable to become a subcomponent of the input system such that it would be assigned a minor number and share a major number with other drivers.  It also provides user language that is more natural to individuals that have been working with X10 for a long time.  The language of the interface isn't very natural from the command line, but from a programming language like Java, Perl, or C, it is much cleaner to have only one file open and to be able to write a string like "E15 ON".  With the standard approach, C, Java, and Perl have to open up a different device for each switch they want to talk to.  It also make the device driver act like an unintelligent device driver.  Arguments have been made that the standard interface puts to much intelligence into the driver by maintaining the status of the devices.  Indeed, the guides to writing device drivers push for the driver to be as simple as possible and to do as little as possible so that the userspace program can create the robustness for the system.  Since the userspace program must be written to understand the device types to truly understand what the network status should be, having the status in the driver is redundant and a system resource loss.
  4. Q:  What are the advantages of the standard interface over the Unidev interface?
    A: 
    The standard interface is much easier to use from a command line.  It also allows for file system links to be made to the /dev/x10/* devices so that you can have more naturally named devices to write to.  Being able to type "echo on > /automation/foyer" was one of the first reasons that I went down the path to writing the drivers.  The standard interface also does the work necessary to know the status of a device or what the status should be if it were a light.  This simplifies both scripting and programming because the userspace program does not have to read the log and interpret the states to determine what action to take.  The standard interface use of /dev/x10/status also makes it easy for a programming language to create a matrix display of all 256 devices in the network without having to do any work.  
  5. Q:  Why are both interfaces provided?
    A: 
    Over time, one interface will be favored over the other by the user community.  At some point, one of them will be dropped.  It is also possible that someone else will develop a better interface which will replace both interfaces.  By developing unidev, the development community is given an example of how to replace the user interface.  It also proved that it could easily be done without a single change to the line discipline or the protocol translator.
  6. Q:  Can the drivers for the standard and unidev interface be loaded at the same time?
    A:
      No.  Both utilize the same resources so they cannot coexist.

Installation

1) Compiling the drivers

To use the drivers, you will must have a working kernel source tree installed even if you already have a working kernel.  The reason is that to compile the drivers, information about the kernel has to be known. This comes from the .config in the /usr/src/linux top directory, and from the dependencies that are created when the "make dep" command is run.

Note that the drivers will only compile for kernel version 2.4.x.  Kernel 2.2.x does not support some features required by the drivers.  Kernel 2.5.x has substantially changed the system call interfaces and the drivers will not compile with 2.5.x.  Note that I will be working on porting to 2.5 very shortly.

Compiling as a standalone set of drivers (e.g. not installing in the kernel source tree):

You likely already have a working kernel and you don't want to recompile the entire kernel just to use the drivers.  You can still compile the modules without reinstalling your kernel by only performing the steps necessary to run "make dep" in the kernel top level directory. There are a couple of prerequisites to compiling the drivers.  

Once you have the kernel sources set up properly, you can compile the drivers.

  1. Type "make unidev" (without the quotes) in the top level directory of the WiSH source code.  Do not proceed if any errors occur.
  2. To automatically install the drivers and the utilities, type "make installall" (without the quotes) in the top level directory of the WiSH source code.  This will copy the modules to your /lib/modules/<running kernel version>/kernel/drivers/char/x10 directory.  It will also copy the utilities to /usr/sbin.
  3. Create the devices and load the appropriate module to start using your transceiver.

Compiling with the kernel (e.g. installing in the kernel source tree):

The unidev version of the interface will compile with the kernel but the Config.in and Makefiles have not been created.  Therefore, this option is not readily available at this time.

2) Create the devices (Note, this only applies to a system which does not have DEVFS.  If DEVFS is in use, the devices will be created automatically when x10.o loads):

There is only one device to create so I haven't created a script to do it for you.  To create the device, type:

# mknod /dev/x10 c 120 0

That's it.  If you want to load multiple drivers simultaneously, change the name of the device to be created and the major device number and then specify the parameter "major=#" when the driver is loaded.

3) Load the modules (Note that the drivers are loaded identically as they are for the standard interface)

Now that you have the the devices created, you need to load the modules to respond to the devices.  Scripts have been provided in the example_scripts/ directory to automate starting and stopping the drivers.  Copy the appropriate script to /etc/rc.d/init.d/x10 for for a RedHat system or call the appropriate script from /etc/rc.d/rc.local on a Slackware system.  On a RedHat system, you can run chkconfig to install in the appropriate runlevels.  For example, "chkconfig --level 35 x10" will cause the drivers to be loaded for run levels 3 and 5.

To load the modules by hand (recommended when testing for the first time), load the driver for your specific transceiver (ref the table below), and load the userspace program if needed.  The syntax of the action would be as follows:

# insmod x10_<driver>.o <- transceiver specific driver
# x10attach -<driver> /dev/<port> <- userspace program to connect driver to tty ldisc supported port.  This is not needed for the USB or Firecracker devices.
# x10logd -i /dev/x10 <- start the userspace program to capture X10 network traffic to /var/log/x10log and specify the name of the unified driver device file.

Below is the table of drivers and the required helpers:

Manufacturer driver line discipline userspace helper
PowerLinc Serial x10_pl.o TTY x10attach -pl /dev/ttyS#
PowerLinc USB x10_plusb.o none none
CM11A x10_cm11a.o TTY x10attach -11a /dev/ttyS#
Firecracker x10_cm17a.o none none

For example, to load the driver for the PowerLinc transceiver and connect to /dev/ttyS0, use the following sequence of commands:

# insmod x10_pl.o
# x10attach -pl /dev/ttyS0 &
# x10logd

The parameters that can be specified for the modules are:

Specific driver loads:

PowerLinc serial:
#insmod x10_pl
#x10attach -pl /dev/ttyS0 &
#x10logd -i /dev/x10

CM11A serial:
#insmod x10_cm11a
#x10attach -pl /dev/ttyS0 &
#x10logd -i /dev/x10

FireCracker radio transmitter:
#insmod x10_cm17a port=0x3f8
#x10logd -i /dev/x10

PowerLinc USB
#rmmod hid
#insmod x10_plusb
#x10logd -i /dev/x10

Note that for the PowerLinc USB, it may be necessary to unload the HID driver using the command "rmmod hid".  This is because the PowerLinc USB is a HID device, and the USB helper programs default to load both hid.o and x10_plusb.o.  hid.o intercepts all HID devices and prevents x10_plusb.o from gaining access to the transceiver if hid.o is loaded first.

If everything has been done correctly, you should now have the drivers loaded and you should have a line in your kernel log indicating that they have started.  Move on to the userspace usage for actually sending commands and watching the status of the network.

4) File locations

Once you know that the drivers are working properly, you can install the files into their permanent homes for the system.  install.sh has been provided to automate but there may be reasons that administrators prefer to install the drivers by hand.  

  1. Copy utils/x10attach to /usr/sbin/x10attach (cp -f utils/x10attach /usr/sbin/x10attach)
  2. Copy utils/nbread to /usr/bin/nbread (cp -f utils/nbread /usr/bin/nbread)
  3. Copy utils/x10logd to /usr/sbin/x10logd (cp -f utils/x10logd /usr/sbin/x10logd)
  4. Copy the drivers to the /lib/modules area (cp -f x10_pl.o x10_plusb.o x10_cm11a.o x10_cm17a.o /lib/modules/<kernel ver>/kernel/drivers/char/x10)
  5. Copy the appropriate startup script from the example_scripts directory to automate the startup of the drivers when the computer is booted.  Copy the appropriate x10.*.sh file to /var/rc.d/init.d/x10 on a redhat system or to /var/rc.d/x10.sh on a slackware system (and edit /etc/rc.d/rc.local to call x10.sh).  For a redhat system, you can then specify which runlevels require the drivers using "chkconfig --level <levels> x10" where <levels> is usually "35" to start the drivers for run levels 3 and 5.

Userspace Usage:

Once the device has been created and the module has been loaded, /var/log/messages should show "x10 Transciever module v<version> (wsh@sprintmail.com)" to indicate that all went well.

At this point, normal userspace programs can be used to access the X10 network. The commands that are sent to the device must be of the following format:

<housecode>[<unitcode>] [<functioncode>]

Either the unitcode or the functioncode or both may be specified.  There is no space between the housecode and the unitcode, and there must be a space before the functioncode. All commands may be any combination of uppercase or lowercase.

The <housecode> field may be any letter from A to P.  The <unitcode> may be any number from 1 to 16.

The <functioncode> may be anything from the first column of the following table.  The BOLD characters are required and the remainder is optional.

Function Text
ALL_UNITS_OFF, AUO Turns off all X10 units on a single housecode.
ALL_LIGHTS_ON, AON Turns on all X10 devices on a single housecode that fall into the category of "light".  Devices such as wall outlets or relays will not respond to this command.  
ALL_LIGHTS_OFF, AOFF Turns off all X10 devices on a single housecode that fall into the category of "light".  Devices such as wall outlets or relay controller generally do not respond to this command.
ON Turns a device on.  All X10 devices respond to this command.
OFF Turns a device off.  All X10 devices respond to this command.
DIM Dim the X10 device by 1/16.  Not all X10 devices respond to this command.  Fluorescent lighting, wall outlets, and relay controls are examples of devices that do not respond to this command.
BRIGHT Brighten the X10 device by 1/16.  Not all X10 devices respond to this command.  Fluorescent lighting, wall outlets, and relay controls are examples of devices that do not respond to this command.
EXTENDED_CODE, ECODE Currently not supported in the drivers.  This is used to allow non-standard codes to be put on the line to allow new devices that aren't specifically supported by the standard to be managed.
HAIL_REQUEST, HREQ This command allows a transceiver to send a request for other transmitters to identify themselves.  The driver takes no action when this command is received.  A user space program should intercept this command and send a HAIL_ACKNOWLEDGE if implementing the hail protocol.
HAIL_ACKNOWLEDGE, HACK This command is sent in response to a HAIL_REQUEST to implement the hail protocol.  The driver does not take Action on this command.  A user space program should intercept and implement the hail protocol if desired.
PRESETDIMHIGH, PDHIGH Used to dim or brighten a light to a specific dim level.  The level is determined by the housecode that is sent with the preset command.  The protocol simulator will properly update the status to reflect the dim level set for the unit.  
PRESETDIMLOW, PDLOW Used to dim or brighten a light to a specific dim level.  The level is determined by the housecode that is sent with the preset command.  The protocol simulator will properly update the status to reflect the dim level set for the unit.
EXTENDED_DATA, EDATA Only the Powerlinc Serial driver supports this function at this time.  This function is used to allow non-standard data to be put on the X10 line for communication with devices that were not anticipated when the protocol was written.
STATUS=ON, S=ON This command is typically sent by a two-way X10 device in response to a STATUS request to indicate that the device is currently on.  The driver supports sending and receiving this command.
STATUS=OFF, S=OFF This command is typically sent by a two-way X10 device in response to a STATUS request to indicate that the device is currently off.  The driver supports sending and receiving this commnd.
STATUS This command is transmitted by a device to request that an X10 device respond with its current status.  Only two-way devices support this command.
ps1 through ps32 Send preset dim sequence 1 through 32.

Example usage:

$ echo a11 on > /dev/x10 Turns on device A10
$ echo a11 off > /dev/x10 Turns off device A11
$ echo a11 bri > /dev/x10 Sends bright command to device A11
$ echo a11 dim > /dev/x10 Sends dim command to device A11
$ echo e1 > /dev/x10
$ echo e on > /dev/x10
Turns on device E1
$ echo e1 on > /dev/x10 Turns on device E1
$ echo e aon > /dev/x10 Sends All Lights On to housecode E
$ cat /dev/x10 Reads the X10 log of transactions
$ echo e5 > /dev/x10
$ echo e10 > /dev/x10
$ echo e on > /dev/x10
The first two commands target the individual units without a command so that they group on the line.  After sending the units without commands, a command can be sent on the line and all grouped units will respond to the command.  In this case, both E5 and E10 will turn on.  The grouping remains in effect until a different housecode is sent on the line or another individual unit is specified.
$ echo e15 > /dev/x10
$ echo g pdl > /dev/x10
Sets the unit at address E15 to light level 32%. 
$ echo e15 ps11 > /dev/x10 Sets the unit at address E15 to light level 32%

Preset Dimming requires some extra work on the users part.  The way the protocol specifies all operations for X10 is that you first address a unit in one transmission, and then you send the command.  Normally the command is sent to the same housecode as the addressed unit so the driver can accept both the command and the housecode in one transaction; however, Preset Dim uses the housecode as the dim level.  Rather than requiring the userspace program to understand the sequence and mapping, the driver will accept the command "ps#" where # is a value between 1 and 32 inclusive. The following table shows the mapping of the housecodes to dim levels:

level CMD PresetDim Low
housecode
  level CMD PresetDim Low
housecode
  level CMD PresetDim High
housecode
  level CMD PresetDim High
housecode
0% ps1 M   26% ps9 E   52% ps17 M   77% ps25 E
3% ps2 N   29% ps10 F   55% ps18 N   81% ps26 F
6% ps3 O   32% ps11 G   58% ps19 O   84% ps27 G
10% ps4 P   35% ps12 H   61% ps20 P   87% ps28 H
13% ps5 C   39% ps13 K   65% ps21 C   90% ps29 K
16% ps6 D   42% ps14 L   68% ps22 D   94% ps30 L
19% ps7 A   45% ps15 I   71% ps23 A   97% ps31 I
23% ps8 B   48% ps16 J   74% ps24 B   100% ps32 J

To accomplish sending a preset dim command, the user can send the housecode/unit address in one command and then the preset command to the appropriate housecode.  For example, to send PRESET DIM 32% to E15, the commands would be:
# echo e15 > /dev/x10
# echo g pdl > /dev/x10

Alternately, to accomplish sending PRESET DIM 32% to E15, the command would be:
# echo e15 ps11 > /dev/x10

Script Examples

Scripting is considerably more complicated because there is no mechanism in the driver to track the status of an individual device.  To get the same functionality that is provided in the standard interface the userspace program needs to read the x10 device to capture events. It is still possible to write scripts that can watch for activity.

Activity logs

The driver also maintains a short circular log of traffic on the network which can be captured and decoded to store in a human readable log file.  All other information is written to the system log the syslogd facility.  The actual location of the logs is defined in /etc/syslog.conf.  All messages are logged to the console by default.  Below is a description of the classes that the driver uses for logging information to the system.

kern.info: this class of information is typically written to the console, /var/log/messages, and /var/log/dmesg.  This type of information is benign data being reported by the driver.  At startup the driver will write the version of each of the files in the module and indicate that it successfully started.  

kern.warning: this class of information is typically written to the console, /var/log/messages, and /var/log/dmesg.  This type of information warns of some activity that failed to complete but which is not fatal to the driver.  Typical uses of the warning messages are when the transceiver prematurely stops transmitting data due to a collision on the line.  Most bi-directional transceivers also require a handshaking protocol for proper communications and if that protocol times out while waiting for a message from the transceiver, a warning message will be produced.

kern.error: this class of information is typically written to /var/log/messages, the console, and /var/log/dmesg.  This type of information indicates that a catastrophic error has occurred in the driver.  As a result, the driver will usually try to unload itself.  In many cases the driver is unable to unload and the driver will be unusable or unstable.  It is highly recommended that the machine be rebooted to eliminate potential crashes of the entire system if the error text indicates an unstable situation.

kern.debug: this class of information is typicaly written to /var/log/messages, the console, and /var/log/dmesg.  This information is only generated when the debug parameter is passed to the driver.  It is recommended that the driver not be run with debugging turned on unless you are troubleshooting a problem and need to send the output of the driver for analysis.  Debugging the driver produces an enormous about of data and will quickly fill up system log files.

In addition to the system logs which are written to the hard drive, the driver maintains a log in memory.  The log is cleared whenever the driver is loaded or unloaded.  The log contains all transactions that occur on the X10 network as well as the virtual status of all devices on the network.  

Traffic log:  Whiles the driver attempts to maintain a virtual status of the X10 network, the logic for the status updates makes assumptions about the features of devices.  For flexibility, the driver also maintains a circular log of all commnds received from the network.  Each event is also timestamped with a double long value representing the time value in seconds.  This number can be imported directly into a userspace program and stuffed into a timeval structure to use standard library routines to manipulate teh time.  The log is accessed through /dev/x10/log blocks on read if no data is available.  The format of the data in the log follows the following 3 formats:

x10logd:  To make x10logd work with the unified drivers, the -i option must be specified to identify the location of the x10 device.


Contacting the author

My contact information is:

wsh@sprintmail.com

I am happy to answer questions regarding problems compiling and using the drivers. I am also happy to help explain the X10 protocol and how to use the drivers to manage the X10 network.  I am fully open to suggestions on how to improve the drivers but I may not accept all suggestions.  Please do not be offended or insulted if your suggestion is not incorporated.  Please read the Frequently Asked Questions (FAQ) prior to sending a request.  Also, at times the email can become overwhelming, so do not be insulted if it takes me time to respond to requests.  Bug reports will always get the highest priority.  

This project is hosted on Sourceforge and has the following resources:

Enjoy!

Scott Hiles


Reference:

  1. Phil Kingery's X10 Technical Series. A great deal of information for the X10 protocol.
  2. Linux Device Drivers, 2nd Edition, By Alessandro RubiniJonathan Corbet. (online)
  3. USB Sniffer, a free, software-only tool for monitoring USB traffic.
  4. PowerLinc Serial Programming Manual