diff service/usbReset/usbReset.py @ 846:5c6133c227d0

usb reset mess Ignore-this: 66107d6e9c5740a91d11a6ece7efc433 darcs-hash:20120827055524-312f9-ffe0e119c519786ace0b3ff8765e9c2c7a7bdb81
author drewp <drewp@bigasterisk.com>
date Sun, 26 Aug 2012 22:55:24 -0700
parents
children 022921fdafd0
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/service/usbReset/usbReset.py	Sun Aug 26 22:55:24 2012 -0700
@@ -0,0 +1,271 @@
+#!bin/python
+"""
+resets usb ports and restarts other services. Must run as root for the usb part.
+
+Other systems that might be able to do conditional tests and commands like this:
+  https://github.com/azkaban/azkaban
+  nagios
+  
+
+
+run this as root to reset a whole usb device. Ported from
+http://marc.info/?l=linux-usb&m=121459435621262&w=2
+
+how to learn what device to reset? lsusb or lsusb -t
+
+how to learn the ioctl number? cpp on this file:
+
+  #include "/usr/include/linux/usbdevice_fs.h"
+  #include "/usr/include/asm-generic/ioctl.h"
+  USBDEVFS_RESET
+
+last line comes out like this:
+(((0U) << (((0 +8)+8)+14)) | ((('U')) << (0 +8)) | (((20)) << 0) | ((0) << ((0 +8)+8)))
+
+this is a py expression:
+(((0) << (((0 +8)+8)+14)) | ((ord('U')) << (0 +8)) | (((20)) << 0) | ((0) << ((0 +8)+8)))
+
+
+"""
+
+from __future__ import division
+
+import cyclone.web, json, traceback, os, sys, time, logging
+import os, fcntl, commands, socket, logging, time, xmlrpclib, subprocess
+
+from twisted.internet import reactor, task
+from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.web.client import getPage
+sys.path.append("/my/proj/house/frontdoor")
+from loggingserial import LoggingSerial        
+sys.path.append("/my/proj/homeauto/lib")
+from cycloneerr import PrettyErrorHandler
+from logsetup import log
+
+USBDEVFS_RESET = 21780
+
+class Id(object):
+    ftdi = "0403:6001"
+    frontDoorHub0 = "8087:0024" # bus2 dev 2
+    frontDoorHub1 = "0451:2046" # bus2 dev 3
+    frontDoorHub2 = "1a40:0101" # bus2 dev 7
+    frontDoorHub3 = "0409:0058" # bus2 dev 62
+    frontDoorCam = "0ac8:307b"
+
+    bedroomHub0 = "8087:0020"
+    bedroomHub1 = "05e3:0608"
+    bedroomHub2 = "058f:6254"
+    bedroomHub3 = "0409:005a"
+    bedroomCam = "046d:08aa"
+    bedroomSba = "04d8:000a"
+    bedroomArduino = "0403:6001"
+
+    garageHub0 = "1d6b:0002" # bus2 dev1
+    garageHub1 = "05e3:0604" # bus2 dev4
+    garageArduino = "2341:0001"
+    garagePowerSerial = "0557:2008"
+
+    blueHub = "1a40:0101"
+
+hostname = socket.gethostname()
+
+@inlineCallbacks
+def getOk(url, timeout=1):
+    """can we get a successful status from this url in a short time?"""
+    log.debug("testing %s" % url)
+    try:
+        resp = yield getPage(url, timeout=timeout)
+    except Exception, e:
+        log.warn("getPage %s", e)
+        returnValue(False)
+
+    returnValue(True)
+
+def hubDevice(usbId="1a40:0101"):
+    """
+    what's the /dev path to the device with this usb id
+    """
+    for line in commands.getoutput("lsusb").splitlines():
+        if 'ID '+usbId in line:
+            words = line.split()
+            return "/dev/bus/usb/%s/%s" % (words[1], words[3].rstrip(':'))
+    raise ValueError("no usb device found with id %r" % usbId)
+
+def haveDevice(usbId):
+    try:
+        log.debug("checking for %s", usbId)
+        hubDevice(usbId)
+        return True
+    except ValueError:
+        return False
+
+def resetDevice(dev):
+    """
+    send USBDEVFS_RESET to the given /dev address
+    """
+    log.debug("resetting %s" % dev)
+    f=os.open(dev, os.O_WRONLY)
+    ret = fcntl.ioctl(f, USBDEVFS_RESET, 0)
+    if ret != 0:
+        raise ValueError("ioctl failed with %s" % ret)
+    time.sleep(3)
+
+def supervisorRestart(cmds, supervisor="http://localhost:9001"):
+    serv = xmlrpclib.ServerProxy(supervisor)
+    for c in cmds:
+        log.info("restarting %s", c)
+        try:
+            serv.supervisor.stopProcessGroup(c)
+        except xmlrpclib.ResponseError, e:
+            log.warn("supervisor.stopProcessGroup error %r, ignoring", e)
+        serv.supervisor.startProcess(c)
+
+def resetModules(modules):
+    log.info("reloading modules: %s", modules)
+    for m in modules:
+        subprocess.call(['modprobe', '-r', m])
+    for m in modules:
+        subprocess.check_call(['modprobe', m])
+
+
+class Background(object):
+    def __init__(self, config, period):
+        self.config = config
+        self.period = period
+        self.lastPollTime = 0
+
+    def assertIsCurrent(self):
+        """raise an error if the poll data is not fresh"""
+        dt = time.time() - self.lastPollTime
+        if dt > self.period * 2:
+            raise ValueError("last poll time was too old: %.1f sec ago" % dt)
+    
+    @inlineCallbacks
+    def step(self):
+        now = time.time()
+        try:
+            if hostname == 'bang':
+                if (not haveDevice(Id.bedroomCam) or
+                    not haveDevice(Id.bedroomArduino)):
+                    if haveDevice(Id.bedroomHub3):
+                        resetDevice(hubDevice(Id.bedroomHub3))
+                    else:
+                        if haveDevice(Id.bedroomHub2):
+                            resetDevice(hubDevice(Id.bedroomHub2))
+                        else:
+                            if haveDevice(Id.bedroomHub1):
+                                resetDevice(hubDevice(Id.bedroomHub1))
+                            else:
+                                if haveDevice(Id.bedroomHub0):
+                                    resetDevice(hubDevice(Id.bedroomHub0))
+                                else:
+                                    raise ValueError(
+                                        "don't even have the first hub")
+                    resetModules(['gspca_zc3xx'])
+                    supervisorRestart(['webcam_9053'])
+                else:
+                    log.debug("usb devices look ok")
+
+            elif hostname == 'slash':
+                haveFrontHub0 = haveDevice(Id.frontDoorHub0)
+                haveFrontHub1 = haveDevice(Id.frontDoorHub1)
+                haveFrontHub2 = haveDevice(Id.frontDoorHub2)
+                haveFrontHub3 = haveDevice(Id.frontDoorHub3)
+                haveGarageHub0 = haveDevice(Id.garageHub0)
+                haveGarageHub1 = haveDevice(Id.garageHub1)
+                haveFrontDoorCam = haveDevice(Id.frontDoorCam)
+                haveV4lDevice = os.path.exists(
+                    "/dev/v4l/by-id/usb-Vimicro_Corp._PC_Camera-video-index0")
+                haveFrontArduinoServe = (yield getOk('http://slash:9080/'))
+                haveFrontWebcamImage = (yield getOk(
+                    "http://slash:9023/frontDoor", timeout=10))
+                
+                log.info(str(vars()))
+
+                if not haveDevice(Id.ftdi):
+                    if haveFrontHub3:
+                        resetDevice(Id.frontDoorHub3)
+                    else:
+                        if haveFrontHub2:
+                            resetDevice(Id.frontDoorHub2)
+                        else:
+                            if haveFrontHub1:
+                                resetDevice(Id.frontDoorHub1)
+                            else:
+                                if haveFrontHub0:
+                                    resetDevice(Id.frontDoorHub0)
+                                else:
+                                    raise ValueError("don't have the first hub")
+                else:
+                    log.debug("front door chain looks ok")
+
+                if not haveDevice(Id.garagePowerSerial):
+                    if haveGarageHub1:
+                        resetDevice(Id.garageHub1)
+                    else:
+                        if haveGarageHub0:
+                            resetDevice(Id.garageHub0)
+                        else:
+                            raise ValueError("don't have the first hub")
+                else:
+                    log.debug("garage chain looks ok")
+                    
+                if not haveDevice(Id.garageArduino):
+                    if haveGarageHub1:
+                        resetDevice(hubDevice(Id.garageHub1))
+                    else:
+                        raise ValueError("don't even have the first hub")
+                    resetModules(['gspca_zc3xx'])
+                    supervisorRestart(['frontDoorArduino_9080'])
+                else:
+                    if not haveFrontArduinoServe:
+                        resetDevice(hubDevice(Id.frontDoorHub3))
+                        supervisorRestart(['frontDoorArduino_9080'])
+                        time.sleep(10)
+                    else:
+                        log.debug("frontDoorArduino looks ok")
+
+                if not haveFrontWebcamImage:
+                    supervisorRestart(['webcam_frontDoor_9023'])
+                else:
+                    log.debug("front webcam looks ok")
+
+            elif hostname == 'dash':
+                if not os.path.exists("/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A900gbcG-if00-port0"):
+                    resetDevice("/dev/bus/usb/003/001")
+
+            else:
+                raise NotImplementedError
+
+            log.debug(" -- finished")
+            self.lastPollTime = now
+
+        except Exception, e:
+            print "poll error", e
+            traceback.print_exc()
+
+class Index(PrettyErrorHandler, cyclone.web.RequestHandler):
+    def get(self):
+        self.settings.background.assertIsCurrent()
+        
+        self.set_header("Content-Type", "application/xhtml+xml")
+        self.write("usbreset is ok")#open("index.html").read())
+
+if __name__ == '__main__':
+    config = { # to be read from a file
+        'servePort' : 9100,
+        'checkPeriod' : 30,
+        }
+
+    from twisted.python import log as twlog
+    #twlog.startLogging(sys.stdout)
+
+    log.setLevel(logging.INFO)
+
+    p = Background(config, config['checkPeriod'])
+    task.LoopingCall(p.step).start(config['checkPeriod'])
+
+    reactor.listenTCP(config['servePort'], cyclone.web.Application([
+        (r"/", Index),
+        ], background=p))
+    reactor.run()