summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Stone <daniels@collabora.com>2016-02-23 14:41:11 (GMT)
committerDaniel Stone <daniels@collabora.com>2016-02-23 14:41:11 (GMT)
commitf75d3524bcf9be86d6053332ed01b6c797672840 (patch)
tree2d117fdcfa9467ee25e4fa689ce0ad3465d6ddcb
parent8e9e4408ec2f362493289b3eddf9f90559a8bdc3 (diff)
parentafd466e534820814ce12b585015027225d26863c (diff)
downloadcollabora-ci-tools-master.tar.gz
collabora-ci-tools-master.tar.xz
Add 'google-servo/' from commit 'afd466e534820814ce12b585015027225d26863c'HEADmaster
git-subtree-dir: google-servo git-subtree-mainline: 8e9e4408ec2f362493289b3eddf9f90559a8bdc3 git-subtree-split: afd466e534820814ce12b585015027225d26863c
-rw-r--r--google-servo/README83
-rw-r--r--google-servo/conf/google-servo.conf13
-rw-r--r--google-servo/kernel/Makefile11
-rw-r--r--google-servo/kernel/google-servo.c126
-rw-r--r--google-servo/servod/run-servod192
-rw-r--r--google-servo/systemd/google-servo@.service10
-rw-r--r--google-servo/udev/99-servo.rules4
7 files changed, 439 insertions, 0 deletions
diff --git a/google-servo/README b/google-servo/README
new file mode 100644
index 0000000..903b1d1
--- /dev/null
+++ b/google-servo/README
@@ -0,0 +1,83 @@
+How do I shot Servo
+-------------------
+
+This set of tools lets you use a Servo control board for Chromebooks in some
+kind of vaguely automated fashion. After installing these and configuring for
+your local boards, you should have access to the CPU UART as
+/dev/google-servo/$devicename/cpu-uart, and the EC as
+/dev/google-servo/$devicename/ec-uart, as well as having servod itself
+listening for more complex commands on a predictable port.
+
+
+hdctools
+--------
+
+In order to actually communicate with the servo, you'll need to have hdctools
+installed somewhere we can see it. This is available at:
+https://chromium.googlesource.com/chromiumos/third_party/hdctools
+
+You'll need libftdi-dev, tidy, and python-setuptools.
+
+make
+sudo make install
+python setup.py build
+sudo python setup.py install
+sudo python setup.py develop
+
+
+Kernel module
+-------------
+
+Build and install this kernel module. Those of you who pay attention to the
+source may notice that it, in fact, does nothing. This is just here so the
+kernel will actually bind to it, which will trigger udev rules, which will in
+turn trigger systemd to start servod. There might be a better way to do this.
+
+Anyway, just build and install it.
+
+
+udev rules
+----------
+
+This udev rule is blindingly simple. Install it to /etc/udev/rules.d/.
+
+
+systemd
+-------
+
+As above, but /etc/systemd/system/.
+
+
+servod configuration
+--------------------
+
+The configuration, in /etc/google-servo.conf takes the form:
+localalias,serial,port,boardtype
+
+The localalias is what gets aliased into /dev/google-servo/$devicename/*, e.g.
+peach-pi-on-my-desk, peach-pi-over-there, big-one, blaze-two. You get the
+picture. The serial number comes from /sys/devices/*/serial; I recommend
+discovering this with:
+udevadm info --attribute-walk --name /dev/usb/google-servoN
+The immediate parent of that device shown, should have an ATTR{serial} entry
+in the form of "123456-12345".
+
+Port is the port number to bind to for dut-control to use later. boardtype is
+an internal value used by servo to work out how to communicate with the target
+device; see hdctools/servo/data/ for something resembling a list of possible
+values.
+
+
+servod wrapper
+--------------
+
+This fairly trivial wrapper runs out of systemd and creates the device links
+for /dev/google-servo/$devicename/*. You'll need python-numpy, python-pexpect,
+python-pyudev, python-serial, and python-usb to run it.
+
+
+Putting it all together
+-----------------------
+
+Once you've done all this, servod should turn up whenever you plug your board
+in, and disappear once you haven't. Brilliant.
diff --git a/google-servo/conf/google-servo.conf b/google-servo/conf/google-servo.conf
new file mode 100644
index 0000000..0c173d1
--- /dev/null
+++ b/google-servo/conf/google-servo.conf
@@ -0,0 +1,13 @@
+# This file describes servo boards connected to this host.
+#
+# Line text starting with # to end of line is ignored, as well as empy lines.
+#
+# Configuration lines consist of up to 4 comma separated fields, the last
+# two are optional:
+#
+# name serial-number port-number board
+#
+
+big, 905537-00427, 9900, nyan, 192.168.15.23
+#blaze, 905537-00427, 9901, nyan, 192.168.15.23
+#pi, 905537-00427, 9902, peach_pi, 192.168.15.23
diff --git a/google-servo/kernel/Makefile b/google-servo/kernel/Makefile
new file mode 100644
index 0000000..8863a8b
--- /dev/null
+++ b/google-servo/kernel/Makefile
@@ -0,0 +1,11 @@
+ifneq ($(KERNELRELEASE),)
+obj-m := google-servo.o
+else
+KDIR ?= /lib/modules/$(shell uname -r)/build
+
+all:
+ $(MAKE) -C $(KDIR) M=$(CURDIR) modules
+
+clean:
+ $(MAKE) -C $(KDIR) M=$(CURDIR) clean
+endif
diff --git a/google-servo/kernel/google-servo.c b/google-servo/kernel/google-servo.c
new file mode 100644
index 0000000..b8b634c
--- /dev/null
+++ b/google-servo/kernel/google-servo.c
@@ -0,0 +1,126 @@
+/*
+ * Google Servo USB driver
+ *
+ * Copyright (C) 2003 David Glance <davidgsf@sourceforge.net>
+ * 2001-2004 Juergen Stuber <starblue@users.sourceforge.net>
+ * 2015 Collabora Ltd. <daniels@collabora.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * derived from USB Lego Tower driver
+ * derived from USB Skeleton driver - 0.5
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+
+#define GOOGLE_SERVO_MINOR_BASE 160
+
+#define GOOGLE_USB_VENDOR_ID 0x18d1
+#define SERVO_V2_PRODUCT_ID 0x5002
+
+static const struct usb_device_id servo_table[] = {
+ { USB_DEVICE(GOOGLE_USB_VENDOR_ID, SERVO_V2_PRODUCT_ID) },
+ { }
+};
+MODULE_DEVICE_TABLE (usb, servo_table);
+
+static const struct file_operations servo_fops = {
+ .owner = THIS_MODULE,
+ .read = NULL,
+ .write = NULL,
+ .open = NULL,
+ .release = NULL,
+ .poll = NULL,
+ .llseek = NULL,
+};
+
+static char *servo_devnode(struct device *dev, umode_t *mode)
+{
+ return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev));
+}
+
+static struct usb_class_driver servo_class = {
+ .name = "google-servo%d",
+ .devnode = servo_devnode,
+ .fops = &servo_fops,
+ .minor_base = GOOGLE_SERVO_MINOR_BASE,
+};
+
+static DEFINE_MUTEX(open_disc_mutex);
+
+/**
+ * Register the Servo device with the USB core
+ */
+static int servo_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+ struct device *idev = &interface->dev;
+ struct usb_device *udev = interface_to_usbdev(interface);
+ int retval = 0;
+
+ if (interface->cur_altsetting->desc.bInterfaceNumber != 0) {
+ printk("servo: ignoring interface %d\n", interface->cur_altsetting->desc.bInterfaceNumber);
+ return -ENODEV;
+ }
+
+ mutex_lock(&open_disc_mutex);
+
+ usb_set_intfdata(interface, udev);
+ retval = usb_register_dev(interface, &servo_class);
+ if (retval) {
+ /* something prevented us from registering this driver */
+ dev_err(idev, "Not able to get a minor for this device.\n");
+ goto error;
+ }
+
+ mutex_unlock(&open_disc_mutex);
+
+ return retval;
+
+error:
+ usb_set_intfdata(interface, NULL);
+ mutex_unlock(&open_disc_mutex);
+ return retval;
+}
+
+
+/**
+ * Unregister a device from the USB core
+ */
+static void servo_disconnect(struct usb_interface *interface)
+{
+ mutex_lock(&open_disc_mutex);
+ usb_set_intfdata(interface, NULL);
+ usb_deregister_dev(interface, &servo_class);
+ mutex_unlock(&open_disc_mutex);
+}
+
+static struct usb_driver servo_driver = {
+ .name = "google-servo",
+ .probe = servo_probe,
+ .disconnect = servo_disconnect,
+ .id_table = servo_table,
+};
+
+module_usb_driver(servo_driver);
+
+/* Version Information */
+#define DRIVER_VERSION "v0.1"
+#define DRIVER_AUTHOR "Daniel Stone <daniels@collabora.com>"
+#define DRIVER_DESC "Google Servo USB Driver"
+
+
+MODULE_AUTHOR("Daniel Stone <daniels@collabora.com>");
+MODULE_DESCRIPTION("Google Servo USB control board");
+MODULE_LICENSE("GPL");
diff --git a/google-servo/servod/run-servod b/google-servo/servod/run-servod
new file mode 100644
index 0000000..c76d631
--- /dev/null
+++ b/google-servo/servod/run-servod
@@ -0,0 +1,192 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright © 2015 Collabora, Ltd.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+#
+# Author: Daniel Stone <daniels@collabora.com>
+#
+#
+# A wrapper around servod: fork servod itself, wait for it to start, and create
+# any device links.
+
+import logging
+import os
+import pyudev
+import signal
+import sys
+
+from servo import client, multiservo, servod
+
+# Icky global variables. But then, icky signal handling.
+dev_dir = None
+child_pid = 0
+
+def do_cleanup(status):
+ try:
+ os.unlink("%s/ec-uart" % dev_dir)
+ except:
+ pass
+
+ try:
+ os.unlink("%s/cpu-uart" % dev_dir)
+ except:
+ pass
+
+ try:
+ os.rmdir("%s" % dev_dir)
+ except:
+ pass
+
+ try:
+ os.kill(pid, signal.SIGTERM)
+ except:
+ pass
+
+ sys.exit(status)
+
+def handle_sigterm(sig, stack):
+ do_cleanup(128 + signal.SIGTERM)
+ sys.exit(0)
+
+def do_connect(port, board_name):
+ servo_client = client.ServoClient(host="127.0.0.1", port=port)
+
+ results = servo_client.set_get_all(["ec_uart_pty", "cpu_uart_pty"])
+
+ print "EC UART: %s" % results[0]
+ print "CPU UART: %s" % results[1]
+
+ os.symlink(results[0], "%s/ec-uart" % dev_dir)
+ os.symlink(results[1], "%s/cpu-uart" % dev_dir)
+
+ signal.signal(signal.SIGTERM, handle_sigterm)
+
+ # Yes, this should really be python-systemd; however, that's
+ # only available for python3, and servo only works with
+ # python2 ...
+ os.system("systemd-notify --ready --status='Board %s on port %d'" % (board_name, port))
+
+def run_servod():
+ logger = logging.getLogger()
+
+ try:
+ dev_path = sys.argv[1]
+ except:
+ print "usage: %s device-path" % sys.argv[0]
+ sys.exit(1)
+
+ ud_ctx = pyudev.Context()
+ if not ud_ctx:
+ print "couldn't create udev context"
+ sys.exit(1)
+
+ # XXX: This isn't very pleasant. What I'd hoped to do is use %P from
+ # the udev rule to get the parent device, but this didn't play
+ # well with systemd's BindsTo: the service just never started.
+ # Would be good to fix this.
+ #
+ # Anyway, the thinking is: get the device path from the usbmisc
+ # device, walk up to the usb subdevice which actually represents
+ # the interface, then walk up to its parent which represents the
+ # entire device, and thus has the serial property.
+ try:
+ ud_dev = pyudev.Device.from_device_file(ud_ctx, dev_path).parent.parent
+ except:
+ print "couldn't find udev device from %s" % dev_path
+ sys.exit(1)
+
+ if not "serial" in ud_dev.attributes:
+ print "parent-of-parent device %s has no serial attribute" % dev_path
+ sys.exit(1)
+
+ target_serial = ud_dev.attributes["serial"]
+
+ board_name = None
+ all_boards = multiservo.parse_rc(logger, "/etc/google-servo.conf")
+ for board in all_boards:
+ if not all_boards[board]["sn"] == target_serial:
+ continue
+ board_name = board
+
+ if not board_name:
+ print "Couldn't get board name for serial %s" % target_serial
+ sys.exit(91)
+
+ print "Board name: %s" % board_name
+
+ global dev_dir
+ dev_dir = "/dev/google-servo/%s" % board_name
+ os.makedirs(dev_dir)
+
+ servod_output = os.pipe()
+ pid = os.fork()
+ if pid == 0: # child
+ os.dup2(servod_output[1], 1)
+ os.dup2(servod_output[1], 2)
+ os.close(servod_output[0])
+ os.close(servod_output[1])
+
+ # A comment in servod says they should fix it to not parse
+ # sys.argv directly. Quite.
+ sys.argv = ['servod', '--rcfile', '/etc/google-servo.conf',
+ '--serialname', ud_dev.attributes["serial"]]
+ servod.main_function()
+ else: # parent
+ # Run servod and pull its stdout/stderr.
+ # FIXME: Smarter mainloop:
+ # - remove devices when killed.
+ # - always take child down with us.
+
+ global child_pid
+ child_pid = pid
+
+ servod_in = os.fdopen(servod_output[0])
+ os.close(servod_output[1])
+
+ line = servod_in.readline()
+ while line:
+ print line
+
+ # Oh dear.
+ if 'INFO - Listening on' in line:
+ try:
+ port = int(line.split(' ')[11])
+ except:
+ print "Couldn't get port - shut it all down"
+ sys.exit(99)
+
+ do_connect(port, board_name)
+ try:
+ pass
+ except:
+ print "Could not connect to servo daemon"
+ sys.exit(90)
+
+ line = servod_in.readline()
+
+ print "Child exited - closing"
+
+ (pid_again, child_status) = os.waitpid(child_pid, 0)
+ do_cleanup(os.WEXITSTATUS(child_status))
+
+if __name__ == '__main__':
+ run_servod()
diff --git a/google-servo/systemd/google-servo@.service b/google-servo/systemd/google-servo@.service
new file mode 100644
index 0000000..5e515fb
--- /dev/null
+++ b/google-servo/systemd/google-servo@.service
@@ -0,0 +1,10 @@
+[Unit]
+Description="Google Servo control board manager"
+ConditionPathExists=/etc/google-servo.conf
+BindsTo=dev-usb-googleservo%i.device
+
+[Service]
+Type=notify
+NotifyAccess=all
+ExecStart=/usr/bin/run-servod /dev/usb/google-servo%i
+Restart=on-failure
diff --git a/google-servo/udev/99-servo.rules b/google-servo/udev/99-servo.rules
new file mode 100644
index 0000000..da252dd
--- /dev/null
+++ b/google-servo/udev/99-servo.rules
@@ -0,0 +1,4 @@
+# google-servo* matches on our usbmisc child device, then we specify
+# SUBSYSTEMS/DRIVERS to walk up from the specific interface, to the overall
+# device, which will actually have a serial
+ACTION=="add",KERNEL=="google-servo*",SUBSYSTEMS=="usb",DRIVERS=="usb",TAG+="systemd",ENV{SYSTEMD_WANTS}+="google-servo@%n.service",ENV{ID_MODEL}="Google Servo control board %n"