Linux X10 universal device driver
(aka Project WiSH)
x10dev Version 1.6.11
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
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.
- 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
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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..
- 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.)
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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".
- 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.
- 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).
- 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.
- 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.
- 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.
- 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
- 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.
- 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.
- 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).
- 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.
- 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.
- 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".
- 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.
- 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.
- 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" &
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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
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.
- Prepare your kernel source tree by creating a valid .config file. On
RedHat systems, the config files for the various distributions are installed
in /boot/. Copy the file to your kernel source top level directory
under the name .config. You might want to run "make menuconfig"
to make sure that the .config is valid.
- You may need to run "make mrproper" and "make
symlinks" in the top level source directory if you got the kernel
from www.kernel.org.
- On Redhat distributions, the include/linux/modules/* files will cause
problems when you compile the drivers. So, you need to remove these
files and recreate them with the "make dep" command.
- First remove the include/linux/modules/* files with a command like
"# rm -f include/linux/modules/*" from the kernel
source top level directory
- Now recreate the versions using the command "make dep"
Once you have the kernel sources set up properly, you can compile the drivers.
- 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
- 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:
- If KERNELDIR is specified on the make commandline, it will be used.
If not specified, go to #2
- If /usr/src/linux/ exists, it will be used. If /usr/src/linux/
doesn't exist, go to #3
- The directory /usr/src/linux-`uname -r` will be used. If /usr/src/linux-`uname
-r` doesn't exist, the make will fail
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.
- Make sure that your kernel source is linked to /usr/src/linux
- From the top level directory of the WiSH source code, type "./patch_kernel_src.sh"
(without the quotes).
- Remove all of the files in /usr/src/linux/include/linux/modules/ with the
command "rm -f /usr/src/linux/include/linux/modules/*".
- Alternately: You wil need to recreate your .config file after
running these commands so backup your .config first.
- Type "make mrproper" (without the quotes) in the top level
directory of your kernel source.
- Type "make symlinks" (without the quotes) in the top level
directory of your kernel source.
- Go to /usr/src/linux, and configure your kernel with "make menuconfig",
"make xconfig", or "make config".
- In the configuration menus, under Character Device drivers, enable X10
support.
- From the X10 configuration submenu, turn on the drivers that you would
like to use.
- Save your new configuration and exit from the config tool
- Run "make dep" from the top level directory of the kernel source
- Run "make" from the top level directory of the kernel source
- Run "make modules" from the top level directory of the kernel
source
- Run "make modules_install" from the top level directory of the
kernel source
- 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.
- 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.
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.
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.
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:
- If running DEVFS
- devroot: the top level directory for the device files
(default=x10 which results in /dev/x10)
- devfs: Enable or disable use of DEVFS. Set to 0 to
disable use of DEVFS. (default=1) (This option is provide so
that the driver can be used on a kernel compiled with DEVFS when the
kernel is booted with devfs=nomount or when DEVFS is not automatically
mounted.)
- If NOT running DEVFS
- data_major: the major device number for
accessing the individual units (default=120)
- control_major: the major device number for
accessing the housecodes and status/log functions (default=101)
- Independent of DEVFS
- debug: setting to 1 will write tons of stuff to
the console to trace the API (default=0)
- timeout: the amount of time to wait for a
response from the transceiver when expecting something (default=1000 which
is 1 second)
- delay: amount of time to delay between
transmissions (measured in ms) (default=1000 which is 1 second)
- retries: the number of times to try to retransmit
when timeout expires while waiting to receive data (default=2)
- fakereceive: causes the driver to simulate receiving what it
sends. The default depends on the driver being used. Most
transceivers do not hear what they send at the time that they send it.
However, transceivers like the PowerLinc Serial hear their own
signals. The default for the driver can be overridden on the command
line. Note that the log file will have a different format for any data
that has been artificially logged due to fakereceive.
- syslogtraffic: when this flag is set to 1, all traffic on the
X10 network will be written to syslogd as kern.notice messages. These
will typically go to the console, /var/log/dmesg, and /var/log/messages.
If you have a busy network, the traffic on the X10 network could easily fill
up your network. Setting this to 0 stops the driver from logging
traffic to the syslogs. (default=0 which is off).
- Specific to the FireCracker
- port: This applies only to the FireCracker/CM17A. The
port can be specified as a hex address. The standard IBM port locations
for the PC are: com1=0x3f8, com2=0x2f8, com3=0x3e8, and
com4=0x2e8. (default=0x3f8)
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.
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.
- Copy utils/x10attach to /usr/sbin/x10attach (cp -f utils/x10attach /usr/sbin/x10attach)
- Copy utils/nbread to /usr/bin/nbread (cp -f utils/nbread /usr/bin/nbread)
- Copy utils/x10logd to /usr/sbin/x10logd (cp -f utils/x10logd /usr/sbin/x10logd)
- 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)
- 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.
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:
$ 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
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.
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.
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.
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:
- <timestamp> <dir> <housecode><unitcode> - X10
unit address packet. <timestamp> is the number of seconds since
epoch. <dir> is the direction and will be either "T"
for transmit or "R" for receive. The data will be capitalized. For example,
if a the address for A1 is transmitted on the network, the log will show
"0123456789 T A1". Similarly, if E15 is received from
the network, the log will show "0123456789 R E15".
- <timestamp> <dir> <housecode> <functioncode> -
X10 command packet. <timestamp> is the number of seconds since
epoch. <dir> is the direction and will be either "T"
for transmit or "R" for receive. The data will be capitalized. For example,
if the ON
command is sent to devices addressed on housecode A, the following will show
in the log: "0123456789 T A ON". Note that there is a
space between the housecode and the functioncode. The following table
lists the valid commands that can appear for the functioncode:
Function |
Text |
ALL_LIGHTS_ON |
Turns on all X10 devices on a single housecode that fall into the
category of "light". Devices such as wall outlets or
relays will not respond to this command. |
ALL_LIGHTS_OFF |
Turns off all X10 devices on a single housecode that fall into the
category of "light". Devices such as wall outlets or
relay controller generally do not respond to this command. |
ON |
Turns a device on. All X10 devices respond to this command. |
OFF |
Turns a device off. All X10 devices respond to this command. |
DIM |
Dim the X10 device by 1/16. Not all X10 devices respond to this
command. Fluorescent lighting, wall outlets, and relay controls
are examples of devices that do not respond to this command. |
BRIGHT |
Brighten the X10 device by 1/16. Not all X10 devices respond to
this command. Fluorescent lighting, wall outlets, and relay
controls are examples of devices that do not respond to this command. |
EXTENDED_CODE |
Currently not supported in the drivers. This is used to allow
non-standard codes to be put on the line to allow new devices that
aren't specifically supported by the standard to be managed. |
HAIL_REQUEST |
This command allows a transceiver to send a request for other
transmitters to identify themselves. The driver takes no action
when this command is received. A user space program should
intercept this command and send a HAIL_ACKNOWLEDGE if implementing the
hail protocol. |
HAIL_ACKNOWLEDGE |
This command is sent in response to a HAIL_REQUEST to implement the
hail protocol. The driver does not take Action on this
command. A user space program should intercept and implement the
hail protocol if desired. |
PRESETDIMHIGH |
Used to dim or brighten a light to a specific dim level. The
level is determined by the housecode that is sent with the preset
command. The protocol simulator will properly update the status
to reflect the dim level set for the unit. |
PRESETDIMLOW |
Used to dim or brighten a light to a specific dim level. The
level is determined by the housecode that is sent with the preset
command. The protocol simulator will properly update the status
to reflect the dim level set for the unit. |
EXTENDED_DATA |
Only the Powerlinc Serial driver supports this function at this
time. This function is used to allow non-standard data to be put
on the X10 line for communication with devices that were not
anticipated when the protocol was written. |
STATUS=ON |
This command is typically sent by a two-way X10 device in response
to a STATUS request to indicate that the device is currently on.
The driver supports sending and receiving this command. |
STATUS=OFF |
This command is typically sent by a two-way X10 device in response
to a STATUS request to indicate that the device is currently
off. The driver supports sending and receiving this commnd. |
STATUS |
This command is transmitted by a device to request that an X10
device respond with its current status. Only two-way devices
support this command. |
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>
- <month>: 3 letters representing the month of the year.
Jan, Feb, Mar, Apr, etc.
- <day of month>: 1 or 2 digits representing the day of the month. 1,
2, 3, 4, etc.
- <hh:mm:ss>: Time that the message was received from the network in
the form of hours:minutes:seconds.
- <hostname>: The hostname of the machine that captured the X10
log
- <T/R>: Flag indicating Transmit or Received data. T
means that fakereceive=1 and the data was transmitted by the driver. R
means that the driver received it from the X10 network.
- <X10 activity>: This is identical to the address and
functioncode described in the preceding text.
The arguments to x10logd are:
- -o: specify log output file name, default=/var/log/x10log if -s is not
specified
- -i: specify x10 log source device, default=/dev/x10/log
- -d: debug -p: specify pid file, default=/var/run/x10logd.pid
- -a: start at beginning of device log (default=start logging only what is
received after x10logd starts)
- -s: log output to syslog
- -t <tag>: Prepend each log entry with a tag
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:
- <device>: The device to watch. It must be a
single unit and must be specified for the program to run.
- -0 <action off>: (note it is a zero) This argument is
optional and identifies the command to execute when the device changes from
an on state to an off state. You must specify at least one action
argument.
- -1 <action on>: (note it is a one) This argument is
optional and identifies the command to execute when the device changes from
an off state to an on state. You must specify at least one action
argument.
- -p <seconds>: Number of seconds to delay after each
action. This is intended to allow for debouncing or for delaying to
keep a light on for a time period after an event occurs.
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:
- x10watch /dev/x10/a1 -0 "echo aoff > /dev/x10/e" -1
"echo aon > /dev/x10/e"
- x10watch /dev/x10/a1 -0 /usr/local/etc/x10_alloff.sh
- 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.
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.
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.
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.
- x10_core.c: Creates the /dev/x10/* character devices, the command
processing, X10 logging, and X10 protocol interpretation. The core
file is part of every driver and provides the consistent user interface
regardless of the transceiver actually used. This file contains the
module __init() function which calls the initialization functions for the
other two files. The initialization sequence when the driver is loaded
is to register the /dev/x10/* character devices, then start up the line
discipline handler for the transceiver specific port, and finally to start
up the transceiver protocol translator. x10_core.c is the broker
between the protocol translator and the line discipline such that the two
modules do not communicate directly with one another.
- x10_ldisc_*.c: This is the line discipline handler for the
driver. This file abstracts the physical communications methods for
the transceiver so that x10_core.c can communicate without knowing that the
transceiver is connected to a USB port, a serial port, or any other physical
medium. The line discipline driver communicates with the x10_core
through callback routines provided in a standard structure that every line
discipline driver must implement. When data is received, the line discipline
driver is expected to make a callback to x10_core.c and pass the data one
character at a time. Similarly, when x10_core.c has data to transmit,
it passes the data to the line discipline handler as a pointer to an
unsigned character buffer along with a length for the number of characters
to write. This approach matches the asynchronous nature of the X10
protocol and allows for the transceiver to pass partial communications
without the line discipline driver having to know how anything about the
packets or communications protocol.
- x10_xcvr_*.c: This is the transceiver protocol translator.
This portion of the driver is responsible for translating the standard
commands defined in x10.h into the correct process and commands for the
specific transceiver. Each command is an 8 bit piece of data
representing the header code, the unit code, and the function code.
x10_core.c has no knowledge of what protocol should be used to communicate
with the transceiver nor what the format of the data should look like.
The protocol translator is expected to make the proper call sequence back to
x10_core.c which in turn calls the line discipline driver to send the
data. This makes things slightly complicated when the translator
requires an acknowledge from the transceiver before proceeding (as most do)
however, it is the key to allowing a line discipline driver work for
multiple protocol translators (e.g. x10_ldisc_serial.c is used for both
x10_xcvr_pl.c and x10_xcvr_cm11a.c).
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().
Below are the transceivers and the support limitations that I have with
the drivers for each:
PowerLinc Serial:
- Does not support EXTENDEDCODE.
CM11A:
- Macros - Macro functionality is not supported. It is ignored on
decode. Macros are a nifty feature, but the intention of the WiSH
project is to have the PC listen and react to the network through
scripts. Everything that the Macros do in the CM11A can be done by
listening to the status of the transceiver and reacting to it in
scripts.
- Timers - Managing the timer of the CM11A is not supported. The
CM11A can have timers run to perform events; however, the intention of
making a driver for scripting is to let you run the timers from something
like cron.
- Dimming in the headercode - This is a special feature of the
CM11A. The device interface uses the DIM and BRIGHT function code to
explicitly accomplish the same thing since it is required by the
protocol. The dimming in the headercode is not decoded by the driver.
- Extended Transmissions - The extended transmissions of the CM11A are
not supported. This is different from the Extended function which is
part of the standard protocol. The extended transmissions are also not
decoded.
- Does not support EXTENDEDCODE
- Will not receive EXTENDEDDATA.
Firecracker (CM17A):
- Current driver only works on the Intel platform with a standard
com1,2,3,4 setup. It accesses the hardware control registers of the
8250 direction to control the Firecracker.
- The firecracker only supports Unit on, Unit off, All Units On, All
Units Off, Dim, and Bright.
- The Firecracker is a one-way interface. Therefore the status
information is never updated.
USB PowerLinc:
- Does the basics, but I don't have enough information to do anything
fancy. I know it can do extended data and codes, but I don't know the
protocol sequence to get it to go into the right mode.
- I definitely need the technical specifications from SmartHome to take this
driver any further. Currently it is a guess at what it should do.
- Does not support EXTENDEDDATA or EXTENDEDCODE.
TW523:
- Unsupported: I have written the drivers up but have been unable
to get a serial cable to work properly.
CM10:
- Unsupported: This is very similar to the CM11A. (Thanks to Dave
Houston for this information) The only difference between the
CM10 and CM11 is that the CM10 has no EEPROM/RTC. The only coding difference
is in the response to the POWER_FAIL poll. You need to send a dummy string
of 0x00 bytes in response to "set" the RTC. I think it's 0xFB
followed by 42 0x00 bytes. The exact details are in my VB example code for
the CM11A.
Revision Log
Click here to go to the
sourceforge download project page to download the latest version.
Other revisions:
- Version 1.0.x - (64k) (11/27/2002-12/4/2002) Initial release with only
PowerLinc driver
- Fixes PowerLinc driver
crashes and adds cm11a driver.
- Added
logging and semaphores to block multiple transmissions. Also fixed
reentrancy and blocking for reading the status of the ports so that the
status is returned whenever the data changes.
-
Cleaned up code and formatted per the CodingStyle documentation in the
kernel.
-
Got CM11A driver working. Basic functionality done but needs work to
prevent race conditions. Trued up the versioning and Makefile to allow
it to be compiled with the kernel. Created a Config.in and Makefile
and the necessary diff files to submit to the kernel tree for permanent
inclusion.
- Took
out blocking code on reading the units so that scripts can watch individual
units. Added example scripts and added async/poll functionality to
x10.c
-
BUG: Discovered reentrancy issues on receiver that could cause a race
condition. Also fixed up scripts to have relative reference.
Added install script.
- Added module parameter to drivers to
allow for specific transmit delay to be specified and set for a default of
2000 to allow the transceiver to get the data onto the line.
- Added x10.o module parameter to turn on non-blocking output.
(The initial release implemented blocking for reading the devices but it
was taken out in later releases because cat and tail block. For
this release, nbread has been provided for
scripting to read the devices instead of using cat.)
- Added DEVFS compatibility. Fully tested.
- Have been running x10_pl.o for 3 days without an
issue.
- Version 1.2.0 (12/13/2002-12/15/2002)
- Started working on the PowerLinc USB drivers and realized that I
had to split the communications interface out of the main X10 device
interface. x10_ldisc.c was created to facilitate this.
- Updated the drivers to add a configurable delay after each
transmission to give the transceiver time to react.
- Created Firecracker line discipline manager and Firecracker
driver. This is a good example of how to add line disciplines to
the drivers and proved that the abstraction layer worked well to allow
the new protocols without modification to x10.c
- Cleaned up the main data structures to eliminate overlap and to
eliminate x10port.
- Revamped TW523 to mimic what twseriald does for a state machine in
hopes of getting it working but neither twseriald nor x10_tw523 work
with my tw523. I may scrap the work for the TW523.
- Version 1.3.0 (1/1/2003-1/12/2003)
- Rewrote the drivers to be linked into one module for each transceiver to make it easier for the user to load the drivers.
- Fixed x10_pl_xcvr.c to use an array rather than a structure to build the data to transmit
- BUG: Fixed bug in x10_core.c which was referencing user space data without copying it to a local buffer
- BUG: Fixed bug in translation table for CM11A for housecode A
- BUG: Fixed bug in CM11A code for dealing with received data. Previously I
was trying to interpret the address/function mask the same way as is used for
transmission which is incorrect.
To Do List
- Move logging functionality to /proc
- Get fstat() to see changes in /dev/x10/* when new data is available so
that "tail -f" will work.
- Add ability to save current state/log and be able to restore it from
userspace
- Rewrite USB driver to behave like a serial driver so that the USB
interface is separated from the line discipline. This would add
complexity, but would be aligned with the kernel source such that the USB
interface would be independent of any drivers or user space programs that
utilize it. It would be another /dev/usb/hid device.
- Figure out how to send and receive extended data and extended code for the
PowerLinc USB.
- Figure out why USB driver crashes under linux-2.4.21
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
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:
- Phil
Kingery's X10 Technical Series. A great deal of information for the X10 protocol.
- Linux Device
Drivers, 2nd Edition, By Alessandro Rubini, Jonathan Corbet.
(online)
- USB Sniffer, a free,
software-only tool for monitoring USB traffic.
- PowerLinc Serial
Programming Manual