Mercurial > code > home > repos > homeauto
changeset 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 | edd2486d6e2c |
children | a93be1639c98 |
files | service/usbReset/usbReset.py |
diffstat | 1 files changed, 271 insertions(+), 0 deletions(-) [+] |
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()