SourceForge Logo

Linux X10 universal device driver

(aka Project WiSH)

x10dev Version 1.6.11


Introduction

Let's start out with a short "why do I want this". The X10 protocol is a fairly simple protocol which is used to control home automation.  As simple as it is, every manufacturer of transceivers for the protocol created a unique method of accessing the network.  Even devices from the same manufacturer have different protocols which translate to exactly the same simple commands.  As a result, every transceiver has its own user interface (if one exists), its own API (if one exists), and its own drivers.  A user or programmer shouldn't have to write a new driver or API ever time they want to write an application or add some automation to their home.  So, this project implements the standard X10 protocol at the /dev device level so that the communications protocol for the transceiver is transparent to the user.  As a result, a user application can be written to talk to the X10 network without concern for the type of transceiver attached.  The user application can be a simple shell script utilizing cat and echo, or a more sophisticated C, Perl, or Java program utilizing open(), read(), and write() to manage the network.  One side benefit to running the drivers is that all X10 activity can be written to syslogd or a log file (using x10logd). As a result, you will have a log of all activity on your X10 network for all 16 housecodes.  

Currently the PowerLinc Serial, PowerLinc USB, Firecracker, and CM11A are supported.  The project has been built to allow for maximum flexibility so that new drivers can be written quickly as hardware comes out.

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:  This set of drivers provides unique information that was not available before it was written.  It provides a good example of how to communicate with a HID device in interrupt mode.  It also provides a good example of the state machine for the X10 protocol for anyone that wants to build a simulator.  It provides full documentation on how to talk to the PowerLinc USB transceiver.  And, it provides a fairly straight forward example of how to use the TTY line discipline subsystem. The majority of the time on this project was spent hacking through getting this information.


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.  

  1. Q:  What versions the kernel are the drivers known to be compatible with?
    A: 
    The current version has been compiled without error or warning as both part of the kernel and as a standalone addition for the following versions of linux:

    Version

    Intel

    timesys

    Sun

    Comments

    11A 17A PL PLU 11A 17A PL PLU 11A 17A PL PLU
    linux-2.4.9         Yes               starting with wish-1.5.4
    linux-2.4.16 Yes Yes Yes Yes                 version 1.6.5 fixed the transmitter for x10_plusb
    linux-2.4.18-3 Yes Yes Yes Yes                  
    linux-2.4.18-4 Yes Yes Yes Yes                  
    linux-2.4.18-5                          
    linux-2.4.18-17.7.x Yes Yes Yes Yes                  
    linux-2.4.18-18.7.x Yes Yes Yes Yes                  
    linux-2.4.18-19.7.x Yes Yes Yes Yes                  
    linux-2.4.18-24.7.x Yes Yes Yes Yes                  
    linux-2.4.20 Yes Yes Yes Yes                  
    linux-2.4.20-13.7 Yes Yes Yes Yes                  
    linux-2.4.21-rc1 Yes Yes Yes Yes                  
    linux-2.4.21-rc2 Yes Yes Yes Yes                  
    linux-2.4.21-rc3 Yes Yes Yes Yes                  
    linux-2.5.X No No No No                 Absolutely will not work under 2.5

    11A=CM11A, 17A=CM17A/Firecracker, PL=PowerLinc Serial, PLU = PowerLinc USB
  2. Q:  Why are the driver objects installed in /usr/local/lib/modules instead of /lib/modules?
    For a long time I struggled with why the PowerLinc USB drivers would always say "initializing" when you did an lsmod command.  I combed over the code over and over to try to figure it out.  Then I discovered that the problem was a conflict in the USB system.  Since the system automatically loads the drivers when the device is found, the USB system was loading HID and PLUSB simultaneously.  HID would get the port first and when my scripts removed HID (rmmod hid), the PLUSB drivers would take over and bind to the port.  Although they were fully active and working, they would stick in the initializing state.  

    So, to avoid the conflict, the installation scripts put the drivers in /usr/local/lib/modules.  This is just fine since to load all of the modules, you have to do a manual "insmod".  If I can resolve the conflicts between HID and PLUSB, I will consider moving them back to the normal modules area.
  3. Q:  How to I know when new drivers have been released?
    A:  The drivers are all on sourceforge.net/projects/wish.  Sourceforge has a couple of facilities that are used to notify users of the driver updates.  

    The first is the "Monitor" feature.  If you look at the primary page, for each download group there is a little picture of a mail envelope.  If you click on this you can set yourself up to get notifications whenever new files are placed on the site.  When you "Monitor" the drivers you will get a notification that a version was released but no details on what the release does.

    The second method is through the mailing list. If you scroll down to the bottom of the primary page you will find an item that says "Mailing Lists".  Clicking on this link will allow you to subscribe to the list.  Whenever a new version of the driver is put on sourceforge, a detailed message of the changes is sent to the mailing list.  This allows you to decide if you really need to update or not.  For instance, if the changes were all related to the USB driver and you are using the CM11A, you do not need to update your drivers.
  4. Q: Why low level device drivers?  
    A:
    Fundamentally, these drivers were created as Linux device drivers and were never intended to be ported to any other operating system.  This choice was made because it allows the drivers to be installed at the lowest level so that each X10 unit in the house is seen as a file on the host computer. This brings native management of the X10 network directly to every application on Linux with a standard interface designed around the X10 protocol.  The result is that shell scripts can use cat and echo to fully manipulate the X10 network such that complex sequences of events can created without any background in programming in C, Perl, or Java.  Since the drivers conform to the Linux standards for device drivers, any language that can make a system call will be able to manage the X10 network.  
  5. Q: Why Linux? 
    A: This was a personal choice primarily because Linux is one of the more universally available free operating systems.  One could argue that FreeBSD could use the same drivers; however, my house is filled with Linux machines so Linux was the choice.  At some future date I might port it to FreeBSD.  The drivers have been written to use only standard calls (except for the FireCracker which talks directly to the 8240 or 16550 UARTs) to manage the transceivers so they should work seamlessly with other hardware architectures.  It has been thoroughly tested with Intel hardware, but has not been tested on any other platforms.  And, of course, Windows has tons of software out there for all of the drivers and there is little in the way of shell scripting that can be done on a Windows box so the concept has less applicability for Windows.  All of the source is in the package and even the documentation I was able to reverse engineer from the PowerLinc USB is provided so anyone can use this as a basis to writing drivers for another operating system.
  6. Q: Aren't there already drivers for transceivers?  
    A: Yes, and no.  There are drivers for older transceivers like the CM11A (heyu) and even the newer FireCracker/CM17A which have well documented programming manuals.  But, there are no Linux drivers for the PowerLinc Serial or the PowerLinc USB.  Worse yet, the PowerLinc USB doesn't even have any released information for how to communicate with the device in the first place. But, as mentioned in the introduction, each of these transceivers that has a control program for Linux has its own user interface.  There are also projects on http://www.sourceforge.net that are crating standard APIs to the transceivers for libraries.  But, none of these make the devices universally available to shell scripts and programs with a common API.  So, this project is trying to fill that gap by creating that common API with a simple, human readable command structure that makes it equally useable for shell scripts, command line usage, and programming.
  7. Q:  Why create /dev/x10?  Why not /proc/x10?
    A:  It is fully possible that I may add a /proc/x10 type of interface to the drivers, but with the introduction of DEVFS, the same kind of functionality exists for a dynamic /dev structure so I don't have a compelling reason for creating a /proc interface.  The reason for a /dev/x10 type of interface is that it fulfills the goal of creating a standard interface to the X10 network all the way down to the device mapping.  Using /dev/x10/* for the devices also allows for creating links to human readable names.  For instance, by linking /automation/foyer to /dev/x10/a2 (with the command "ln -s /dev/x10/a2 /automation/foyer"), you could use the command line to send "echo on > /automation/foyer" to turn on the light in the foyer.  Using /dev/x10 creates incredible flexibility for using the driver with just about any program or shell script and brings control to the command line without writing a user program.
  8. Q: Why are you taking up two major character devices and so many minor devices?
    A:
      This is the $10 million question and the very question that will likely keep the drivers from becoming part of the kernel distribution.  If these drivers become widely used and there is sufficient demand to make the argument for getting them into the kernel, I will do so.  For the concept of making each X10 unit in the house appear as a device to the host computer, you have to have a minor number for each of the possible devices.  Since you have 16 housecodes and 16 units per housecode, you have just used up 256 minor numbers which is a full major number.  That still leaves you without the ability to have a log, status, or a control interface to the driver.  You could also argue (as some have) that I could have used IOCTL calls for the status and controls or used only 16 minor numbers for the housecodes and used IOCTL for management of the units.  But, using IOCTL makes the driver useless to shell scripts and would relegate the driver to being just an API to programmers.  The basic philosophy is that all of the management of the X10 network should be fully accessible from the command line as well as be accessible from a programming API.
  9. Q: Why do some programs require x10attach?
    A:
    This could change if I figure out a better way.  When the project started out, the only two drivers targeted were the CM11A and the PowerLinc Serial.  I put a general call out to the comp.home.automation newsgroup asking for help and it was suggested that I look at Vojtech Pavlik's code for the input abstraction layer.  This was a pretty good match for what I was doing so I adapted his approach to create my abstraction. But, for it to work, you have to have a user-space program register the TTY device with the line discipline driver.  The Firecracker, on the other hand, requires direct access to the CTS and RTS lines to signal the transceiver.  It isn't a standard serial device and actually draws power and signal from the CTS and RTS pins.  As a result, the line discipline driver for the FireCracker is specific to the 8240 and 16550 UART chips and accepts the port information on the command line when loading the driver.  Finally, the PowerLinc USB doesn't communicate through the TTY interface so it talks directly through the USB core drivers where the tty line discipline subsystem doesn't come into play..  
  10. Q: How did you get the programming documentation for the PowerLinc USB?  
    A: This was the most difficult transceiver to write code for.  I started by contacting SmartHome and they were very polite but said that they didn't have the time to provide the information.  The original PowerLinc Serial manual was somewhat limited and took a few days of experimentation to make work.  Imagine the number of technical support calls they would get by releasing programming information for a USB interface that few people have programmed to in the first place. I can't fault SmartHome at all because USB isn't as simple as writing to a serial port and generally is not likely to be a simple weekend project for someone.  To write a transceiver driver, you need 4 basic things: 1) the communications method for the device, 2) the translation table for generating the X10 signals, 3) the transmission communications sequence and packet format, 4) the receive communications sequence and packet format.  The communications method was easy enough to figure out by interrogating the USB device to find out that it was a HID class device which preferred interrupt mode for communications.  All of the documentation for USB drivers skips over how to communicate in interrupt form and always uses polling with bulk transfers.  Even the Synapse software from SmartHome says that it uses polling.  I turned to the IBM Bluetooth USB drivers that had an interrupt driven receiver and figured out how to at least write a receiver.  By complete accident I was just testing out the receiver to see if I could even load the module at 10:30pm at night when my main computer started sending the X10 signals to put the house to sleep.  I received all of the signals and found out that the receive protocol is incredibly simple in that it doesn't require any handshaking (identical in process to the PowerLinc Serial).  From there I turned on the receiver and sent enough codes on the line to figure out the translation table (minus the extended codes).  The last one was the most difficult.  After banging against the device for a day thinking that the transmission process was as simple as the receive, I went to www.usb.org and found a link to USB Central which had a freeware USB sniffer for windows.  I fired it up, fired up the USB Synapse software from SmartHome, and captured the USB traffic.  The protocol is similar in process to that of the PowerLinc Serial.  The actual data to be sent is very different, and the packet format is different, but the concept is similar.  After a bit of signaling, I was able to figure out what the fields were for the basic X10 protocol.  After about 32 hours of trial and error hacking with the device, I had the protocol documented, and about 4 hours later I had fully working drivers.  The documentation for what I worked out is provided in the wish-1.4.0 and later packages in PDF format.

    (19 Feb, 2003 - I recently got my hands on a programming specification for the PowerLinc USB and extracted more information out of the document and updated the PowerLinc USB .pdf file to reflect the new information.)
  11. Q:  When I compile I get errors from kernel.h?
    A:
      Normally when you compile the kernel, the directory /usr/src/linux/include/linux/modules/ is populated with the version information for module versioning. In order to recompile a RedHat kernel, you must remove all of the .ver and .stamp files from the /usr/src/linux/include/linux/modules/ directory and then run "make dep" to recreate them.  If you do not remove the files you will not be able to compile the drivers and you will not be able to create the modules for your kernel in the source tree.  You will get errors like:

    In file included from x10_core.c:52:
    /usr/src/linux/include/linux/kernel.h:60: nondigits in number and not hexadecimal
    /usr/src/linux/include/linux/kernel.h:60: nondigits in number and not hexadecimal
    /usr/src/linux/include/linux/kernel.h:60: parse error before `01075bf0'

    Execute the command "rm -f /usr/src/linux/include/linux/modules/*" to clean out the directory.  Run "make dep" to recreate the files, and then try recompiling the driver.Alternately, if you just installed the kernel source, make sure that you followed the directions and performed a "make mrproper" and "make symlinks" so that the kernel is set up for your architecture.
  12. Q:  Why are the drivers limited to kernel 2.4.x?
    A:
      Kernel 2.4.x has been out for 2 years and is very solid.  To make the drivers work for previous releases a considerable amount of conditional compilation would have to be built into the code to account for the differences in the kernel layout and the functionality.  Fortunately the changes necessary to make it work with 2.5 are backwards compatible with 2.4 with the exception of the USB drivers.  The USB interface has changed so significantly in 2.5 that a new driver for the PowerLinc will be necessary.  Additionally, the makefile system for 2.5 has been modified so that it no longer has a Rules.make file that I can use to create the compiler flags.  I have figured out the Makefile strategy, but it is far from pretty.  Considering the constant changes in the kernel code, I will wait till 2.6 is actually released to release the drivers for 2.6.
  13. Q:  Why do I have to have a working kernel source tree installed?
    A:  The pre-compiled object files that are distributed in the RPMs are compiled without module versions turned on.  This allows the module objects to work with your kernel.  The pre-compiled object files that are distributed are limited to stable and recent revisions of the kernel and only for the Intel platform.  If you are running on something different from the Intel platform or are using a kernel version that is not available in pre-compiled objects, you will need to compile the drivers from source.  To compile the drivers, you need to have information from your kernel source tree so that the modules can be used by the system.  Most significantly, the drivers have to be compiled after "make dep" has been performed on the source tree so that the drivers module version numbers will match the kernel.  Additionally, the driver uses the Makefile rules from the kernel source tree in order to know what options to use when compiling the drivers.  
  14. Q:  Can I create my own RPMs for my kernel?
    A: 
    Absolutely.  The script that is used to create the object RPMs is part of the source distribution.  The script is called mkrpm.sh.  It requires 3 parameters and must be run as root.  In most cases the resulting RPM will be put in the /usr/src/redhat/RPMS/<arch> directory.  You can change that by setting up /etc/rpmrc.  

    The syntax for the script is:
    # mkrpm.sh wish-version-number kernel-version-number

    So, to make the rpm for version 1.6.5 for kernel 2.4.20, you would use the command:
    # mkrpm.sh 1.6.5 2.4.20

    #make rpm can also be used and can accept the KERNELDIR variable to specify a kernel or by default will create the RPM for the current version and the current kernel.
  15. Q:  Why would I install this in my kernel source tree?
    A:  I am guessing that few people will.  The advantage to installing in the kernel source tree is that it whenever you recompile and install your kernel, you will automatically get the modules in /lib/modules/*.  But, in reality, most of us compile the kernel once or twice to get a working version that has everything that we need, and then we never recompile that same kernel again.  We usually go on to the next kernel version.  But, by making it possible to build the drivers into the kernel and doing all of the upfront work, I am hoping that anyone that distributes modified kernels (like RedHat) might go ahead and install the drivers into the kernel source as part of a distribution.  In the long run, if I do attempt to get the drivers into the base kernel distribution, all of the upfront work will have been done.
  16. Q:  What happened to the TW523 and PL503 drivers?
    A:  I thought I could make these work through the same process used by twserial-0.2.tar.gz but I couldn't get the driver to do anything. I later realized that twserial was using the $100 serial adapter sold by SmartHome.  The question to ask is why anyone would buy a TW523 for $30, and a serial adapter for $100 when they could buy a PowerLinc Serial for $30?  So, I abandoned that work.  The code is still in RCS and the state machine is fully built, but I am no longer pursuing it.
  17. Q:  Why does "echo on > /dev/x10/a" inconsistently turn on lights?
    A:  A couple of people have asked this question so it is worth explaining.  This question implies that the user doesn't fully understand the X10 protocol.  Don't take that as an insult if you asked the question.  It took me a couple of weeks of reading and testing to understand the X10 protocol and there is likelihood that I still don't fully understand it.  Joe User likely hasn't spent that much time reading through the X10 standard so this is a valid question.  The answer is that the protocol allows you to gang devices together by sending their addresses on the line without a command.  Every device that hears its address will then start listening for a command on the line.  Once a command is received, if another address is seen, the devices will all reset and stop listening for a command and start listening for their address again.  As a result, you can send A1, A2, A5, AON, A3, A4, A6, A8, AOFF to turn A1, A2, and A5 on, then turn A3, A4, A6, and A8 off.  This would be equivalent to the following:
        # echo null > /dev/x10/a1
        # echo null > /dev/x10/a2
        # echo null > /dev/x10/a3
        # echo on > /dev/x10/a
        # echo null > /dev/x10/a3
        # echo null > /dev/x10/a4
        # echo null > /dev/x10/a6
        # echo null > /dev/x10/a8
        # echo off > /dev/x10/a
    The exact same results could be obtained by sending A1, AON, A2, AON, A5, AON, A3, AOFF, A4, AOFF, A6, AOFF, A8, AOFF. This would be equivalent to the following:
        # echo on > /dev/x10/a1
        # echo on > /dev/x10/a2
        # echo on > /dev/x10/a5
        # echo off > /dev/x10/a3
        # echo off > /dev/x10/a4
        # echo off > /dev/x10/a6
        # echo off > /dev/x10/a8
    Notice that using the first approach of grouping the devices, you only send 9 signals on the line.  If you do it explicitly, you end up sending 14 signals on the line.  Each signal takes about 1 second (the driver automatically adds delay to make sure that the signal gets out on the line) so you are looking at a time difference in what it takes to send the signal.  The second approach has fewer user space commands and is easier to understand but takes longer to perform since the commands in the grouping example take 1 second, and the commands in the second example take 2 seconds each.  
  18. Q: Why is the status out of synch with the actual X10 units?
    A:  One of the goals of the driver was to simplify the monitoring of the X10 network.  At the same time, I didn't want to hide any of the protocol from the user.  So, I implemented a software version of the X10 protocol (in the file x10_core.c) which interprets any X10 commands that are seen on the line and simulates what should have happened to the units in the house.  Unfortunately, there are situations where units do not receive the command, may have been manually turned on or off, or the unit doesn't respond to that particular command.  For instance, wall receptacles rarely support dim/bright, or ALL_LIGHTS_ON/OFF.  The x10 interpreter doesn't know which devices are lights and which are receptacles so it assumes that everything is a light and responds to every X10 command.  Another example is that florescent lighting switches rarely support bright/dim commands but the driver doesn't know that they are florescent so it goes ahead and updates the status table as if a bright/dim actually occurred.  The status table is nothing more than an approximation of the network based on treating everything as a full featured light.
  19. Q:  How do I get a more accurate software status of the X10 units?
    A:  The simplest answer is that you can build user level software that watches /dev/x10/log and creates its own implementation of the X10 status but with awareness of the type of device for each unit in the house.  This will require reading and understanding the X10 protocol or extracting the state table out of the x10_core.c file.  A second approach would be to buy two-way devices and send a status request (with the command "echo status > /dev/x10/<unit>") to the desired X10 unit.  The unit will then respond with a status answer and the driver will update the state table accordingly.  The added advantage of two-way devices is that whenever they change state, they send a notice to the network that they have changed.  Replacing your switches with two-way switches is currently pretty expensive considering that two-way X10 switches cost about 4 times as much as a traditional X10 switch.
  20. Q: Why doesn't "cat" return when reading the X10 devices?
    A:  When the program "cat" opens a file to read, it opens it in blocking mode.  Since the driver continually receives information, there is no real end-of-file, therefore, when you read the device in blocking mode, the read will wait for more data to arrive for the device.  Since most scripts need to just read the current value and then exit, a utility called "nbread" (non-blocking read) has been provided in the utils directory.  This utility opens the device with the flag O_NONBLOCK so that when the driver has no more data it returns 0 bytes causing the read() request to return.  Realizing that there may be cases where you want the driver to never block for a status read, the flag "nonblockread" can be passed to the module when it is loaded to cause it to never block.  The syntax for loading with nonblocking reads is "insmod x10_<transceiver> nonblockread=1".
  21. Q:  What about the timers and macros for the CM11A?
    A:  While the timers and macros are really neat features of the CM11A, they aren't part of the standard X10 protocol.  It would be fairly easy to add some IOCTL calls to control these features but I just haven't spent the time.  My CM11A is somewhat flaky in that it locks up a good bit and has some issues with sending me information when it receives it so I have focused on the other drivers more.  
  22. Q:  What if I find a bug?
    A:
      If you think you have found a bug in the drivers, by all means send me an email.  Please try to send me enough information to repeat your situation.  If it is fully repeatable, turn on debugging by loading the module with debug=1 on the command line and then send me the output of your /var/log/messages (or wherever your kernel logs KERN_INFO messages).  
  23. Q:  Does it work with an SMP machine?
    A:
      It should, but it doesn't.  A close friend has an SMP machine that locks up whenever the PowerLinc Serial driver is loaded.  We have traced it down to the tty->write() call that enters the tty line discipline drivers.  At this point I am not willing to claim that there is a bug in the tty subsystem but I have not been able to figure out how to fix this bug.  So, for now, the drivers appear to work on every uniprocessor machine that I have encountered.  
  24. Q: What X10 features are supported?
    A:  All drivers support Single Unit On, Single Unit Off, Bright, Dim.  All drivers except the Firecracker (one-way), support All Units OFF, All Lights On, All Lights Off, Status request, Status=on response, Status=off response, hail request, hail response.  The PowerLinc Serial and the CM11A support Extended Data.  The PowerLinc Serial, PowerLinc USB, and CM11A support Preset Dim High/Low.  None support Extended Code.
  25. Q:  My switch won't respond to the preset dim commands?
    A: 
    Most X10 switches do not respond to preset dim commands.  You must buy a switch that is explicitly designed to respond to the commands. Sometimes they are called switches with "scenes capability".  They typically cost significantly more than common X10 switches.  
  26. Q:  I have an X10 device that requires that I program it by sending electronic signals.  How do I program it?
    A:
      Manufacturers have moved away from using dials to set the X10 address in switches.  Many switches are now set up to respond to a unit code by electronically programming them through the X10 network.  SmartHome advises getting Maxi Controller because it can send unitcodes without commands and housecodes without unitcodes.  If the WiSH device drivers are installed, the switches can be programmed without purchasing the Maxi Controller.  The device driver makes programming these devices simple because it has the capability to let the userspace program send raw commands onto the line.  

    Programming usually involves putting the switch into a program mode by holding in or pulling out a button on the device.  For Leviton switches it is the green button on the top of the switch.  For SmartHome devices it is usually a small reset button.  The instructions will tell you to put the device into programming mode by manipulating the switch and then sending the device's address on the X10 line.  Sending the address is accomplished by issuing the command:
    # echo null > /dev/x10/<housecode><unitcode>

    For example, if you wanted to program a LampLink (SmartHome product 2000STW) to respond to unit E16 and turn on the feature for recognizing local load, you would press the small black button on the side and hold for 5 seconds.  The lamp would come on.  You then issue the commands:
    # echo null > /dev/x10/e15
    # echo on > /dev/x10/e
    The first command programs the switch as E15, and the second one tell it to turn on local load detection.

    Similarly, to transmit the clear sequence of "O16, N16, M16, P16, M16" to a LampLink, you would issue the following commands:
    # echo null > /dev/x10/o16
    # echo null > /dev/x10/n16
    # echo null > /dev/x10/m16
    # echo null > /dev/x10/p16
    # echo null > /dev/x10/m16
  27. Q:  Why isn't Manufacturer X's transceiver supported?
    A:
      My intention is to write drivers for every transceiver that would fit with the philosophy of the project.  Things like the Othello or Timecommander don't really make sense for this project.  Also, I am paying for this out of my pocket so if the transceiver is overly expensive or there are is no programming documentation, the chances of me buying one are slim.  
  28. Q:  Have you tested the drivers with Manufacturer X's switch?
    A:
      Again, I am limited in budget and can't afford to buy every switch that is out there.   SmartHome was very kind and gave me a couple of two way switches to do testing.  This enabled me to complete the preset dim/bright portion of the drivers.  
  29. Q:  How do I write a driver on my own?
    A:  First, read the entire Technical Details section.  The distribution has skeleton files that are used as a basis to start each driver.  The skeleton files are skel-x10_ldisc.c, skel-x10_xcvr.c, and skel-x10_xcvr.h.  You should never modify x10_core.c as it is loaded for every driver.  The file skel-x10_ldisc.c provides the minimum functionality needed to create the line discipline portion of the driver.  Similarly, the file skel-x10_xcvr.c provides the minimum functionality to create the protocol translator portion of a driver.  I have tried to add lots of comments so that it is clear as to what each function must do.  Use the working source code for examples of how to accomplish various tasks.  x10_xcvr_pl.c has a very straightforward decode() and encode() example and also provides an example of how to handle handshaking with the transceiver for transmit (i.e. when the xcvr_transmit() function must send something and wait for a specific response from the transceiver before proceeding.).  x10_xcvr_cm17a.c provides an example of how to write a unidirectional driver (i.e. no receive functionality).  
  30. Q: Occasionally, when I issue a command to the driver it will pause for a minute without sending a signal on the line?
    A: 
    This is normal behavior for the driver when a command is sent while the driver is busy.  The drivers have been written to make sure that command sequences go to the line in the exact order that was sent from the user.  Since the driver has been written to be used concurrently from multiple processes, the driver locks the transmitter and queues up commands while it is transmitting a sequence.  Some X10 transceivers will recognize traffic on the network and will automatically halt transmissions or not allow transmission to start if data is being received.  When this happens, the driver will time out trying to send and exit.  This frequently occurs when the X10 network has a lot of noise.   
  31. Q:  I keep seeing X10 codes, but I am not sending them?
    A:
      These messages likely existed before the driver was installed but now that you are seeing everything that is on your X10 network, you are seeing this noise.  There are a couple of things that can cause this.  On my system, when a florescent light begins to reach its end of life, before I can visibly notice the flickering, the X10 network starts getting flooded with "STATUS J" messages.  Mine have always been very consistent when the florescent lights wither.  Another common cause for it is one of your neighbors using X10.  One of the reasons that so many housecodes were provided was so that neighbors on the same line can coexist.  If you are seeing the same housecode that you are using, change to a different housecode so that you and your neighbor can coexist.  If you are really set on using the housecode that you have, you could talk to your neighbor about chainging.
  32. Q:  All of your examples are run as the user "root".  Isn't that unsafe?
    A:
      It is potentially unsafe so you might want to run your X10 network from a less priveledged user.  You must change the permissions on the files in /dev/x10/ so that the user can read and write to the device files.  By default the files are created with write capability only for "root".
  33. Q:  Why are you maintaining logging in kernel space?
    A: 
    The driver maintains a very short circular log of information sent and received on the X10 network.  The log is intended to buffer the information so that a daemon can watch the log without falling behind or missing data when there is a great deal of traffic.  The log is 16 entries long which should be sufficient.  The daemon utils/x10logd is a fairly robust utility for reading the log, decoding it, and writing a human readable form to a file.  
  34. Q:  Can I load two transceivers at the same time on the same machine?
    *** A:
      If you are NOT running DEVFS, then yes you can provided that you load the second drivers with the data_major and control_major parameters set to something other than what is used by the first driver.  You will need to set up different character devices for each x10 device that you intend to control.  makedev.sh will accept 3 parameters to override the defaults.  The first must be the major character device for data, the second the major character device for control, and the third must be the directory for the devices.  For example, to set the system up to have both the Firecracker and the CM11A loaded at the same time on ttyS0, the following commands can be used:

    # scripts/makedev.sh 120 121 /dev/cm11a
    # scripts/makedev.sh 122 123 /dev/cm17a
    # insmod x10_cm17a.o data_major=122 control_major=123 port=0x3f8
    # insmod x10_cm11a.o data_major=120 control_major=121
    # utils/x10attach -11a /dev/ttyS0

    To turn on e15 through the cm11a, execute the command:
    # echo 1 > /dev/cm11a/e15

    To turn off e15 through the cm17a, execute the command:
    # echo 0 > /dev/cm17a/e15

    *** A: If you ARE running DEVFS, then yes you can load multiple modules by specifying the toplevel directory for the device files with the devroot parameter.  The default is "x10" which will result in devices being created in /dev/x10.  For example, to set the system up to have both the Firecracker and teh CM11A loaded at the same time on ttyS0, the following commands can be used:

    # insmod x10_cm11a.o devroot=cm11a
    # insmod x10_cm17a.o port=0x3f8 devroot=cm17a
    # x10attach -11a /dev/ttyS0

    To turn on e15 through the cm11a, execute the command:
    # echo 1 > /dev/cm11a/e15

    To turn off e15 through the cm17a, execute the command:
    # echo 0 >/dev/cm17a/e15

    *** A: You cannot load two of the same type of module simultaneously regardless of the parameters specified.  x10attach can't distinguish between the different instances, and the USB drivers can handle two devices of the same type.  That said, the FireCracker can have as many instances as there are ports on the machine.
  35. Q:  How do I set up aliases for my X10 devices?
    A:
      This is one of the primary advantages to the standard interface.  By creating symbolic links to the device, you can write scripts or programs to control and watch the devices and can just change the links whenever you want to change the configuration of the house. For instance, with the following commands exectuted:

    # mkdir /automation
    # ln -s /dev/x10/e1 /automation/frontflood
    # ln -s /dev/x10/e2 /automation/backflood
    # ln -s /dev/x10/e3 /automation/extbreakfast
    # ln -s /dev/x10/e4 /automation/frontgarage
    # ln -s /dev/x10/e5 /automation/backgarage
    # ln -s /dev/x10/e6 /automation/frontporch
    # ln -s /dev/x10/e7 /automation/backporch
    # ln -s /dev/x10/e8 /automation/extbasement
    # ln -s /dev/x10/e9 /automation/gym
    # ln -s /dev/x10/e10 /automation/foyer
    # ln -s /dev/x10/e11 /automation/familyroom
    # ln -s /dev/x10/e15 /automation/curio
    # ln -s /dev/x10/e16 /automation/lightsensor
    # ln -s /dev/x10/g1 /automation/garagestatus
    # ln -s /dev/x10/g11 /automation/statusgaragedouble
    # ln -s /dev/x10/g12 /automation/statusgaragesingle

    You could now execute the following command to turn on the light in the foyer:

    $ echo 1 > /automation/foyer

    If you want to have the light sensor turn on the flood lights, use the x10watch utility as follows:

    $ x10watch /automation/lightsensor -1 "echo on > /automation/frontflood" -0 "echo off > /automation/frontflood" &
    $ x10watch /automation/lightsensor -1 "echo on > /automation/backflood" -0 "echo off > /automation/backflood" &
  36. Q:  When I execute "rmmod x10_pl" or "rmmod x10_cm11a", my system crashes?
    A:
      Did you remember to kill x10attach?  If you remove the driver while x10attach is running, your system will crash.  I haven't figured a way around this one yet.  If you remove the driver while x10attach has the file open, it is the equivalent of removing your driver for your hard drive while you are trying to read from it.
  37. Q: Why is x10attach required for some drivers?
    A:
    For transceivers that can connect through the tty line discipline API, a userspace helper program is used to set the serial port up and connect the tty port (usually N_X10) to drivers.  For drivers that talk directly to the hardware (FireCracker) or through a different subsystem (USB), the initialization and setup does not use the userspace program.  To be consistent with the philosophy that the kernel is moving, the USB interface needs to be rewritten to work with the tty line discipline subsystem so that it too would require the userspace helper program. 
  38. 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.
  39. 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.
  40. Q:  What is wishweb?
    A:
      To demonstrate the ease of programming to the X10 device drivers, a small client and server have been created.  Combined they are called wishweb.  The client is written in Java as an applet but it can run as a standalone Java application on any machine with the java 1.4 or higher runtime environment.  A precompiled version of the Java applet is provided in x10web.jar.  The applet looks almost identical to the SmartHome Synapse software and provides a very simple interface to controlling the X10 devices.  It runs in Netscape Navigator or in Internet Explorer and will automatically request to download the Java runtime environment if the environment has not been installed on the local client.  
  41. Q:  Um, wishweb is kinda dorky.  Why something so simple?
    A:
      Dude, you gotta crawl before you can walk.  I modeled wishweb after the user interface from the SmartHome Synapse software and yes, it is a very limited subset of what the SmartHome software can do.  But, it is a very good representation of the raw protocol.  Prior to this project, the most complex Java application I had written was a News Client a year ago when I happened across a Java programming book called "Learning Java" and decided to learn Java.  Getting it all to work together was a pretty big challenge.  As with the entire history of this project, I build things by generating the concept, prototyping something and getting it working for proof of concept, and once all works, I start expanding.  So, wishweb is the proof of concept.  x10home is the more advanced Java interface that you may want to consider if you find x10web to simple.
  42. Q:  Why is there such a long delay (20 seconds) for the CM11A when I issue a command?
    A: 
    This question comes up a lot.  Consistently, this happens when the CM11A is plugged into a power strip, UPS, or the same outlet as the computer.  The problem is caused by noise on the line.  Since the CM11A has a low signal strength (100mv) and it will interrupt anything that it is doing when it hears a signal, the long delay is really just the driver timing out trying to communicate with the CM11A.  The typical sequence of events are you send a command to the X10 network.  WiSH sends a startup command to the CM11A and waits for an OK to proceed.  Then the CM11A gets some noise and never sends the OK.  The WiSH drivers then time out waiting for a response and retry the command.  This repeats a number of times before the driver gives up.  This continual retry is the cause of the 20 second delay.

    The fix is to plug the CM11A into a different outlet.  It doesn't matter if it is on the same circuit as a little distance will make a significant difference in the noise that the CM11A hears.  
  43. Q:  Why won't the PowerLinc USB drivers work with all kernels.
    A: 
    Every iteration of the kernel has resulted in significant changes to the USB subsystem.  It was fairly stabile from version 2.4.18 to 2.4.20, but changed again at 2.4.21.  2.5.x is so different that when 2.6 comes out I will release a new driver just for the 2.6 series rather than try to code IFDEF statements everywhere.  
  44. Q:  Why does the x10log show that commands were transmitted even though the command timed out?
    A
    :  This is a tradeoff of either having the state machine reflect the order of transmission or reflect the ability to transmit.  The problem that is being traded off here is that when sending the status command, some transceivers respond only with their status (rather than with their address followed by their status).  The protocol specification isn't clear on what the transmitter must do in this case.  In the case where the two-way switch is sending only its status, it is assuming that since the original status request was sent with a specific address, any response will be for that same address.  Regarding the drivers, the reception of the status from the network always comes back before the transmit function can complete resulting in the state machine thinking that it received something before it transmitted something.  The transmitter only updated the state machine if the transmit function completed.  The reasons for it not completed would include the transceiver being unavailable or the transmitter being blocked by a noisy line.  Prior to version 1.6.3, the driver took the safe method of only updating the state machine if the transmitter completed.  But, starting with version 1.6.3, the state machine is updated regardless of the completion of the transmitter to make sure that the state machine reflects the optimistic or expected state of the network.
  45. Q:  I am just getting into X10.  What transceiver and switches would you recommend?
    A:
      In writing the drivers I have tried to stay neutral in my comments and support for the various transceivers.  I have all 4 that are supported and have used all 4 for a significant length of time and they all work.  So, I will not recommend one over the other.  I will tell you that I use the PowerLinc USB and PowerLinc Serial for my own home automation so you will tend to see that I release new features on these first.  Be aware that the choice of the PowerLinc USB does limit you to the kernel version that are supported and tested with the wish drivers.  If you like to update your kernel regularly, you may want to stick with a serial transceiver as the line discipline subsystem has not changed in many kernel revisions.

    As for switches, the drivers were written to work with all standard X10 switches and devices.  Devices that require the extended analog data have not been tested with the drivers so your mileage may vary when buying these devices.

    So, for your initial setup, start small and learn as you go.  No need to drop $1000 on a bunch of equipment if you aren't ready to use it yet.  Get a transceiver (PowerLinc USB, PowerLinc Serial, or CM11A) and get a switch module that plugs into the wall (something like the LampLinc 2-way from Smarthome).  Get your Linux system set up properly, install the kernel source, and build the drivers.  The X10 hardware for this will cost about $70.  If you are an avid Linux user and administrator, this will take you all of an hour to get going.  If you are new to Linux, this could take days or weeks depending on how fast you learn and how good you are at following directions.  Once you have the drivers working, tackle getting the WEB interface going so that you have GUI control of the switch.  After you have mastered that, buy more switches depending on what you want to do.
  46. Q:  What are some projects or examples of uses of the WiSH drivers?
    A:
      Wow, this is a broad question.  Before I put out some examples, let me just say that I wrote the drivers to provide as much flexibility as possible so the limits to the projects are only your budget and imagination.

    Garage Door alarm and night light:  Get an X10 Chime module (SmartHome Item 2045), an X10 IOLinc 4-termina two way sensor (SmartHome Item 1624), garage door sensors (SmartHome Item 7456), and a LampLinc Module (SmartHome Item 2000S).  The garage door sensor model 7456 has both NO and NC wires.  Connect the NO wires to the IOLinc terminals and mount the sensor on the wall with the magnet on the garage door.  When the door is closed the light on the IOLinc will be off.  When the door is open, the light will turn on.  Program the IOLinc to send its signal on a housecode (I use G for garage) and a unit (such as 10).  Now, set the computer up to use x10watch to listen for G10 to change state.  When G10 is off, turn off the LampLinc.  When G10 turns on, turn on the LampLinc and turn on the Chime module.  Set the x10watch program to delay for 5 minutes after the signal is heard so that  the light stays on to give you time to walk in the house and then turns off after 5 minutes when the door is closed.

    Automatic garage closer:  Get an X10 IOLinc 4-termina two way sensor (SmartHome Item 1624), and a garage door sensor (SmartHome Item 7456) [same as above].  Program the first channel of the IOLinc as input and wire it to the garage door sensor.  Program the second channel of the IOLinc as an output and connect it to the wires that are used by the button to open and close the garage door from the wall.  Now, write a C program or shell script to watch the first channel of the IOLinc for the door to open, count down a timer, and if the door isn't already closed, send an ON then OFF signal to the second channel of the IOLinc to get the garage door motor to close the door. If you want to get really fancy, use the NO circuit of the sensor to prevent the system from opening the garage door.  (Note, I was initially concerned that the IOLinc might never get the off signal with the effect of holding the button down.  If you do the fancy version, this won't be a problem at all.  But, if you don't use the door sensor to complete the circuit, you can still just send numerous off signals to make sure that it gets it.)

    Alarm automation:  I have a DSC32 alarm in the house that can produce X10 signals.  Every light in the hall and public rooms in the house have X10 light switches.  The alarm is wired so that each individual door is a single zone so when the alarm goes off, it can send an X10 signal to indicate the entry point.  Using x10watch, the computer can watch for the DSC codes on the X10 network and turn the lights on throughout the house in a sequence such that it appears that someone is walking through the house turning lights on as the person enters the room.  

    Dusk/Dawn light control:  Get a Leviton Dusk/Dawn sensor (SmartHome Item 4235) and wire it either permanently to the outside of the house or set it in a window where it can sense the outside light.  Set x10watch up to listen for the signal from the sensor and turn the outside lights for the house on and off based on the light.  Extend this just a tad by using the sensor to have x10watch turn on the flood lights, porch lights, and garage lights.  At bedtime, say around 11:00pm, use a cron job to turn off the flood lights so that you can sleep without the room being filled with light, turn off the outside lights, and turn on the low level lights over each of the outside doors.  At dawn, use the signal from the sensor to have x10watch turn off all lights.

    Well flood control:  If you live out in a rural area and have a well, you can have the well pump turn off if there is flooding in the house.  Get a WaterBug (SmartHome Item 7160), a PowerFlash interface (SmartHome Item 4060), and a 220 V heavy duty X10 switch (SmartHome Item 2210).  Wire the WaterBug up so that it can sense water on the floor in the basement and wire the bug to the powerflash interface.  Now use x10watch to watch for a signal from the powerflash indicating that there is water.  When the indicator is seen, turn off the well pump with the 220 V switch.  

Downloading

The drivers are downloaded from sourceforge.  Go to 

Installation (from source)

1) Compiling the drivers

To compile 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 top directory of the kernel source tree, 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 KENRNLDIR=<kernel source directory>" where <kernel source dir> is the directory where the kernel sources are installed.  Do not proceed if any errors occur.  You can leave off the KERNELDIR parameter if you have your kernel source in /usr/src/linux.  Othewise, you can specify the directory for your kernel sources.  For example, if the kernel source is located in /usr/src/linux-2.4.20, type the following command:

    # make KERNELDIR=/usr/src/linux-2.4.20
  2. To automatically install the drivers and the utilities, type "make installall KERNELDIR=<kernel source directory>" (without the quotes) in the top level directory of the WiSH source code.  This will copy the modules to your /lib/modules/<kernel version>/kernel/drivers/char/x10 directory.  It will also copy the utilities to /usr/sbin and /usr/bin as well as make the devices in /dev/x10 if the directory doesn't exist.

Note that the logic used for determining the kernel version is:

  1. If KERNELDIR is specified on the make commandline, it will be used.  If not specified, go to #2
  2. If /usr/src/linux/ exists, it will be used.  If /usr/src/linux/ doesn't exist, go to #3
  3. The directory /usr/src/linux-`uname -r` will be used.  If /usr/src/linux-`uname -r` doesn't exist, the make will fail

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

The drivers have been written to be installed directly into the kernel source tree for version 2.4.x of the Linux kernel distribution.  The script patch_kernel_src.sh has been provided to automate putting it into the kernel source.  This script creates /usr/src/linux/drivers/char/x10 and then puts the source, a Makefile, and a Config.in into the directory.  It then patches /usr/src/linux/drivers/char/Makefile and /usr/src/linux/drivers/char/Config.in so that the source is included in the configuration scripts and in the makefiles for the source.  

1) Merge into the kernel tree and recompile the kernel (not recommended any longer)

Once the drivers have been installed in the source tree, you need to reconfigure the kernel and select the x10 drivers in the character driver portion of the configuration.  Use the standard commands "make menuconfig", "make xconfig" or "make config" in the top level kernel source directory.  The drivers will be found at the bottom of the list of Character Device drivers.  Then recompile the kernel as usual.

  1. Make sure that your kernel source is linked to /usr/src/linux
  2. From the top level directory of the WiSH source code, type "./patch_kernel_src.sh" (without the quotes).  
  3. Remove all of the files in /usr/src/linux/include/linux/modules/ with the command "rm -f /usr/src/linux/include/linux/modules/*".
  4. Alternately:  You wil need to recreate your .config file after running these commands so backup your .config first.
    1. Type "make mrproper" (without the quotes) in the top level directory of your kernel source.
    2. Type "make symlinks" (without the quotes) in the top level directory of your kernel source.
  5. Go to /usr/src/linux, and configure your kernel with "make menuconfig", "make xconfig", or "make config".  
  6. In the configuration menus, under Character Device drivers, enable X10 support.
  7. From the X10 configuration submenu, turn on the drivers that you would like to use.
  8. Save your new configuration and exit from the config tool
  9. Run "make dep" from the top level directory of the kernel source
  10. Run "make" from the top level directory of the kernel source
  11. Run "make modules" from the top level directory of the kernel source
  12. Run "make modules_install" from the top level directory of the kernel source
  13. If you have changed any parameters of the kernel, reinstall your kernel and reboot.  If no changes have been made other than to create the X10 modules, you do not have to install the new kernel.
  14. Create the devices and load the appropriate module to start using your transceiver.

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):

The script makedev.sh is provided to create all of the supported devices in /dev/x10/.  (This script is only necessary is you are NOT running with the dynamic device system.  When modules are compiled, they will automatically use whatever settings the kernel source has.) The script can accept 3 arguments to override the default major character devices and directory for the device files.  The syntax is:

./makedev data_major control_major devicedirectory

The defaults that the script uses are equivalent to "./makedev 120 121 /dev/x10"

The devices can be created by hand as follows:

# mkdir /dev/x10
# mknod /dev/x10/a1 c 120 0
# mknod /dev/x10/a2 c 120 1
...
# mknod /dev/x10/b1 c 120 16
# mknod /dev/x10/b2 c 120 17
...
# mknod /dev/x10/a c 121 0
# mknod /dev/x10/b c 121 1
...
# mknod /dev/x10/araw c 121 16
# mknod /dev/x10/braw c 121 17
...
# mknod /dev/x10/statusraw c 121 32
# mknod /dev/x10/status c 121 33

Once you have all of the devices created, you an easily create links to the devices to create more readable devices. For example, you could link the name "livingroomoutlet" to /dev/x10/a1 with the following command:

# ln -s /dev/x10/a1 /dev/x10/livingroomoutlet

You could place the links anywhere that you like but this would enable a more readable form of your scripts and potentially allow you to move your unit numbers around without having to change your scripts.

Now continue with step 3 to load the modules.

Installation (from RPM)

Understandably having to recompile the driver source is somewhat redundant and prevents the drivers from being used by someone that does not have the kernel sources or a compiler.  To alleviate this situation, drivers have been compiled under various kernel versions to allow installation without compilation.  The number of kernels supported is somewhat limited.  If your kernel is not listed in the table below, you must resort to using the driver sources. 

To download the file for your system, select the hyperlink in the table below for the transceiver and system that you are using.  For example, to download the driver for RedHat kernel 2.4.18-24.7.x for an Intel/AMD system using a CM11A transceiver, select the word "Yes" in the column under Intel 11A for the row for RedHat kernel2.4.18-24.7.x.  If the kernel you are using is listed as being tested but is not available in the table below, send a message to wsh@sprintmail.com asking for the rpm and it will be created whenever I have time.

Kernel

Intel

Sun

Comments

11A 17A PL PLU 11A 17A PL PLU
Linux 2.4.20 Yes Yes Yes Yes          
RedHat kernel 2.4.20-8 Yes Yes Yes Yes          
RedHat kernel 2.4.20-13.7 Yes Yes Yes Yes          
                   
                   
                   

Once you have downloaded the appropriate RPM, you need to install the RPM.  Use the following command:

# rpm -i --force <file from table above>

This will create your devices as well install the utilities, driver modules, and a set of startup scripts in /usr/local/etc.  This html file will also be installed in /usr/src/doc/wish-<version>

Now continue with step 3 to load the modules.

3) Load the modules

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 (and installed to /usr/local/etc) 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 <- start the userspace program to capture X10 network traffic to /var/log/x10log

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.o
#x10attach -pl /dev/ttyS0 &
#x10logd

CM11A serial:
#insmod x10_cm11a.o
#x10attach -pl /dev/ttyS0 &
#x10logd

FireCracker radio transmitter:
#insmod x10_cm17a.o port=0x3f8
#x10logd

PowerLinc USB
#rmmod hid
#insmod x10_plusb.o
#x10logd

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.  "make installall" 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 /usr/local/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 devices have been created and the modules have 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 following commands can be sent to both the individual units (/dev/x10/a1) or the housecodes (/dev/x10/a).  (If a command is more than 3 characters long, the first 3 characters can be sent to have the same effect if the command is unique in the first three characters.):

CMD Explanation
1, on, ON Turns the device or group of devices on
0, off, OFF Turns the device or group of devices off
-, dim, DIM Sends the DIM command to the device or group of devices.  Note that not all devices support the DIM command in which case the device will not change.
+, bright, BRIGHT Sends the BRIGHT command to the device or group of devices.  Note that not all devices support the BRIGHT command in which case the device will not change.
status Sends a status request to the last individual device specified

The following commands can be sent to the housecodes but not to the individual units:

CMD Explanation
aon Turns all lights on for the specified housecode
aoff Turns all lights off for the specified housecode
uoff Turns all units off for the specified housecode
pdimhigh For devices that support Preset Dim, this will send the housecode as the dim level for the light.  See below for how to utilize the preset dim levels.
pdimlow For devices that support PresetDim, this will send the housecode as the dim level for the light.  See below for how to utilize the preset dim levels.

The following command can be sent only to full unitcode addresses (e.g. a1, a2, a3)

CMD Explanation
nothing, null Sends unit code on line without a function code (used to group unit codes.  See example below.)
ps# Sends a preset dim sequence to the target unit.

The following standard X10 commands are not supported at this time:

Example usage:

$ echo 1 > /dev/x10/a10 Turns on device A10
$ echo 0 > /dev/x10/a11 Turns off device A11
$ echo bri > /dev/x10/a11 Sends bright command to device A11
$ echo - > /dev/x10/a11 Sends dim command to device A11
$ echo null > /dev/x10/e1
$ echo on > /dev/x10/e1
Turns on device E1
$ echo on > /dev/x10/e1 Turns on device E1
$ echo aon > /dev/x10/e Sends All Lights On to housecode E
$ cat /dev/x10/e11 Reads the last known status of device E11
$ cat /dev/x10/a Reads the last known status of all units on housecode A
$ cat /dev/x10/status Reads the last known status of all 256 units in the system
$ echo null > /dev/x10/e5
$ echo > /dev/x10/e10
$ echo on > /dev/x10/e
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 null > /dev/x10/e15
$ echo pdimlow > /dev/x10/g
Sets the unit at address E15 to light level 32%. 
$ echo ps11 > /dev/x10/e15 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 null > /dev/x10/e15
# echo pdimlow > /dev/x10/g

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

Script Examples

The directory example_scripts provides some scripts that I use at home to automate a few basic tasks.  

The scripts named x10.*.sh are examples of the startup scripts that can be copied to /etc/rc.d to automate starting the X10 drivers.  These scripts automatically start the x10logd daemon and load the drivers.  The /var/log/x10log is cleared by the script at startup.  To use the script outside of the startup environment, it requires one argument to tell it what action to take.  For example, to start the PowerLinc USB driver, you would execute the command "x10.plusb.sh start".  Similary, to stop the PowerLinc module, you would run "x10.plusb.sh stop".

The other files in the example_scripts directory are used for automating tasks.  To make things somewhat flexible, I created links on my system so that I could change the devices around without having to rewrite any scripts.  Here are the commands for setting up the links/aliases:

# mkdir /automation
# ln -s /dev/x10/e1 /automation/frontflood
# ln -s /dev/x10/e2 /automation/backflood
# ln -s /dev/x10/e3 /automation/extbreakfast
# ln -s /dev/x10/e4 /automation/frontgarage
# ln -s /dev/x10/e5 /automation/backgarage
# ln -s /dev/x10/e6 /automation/frontporch
# ln -s /dev/x10/e7 /automation/backporch
# ln -s /dev/x10/e8 /automation/extbasement
# ln -s /dev/x10/e9 /automation/gym
# ln -s /dev/x10/e10 /automation/foyer
# ln -s /dev/x10/e11 /automation/familyroom
# ln -s /dev/x10/e15 /automation/curio
# ln -s /dev/x10/e16 /automation/lightsensor
# ln -s /dev/x10/g1 /automation/garagestatus
# ln -s /dev/x10/g11 /automation/statusgaragedouble
# ln -s /dev/x10/g12 /automation/statusgaragesingle


With these links in place, the watcher scripts can be run to look for events from the X10 network.  This script assumes that everything should be off when it starts, and then just continually cycles.  It uses the utility "nbread" (distributed with the source code and installed by default in /usr/bin/) to read the status of the Leviton Photocell.  When it gets dark, the cell sends a 1 on its unitcode and when it gets light, it sends a 0.  

#!/bin/sh
#
dir="`dirname $0`/"
device="/automation/photocell"
old_status=" "
while [ 1 = 1 ]
do
    status=`nbread $device`
    if [ "$status" != "$old_status" ]
    then
        old_status="$status"
        if [ "$status" = "000" ]
        then
            ${dir}action_alloff.sh
        else
            ${dir}action_night.sh on
        fi
    fi
    sleep 10
done

Another useful script is one that reconfigures the house to turn on the night lights and turn off the spotlights.  This script is usually run out of cron at a specific time of the night.  I made this one reversable so that I could turn off the lights that were turned on; however, I rarely use it that way and instead send "aoff" to the system when the photocell senses light in the morning.

#!/bin/sh
#
dir="`dirname $0`/"
case "$1" in
    on)
        action="on"
        ;;
    off)
        action="off"
        ;;
    *)
    echo $"Usage: $0 {on|off}"
    exit 1
esac

echo $action > /automation/frontflood
echo $action > /automation/backflood
echo $action > /automation/extbreafast
echo $action > /automation/frontgarage
echo $action > /automation/extbasement
echo $action > /automation/foyer

If you want to run this out of cron, put something like this in cron by editing with "crontab -e -u root"

# sleep time. Turn off interior lights, and all exterior lights except
# lights on main entrances at 10:30 each night
# minute hour dayofmonth month dayofweek command
30 22 * * * /usr/local/etc/x10/action_sleep.sh

These are 3 really simple ways to take advantage of the X10 network using shell scripts.  

More advanced programming

The drivers can be used just like any other device on the system.  When accessed from a programming language like C, Java, or Perl, the drivers also provide the means to send and receive extended data if the transceiver supports it.  Below is a code fragment for reading/writing extended data to the network.  

/* write extended data to the line */
fd = open("/dev/x10/e",O_RDWR)
if (fd < 0){
    fprintf(stderr,"Error opening x10 device\n");
    exit 1;
}

/* just turn all lights on for the housecode */
c="aon";
write(fd,&c,3);


/* now set the interface into extended data mode.  Turns off standard interface, and writes data directly to the line */
mode=X10IOMODE_EXTENDED;        // this is defined in x10.h
ret = ioctl(fd,X10IOCSMODE,&mode)
if (ret)
    fprintf(stderr,"Transceiver does not support extended data mode\n");
else {
    c="this is just random data to put on the line";
    write(fd,c,strlen(c));

    /* we should get our own data back unless some bits were dropped */
    len = read(fd,buf,256);
    if (len > 0)
        printf("Received Extended Data:  %s",buf);

    /* now switch back to standard mode */
    mode=X10IOMODE_STANDARD;
    ioctl(fd,X10IOCSMODE,&mode);
}

/* if we successfully exited extended data mode, we should be able to turn everything off */
c="aoff";
write(fd,c,4);
close(fd);

The bit advantage to using a programming language is that you can use blocking reads or non-blocking reads as needed to watch the status so that you only loop when data is ready.  By doing a blocking read, you eliminate the need for sleep loops while waiting for an update to the line.  For instance, a fragment of code that does something similar to the shell script for watching the light sensor is:

fd = open("/automation/lightsensor,O_RDWR);
status = 0;
if (fd < 0) {
    fprintf(stderr,"Error opening x10 device\n");
    exit 1;
}
while (1) {
    n = read(inf,line,256);
    if (n < 0) {
        printf("Error: Unable to read %s\n",argv[1]);
        return 1;
    }
    if (!strcmp(line,"000")) {        // new status from light sensor indicates it senses light
        fdtmp=open("/dev/x10/e",O_RDWR);
        if (fdtmp < 0) {
            fprintf(stderr,"Error opening /dev/x10/e\n");
            break;
        }
        write(fdtmp,"aoff",4);            // turn all lights off on housecode E
        close(fdtmp);
    }
    else {                                    // new status from light sensor indicates it senses darkness
        nightlightfd=open("/automation/foyer",O_RDWR);   
        if (nightlightfd < 0) {
            fprintf(stderr,"Error opening /automation/foyer\n");
            break;
        }     
        write(nightlightfd,"on",2);
        close(nightlightfd);
    }
}
close(fd);

Of course you would want to have better coding style than this, but it takes about as much time to write it this way as it takes to write a shell script. 

Extended data (analog):

The X10 standard provides for two additional bytes to be sent in the Extended Data mode.  This allows for additional devices and controls to be created without necessarily having to update all of the existing devices and controllers.  While the standard only provides for 2 bytes to be written, some transceivers support more (e.g. the PowerLinc).  

When writing to the power line in extended mode, your program is writing to a housecode.  Your code has to change the mode of the driver from standard mode to extended mode for the housecode using an ioctl call.  The driver will remain in this mode until the it is put back into standard mode or the housecode is closed.  The driver handles converting the bytes to X10 standard inverted data with the proper header.

To a housecode into extended mode, execute the following code fragment:

mode=X10IOMODE_EXTENDED;
ioctl(filedescriptor,X10IOCSMODE,&mode);

To put it back into standard mode:

mode=X10IOMODE_STANDARD;
ioctl(filedescriptor,X10IOCSMODE,&mode);

An example of using these modes is provided in the utils directory.  extwrite (extwrite.c) will write arbitrary bytes to the line.  extread (extread.c) will read arbitrary data from the line.  The driver accumulates any extended data for the housecode in a circular buffer so it is not necessary to create a separate read process unless you are doing some real-time 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.  As noted in the FAQ, the virtual status is an approximation and assumes that every device is a light.  Note that a future revision of the driver will provide a method to dump the log and reload it at a later date.

Status of individual X10 Units:  Every command that is received by the driver is used to update an array holding the last known status of the units in the X10 network.  Each individual status can be retrieved by reading /dev/x10/<housecode><unitcode>.  The result will be a number between 0 and 100 inclusive indicating the lighting level.  Since most X10 devices are unidirectional, there is no way to know the actual status of a light.  If two-way X10 devices re present on the network, the command "echo status > /dev/x10/<housecode><unitcode>" to request that the unit send an message to update its status.  The driver assumes that every device supports all commands; therefore, if a unit doesn't support a command (e.g. a wall receptacle doesn't support dim and bright) the protocol simulator will not be aware of the limitation and will update the status as if it were supported.  Note that by default reading an individual unit will block unless opened in O_NONBLOCK mode.  As a result, programs such as "cat" will continue to read the status and whenever the status changes, the cat program will show the new change.  

Status of all 16 units in a housecode:  In anticipation that scripts or lower level languages will be used to create a GUI for managing the X10 network, the drivers provide the ability for all 16 units of a housecode to be read in one call.  The format of the output depends on the actuall ca..  If /dev/x10/<housecode> is read, the result will be a header row followed by a data row with 16 entries that align with the header row.  Each entry will be 3 characters separated by a space.  If /dev/x10/<housecode>raw is read, the result will be 16 data entries without a header row.  As with the individual units, reading a housecode or a raw housecode will block unless the device is opened with O_NONBLOCK.  "nbread" can be used by scripts to read the status without blocking. 

Status of all 256 X10 units:  Most transceivers monitor the entire network and capture every activity for all 256 possible units.  Anticipating that a GUI could benefit from the speed of reading the status of all units at the same time, the driver will provide a snapshot of the entire status matrix.  As with reading a housecode, all 256 units can be read with or without a header.  If /dev/x10/status is read, the result will be a 16x16 matrix of 3 digit numbers with a header row indicating the unit for the column and a header column to indicate the housecode for the row.  If /dev/x10/statusraw is read, the same results will be returned without the header row and header column.  

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:

Utilities

Attached are some utilities provided with the drivers to aid in use of the drivers.

X10 Log Daemon:  (run this as root or a priveledged user that can write to syslog) 

x10logd is a utility has been provided to read the log file continuously and write its contents to a file on the system.  The utility is installed by default in /usr/sbin/x10logd and loaded by automatically if one of the scripts from example_scripts is used to start the drivers.  The default parameters for x10logd are debug=off, x10 log=/dev/x10/log, log file=/var/log/x10log, and pid file=/var/run/x10logd.pid.  Each of these change changed via flags when x10logd is started.  The format of the output for x10logd is as follows:

<month> <day of month> <hh:mm:ss> <hostname> <T/R> <X10 activity>

The arguments to x10logd are:

If -s is specified, and -o is not specified, all logging will go to syslog and show up as local5.*.  If -o is specified and -s is not specified, then all logging will go to the specified -o file.  If neither -s nor -o are specified, then logging will go to the default of /var/log/x10log.  If both -o and -s are specified, then logging will go to both the syslog and the specified -o file.

Non-Blocking Read Utility:  

nbread is a utility to allow a shell script to read the status of a device and immediately exit, the script should make a call to the utility called "nbread".  This utility is in the utils/ directory and is installed as /usr/bin/nbread by if the installation script is used.  "nbread" simply accepts the name of the file to read and will read until there is no more data.  It will return all data to the script and then exit.  If nothing else, the source code to "nbread" provides an example of how to read without blocking.

The arguments to nbread are:

X10 Watch Utility:

x10watch is a utility that will watch an individual housecode and take action when the housecode changes from on to off.  This utility effectively does exactly what a macro would do for the CM11A.  Note that the program considers anything that is not OFF to be on.  For example, preset dim 1 is the same as on since the status indicator will be non-zero for the device.  This utility should be run as a user that has access to write to the X10 devices and should not be run as root since it takes action by calling system().

The syntax to x10watch is:  x10watch <device> [-0 <action off>] [-1 <action on>] [-t <tag>] [-p <seconds>]

The arguments to x10watch are:

This utility should not be run as root.  To run as a less priveledged user from root scripts, use "su -c" to run the program.  For example, the following could be put into a script to start up x10watch:  su whiles -c '/usr/bin/x10watch /dev/x10/e10 -0 "echo 0 > /dev/x10/e15" -1 "echo 1 > /dev/x10/e15" -d'

The action arguments can specify anything that you could type at the command line and are passed to a shell for execution.  The following are valid action commands:

  1. x10watch /dev/x10/a1 -0 "echo aoff > /dev/x10/e" -1 "echo aon > /dev/x10/e"
  2. x10watch /dev/x10/a1 -0 /usr/local/etc/x10_alloff.sh
  3. x10watch /dev/x10/a1 -1 /usr/local/etc/x10_allon.sh

1) This one watches device A1 and if it goes from on to off, it will turn all lights off for housecode e.  IF it goes from off to on, it will turn all lights on for housecode e.
2) This one watches device A1, and will execute /usr/local/etc/x10_alloff.sh if A1 transitions from on to off.  It will take no action when A1 transitions from off to on
3) This one watches device A1, and will execute /usr/local/etc/x10_allon.sh if A1 transitions from off to on.  It will take no action when A1 transitions from on to off.

Why use x10watch instead of nbread in a shell script?  It is a bit cleaner of an implementation of the same functionality that the example scripts provide but it has the advantage that it never has to enter a sleep cycle.  It simply uses a blocking read to read the status of the x10 device.  If the status never changes, the program never takes action and takes very little system overhead.  This program was actually written because I was watching my system and noticed that whenever my scripts ran the hard drive light would blink as the nbread command opened the device file.  By opening the file once when the program starts, it does not cause any disk activity when it is actually reading the data.


Technical Details

This section is going to talk about the technical details of how the code is written and how the X10 protocol works.  Only read this section if you are interested in writing code for a transceiver or you are trying to debug what is happening.  The details will start with a generic look at the environment for the drivers and how it relates to the X10 protocol.  The details will then dig into how the 3 files for each driver fit together.

/dev/x10 entries

For each device on the X10 network, each housecode, and the entire network, character devices are created in the /dev/x10 directory.  The major and minor numbers for the devices communicate with x10_core.c which creates the generic interface.  All individual units are controlled by a single major character device.   The default is 120.  To access the housecodes without the specific units, a second major character device is used.  The default is 121.  

Access to the individual units is done through the data_major device.  The default major number for this device is 120.  The housecode is encoded into the upper nibble of the minor number and the unitcode is encoded into the lower nibble of the minor number.  While X10 references the unitcodes as 1 through 16, to make things more Unix like, the unitcodes are mapped as 0 through 15.  Following is a table with the mapping of the housecodes and unitcodes.

Housecode upper nibble   Unitcode lower nibble
A 0000 0000 (0x00)   1 0000 0000 (0x00)
B 0001 0000 (0x10)   2 0000 0001 (0x01)
C 0010 0000 (0x20)   3 0000 0010 (0x02)
D 0011 0000 (0x30)   4 0000 0011 (0x03)
E 0100 0000 (0x40)   5 0000 0100 (0x04)
F 0101 0000 (0x50)   6 0000 0101 (0x05)
G 0110 0000 (0x60)   7 0000 0110 (0x06)
H 0111 0000 (0x70)   8 0000 0111 (0x07)
I 1000 0000 (0x80)   9 0000 1000 (0x08)
J 1001 0000 (0x90)   10 0000 1001 (0x09)
K 1010 0000 (0xa0)   11 0000 1010 (0x0a)
L 1011 0000 (0xb0)   12 0000 1011 (0x0b)
M 1100 0000 (0xc0)   13 0000 1100 (0x0c)
N 1101 0000 (0xd0)   14 0000 1101 (0x0d)
O 1110 0000 (0xe0)   15 0000 1110 (0x0e)
P 1111 0000 (0xf0)   16 0000 1111 (0x0f)

As a result, the A1 is minor 0x00 or 0, A2 is minor 0x01 or 1, B1 is minor 0x10 or 16, and B4 is 0x13 or 19.

Housecodes, status, and control interface:  The control interface provides additional access beyond the individual units on the house.  The encoding of the minor code depends on the function being accessed.  The upper nibble is the action and the lower nibble is the target.  

Housecode control:  Commands can be sent to all units on a housecode or a subset of the units on the housecode.  Further, the status of the entire housecode can be read in both a raw format and a prettier format which includes headers.  The upper nibble (action) identifies if headers are to be displayed and the lower nibble (target) specifies the actual housecode as follows:

action upper nibble   target lower nibble
header 0000 0000 (0x00)   A 0000 0000 (0x00)
no header 0001 0000 (0x10)   B 0000 0001 (0x01)
      C 0000 0010 (0x02)
      D 0000 0011 (0x03)
      E 0000 0100 (0x04)
      F 0000 0101 (0x05)
      G 0000 0110 (0x06)
      H 0000 0111 (0x07)
      I 0000 1000 (0x08)
      J 0000 1001 (0x09)
      K 0000 1010 (0x0a)
      L 0000 1011 (0x0b)
      M 0000 1100 (0x0c)
      N 0000 1101 (0x0d)
      O 0000 1110 (0x0e)
      P 0000 1111 (0x0f)

If /dev/x10/e is mapped to minor code 0x04 (4), the following command will result in the shown output:

$cat /dev/x10/e
    1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16
E: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000

Similarly if /dev/x10/eraw is mapped to minor code 0x14 (20), the following command will result in the shown output:

$cat /dev/x10/eraw
000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000

Status control:  The upper nibble of the minor number is set to 0x02 to get the status of all housecodes and units in the X10 network.  The lower nibble specifies what status is returned and the format.  The rightmost bit specifies whether headers are to be added.  The second bit from the right specifies if it is to be the actual value (percent from 0 to 100) or just the change status.  This is summarized as follows:

action upper nibble   target lower nibble
status 0010 0000 (0x20)   value wo/hdr 0000 0000 (0x00)
      value w/hdr 0000 0001 (0x01)
      change wo/hdr 0000 0010 (0x02)
      chane w/hdr 0000 0011 (0x03)

If the device /dev/x10/status is mapped to minor number 0x20 (32), the following command results in the associated output:

$ cat /dev/x10/status
    1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16
A: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
B: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
C: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
D: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
E: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
F: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
G: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
H: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
I: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
J: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
K: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
L: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
M: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
N: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
O: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
P: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000

When the /dev/x10/change device is read, the table shows a 1 for every device that has been changed since the last status of that device was read.  When the status of a device is read through a single unit, a housecode, or the status device, the change status is reset to 0.  This allows a user program to know what devices have changed since the last time the data was read.

Driver File Layout

Every driver is made up of 3 source code files.  The individual files provide a level of abstraction for a particular function as defined in the following description.

Initialization:

Figure 1 shows the high level logic flow for starting and stopping the driver.

Figure 1:  Startup/Shutdown logic flow

The driver is started up by a standard module startup call to module_init() in x10_core.c. Through a macro, module_init() is translated to x10_init().  This in turn registers all of the /dev devices, creates the /dev/x10/* devices if using dynamic /dev (DEVFS), and then initializes the line discipline and then the protocol translator.  It is important to know the sequence of the initialization as some assumptions may need to be made about the ability of the driver to handle traffic.  

The line discipline driver is started by a call to ldisc_init() which is identical to module_init() for any other module.  The ldisc_init() function is expected to do any setup necessary for the physical port and then make a call back to x10_core.c to register itself and provide a standard x10io_comm structure (defined in x10.h) to allow x10_core.c to communicate with the hardware.  The registration with x10_core.c is accomplished by calling the external function x10_register_comm(struct x10io_comm *). Once started, the line discipline driver should buffer any data received so that no data is lost and should call x10_core.c outside of an interrupt context.  This is particularly important because x10_core.c is interruptible and will take naps occasionally to ensure proper timing.  This can create havoc if it is called from an interrupt context.

The translator is started by a call to xcvr_init() which is identical to __init() for any other module.  xcvr_init() is expected to then make a call back to x10_core.c to register itself and provide a standard x10io_dev structure (defined in x10.h) to allow x10_core.c to communicate with the translator. The registration with x10_core.c is accomplished by calling the external function x10_register_device(struct x10io_dev *).  This part of the driver is called last to ensure that the physical port has been started and is ready to receive data.  Many transceivers require an initialization sequence to start communications with the transceiver and the translator is expected to initiate that sequence.  Therefore, it must be started after the physical transceiver is ready to communicate.

Control Interface:

Figure 2 shows the logic flow for control requests to the driver.  For clarity and simplicity, only the major functions are documented in this figure.  Many other functions for managing the log and status of each unit as well as the logic for simulating the X10 protocol are not specifically identified in this diagram.  

Figure 2:  Control interface logic flow

Figure 3 shows the logic flow for data request to the driver.  Again, for clarity and simplicity, only the major functions are documented in this figure.  The data flow is almost identical to the control flow logic with minor exceptions in management of the status or log data.  Figure 3 is provided separate from figure 2 in case the logic for data deviates from the logic for control as the drivers mature.  

Figure 3:  Data interface logic flow

Finally, Figure 4 shows the flow logic for reception of data from the X10 network.  The receiver is the simplest part of the driver and its simplicity was key to discovering how to manage the PowerLinc USB transceiver.  

Figure 4:  Receiver logic flow

These flow diagrams represents the conceptual logic in x10_xcvr.c.  In reality, a great deal of interaction occurs between the xcvr_transmit() in Figures 2 and 3 and decode() in Figure 4 in order to allow handshaking between the transmitter and receiver to occur.  For all bi-directional transceivers, the xcvr_transmit() function sends data, and then waits to hear that the transceiver accepted it.  To accomplish this handshake, a semaphore is updated in the decoder for the type of packet that xcvr_transmit() is looking for.  In most cases it is an ACK or NAK.  To understand this more clearly, review the code for the function waitforack() and decode().  


Limitations

Below are the transceivers and the support limitations that I have with the drivers for each:

PowerLinc Serial:  

CM11A:  

Firecracker (CM17A):

USB PowerLinc:

TW523:

CM10:


Revision Log

Click here to go to the sourceforge download project page to download the latest version.

Other revisions:


To Do List


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


Evolution

This section is provided to give some background on how the drivers came to be and how they evolved to where they are today.  

Development of this concept required three important events. 

1)  First was a very good program called heyu which created a command line type of interface to allow the user to control a CM11A transceiver.  As good as the program was, it had some problems for me personally because it only supported the CM11A unit and I had a handfull of PowerLinc transceivers.  I could have modified heyu to support the PowerLinc, but other events sent me in a different direction.

2) Second was a file system concept created by Dr. Pete Whiting.  Dr. Whiting had a CM17A but didn't have drivers for Linux.  Being the Linux guru that he is, he created a hardware device driver that mapped the X10 units to /dev entries.  Each /dev entry represented an X10 unit on the network and could accept commands which would be transmitted through the transceiver.  

3) Third was the joystick/mouse abstraction created by Vojtech Pavlik which is included in the kernel distributions in the devices/input/* area.  I came upon this concept after asking for advice on a newsgroup for how to connect to the serial.c drivers someone suggested that I look into the way that Vajtech Pavlik had used the tty line discipline to abstract the joystick.  This concept was exactly what I was trying to do but I couldn't use this approach directly because it only provided for the device specific drivers to use minor numbers. Ref the FAQ for why I require so many minor device numbers. 

Now, combining all of those problems together you end up with the WiSH project.  It basically creates a generic abstraction layer for the X10 protocol that maps /dev character devices to the X10 units on the network and connects to the port for the transceiver through the tty line discipline protocol. 

Version 1.0 of the drivers had all of the logic for simulating the X10 protocol, the logic for communicating with the tty line discipline, and the logic for the protocol translator in a single file.  This worked well for the initial driver to prove the concept.  However, when the driver for the PowerLinc Serial was started, it became obvious that duplicating the x10 protocol and the line discipline portion of the driver was going to create problems keeping them in sync as I found bugs since I would have to make the same update to both files.  

Version 1.2 made the evolutionary step of separating the protocol translator and /dev interface into standalone modules.  This also led to separating the line discipline drivers from the protocol translator.  The result was that the driver was loaded in 3 parts.  First the user had to load x10.o to load the /dev and x10 protocol. simulator.  Then the x10_ldisc.o to load the serial line discipline.  Then the user had to load the transceiver specific module to make it all work.  

Version 1.3 was an evolutionary step in that the source was kept in separate files, but a fairly simple model of communications was implemented so that the three modules could be combined into one object file so that the user was not burdened with making sure that the modules got loaded in the correct order.  While loading 3 modules could be easily scripted, it left the potential open for a module to be left out which would cause debugging difficulties. The 3 driver approach made debugging easier since debug could be turned on for a single module, but it was cumbersome and awkward.

Version 1.4 was the first version to go outside the bounds of serial communications.  With this version, the USB drivers were implemented for the PowerLinc USB and slight changes were made to the overall interface.  The modules were further focused so that each one had clearly distinct responsibilities which resulted in a cleaner implementation and fewer bugs.  The names of all of the files were changed to gain consistency.  


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