Reverse engineering PicoScope drivers to work on Mac & Linux
PicoTech (http://www.picotech.com/) is refusing to release sources of their drivers for PicoScope. Not that I understand their policy: any competitor with a strong enough motivation will reverse-engineer them anyway, while they are driving a lot of people away from buying their otherwise excellent products. By open-sourcing or at least releasing specs of what is needed to get their device working, they would suddenly get a significant base of users who would be willing to provide code & patches for free, port their drivers to Mac OS X and other flavours of unix ... all for free. And last, but not least ... they would be able to hire most motivated contributors to open source version of their drivers, getting qualified developers who already knew lots of ugly details about their products & code. (They have been looking for developers ever since I follow their website.)
Anyway ...
We own one PicoScope from series 4000 which doesn't provide any Linux drivers at all, and one from series 6000 which does provide drivers for Linux, but for Ubuntu 11.04 32-bit only. I discovered multiple bugs while writing code to do data acquisition for my project. Half of the time the support at PicoTech was very helpful, but the other half of the time when drivers didn't work as they were supposed to (like: only being able to record 32k of traces when they claim to support 1M; having to fetch the whole 2GB of data in one step since fetching it stepwise will always start fetching at first sample, ...) I was left in dark. Moreover, I was disappointed for not having Mac drivers. I only have a mac computer, I do use VMWare to run windows though, and that kind-of-works.
Thanks to Aljaž (@g5pw) for pointing it out to me that libusb could be used to run PicoScope on Mac as well. PicoTech ships a modified version of libusb. I figured out that I could recompile libusb_pico in debug mode, run a simple linux program and see what libusb_pico debug reports. In that way I might be able to reconstruct the whole sequence of libusb calls necessary to work with my PicoScope. In my earlier attempts I was trying to sniff USB traffic, but wasn't sure how exactly to do it, and I wasn't sure how to replicate the behaviour even if I would actually manage to understand the USB traffic. With libusb in debug mode in place ... it all sounds a lot easier, and would work both under Linux as well as under Mac OS X.
These drivers are not usable for measurements yet, but as a proof of concept, I can already compile simple programs on Mac OS X which do talk to PicoScope (or at least open and close the device). There is still a very looooong way to go. If I ever get enough functionality to do basic data recording, my next goal is to write a Qt-based GUI which would also be cross-platform.
I decided to report what exactly I'm doing. If there is any other enthusiast out there willing to help, please contact me, but the report might be helpful to others reverse-engineering USB drivers.
I'm a complete newbie
I know some basic programming techniques, but I'm in no way a hacker, I've never worked with any USB drivers, and would never even attempt writing a driver for such a complex device as a printer. The reason why I thought this particular project might be doable is because PicoScope allows calling one single command at a time, for example:
int main()
{
short int status, handle;
status = ps6000OpenUnit(&handle, NULL);
// in the meantime, check what is going on via USB traffic
status = ps6000CloseUnit(handle);
return 0;
}
Getting the sources
Linux drivers for PicoScope 6000 series can be fetched from http://www.picotech.com/linux.html (http://downloads.picotech.com/linux/32bit/ps6000_R1_0_0.tgz - when I last checked).
Apart from instructions and sample code, two files are the most interesting ones:
- ps6000linux_1.0-1_i386.deb
- libusb_pico.tar.gz
The first one includes a package for Ubuntu 11.04 with all binaries & headers already in place, while the second one contains sources of a modified version of libusb version 1.0.8 which PicoScope drivers are using to talk to the scope.
ps6000linux_1.0-1_i386.deb
The file can be extracted (also on Mac OS X) with
$ ar vx ps6000linux_1.0-1_i386.deb
$ tar xvzf data.tar.gz
and contains the following files:
/usr/
/usr/lib/
/usr/lib/libps6000.la
/usr/lib/libps6000.so.1.0.0
/usr/lib/libusb_pico-1.0.a
/usr/lib/libusb_pico-1.0.la
/usr/lib/libusb_pico-1.0.so.1.0.0
/usr/lib/pkgconfig/
/usr/lib/pkgconfig/libusb_pico-1.0.pc
/usr/include/
/usr/include/libps6000-1.3/
/usr/include/libps6000-1.3/PicoStatus.h
/usr/include/libps6000-1.3/ps6000Api.h
/usr/include/libusb_pico-1.0/
/usr/include/libusb_pico-1.0/libusb.h
/usr/share/
/usr/share/doc/
/usr/share/doc/ps6000linux/
/usr/share/doc/ps6000linux/copyright
/usr/share/doc/ps6000linux/README.Debian
/usr/share/doc/ps6000linux/changelog.Debian.gz
/usr/lib/libusb_pico-1.0.so.1
/usr/lib/libusb_pico-1.0.so
/usr/lib/libps6000.so
/usr/lib/libps6000.so.1
libusb_pico
Let me say a few words about Pico's modifications of libusb first. Pico was kind enough to leave the relevant git shasum in the ChangeLog file which was the most important piece of information to find the corresponding original and review changes done by PicoTech (but they have removed a few other important files which make it possible to compile the library, and included doxygen settings which I didn't take a look into yet, but the file is usually autogenerated from sources).
I fetched the libusb git repository with git clone git://git.libusb.org/libusb.git
(as instructed on http://www.libusb.org/wiki/libusb-1.0). I have put a mirror of that repository plus Pico's modifications and some modifications of my own on different branches of https://github.com/mojca/libusb_pico. The main reason why I did this is because I have found that a few trivial changes were needed to make it compile at all, and 1.0.8 doesn't compile on my Mac, probably because newer autotools have different requirements. On the other hand my 1.0.9 merge doesn't work as a drop-in replacement on Ubuntu.
You can compile libusb_pico from (my) repository by running
$ ./autogen.sh
$ ./configure --enable-debug-log
$ make
You might need to install a few packages like autotools/autoconf/automake first if you didn't already.
Simple programs
Probably the most simple program with ps6000 support is the following:
#include <libps6000-1.3/ps6000Api.h>
#include <libps6000-1.3/PicoStatus.h>
int main()
{
short int status, handle;
status = ps6000OpenUnit(&handle, NULL);
status = ps6000CloseUnit(handle);
return 0;
}
while a simple program that you can run on a Mac and doesn't require ps6000 drivers could look like the following:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libusb_pico-1.0/libusb.h>
int main()
{
int i;
int err=0;
struct libusb_device **list;
struct libusb_device *therightdevice = NULL;
struct libusb_device_descriptor desc[1];
struct libusb_device_handle *handle;
libusb_init(NULL);
ssize_t cnt = libusb_get_device_list(NULL, &list);
for (i = 0; i < cnt; i++) {
libusb_get_device_descriptor(list[i], desc);
// this is the idVendor and idProduct of my PicoScope 6403;
// yours might be different
if(desc->idVendor == 0x0ce9 && desc->idProduct == 0x100e) {
therightdevice = list[i];
}
}
if(therightdevice == NULL) {
fprintf(stderr, "You device wasn't found.\n");
} else {
err = libusb_open(therightdevice, &handle);
libusb_close(handle);
}
libusb_free_device_list(list, 1);
libusb_release_interface(handle, 1);
return 0;
}
Compiling and running a program
On Linux one can compile a program with
$ gcc PS6000con.c -o PS6000con -lusb_pico-1.0 -lps6000 -I./include -L./lib -Wl,-rpath,$(pwd)/lib/
where you have to replace ./include
and ./lib
with the path where you keep libusb_pico headers and so files. The last argument enables running the binary without having libusb_pico library installed.
On Mac OS X (where there are not ps6000 drivers availably yet anyway) you need:
$ gcc hello_world.c -o hello_world -lusb_pico-1.0 -I./include -L./lib
where you have to replace ./include
and ./lib
with the path where you keep libusb_pico headers and dylib files. On top of that I had to do
$ install_name_tool -change /usr/local/lib/libusb_pico-1.0.1.dylib $PWD/libusb_pico-1.0.1.dylib libusb_pico-1.0.1.dylib
on the dylib library if I wanted to avoid installing the library to /usr/local/lib and still making sure that the hello_world program would find it. (There is probably a flag in configure script to achieve the same.)
The folder referred to as include
has to contain
include/libps6000-1.3/PicoStatus.h
include/libps6000-1.3/ps6000Api.h
include/libusb_pico-1.0/libusb.h
The first two are optional on Mac OS X until you actually have some usable ps6000 code. The folder 'lib' could contain
lib/libps6000.la
lib/libps6000.so -> libps6000.so.1.0.0
lib/libps6000.so.1 -> libps6000.so.1.0.0
lib/libps6000.so.1.0.0
lib/libusb_pico-1.0.a
lib/libusb_pico-1.0.la
lib/libusb_pico-1.0.so -> libusb_pico-1.0.so.1.0.0
lib/libusb_pico-1.0.so.1 -> libusb_pico-1.0.so.1.0.0
lib/libusb_pico-1.0.so.1.0.0
on linux (you need to take libps6000 from the shipped package and might want to provide libusb_pico yourself, with debugging turned on). On Mac OS X you will probably start with
lib/libusb_pico-1.0.dylib
possibly pointing to libusb_pico-1.0.1.dylib.