--- /dev/null
+#!/usr/bin/python
+# -*- coding: utf8 -*-
+#
+
+import serial
+import time
+import sys
+import struct
+import getopt
+import platform
+from heapq import heappush, heappop
+from threading import Thread, Lock
+import os
+import os.path
+import string
+import traceback
+import thread
+import datetime
+import getpass
+import locale
+
+# Lionel
+import MySQLdb
+
+class OWLError(Exception):
+ pass
+
+VERSION = 1.0
+CM160SERVER_CONFIG_FILE = '.cm160server.cfg'
+PREVIOUS_DB_FILE = 'PREVIOUS_DB_DFILE'
+NON_COMPAT_MODE_TEXT_FILE_HEADER = "TIME DATE AMPS COST PER KWH"
+
+#*********************** UserOutput ***************************
+class UserOutput:
+ """Responsible for user output to stdout and to log files"""
+
+ def __init__(self):
+ self.__logFile = None
+ self.__debug = False
+ self.__fileLock = Lock()
+ self.__stdoutLock = Lock()
+ self.__progressChar='.'
+ self.__progressCharCount=0
+
+ def getDebug(self):
+ """Get the debug state"""
+ return self.__debug
+
+ def getLogFilename(self):
+ """Return the full name of the log file"""
+ return self.__logFile
+
+ def setLogFile(self, logFile):
+ """Set the name of the logfile to use"""
+ self.__logFile = logFile
+
+ def setDebug(self, enabled):
+ """Set debug as True to enable the output of debug messages"""
+ self.__debug=enabled
+
+ def logMessage(self, line):
+ """Add a line to the log file. The line should not contain any EOL
+ characters"""
+ self.__fileLock.acquire()
+ fd = None
+ try:
+ self.__fileLocked = True
+ #If not logfile has been defined quit
+ if self.__logFile == None:
+ return
+ #If the logfile already exists
+ if os.path.isfile(self.__logFile):
+ #Append to it
+ fd = open(self.__logFile, "a")
+ else:
+ #Create it
+ fd = open(self.__logFile, "w")
+
+ fd.write("%s\n" % (line) )
+ finally:
+ if fd != None:
+ fd.close()
+ self.__fileLock.release()
+
+ def __outputLine(self, line):
+ """Send a line of text to stdout and to the log file"""
+ self.logMessage(line)
+ self.__stdoutLock.acquire()
+ try:
+ print line
+ finally:
+ self.__stdoutLock.release()
+
+ def info(self, line):
+ """Show an information message line to the user"""
+ iLine = 'INFO: %s' % (line)
+ self.__outputLine(iLine)
+
+ def warn(self, line):
+ """Show a warning message line to the user"""
+ iLine = 'WARN: %s' % (line)
+ self.__outputLine(iLine)
+
+ def debug(self, line):
+ """Show an debug message line to the user"""
+ if self.__debug:
+ dLine = 'DEBUG: %s' % (line)
+ self.__outputLine(dLine)
+
+ def error(self, line):
+ """Show an error message line to the user"""
+ eLine = 'ERROR: %s' % (line)
+ self.__outputLine(eLine)
+
+ def debugException(self):
+ """
+ Method that runs on python 1.5.2 and python 2.2 to print a stack trace
+ of the last exception. May be used for debug purposes.
+ """
+ lines = traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
+ self.debug(string.rstrip(string.join(lines, '')))
+
+
+
+
+
+#*********************** CM160Data ***************************
+
+class CM160Data:
+ """Responsible for holding the data received from the CM160"""
+
+ COMPATIBLE_MODE_HEADER = "#1000;AMPS;cm160;0;YEAR-MONTH-DAY HOUR:MIN:SEC"
+ INCOMPATIBLE_MODE_HEADER = "#HOUR:MIN:SEC DAY/MONTH/YEAR AMPS PENCE_PER_KWH"
+ LAST_VALID_MONTH = None
+
+ @staticmethod
+ def GetTime(cm160Data):
+ try:
+ return time.strptime("%d %d %d %d %d %d" % (cm160Data.getYear(),\
+ cm160Data.getMonth(),\
+ cm160Data.getDay(),\
+ cm160Data.getHour(),\
+ cm160Data.getMin(),\
+ cm160Data.getSecond()) , "%Y %m %d %H %M %S")
+ except ValueError:
+ return None
+
+ def __init__(self, min, hour, day, month, year, cost, amps):
+ self.__sec = 0 #The CM160 does not return seconds in it's data
+ #stream.
+ self.__min = min
+ self.__hour = hour
+ self.__day = day
+ self.__month = month&0xf # The CM160 server appear to set bits in the
+ # most significant nibble. have seen 0x09 jump
+ # to 0x49. Not sure why but I'll mask them out
+ # so that we don't get invalid month errors.
+ self.__year = year
+ self.__costPerKWH = cost
+ self.__amps = amps
+
+ self.__realTime = False #Changes to True when realtime data is being read
+
+ self.__updateLastValidMonth()
+ #Gets
+ def getYear(self):
+ return self.__year
+
+ def getMonth(self):
+ return self.__month
+
+ def getDay(self):
+ return self.__day
+
+ def getHour(self):
+ return self.__hour
+
+ def getMin(self):
+ return self.__min
+
+ def getSecond(self):
+ return self.__sec
+
+ def getCostPerKWH(self):
+ return self.__costPerKWH
+
+ def getAmps(self):
+ return self.__amps
+
+ def getRealTime(self):
+ return self.__realTime
+
+ #Sets
+ def setSecs(self, sec):
+ self.__sec = sec
+
+ def setRealTime(self, realTime):
+ self.__realTime=realTime
+
+ #Further methods
+
+ def __updateLastValidMonth(self):
+ """Update the static variable that records the last valid month of any CM160Data seen"""
+ if self.__month >= 1 and self.__month <= 12:
+ CM160Data.LAST_VALID_MONTH = self.__month
+ else:
+ if CM160Data.LAST_VALID_MONTH != None:
+ #If not a valid month use the last onoe we saw that was
+ self.__month = CM160Data.LAST_VALID_MONTH
+
+ def getCompatDateStr(self):
+ """Get the date as a string in the same format the original C# based
+ OWLServer used"""
+ return "%04d-%02d-%02d %02d:%02d:%02d" % (self.__year, self.__month, self.__day, self.__hour, self.__min, self.__sec)
+
+ def getCompatStr(self):
+ """Get a string representing the CM160 data in a format that is compatible with the original OWLServer format"""
+ line = "1000;%f;cm160;0;%s" % (self.__amps, self.getCompatDateStr())
+ if self.__month >= 1 and self.__month <= 12:
+ #print 'Date ok'
+ return line
+ #The CM160 sometime returns a month greater than 12. As I don't know how to
+ #interpret the month value we put a hash in front so it should get ignored
+ return "#%s (Invalid month)" % (line)
+
+ def __str__(self):
+ """Return the object as a string"""
+ s="%s %f %f" % (self.getDateStr(), self.__amps, self.__costPerKWH)
+ if self.__month >= 1 and self.__month <= 12:
+ return s
+ #The CM160 sometime returns a month greater than 12. As I don't know how to
+ #interpret the month value we put a hash in front so it should get ignored
+ return "#%s (Invalid month)" % (s)
+
+ def getDatetime(self):
+ """Return the date+time as a python datetime object (naive - no tz)."""
+ return datetime.datetime(self.__year, self.__month,
+ self.__day, self.__hour, self.__min, self.__sec)
+
+ def getDateStr(self):
+ return "%02d:%02d:%02d %02d/%02d/%02d" % \
+ (self.__hour,self.__min,self.__sec,self.__day,self.__month,self.__year)
+
+ def parse(self, line):
+ """Parse a line of text in the __str__() format and load the attributes of this object"""
+ loaded=False
+
+ elems = line.split(" ")
+ if len(elems) == 4:
+ try:
+ self.__amps = float(elems[2])
+ self.__costPerKWH = float(elems[3])
+ t = elems[0]
+ d = elems[1]
+ dateElems = d.split("/")
+ if len(dateElems) == 3:
+ self.__day = int(dateElems[0])
+ self.__month = int(dateElems[1])
+ self.__updateLastValidMonth()
+ self.__year = int(dateElems[2])
+ timeElems = t.split(":")
+ if len(timeElems) == 3:
+ self.__hour = int(timeElems[0])
+ self.__min = int(timeElems[1])
+ self.__sec = int(timeElems[2])
+ loaded=True
+
+ except ValueError:
+ pass
+ return loaded
+
+ def sameMin(self, cm160Data):
+ """Return True if the cm160Data has the same year, day, month, hour and min
+ as this object"""
+ if cm160Data == None:
+ return False
+ if self.__year == cm160Data.getYear() and\
+ self.__month == cm160Data.getMonth() and\
+ self.__day == cm160Data.getDay() and\
+ self.__hour == cm160Data.getHour() and\
+ self.__min == cm160Data.getMin():
+ return True
+ return False
+
+ def after(self, cm160Data):
+ """Return True if the data held in this (self) object has a later date/time
+ than the data held in the cm160Data object"""
+ #If we don't have a CM160Data object
+ if cm160Data == None:
+ #We can't say it's after
+ return False
+
+ selfTime = CM160Data.GetTime(self)
+ if selfTime == None:
+ return False
+
+ cm160DataTime = CM160Data.GetTime(cm160Data)
+ if selfTime > cm160DataTime:
+ return True
+
+ return False
+
+ def between(self, startTime, stopTime):
+ """Return True if the data held in this (self) object is between
+ startTime and stopTime.
+ Also returns true if equal to either startTime and stopTime.
+ """
+ selfTime = CM160Data.GetTime(self)
+ if selfTime == None:
+ return False
+
+ if selfTime >= startTime and selfTime <= stopTime:
+ return True
+
+ return False
+
+
+
+
+
+#*********************** CM160 ***************************
+
+class CM160:
+ """This class is responsible for
+ - Connecting to the CM160 over a serial port
+ - Reading data from the CM160 unit
+ - Saving the data via a DataStore object
+
+ When running the CM160 object has three threads
+ - The main thread, responsible for reading data from the CM160 serial port
+ and pushing the data received into a queue.
+ - The queue reader thread which read packets of data from the above queue
+ and saves them via a DataStore object.
+ """
+
+ DEFAULT_WINDOWS_LOGFILE = "C:\\owl_server_log.txt"
+ DEFAULT_NON_WINDOWS_LOGFILE = "/var/log/owl_server_log.txt"
+
+ DEFAULT_SERVER_PORT = 12745
+ DEVICE_NAME = "OWL+USB (CM160)"
+
+ MESSAGE_ID_0 = 0xa9
+ MESSAGE_ID_1 = 0x59
+ MESSAGE_ID_2 = 0x51
+ MESSAGE_ID_3 = 0x1d # Received once, not sure of the meaning in the protocol
+
+ ID_MSG = "%cIDTCMV001%c" % (MESSAGE_ID_0,1)
+ WAIT_MSG = "%cIDTWAITPCR" % (MESSAGE_ID_0)
+
+ @staticmethod
+ def IsWindows():
+ """Return True if windows platform, False if not"""
+ if platform.system() == "Windows":
+ return True
+ return False
+
+ def __init__(self, uo, serverPort, dataStore, debug):
+ self.__uo = uo
+ self.__serverPort = serverPort
+ self.__dataStore = dataStore
+ self.__debug = debug
+
+ self.__ser = None
+ self.__serialPortName = None
+ self.__serRXBuffer = []
+ self.__queue = []
+
+ self.__queueReader = None
+ self.__server = None
+
+ self.init()
+
+ self.__firstRealTimeDataReceived = False
+
+ def init(self):
+ if self.__queueReader != None:
+ self.__queueReader.waitStop()
+
+ self.__queueReader = QueueReader(self.__queue, self.__dataStore, self.__uo)
+ self.__queueReader.start()
+
+# if self.__server != None:
+# self.__server.waitStop()
+
+# self.__server = Server(self.__uo, self.__serverPort, self.__queueReader, self.__dataStore)
+# self.__server.start()
+
+ def __serialPortScan(self):
+ """scan for available serial ports. return a list of tuples [num, name]"""
+ available = []
+ s=None
+ for i in range(255):
+ try:
+ if CM160.IsWindows():
+ portname ="\\.\\COM%d" % (i)
+ s = serial.Serial(portname)
+ else:
+ portname = "/dev/ttyUSB" + `i` # bmcm-p1: force portname on linux
+ s = serial.Serial(portname)
+ s.close()
+ available.append( (i, s.portstr))
+ except serial.SerialException as e:
+ if e.__str__().find('Permission denied:') != -1:
+ self.__uo.warn("Try running as root to access %s" % (portname) )
+ return available
+
+ def openSerialPort(self):
+ """Open a serial port with the required settings
+ Returns the seialPort object or None if an error occured opening the
+ serial port.
+ """
+ #Ifthe serial port is open, close it
+ if self.__ser != None:
+ self.__ser.close()
+ self.__ser = None
+
+ self.__ser = None
+ try:
+ if CM160.IsWindows():
+ self.__ser = serial.Serial(self.__serialPortName, 250000, bytesize=8, parity='N', stopbits=1, xonxoff=0, rtscts=0, dsrdtr = 0, timeout=30)
+ else:
+ #Assume we have the custom cp210x driver loaded that maps the requested baud rate of 0 to a physical port speed of 250000 Bps required
+ #for the CM160 device.
+ self.__ser = serial.Serial(self.__serialPortName, 0, bytesize=8, parity='N', stopbits=1, xonxoff=False, rtscts=False, dsrdtr = False, timeout=30)
+
+ sd = self.__ser.getSettingsDict()
+ keys = sd.keys()
+ for k in keys:
+ self.__uo.debug("%s=%s" % (k, sd[k]) )
+ self.__uo.info("Opened serial port %s." % (self.__serialPortName) )
+
+ except ValueError:
+ #If we fail to open the serial port with a ValueError then it is probably the baud rate that is the problem
+ raise OWLError("Failed to open %s at a baud rate of 250000 Bps" % (self.__serialPortName) )
+
+ return self.__ser
+
+ def appendSerRXBuffer(self):
+ """Read any bytes waiting to be received into the serial RX buffer
+ """
+ bytesWaiting = self.__ser.inWaiting()
+ if bytesWaiting > 0:
+ buf = self.__ser.read(bytesWaiting)
+ self.__serRXBuffer = self.__serRXBuffer + list(buf)
+
+ def getSerialPortPacket(self, timeoutSeconds=60):
+ """Get the next 11 byte message an OWL+USB device
+ Returns a list object holding the message (string elements) or None
+ if a timeout occured while waiting for a message.
+ """
+ messageLength=11
+ startTime=time.time()
+ while True:
+ self.appendSerRXBuffer()
+
+ #If we have received at least one packet
+ if len(self.__serRXBuffer) >= 11:
+ break
+
+ if time.time() >= startTime+timeoutSeconds:
+ return None
+
+ #Get this packet
+ packet=self.__serRXBuffer[:messageLength]
+ #Remove this message from the rxBuffer
+ self.__serRXBuffer=self.__serRXBuffer[messageLength:]
+ self.__uo.debug("RX PACKET: %s" % (str(packet)) )
+ return packet
+
+ def getString(self, byteList):
+ """Add a list of bytes to a string"""
+ strList=[]
+ for aByte in byteList:
+ strList.append(struct.pack('B' , aByte))
+ return "".join(strList)
+
+ def serSend(self, byteList):
+ """Send a list of bytes to the serial port
+ openSerialPort() must have been successfully called before alling this method
+ """
+ self.__ser.write( self.getString(byteList) )
+ for b in byteList:
+ self.__uo.debug("TX: %03d/0x%02x" % (b,b) )
+
+ def processSerialPacket(self, packet, realTimeData):
+ """Process an 11 byte packet containing sensor data"""
+ if len(packet) != 11:
+ raise OWLError("Invalid data message length. 11 bytes expected, got %d" % (len(packet)) )
+ #Convert strings to list of numbers
+ frame=[]
+ checksum=0
+ byteCount=1
+ for s in packet:
+ v=ord(s)
+ if byteCount < 11:
+ checksum=checksum+v
+ frame.append(v)
+ byteCount=byteCount+1
+
+ #It's only an 8 bit checksum
+ checksum=checksum&0xff
+
+ #MESSAGE_ID_2 is used when the power has changed within the min update period
+ if frame[0] != CM160.MESSAGE_ID_1 and frame[0] != CM160.MESSAGE_ID_2:
+ raise OWLError("Invalid frame ID byte, expected 89, got %d" % (frame[0]) )
+
+ #Check the checksum
+ if checksum != frame[10]:
+ raise OWLError("Invalid checksum, expected %d, got %d" % (frame[10], checksum) )
+
+ year = frame[1]+2000
+ month = frame[2]
+ day = frame[3]
+ hour = frame[4]
+ _min = frame[5]
+ costPerKWH = (frame[6]+(frame[7]<<8))/100.0 #Cost in pence per kWh
+ amps = (frame[8]+(frame[9]<<8))* 0.07 #Conversion factor to Amps
+ cm160Data = CM160Data(_min, hour, day, month, year, costPerKWH, amps)
+ #Set flag to indicate if this CM160Data was read in realtime or during a data download
+ cm160Data.setRealTime(realTimeData)
+ heappush(self.__queue, cm160Data) # read data from the queue by, cm160Data = heappop(self.__queue)
+
+ def readCM160(self, unitTimeout=60):
+ """Read CM160 device"""
+ self.__uo.info("Checking for %s device on %s" % (CM160.DEVICE_NAME, self.__serialPortName) )
+
+ displayedDownloadingDataMessage = False
+ displayedDownloadCompleteMessage = False
+ downloadingData = True
+ detectedUnit = False
+ timeoutTime = time.time()+unitTimeout
+ lastRXDataTime = None
+ downloadStartTime = None
+ try:
+ while True:
+ if len(self.__serRXBuffer) >= 11 or self.__ser.inWaiting() > 0:
+ packet = self.getSerialPortPacket()
+ if ord(packet[0]) == CM160.MESSAGE_ID_0:
+ packetStr = "".join(packet)
+ if packetStr == CM160.ID_MSG:
+ detectedUnit=True
+ self.serSend([0x5a])
+ timeoutTime = time.time()+unitTimeout
+ elif packetStr == CM160.WAIT_MSG:
+ self.serSend([0xa5])
+ timeoutTime = time.time()+unitTimeout
+ else:
+ self.processSerialPacket(packet, displayedDownloadCompleteMessage)
+ timeoutTime = time.time()+unitTimeout
+
+ if displayedDownloadCompleteMessage and not self.__firstRealTimeDataReceived:
+ self.__uo.info("Real time data is now being received.")
+ self.__firstRealTimeDataReceived=True
+
+ if not displayedDownloadingDataMessage and downloadingData:
+ self.__uo.info("Downloading historical data from the %s." % (CM160.DEVICE_NAME) )
+ self.__uo.info("This may take several minutes, please wait...")
+ displayedDownloadingDataMessage=True
+ downloadStartTime=time.time()
+
+ lastRXDataTime=time.time()
+ else:
+ #If no cm160 unit detected and a timeout has occured
+ if not detectedUnit and time.time() > timeoutTime:
+ self.__uo.info("No %s device detected on %s (read error)" % (CM160.DEVICE_NAME, self.__serialPortName) )
+ return
+ if displayedDownloadingDataMessage and not displayedDownloadCompleteMessage and time.time() > lastRXDataTime+1:
+ self.__uo.info("Download complete.")
+ if downloadStartTime != None:
+ downloadTime = time.time()-downloadStartTime
+ self.__uo.info("Data download took %d seconds." % (int(downloadTime)) )
+ displayedDownloadCompleteMessage=True
+ time.sleep(0.1)
+ finally:
+ self.shutdown()
+
+ def shutdown(self):
+ """Preform the actions required when exiting the application"""
+
+ #Close the serial port
+ if self.__ser != None:
+ self.__ser.close()
+ self.__uo.info("%s closed" % (self.__serialPortName) )
+
+# if self.__server != None:
+# self.__server.stop()
+
+ if self.__queueReader != None:
+ self.__queueReader.stop()
+
+ def scanSerialPorts(self):
+ self.__uo.info("Please connect the %s device" % (CM160.DEVICE_NAME) )
+ self.__uo.info("Checking for a %s device" % (CM160.DEVICE_NAME) )
+ while True:
+ try:
+ try:
+ availablePorts = self.__serialPortScan()
+ for availablePort in availablePorts:
+ self.__serialPortName = availablePort[1]
+ #If the serial port will open with the CM160 settings
+ self.openSerialPort()
+ if self.__ser != None:
+ self.readCM160()
+ except:
+ uo.error(sys.exc_value)
+ if self.__serialPortName != None:
+ self.__uo.info("No %s device detected on %s" % (CM160.DEVICE_NAME, self.__serialPortName) )
+ #If in debug mode then throw the exception so that we see the
+ #stack trace on the console
+ #if self.__debug:
+ # raise
+ time.sleep(1)
+ finally:
+ if self.__ser != None:
+ try:
+ self.__ser.close()
+ self.init()
+ uo.info("Serial port closed")
+ time.sleep(5)
+ except:
+ pass
+
+ def connectToSerialPort(self, serialPort):
+ self.__uo.info("The %s device must have been connected recently (serial port %d)" % (CM160.DEVICE_NAME, serialPort))
+ self.__uo.info("as the CM160 device will go to sleep if not detected shortly after it is connected.")
+ self.__serialPortName = None
+
+ #We need to do things differently on windows :-(
+ if CM160.IsWindows():
+ self.__serialPortName="\\.\\COM%d" % (serialPort)
+ else:
+ self.__serialPortName = "/dev/ttyUSB%d" % (serialPort)
+
+ #If the serial port will open with the CM160 settings
+ self.openSerialPort()
+ if self.__ser != None:
+ self.readCM160()
+ else:
+ raise OWLError("Serial port %d/%s, not found." % (serialPort, self.__serialPortName))
+
+
+#*********************** QueueReader ***************************
+
+class QueueReader(Thread):
+ """This object is responsible for reading the queue (held in a CM160 object
+ and writing the data to the log file in a format that is compatible with
+ the C# based OWLServer"""
+ def __init__ (self, queue, dataStore, uo):
+ Thread.__init__(self)
+ self.__queue = queue
+ self.__dataStore = dataStore
+ self.__uo = uo
+ self.__reading = False
+ self.__lastCM160Data = None
+ self._running = False
+
+ self.setDaemon(True)
+
+ def setCompatibleMode(self, enabled):
+ """Set compatible mode.
+ If true then the log file is in a format compatible
+ the original OWLServer (C# based) OWLServer (this does not incde the cost
+ per KW read from the unit.
+ If False then the log file is saved in a format that includes the cost
+ per KW read from the unit."
+ """
+ self.__compatibleMode = enabled
+
+ def stop(self):
+ """Stop reading the queue and stop the thread
+ when the thread is active"""
+ self.__uo.debug("Stopping QueueReader")
+ self.__reading=False
+
+ def waitStop(self):
+ """stop the queue reader and block until it is stopped."""
+ self.stop()
+ self.__uo.debug("Waiting for QueueReader to stop")
+ while self._running:
+ time.sleep(0.1)
+ self.__uo.debug("QueueReader is now stopped")
+
+ def run(self):
+ self.__reading=True
+ minStartTime = time.time()
+ sec=0
+ lastSec=-1
+ self._running=True
+ while self.__reading:
+ if len(self.__queue) > 0:
+ cm160Data = heappop(self.__queue)
+ self.__uo.debug("QUEUE DATA: <%s>" % (str(cm160Data)) )
+
+ #As the CM160 does not return second granularity data, we have to
+ #compensate
+ #If we have a reading in the same min as the last
+ if cm160Data.sameMin(self.__lastCM160Data):
+ #Determine how many seconds have passed in this min
+ sec = int(time.time()-minStartTime)
+ #Ensure the seconds always increment within a min period
+ #When downloading data we will see changes within a min period
+ #but will not know how many seconds passed between readings
+ #As the min update period for the CM160 readings appears to be
+ #about 5 seconds, we use this
+ if sec <= lastSec:
+ sec=lastSec+5
+ #Ensure the seconds never go to high (more than 12 updates a min
+ #This code is probably redundant as I have not seen this many updates
+ #a min from the CM160. However, belt and braces.
+ if sec > 59:
+ sec = 59
+ #Set the seconds to this value
+ cm160Data.setSecs(sec)
+ else:
+ minStartTime = time.time()
+ sec=0
+ lastSec=sec
+
+ self.__lastCM160Data = cm160Data
+ try:
+ self.store(cm160Data)
+ except ValueError:
+ self.__uo.error("Failed to store: %s" % (cm160Data) )
+ self.__uo.info("Attempting to read the next record")
+ else:
+ time.sleep(0.5)
+ self._running=False
+
+ def store(self, cm160Data):
+ """Store the CM160Data object to the data store
+ """
+ self.__dataStore.store(cm160Data)
+ self.__uo.debug(cm160Data)
+
+ def getRealTimeSensorData(self):
+ """Get the sensor data as a String in Electric OWL compatible format
+ Returns None if the sensor data read was not read in real time.
+ """
+ if self.__lastCM160Data != None and\
+ self.__lastCM160Data.getCompatStr().find("#") != 0 and\
+ self.__lastCM160Data.getRealTime():
+ return self.__lastCM160Data.getCompatStr()
+ else:
+ return None
+
+
+# ************* MySQL data store *********************************
+class MySQLDataStore(object):
+ """Responsible for the storage and retrieval of CM160 data using an MySQL
+ database to hold the data"""
+
+ DEFAULT_DBFILE = "cm160_data.db"
+
+ def __init__ (self):
+ self.__db = "owl"
+ self.__StoreConn = None
+ self.__StoreCursor = None
+ self.__displayTime = time.time()
+
+ self.__ensureStoreExists()
+
+ def __connect(self):
+ """Build a connection to the database and return the connection and cursor objects in a tuple"""
+ conn=MySQLdb.connect(user="owl",passwd="owl",db="owl")
+ cursor=conn.cursor()
+ return (conn, cursor)
+
+ def __ensureStoreExists(self):
+ """Ensure the MySQL database exists
+ This also creates a connection to the database used for storing CM160 data.
+ """
+ conn, cursor = self.__connect()
+ try:
+ cursor.execute("CREATE TABLE cm160data(ts timestamp, current float, tariff float, UNIQUE (ts))")
+ # Attempt to create table in case this is first time to run
+ # the server; but thereafter we expect an exception because the
+ # create will fail...
+ except MySQLdb.OperationalError as e:
+# print "Error %d: %s" % (e.args[0], e.args[1])
+ if not(e.args[0] == 1050):
+ raise
+ conn.commit()
+ conn.close()
+ self.info("MySQL database OK: %s" % (self.__db) )
+
+ def store(self, cm160Data):
+ """Store the CM160 data in the MySQL database.
+ There may be duplicate timestamps in the CM160data.
+ If the data was added to the database return True, if
+ not added to the database return False"""
+
+ #Create a connection if we don't already have one
+ if self.__StoreConn == None:
+ self.__StoreConn, self.__StoreCursor = self.__connect()
+
+ stored=False
+ try:
+ self.__StoreCursor.execute("""INSERT INTO cm160data VALUES (%s,%s,%s)""", (cm160Data.getDatetime(),cm160Data.getAmps(),cm160Data.getCostPerKWH(),))
+ stored=True
+
+ except MySQLdb.Error as e:
+ print("MySQL error : " + e.__str__())
+ if (e.args[0] == 1062):
+ # INSERT will fail this way if this is a duplicate
+ # record (as it commonly can be when receiving 30-day
+ # record from CM160 device!)
+ #self.info("Duplicate record discarded: %s" % (cm160Data) )
+ pass
+ else:
+ raise
+
+ #Display data every few seconds to let user know download is
+ #in progress
+ if self.__displayTime < time.time():
+ self.info("stored record: %s" % cm160Data)
+ self.__displayTime=time.time()+2
+ #We used to commit every record this was very slow when
+ #downloading 30 days of data aon slow (E.G raspberypi)
+ #platforms. Now we only comit data as it is displayed.
+ self.__StoreConn.commit()
+
+ return stored
+
+# TODO : what is it ?
+ def getCM160List(self, startTime, stopTime):
+ """Return a list of CM160 data objects containing the data stored in the database between the
+ start and stop times."""
+
+ # NB: As each socket connection is handled by a dedicated
+ # thread, and as sqlite objects can't be shared across
+ # threads, we must open a dedicated sqlite connection
+ # here - it can't be done (much) earlier and kept open
+ # e.g., in the enclosing Server context; and it's
+ # probably not a severe performance hit in this case
+ # anyway...
+ conn, cursor = self.__connect()
+
+ cursor.execute("SELECT * from cm160data WHERE \
+ (ts >= ?) AND (ts <= ?) ORDER BY ts", \
+ [startTime, stopTime])
+
+ # We have to retrieve all rows in order to reliably find out
+ # how many there are in total (which we need to be able to
+ # later give "percentage complete" messages). But if there
+ # are too many rows (i.e., if this takes too long) the EOWL
+ # client seems to get confused (closes the history window and
+ # starts wrongly showing the historical datapoints as part of
+ # the real time plot!). So we retrieve in blocks of at most
+ # 5000 records (which has just been very roughly determined
+ # by trial and error!) and send rather bogus "percentage"
+ # messages to the client as each block is retrieved, just to
+ # keep it awake and waiting...
+
+ rows = []
+ rowset = cursor.fetchmany(5000)
+ while (rowset):
+ rows += rowset
+ rowset = cursor.fetchmany()
+ conn.close()
+ # Could probably release the lock even before the
+ # fetchall() but delaying to after should be very safe...
+
+ cm160DataList=[]
+ rowCount = 0
+ for r in rows:
+ rowCount += 1 ;
+ (ts, current, tariff) = (r)
+ cm160Data = CM160Data(ts.minute, ts.hour, \
+ ts.day, ts.month, ts.year, \
+ tariff, current)
+ cm160DataList.append(cm160Data)
+ self.debug("Row: %s" % cm160Data.__str__())
+
+ return cm160DataList
+
+ def info(self, text):
+ """Display info text"""
+# if self.__uo != None:
+# self.__uo.info(text)
+
+
+
+######################## Presistent dict config ##########################
+
+def getUserHome():
+ """Get the user home path as this will be used to store config files"""
+ try:
+ return os.environ["HOME"]
+ except KeyError:
+ #If we don't have the HOME env variable set then attempt to use
+ #the default windows path
+ homeDrive=os.environ["HOMEDRIVE"]
+ homePath=os.environ["HOMEPATH"]
+ return "%s%s" % (homeDrive, homePath)
+
+def getConfigFileHeader():
+ lines=[]
+ lines.append("#cm160Server.py config file.\n" )
+ lines.append("\n")
+ return "".join(lines)
+
+def saveDict(config, cfgFile):
+ """Save the destClientConfig to a file"""
+ #Save the resultant dict
+ fd = open(cfgFile,'w')
+ fd.write(getConfigFileHeader() )
+ keys=config.keys()
+ keys.sort()
+ for k in keys:
+ t="%s=%s\n" % (k, config[k])
+ fd.write(t)
+ fd.close()
+
+def getLinesFromFile(f):
+ """Get Lines from file"""
+ fd = open(f,"r")
+ lines = fd.readlines()
+ fd.close()
+ return lines
+
+def _removeInvalidChars(line):
+ """Return a copy of line with each ASCII control character (0-31),
+ and each double quote, removed. (Vaguely private)"""
+ output = ''
+ for c in line:
+ if c >= ' ' and c != '"':
+ output=output+c
+ return output
+
+def _addEntry(line,dict):
+ """Parse line into a key and value, adding the result to dict, as in
+ getDict."""
+ #check for a parameter
+ fields=line.split('=')
+ #if at least 2 fields exist
+ if len(fields) > 1:
+ #add the key,value pair to the dictionary
+ key=_removeInvalidChars(fields[0])
+ value=_removeInvalidChars(fields[1])
+ dict[key]=value
+
+def getDict(filename):
+ """Load dict from file
+
+ Lines containing a hash sign as the first non-whitespace character are
+ ignored. Leading and trailing whitespace is ignored.
+
+ Lines not containing an equals sign are also silently ignored, for the
+ moment.
+
+ Lines not ignored are assumed to be in the form key=value, where key
+ does not contain an equals sign; value is assigned to dict[key].
+ Control characters and double quotes in both key and value are silently
+ discarded. value is also truncated just before the first whitespace or
+ equals sign it contains.
+ """
+ d = {}
+ lines = getLinesFromFile(filename)
+ for line in lines:
+ #strip leading and trailing whitespaces
+ line = line.strip()
+ #if a comment line then ignore
+ if line.find('#') == 0:
+ continue
+ #add an entry to the dict
+ _addEntry(line, d)
+ return d
+
+def getConfigFile():
+ """Get the absolute path of the dest client config file"""
+ return os.path.join(getUserHome(), CM160SERVER_CONFIG_FILE)
+
+######################## Command line interface ##########################
+
+
+
+def usage(uo):
+ uo.info("-p : Followed by the server port (default=%d)." % (CM160.DEFAULT_SERVER_PORT) )
+ uo.info("-s : Followed by the serial port to check for the")
+ uo.info(" %s device." % (CM160.DEVICE_NAME) )
+ uo.info(" If not supplied then a search for a connected")
+ uo.info(" %s device is performed." % (CM160.DEVICE_NAME))
+ uo.info("-r: Read data from the database and display on stdout. If -i/-l")
+ uo.info(" arguments are not supplied then the entire database is displayed.")
+ uo.info("-i: Followed by the inital date/time. Only used if the -r argument")
+ uo.info(" is provided.")
+ uo.info(" Format of date time may be")
+ uo.info(" dd/mm/yyyy or")
+ uo.info(" hh:mi:dd/mm/yyyy")
+ uo.info("-l: Followed by the last date/time. Only used if the -r argument")
+ uo.info(" is provided (same format as used for the -i argument).")
+ uo.info("-t: Forward the data from the sqlite database to a text file ")
+ uo.info(" named as per the sqlite DB, but with .txt extension")
+ uo.info(" added. This is provided to allow user scripts to parse the")
+ uo.info(" text file.")
+ uo.info("-o: Display data (when -r or -t arguments are used) in old compatible")
+ uo.info(" mode format. This was the original output format for CM160")
+ uo.info(" data and is retained only because some users have developed")
+ uo.info(" scripts that parse data in this format.")
+ uo.info("-q : Quiet mode. No text is sent to stdout in this mode.")
+ uo.info("-d: Debug on. Send extra debug text to stdout if not in quiet mode.")
+ uo.info("-h : Display this help text.")
+
+def getInput(prompt="", noEcho=False):
+ """Get input from user"""
+ p = "INPUT: %s: " % (prompt)
+ if noEcho:
+ return getpass.getpass(p, sys.stdout)
+ return raw_input(p)
+
+def getDateTime(text):
+ """Return a datetime object from the given text
+ text may contain a datetime as either
+ dd/mm/yyyy or
+ hh:mi:dd/mm/yyyy
+ """
+
+ dt=None
+
+ #if just the date has been entered
+ if text.find('/') != -1 and text.find(':') == -1:
+ dt=datetime.datetime.strptime(text,"%d/%m/%Y")
+
+ elif text.find('/') != -1:
+ dt=datetime.datetime.strptime(text,"%H:%M:%d/%m/%Y")
+
+ else:
+ raise OWLError("%s is not a valid datetime" % (text) )
+
+ return dt
+
+# MAIN
+
+if __name__=='__main__':
+ uo = UserOutput()
+
+ try:
+ #Set to the correct locale.
+ locale.setlocale(locale.LC_ALL, '')
+ except:
+ #Not all platforms allow locale setting.
+ uo.warn("Failed to set locale.")
+
+ #Load the saved config if present
+ cfgDict=None
+ cfgFilename = getConfigFile()
+ if os.path.isfile(cfgFilename):
+ cfgDict = getDict(cfgFilename)
+
+ cm160 = None
+ debug = False
+ try:
+ #Parse command line
+ opts, args = getopt.getopt(sys.argv[1:], 'toi:l:rf:p:qds:h', ["help"])
+
+ quietMode = False
+ serverPort = CM160.DEFAULT_SERVER_PORT
+ serialPort = None
+ readDatabase=False
+ readStartTime=datetime.datetime.min
+ readStopTime=datetime.datetime.max
+ compatMode=False
+ forwardToTextFile=False
+
+ for o, a in opts:
+ if o == '-f':
+ dbFile=a
+ elif o == '-p':
+ serverPort=int(a)
+ elif o == '-q':
+ quietMode=True
+ elif o == '-s':
+ serialPort=int(a)
+
+ #Validate serial port
+ lowSerialPort=0
+ if CM160.IsWindows():
+ if serialPort == 0:
+ raise OWLError("Serial port numbering starts at 1, not 0 on Windows platforms.")
+ lowSerialPort=1
+
+ if serialPort < lowSerialPort or serialPort > 255:
+ raise OWLError("Serial port out of range (%d - 255)." % (lowSerialPort) )
+
+ elif o == "-d":
+ debug = True
+ elif o == "-r":
+ readDatabase = True
+ elif o == "-i":
+ readStartTime=getDateTime(a)
+ elif o == "-l":
+ readStopTime=getDateTime(a)
+ elif o == "-o":
+ compatMode=True
+ elif o == "-t":
+ forwardToTextFile=True
+ elif o == "-h":
+ usage(uo)
+ sys.exit(0)
+
+ #Don't really need to log stdout, uncomment if you really need to
+ #uo.setLogFile("c:\\cm160.uo.log")
+
+ if not compatMode:
+ uo.info("CM160Server v %2.2f" % (VERSION) )
+
+ uo.setDebug(debug)
+
+# Démarrage du datastore :
+
+ DataStore = MySQLDataStore()
+
+# Démarrage du collect data
+ cm160 = CM160(uo, serverPort, DataStore, debug)
+
+#Â Connexion au serial port :
+ if serialPort == None:
+ cm160.scanSerialPorts()
+
+ else:
+ cm160.connectToSerialPort(serialPort)
+
+# Puis ne fait plus qu'attendre une erreur, tenter de l'interpréter, et sortir "proprement"
+ except getopt.GetoptError:
+ if debug:
+ uo.debugException()
+ else:
+ uo.error(sys.exc_value)
+ usage(uo)
+ sys.exit(-1)
+
+ except SystemExit:
+ pass
+
+ except:
+ if debug:
+ raise
+ #uo.debugException()
+ else:
+ uo.error(sys.exc_value)
+ sys.exit(-1)
+
+
+
+
--- /dev/null
+/*
+ * Silicon Laboratories CP210x USB to RS232 serial adaptor driver
+ *
+ * Copyright (C) 2005 Craig Shelley (craig@microtron.org.uk)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * Support to set flow control line levels using TIOCMGET and TIOCMSET
+ * thanks to Karl Hiramoto karl@hiramoto.org. RTSCTS hardware flow
+ * control thanks to Munir Nassar nassarmu@real-time.com
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/usb.h>
+#include <linux/uaccess.h>
+#include <linux/usb/serial.h>
+
+#define DRIVER_DESC "Silicon Labs CP210x RS232 serial adaptor driver"
+
+/*
+ * Function Prototypes
+ */
+static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *);
+static void cp210x_close(struct usb_serial_port *);
+static void cp210x_get_termios(struct tty_struct *, struct usb_serial_port *);
+static void cp210x_get_termios_port(struct usb_serial_port *port,
+ unsigned int *cflagp, unsigned int *baudp);
+static void cp210x_change_speed(struct tty_struct *, struct usb_serial_port *,
+ struct ktermios *);
+static void cp210x_set_termios(struct tty_struct *, struct usb_serial_port *,
+ struct ktermios*);
+static int cp210x_tiocmget(struct tty_struct *);
+static int cp210x_tiocmset(struct tty_struct *, unsigned int, unsigned int);
+static int cp210x_tiocmset_port(struct usb_serial_port *port,
+ unsigned int, unsigned int);
+static void cp210x_break_ctl(struct tty_struct *, int);
+static int cp210x_startup(struct usb_serial *);
+static void cp210x_release(struct usb_serial *);
+static void cp210x_dtr_rts(struct usb_serial_port *p, int on);
+
+static const struct usb_device_id id_table[] = {
+ { USB_DEVICE(0x045B, 0x0053) }, /* Renesas RX610 RX-Stick */
+ { USB_DEVICE(0x0471, 0x066A) }, /* AKTAKOM ACE-1001 cable */
+ { USB_DEVICE(0x0489, 0xE000) }, /* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */
+ { USB_DEVICE(0x0489, 0xE003) }, /* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */
+ { USB_DEVICE(0x0745, 0x1000) }, /* CipherLab USB CCD Barcode Scanner 1000 */
+ { USB_DEVICE(0x0846, 0x1100) }, /* NetGear Managed Switch M4100 series, M5300 series, M7100 series */
+ { USB_DEVICE(0x08e6, 0x5501) }, /* Gemalto Prox-PU/CU contactless smartcard reader */
+ { USB_DEVICE(0x08FD, 0x000A) }, /* Digianswer A/S , ZigBee/802.15.4 MAC Device */
+ { USB_DEVICE(0x0BED, 0x1100) }, /* MEI (TM) Cashflow-SC Bill/Voucher Acceptor */
+ { USB_DEVICE(0x0BED, 0x1101) }, /* MEI series 2000 Combo Acceptor */
+ { USB_DEVICE(0x0FCF, 0x1003) }, /* Dynastream ANT development board */
+ { USB_DEVICE(0x0FCF, 0x1004) }, /* Dynastream ANT2USB */
+ { USB_DEVICE(0x0FCF, 0x1006) }, /* Dynastream ANT development board */
+ { USB_DEVICE(0x0FDE, 0xCA05) }, /* OWL Wireless Electricity Monitor CM-160 */
+ { USB_DEVICE(0x10A6, 0xAA26) }, /* Knock-off DCU-11 cable */
+ { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */
+ { USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */
+ { USB_DEVICE(0x10C4, 0x0F91) }, /* Vstabi */
+ { USB_DEVICE(0x10C4, 0x1101) }, /* Arkham Technology DS101 Bus Monitor */
+ { USB_DEVICE(0x10C4, 0x1601) }, /* Arkham Technology DS101 Adapter */
+ { USB_DEVICE(0x10C4, 0x800A) }, /* SPORTident BSM7-D-USB main station */
+ { USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */
+ { USB_DEVICE(0x10C4, 0x8044) }, /* Cygnal Debug Adapter */
+ { USB_DEVICE(0x10C4, 0x804E) }, /* Software Bisque Paramount ME build-in converter */
+ { USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */
+ { USB_DEVICE(0x10C4, 0x8054) }, /* Enfora GSM2228 */
+ { USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */
+ { USB_DEVICE(0x10C4, 0x806F) }, /* IMS USB to RS422 Converter Cable */
+ { USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */
+ { USB_DEVICE(0x10C4, 0x80C4) }, /* Cygnal Integrated Products, Inc., Optris infrared thermometer */
+ { USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */
+ { USB_DEVICE(0x10C4, 0x80DD) }, /* Tracient RFID */
+ { USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */
+ { USB_DEVICE(0x10C4, 0x8115) }, /* Arygon NFC/Mifare Reader */
+ { USB_DEVICE(0x10C4, 0x813D) }, /* Burnside Telecom Deskmobile */
+ { USB_DEVICE(0x10C4, 0x813F) }, /* Tams Master Easy Control */
+ { USB_DEVICE(0x10C4, 0x814A) }, /* West Mountain Radio RIGblaster P&P */
+ { USB_DEVICE(0x10C4, 0x814B) }, /* West Mountain Radio RIGtalk */
+ { USB_DEVICE(0x2405, 0x0003) }, /* West Mountain Radio RIGblaster Advantage */
+ { USB_DEVICE(0x10C4, 0x8156) }, /* B&G H3000 link cable */
+ { USB_DEVICE(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */
+ { USB_DEVICE(0x10C4, 0x815F) }, /* Timewave HamLinkUSB */
+ { USB_DEVICE(0x10C4, 0x818B) }, /* AVIT Research USB to TTL */
+ { USB_DEVICE(0x10C4, 0x819F) }, /* MJS USB Toslink Switcher */
+ { USB_DEVICE(0x10C4, 0x81A6) }, /* ThinkOptics WavIt */
+ { USB_DEVICE(0x10C4, 0x81A9) }, /* Multiplex RC Interface */
+ { USB_DEVICE(0x10C4, 0x81AC) }, /* MSD Dash Hawk */
+ { USB_DEVICE(0x10C4, 0x81AD) }, /* INSYS USB Modem */
+ { USB_DEVICE(0x10C4, 0x81C8) }, /* Lipowsky Industrie Elektronik GmbH, Baby-JTAG */
+ { USB_DEVICE(0x10C4, 0x81E2) }, /* Lipowsky Industrie Elektronik GmbH, Baby-LIN */
+ { USB_DEVICE(0x10C4, 0x81E7) }, /* Aerocomm Radio */
+ { USB_DEVICE(0x10C4, 0x81E8) }, /* Zephyr Bioharness */
+ { USB_DEVICE(0x10C4, 0x81F2) }, /* C1007 HF band RFID controller */
+ { USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */
+ { USB_DEVICE(0x10C4, 0x822B) }, /* Modem EDGE(GSM) Comander 2 */
+ { USB_DEVICE(0x10C4, 0x826B) }, /* Cygnal Integrated Products, Inc., Fasttrax GPS demonstration module */
+ { USB_DEVICE(0x10C4, 0x8281) }, /* Nanotec Plug & Drive */
+ { USB_DEVICE(0x10C4, 0x8293) }, /* Telegesis ETRX2USB */
+ { USB_DEVICE(0x10C4, 0x82F9) }, /* Procyon AVS */
+ { USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */
+ { USB_DEVICE(0x10C4, 0x8382) }, /* Cygnal Integrated Products, Inc. */
+ { USB_DEVICE(0x10C4, 0x83A8) }, /* Amber Wireless AMB2560 */
+ { USB_DEVICE(0x10C4, 0x83D8) }, /* DekTec DTA Plus VHF/UHF Booster/Attenuator */
+ { USB_DEVICE(0x10C4, 0x8411) }, /* Kyocera GPS Module */
+ { USB_DEVICE(0x10C4, 0x8418) }, /* IRZ Automation Teleport SG-10 GSM/GPRS Modem */
+ { USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */
+ { USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */
+ { USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */
+ { USB_DEVICE(0x10C4, 0x85EB) }, /* AC-Services CIS-IBUS */
+ { USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */
+ { USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */
+ { USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */
+ { USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */
+ { USB_DEVICE(0x10C4, 0x88A5) }, /* Planet Innovation Ingeni ZigBee USB Device */
+ { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */
+ { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */
+ { USB_DEVICE(0x10C4, 0xEA70) }, /* Silicon Labs factory default */
+ { USB_DEVICE(0x10C4, 0xEA80) }, /* Silicon Labs factory default */
+ { USB_DEVICE(0x10C4, 0xEA71) }, /* Infinity GPS-MIC-1 Radio Monophone */
+ { USB_DEVICE(0x10C4, 0xF001) }, /* Elan Digital Systems USBscope50 */
+ { USB_DEVICE(0x10C4, 0xF002) }, /* Elan Digital Systems USBwave12 */
+ { USB_DEVICE(0x10C4, 0xF003) }, /* Elan Digital Systems USBpulse100 */
+ { USB_DEVICE(0x10C4, 0xF004) }, /* Elan Digital Systems USBcount50 */
+ { USB_DEVICE(0x10C5, 0xEA61) }, /* Silicon Labs MobiData GPRS USB Modem */
+ { USB_DEVICE(0x10CE, 0xEA6A) }, /* Silicon Labs MobiData GPRS USB Modem 100EU */
+ { USB_DEVICE(0x13AD, 0x9999) }, /* Baltech card reader */
+ { USB_DEVICE(0x1555, 0x0004) }, /* Owen AC4 USB-RS485 Converter */
+ { USB_DEVICE(0x166A, 0x0201) }, /* Clipsal 5500PACA C-Bus Pascal Automation Controller */
+ { USB_DEVICE(0x166A, 0x0301) }, /* Clipsal 5800PC C-Bus Wireless PC Interface */
+ { USB_DEVICE(0x166A, 0x0303) }, /* Clipsal 5500PCU C-Bus USB interface */
+ { USB_DEVICE(0x166A, 0x0304) }, /* Clipsal 5000CT2 C-Bus Black and White Touchscreen */
+ { USB_DEVICE(0x166A, 0x0305) }, /* Clipsal C-5000CT2 C-Bus Spectrum Colour Touchscreen */
+ { USB_DEVICE(0x166A, 0x0401) }, /* Clipsal L51xx C-Bus Architectural Dimmer */
+ { USB_DEVICE(0x166A, 0x0101) }, /* Clipsal 5560884 C-Bus Multi-room Audio Matrix Switcher */
+ { USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */
+ { USB_DEVICE(0x16DC, 0x0010) }, /* W-IE-NE-R Plein & Baus GmbH PL512 Power Supply */
+ { USB_DEVICE(0x16DC, 0x0011) }, /* W-IE-NE-R Plein & Baus GmbH RCM Remote Control for MARATON Power Supply */
+ { USB_DEVICE(0x16DC, 0x0012) }, /* W-IE-NE-R Plein & Baus GmbH MPOD Multi Channel Power Supply */
+ { USB_DEVICE(0x16DC, 0x0015) }, /* W-IE-NE-R Plein & Baus GmbH CML Control, Monitoring and Data Logger */
+ { USB_DEVICE(0x17A8, 0x0001) }, /* Kamstrup Optical Eye/3-wire */
+ { USB_DEVICE(0x17A8, 0x0005) }, /* Kamstrup M-Bus Master MultiPort 250D */
+ { USB_DEVICE(0x17F4, 0xAAAA) }, /* Wavesense Jazz blood glucose meter */
+ { USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */
+ { USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */
+ { USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */
+ { USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */
+ { USB_DEVICE(0x1BE3, 0x07A6) }, /* WAGO 750-923 USB Service Cable */
+ { USB_DEVICE(0x1E29, 0x0102) }, /* Festo CPX-USB */
+ { USB_DEVICE(0x1E29, 0x0501) }, /* Festo CMSP */
+ { USB_DEVICE(0x1FB9, 0x0100) }, /* Lake Shore Model 121 Current Source */
+ { USB_DEVICE(0x1FB9, 0x0200) }, /* Lake Shore Model 218A Temperature Monitor */
+ { USB_DEVICE(0x1FB9, 0x0201) }, /* Lake Shore Model 219 Temperature Monitor */
+ { USB_DEVICE(0x1FB9, 0x0202) }, /* Lake Shore Model 233 Temperature Transmitter */
+ { USB_DEVICE(0x1FB9, 0x0203) }, /* Lake Shore Model 235 Temperature Transmitter */
+ { USB_DEVICE(0x1FB9, 0x0300) }, /* Lake Shore Model 335 Temperature Controller */
+ { USB_DEVICE(0x1FB9, 0x0301) }, /* Lake Shore Model 336 Temperature Controller */
+ { USB_DEVICE(0x1FB9, 0x0302) }, /* Lake Shore Model 350 Temperature Controller */
+ { USB_DEVICE(0x1FB9, 0x0303) }, /* Lake Shore Model 371 AC Bridge */
+ { USB_DEVICE(0x1FB9, 0x0400) }, /* Lake Shore Model 411 Handheld Gaussmeter */
+ { USB_DEVICE(0x1FB9, 0x0401) }, /* Lake Shore Model 425 Gaussmeter */
+ { USB_DEVICE(0x1FB9, 0x0402) }, /* Lake Shore Model 455A Gaussmeter */
+ { USB_DEVICE(0x1FB9, 0x0403) }, /* Lake Shore Model 475A Gaussmeter */
+ { USB_DEVICE(0x1FB9, 0x0404) }, /* Lake Shore Model 465 Three Axis Gaussmeter */
+ { USB_DEVICE(0x1FB9, 0x0600) }, /* Lake Shore Model 625A Superconducting MPS */
+ { USB_DEVICE(0x1FB9, 0x0601) }, /* Lake Shore Model 642A Magnet Power Supply */
+ { USB_DEVICE(0x1FB9, 0x0602) }, /* Lake Shore Model 648 Magnet Power Supply */
+ { USB_DEVICE(0x1FB9, 0x0700) }, /* Lake Shore Model 737 VSM Controller */
+ { USB_DEVICE(0x1FB9, 0x0701) }, /* Lake Shore Model 776 Hall Matrix */
+ { USB_DEVICE(0x3195, 0xF190) }, /* Link Instruments MSO-19 */
+ { USB_DEVICE(0x3195, 0xF280) }, /* Link Instruments MSO-28 */
+ { USB_DEVICE(0x3195, 0xF281) }, /* Link Instruments MSO-28 */
+ { USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */
+ { } /* Terminating Entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+struct cp210x_serial_private {
+ __u8 bInterfaceNumber;
+};
+
+static struct usb_serial_driver cp210x_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "cp210x",
+ },
+ .id_table = id_table,
+ .num_ports = 1,
+ .bulk_in_size = 256,
+ .bulk_out_size = 256,
+ .open = cp210x_open,
+ .close = cp210x_close,
+ .break_ctl = cp210x_break_ctl,
+ .set_termios = cp210x_set_termios,
+ .tiocmget = cp210x_tiocmget,
+ .tiocmset = cp210x_tiocmset,
+ .attach = cp210x_startup,
+ .release = cp210x_release,
+ .dtr_rts = cp210x_dtr_rts
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+ &cp210x_device, NULL
+};
+
+/* Config request types */
+#define REQTYPE_HOST_TO_INTERFACE 0x41
+#define REQTYPE_INTERFACE_TO_HOST 0xc1
+#define REQTYPE_HOST_TO_DEVICE 0x40
+#define REQTYPE_DEVICE_TO_HOST 0xc0
+
+/* Config request codes */
+#define CP210X_IFC_ENABLE 0x00
+#define CP210X_SET_BAUDDIV 0x01
+#define CP210X_GET_BAUDDIV 0x02
+#define CP210X_SET_LINE_CTL 0x03
+#define CP210X_GET_LINE_CTL 0x04
+#define CP210X_SET_BREAK 0x05
+#define CP210X_IMM_CHAR 0x06
+#define CP210X_SET_MHS 0x07
+#define CP210X_GET_MDMSTS 0x08
+#define CP210X_SET_XON 0x09
+#define CP210X_SET_XOFF 0x0A
+#define CP210X_SET_EVENTMASK 0x0B
+#define CP210X_GET_EVENTMASK 0x0C
+#define CP210X_SET_CHAR 0x0D
+#define CP210X_GET_CHARS 0x0E
+#define CP210X_GET_PROPS 0x0F
+#define CP210X_GET_COMM_STATUS 0x10
+#define CP210X_RESET 0x11
+#define CP210X_PURGE 0x12
+#define CP210X_SET_FLOW 0x13
+#define CP210X_GET_FLOW 0x14
+#define CP210X_EMBED_EVENTS 0x15
+#define CP210X_GET_EVENTSTATE 0x16
+#define CP210X_SET_CHARS 0x19
+#define CP210X_GET_BAUDRATE 0x1D
+#define CP210X_SET_BAUDRATE 0x1E
+
+/* CP210X_IFC_ENABLE */
+#define UART_ENABLE 0x0001
+#define UART_DISABLE 0x0000
+
+/* CP210X_(SET|GET)_BAUDDIV */
+#define BAUD_RATE_GEN_FREQ 0x384000
+
+/* CP210X_(SET|GET)_LINE_CTL */
+#define BITS_DATA_MASK 0X0f00
+#define BITS_DATA_5 0X0500
+#define BITS_DATA_6 0X0600
+#define BITS_DATA_7 0X0700
+#define BITS_DATA_8 0X0800
+#define BITS_DATA_9 0X0900
+
+#define BITS_PARITY_MASK 0x00f0
+#define BITS_PARITY_NONE 0x0000
+#define BITS_PARITY_ODD 0x0010
+#define BITS_PARITY_EVEN 0x0020
+#define BITS_PARITY_MARK 0x0030
+#define BITS_PARITY_SPACE 0x0040
+
+#define BITS_STOP_MASK 0x000f
+#define BITS_STOP_1 0x0000
+#define BITS_STOP_1_5 0x0001
+#define BITS_STOP_2 0x0002
+
+/* CP210X_SET_BREAK */
+#define BREAK_ON 0x0001
+#define BREAK_OFF 0x0000
+
+/* CP210X_(SET_MHS|GET_MDMSTS) */
+#define CONTROL_DTR 0x0001
+#define CONTROL_RTS 0x0002
+#define CONTROL_CTS 0x0010
+#define CONTROL_DSR 0x0020
+#define CONTROL_RING 0x0040
+#define CONTROL_DCD 0x0080
+#define CONTROL_WRITE_DTR 0x0100
+#define CONTROL_WRITE_RTS 0x0200
+
+/*
+ * cp210x_get_config
+ * Reads from the CP210x configuration registers
+ * 'size' is specified in bytes.
+ * 'data' is a pointer to a pre-allocated array of integers large
+ * enough to hold 'size' bytes (with 4 bytes to each integer)
+ */
+static int cp210x_get_config(struct usb_serial_port *port, u8 request,
+ unsigned int *data, int size)
+{
+ struct usb_serial *serial = port->serial;
+ struct cp210x_serial_private *spriv = usb_get_serial_data(serial);
+ __le32 *buf;
+ int result, i, length;
+
+ /* Number of integers required to contain the array */
+ length = (((size - 1) | 3) + 1) / 4;
+
+ buf = kcalloc(length, sizeof(__le32), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* Issue the request, attempting to read 'size' bytes */
+ result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+ request, REQTYPE_INTERFACE_TO_HOST, 0x0000,
+ spriv->bInterfaceNumber, buf, size,
+ USB_CTRL_GET_TIMEOUT);
+
+ /* Convert data into an array of integers */
+ for (i = 0; i < length; i++)
+ data[i] = le32_to_cpu(buf[i]);
+
+ kfree(buf);
+
+ if (result != size) {
+ dev_dbg(&port->dev, "%s - Unable to send config request, request=0x%x size=%d result=%d\n",
+ __func__, request, size, result);
+ if (result > 0)
+ result = -EPROTO;
+
+ return result;
+ }
+
+ return 0;
+}
+
+/*
+ * cp210x_set_config
+ * Writes to the CP210x configuration registers
+ * Values less than 16 bits wide are sent directly
+ * 'size' is specified in bytes.
+ */
+static int cp210x_set_config(struct usb_serial_port *port, u8 request,
+ unsigned int *data, int size)
+{
+ struct usb_serial *serial = port->serial;
+ struct cp210x_serial_private *spriv = usb_get_serial_data(serial);
+ __le32 *buf;
+ int result, i, length;
+
+ /* Number of integers required to contain the array */
+ length = (((size - 1) | 3) + 1) / 4;
+
+ buf = kmalloc(length * sizeof(__le32), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* Array of integers into bytes */
+ for (i = 0; i < length; i++)
+ buf[i] = cpu_to_le32(data[i]);
+
+ if (size > 2) {
+ result = usb_control_msg(serial->dev,
+ usb_sndctrlpipe(serial->dev, 0),
+ request, REQTYPE_HOST_TO_INTERFACE, 0x0000,
+ spriv->bInterfaceNumber, buf, size,
+ USB_CTRL_SET_TIMEOUT);
+ } else {
+ result = usb_control_msg(serial->dev,
+ usb_sndctrlpipe(serial->dev, 0),
+ request, REQTYPE_HOST_TO_INTERFACE, data[0],
+ spriv->bInterfaceNumber, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+ }
+
+ kfree(buf);
+
+ if ((size > 2 && result != size) || result < 0) {
+ dev_dbg(&port->dev, "%s - Unable to send request, request=0x%x size=%d result=%d\n",
+ __func__, request, size, result);
+ if (result > 0)
+ result = -EPROTO;
+
+ return result;
+ }
+
+ return 0;
+}
+
+/*
+ * cp210x_set_config_single
+ * Convenience function for calling cp210x_set_config on single data values
+ * without requiring an integer pointer
+ */
+static inline int cp210x_set_config_single(struct usb_serial_port *port,
+ u8 request, unsigned int data)
+{
+ return cp210x_set_config(port, request, &data, 2);
+}
+
+/*
+ * cp210x_quantise_baudrate
+ * Quantises the baud rate as per AN205 Table 1
+ */
+static unsigned int cp210x_quantise_baudrate(unsigned int baud)
+{
+ if( baud == 0) baud = 250000; /* KLUDGE for Owl Energy Monitor, Use baud rate (B0 Hang up) that is unlikley to be of any use */
+ else if (baud <= 300) baud = 300;
+ else if (baud <= 600) baud = 600;
+ else if (baud <= 1200) baud = 1200;
+ else if (baud <= 1800) baud = 1800;
+ else if (baud <= 2400) baud = 2400;
+ else if (baud <= 4000) baud = 4000;
+ else if (baud <= 4803) baud = 4800;
+ else if (baud <= 7207) baud = 7200;
+ else if (baud <= 9612) baud = 9600;
+ else if (baud <= 14428) baud = 14400;
+ else if (baud <= 16062) baud = 16000;
+ else if (baud <= 19250) baud = 19200;
+ else if (baud <= 28912) baud = 28800;
+ else if (baud <= 38601) baud = 38400;
+ else if (baud <= 51558) baud = 51200;
+ else if (baud <= 56280) baud = 56000;
+ else if (baud <= 58053) baud = 57600;
+ else if (baud <= 64111) baud = 64000;
+ else if (baud <= 77608) baud = 76800;
+ else if (baud <= 117028) baud = 115200;
+ else if (baud <= 129347) baud = 128000;
+ else if (baud <= 156868) baud = 153600;
+ else if (baud <= 237832) baud = 230400;
+ else if (baud <= 254234) baud = 250000;
+ else if (baud <= 273066) baud = 256000;
+ else if (baud <= 491520) baud = 460800;
+ else if (baud <= 567138) baud = 500000;
+ else if (baud <= 670254) baud = 576000;
+ else if (baud < 1000000)
+ baud = 921600;
+ else if (baud > 2000000)
+ baud = 2000000;
+ return baud;
+}
+
+static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+ int result;
+
+ result = cp210x_set_config_single(port, CP210X_IFC_ENABLE,
+ UART_ENABLE);
+ if (result) {
+ dev_err(&port->dev, "%s - Unable to enable UART\n", __func__);
+ return result;
+ }
+
+ /* Configure the termios structure */
+ cp210x_get_termios(tty, port);
+
+ /* The baud rate must be initialised on cp2104 */
+ if (tty)
+ cp210x_change_speed(tty, port, NULL);
+
+ return usb_serial_generic_open(tty, port);
+}
+
+static void cp210x_close(struct usb_serial_port *port)
+{
+ usb_serial_generic_close(port);
+ cp210x_set_config_single(port, CP210X_IFC_ENABLE, UART_DISABLE);
+}
+
+/*
+ * cp210x_get_termios
+ * Reads the baud rate, data bits, parity, stop bits and flow control mode
+ * from the device, corrects any unsupported values, and configures the
+ * termios structure to reflect the state of the device
+ */
+static void cp210x_get_termios(struct tty_struct *tty,
+ struct usb_serial_port *port)
+{
+ unsigned int baud;
+
+ if (tty) {
+ cp210x_get_termios_port(tty->driver_data,
+ &tty->termios.c_cflag, &baud);
+ tty_encode_baud_rate(tty, baud, baud);
+ } else {
+ unsigned int cflag;
+ cflag = 0;
+ cp210x_get_termios_port(port, &cflag, &baud);
+ }
+}
+
+/*
+ * cp210x_get_termios_port
+ * This is the heart of cp210x_get_termios which always uses a &usb_serial_port.
+ */
+static void cp210x_get_termios_port(struct usb_serial_port *port,
+ unsigned int *cflagp, unsigned int *baudp)
+{
+ struct device *dev = &port->dev;
+ unsigned int cflag, modem_ctl[4];
+ unsigned int baud;
+ unsigned int bits;
+
+ cp210x_get_config(port, CP210X_GET_BAUDRATE, &baud, 4);
+
+ dev_dbg(dev, "%s - baud rate = %d\n", __func__, baud);
+ *baudp = baud;
+
+ cflag = *cflagp;
+
+ cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
+ cflag &= ~CSIZE;
+ switch (bits & BITS_DATA_MASK) {
+ case BITS_DATA_5:
+ dev_dbg(dev, "%s - data bits = 5\n", __func__);
+ cflag |= CS5;
+ break;
+ case BITS_DATA_6:
+ dev_dbg(dev, "%s - data bits = 6\n", __func__);
+ cflag |= CS6;
+ break;
+ case BITS_DATA_7:
+ dev_dbg(dev, "%s - data bits = 7\n", __func__);
+ cflag |= CS7;
+ break;
+ case BITS_DATA_8:
+ dev_dbg(dev, "%s - data bits = 8\n", __func__);
+ cflag |= CS8;
+ break;
+ case BITS_DATA_9:
+ dev_dbg(dev, "%s - data bits = 9 (not supported, using 8 data bits)\n", __func__);
+ cflag |= CS8;
+ bits &= ~BITS_DATA_MASK;
+ bits |= BITS_DATA_8;
+ cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
+ break;
+ default:
+ dev_dbg(dev, "%s - Unknown number of data bits, using 8\n", __func__);
+ cflag |= CS8;
+ bits &= ~BITS_DATA_MASK;
+ bits |= BITS_DATA_8;
+ cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
+ break;
+ }
+
+ switch (bits & BITS_PARITY_MASK) {
+ case BITS_PARITY_NONE:
+ dev_dbg(dev, "%s - parity = NONE\n", __func__);
+ cflag &= ~PARENB;
+ break;
+ case BITS_PARITY_ODD:
+ dev_dbg(dev, "%s - parity = ODD\n", __func__);
+ cflag |= (PARENB|PARODD);
+ break;
+ case BITS_PARITY_EVEN:
+ dev_dbg(dev, "%s - parity = EVEN\n", __func__);
+ cflag &= ~PARODD;
+ cflag |= PARENB;
+ break;
+ case BITS_PARITY_MARK:
+ dev_dbg(dev, "%s - parity = MARK\n", __func__);
+ cflag |= (PARENB|PARODD|CMSPAR);
+ break;
+ case BITS_PARITY_SPACE:
+ dev_dbg(dev, "%s - parity = SPACE\n", __func__);
+ cflag &= ~PARODD;
+ cflag |= (PARENB|CMSPAR);
+ break;
+ default:
+ dev_dbg(dev, "%s - Unknown parity mode, disabling parity\n", __func__);
+ cflag &= ~PARENB;
+ bits &= ~BITS_PARITY_MASK;
+ cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
+ break;
+ }
+
+ cflag &= ~CSTOPB;
+ switch (bits & BITS_STOP_MASK) {
+ case BITS_STOP_1:
+ dev_dbg(dev, "%s - stop bits = 1\n", __func__);
+ break;
+ case BITS_STOP_1_5:
+ dev_dbg(dev, "%s - stop bits = 1.5 (not supported, using 1 stop bit)\n", __func__);
+ bits &= ~BITS_STOP_MASK;
+ cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
+ break;
+ case BITS_STOP_2:
+ dev_dbg(dev, "%s - stop bits = 2\n", __func__);
+ cflag |= CSTOPB;
+ break;
+ default:
+ dev_dbg(dev, "%s - Unknown number of stop bits, using 1 stop bit\n", __func__);
+ bits &= ~BITS_STOP_MASK;
+ cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
+ break;
+ }
+
+ cp210x_get_config(port, CP210X_GET_FLOW, modem_ctl, 16);
+ if (modem_ctl[0] & 0x0008) {
+ dev_dbg(dev, "%s - flow control = CRTSCTS\n", __func__);
+ cflag |= CRTSCTS;
+ } else {
+ dev_dbg(dev, "%s - flow control = NONE\n", __func__);
+ cflag &= ~CRTSCTS;
+ }
+
+ *cflagp = cflag;
+}
+
+/*
+ * CP2101 supports the following baud rates:
+ *
+ * 300, 600, 1200, 1800, 2400, 4800, 7200, 9600, 14400, 19200, 28800,
+ * 38400, 56000, 57600, 115200, 128000, 230400, 460800, 921600
+ *
+ * CP2102 and CP2103 support the following additional rates:
+ *
+ * 4000, 16000, 51200, 64000, 76800, 153600, 250000, 256000, 500000,
+ * 576000
+ *
+ * The device will map a requested rate to a supported one, but the result
+ * of requests for rates greater than 1053257 is undefined (see AN205).
+ *
+ * CP2104, CP2105 and CP2110 support most rates up to 2M, 921k and 1M baud,
+ * respectively, with an error less than 1%. The actual rates are determined
+ * by
+ *
+ * div = round(freq / (2 x prescale x request))
+ * actual = freq / (2 x prescale x div)
+ *
+ * For CP2104 and CP2105 freq is 48Mhz and prescale is 4 for request <= 365bps
+ * or 1 otherwise.
+ * For CP2110 freq is 24Mhz and prescale is 4 for request <= 300bps or 1
+ * otherwise.
+ */
+static void cp210x_change_speed(struct tty_struct *tty,
+ struct usb_serial_port *port, struct ktermios *old_termios)
+{
+ u32 baud;
+
+ baud = tty->termios.c_ospeed;
+
+ /* This maps the requested rate to a rate valid on cp2102 or cp2103,
+ * or to an arbitrary rate in [1M,2M].
+ *
+ * NOTE: B0 is not implemented.
+ */
+ baud = cp210x_quantise_baudrate(baud);
+
+ dev_dbg(&port->dev, "%s - setting baud rate to %u\n", __func__, baud);
+ if (cp210x_set_config(port, CP210X_SET_BAUDRATE, &baud,
+ sizeof(baud))) {
+ dev_warn(&port->dev, "failed to set baud rate to %u\n", baud);
+ if (old_termios)
+ baud = old_termios->c_ospeed;
+ else
+ baud = 9600;
+ }
+
+ tty_encode_baud_rate(tty, baud, baud);
+}
+
+static void cp210x_set_termios(struct tty_struct *tty,
+ struct usb_serial_port *port, struct ktermios *old_termios)
+{
+ struct device *dev = &port->dev;
+ unsigned int cflag, old_cflag;
+ unsigned int bits;
+ unsigned int modem_ctl[4];
+
+ cflag = tty->termios.c_cflag;
+ old_cflag = old_termios->c_cflag;
+
+ if (tty->termios.c_ospeed != old_termios->c_ospeed)
+ cp210x_change_speed(tty, port, old_termios);
+
+ /* If the number of data bits is to be updated */
+ if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
+ cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
+ bits &= ~BITS_DATA_MASK;
+ switch (cflag & CSIZE) {
+ case CS5:
+ bits |= BITS_DATA_5;
+ dev_dbg(dev, "%s - data bits = 5\n", __func__);
+ break;
+ case CS6:
+ bits |= BITS_DATA_6;
+ dev_dbg(dev, "%s - data bits = 6\n", __func__);
+ break;
+ case CS7:
+ bits |= BITS_DATA_7;
+ dev_dbg(dev, "%s - data bits = 7\n", __func__);
+ break;
+ case CS8:
+ bits |= BITS_DATA_8;
+ dev_dbg(dev, "%s - data bits = 8\n", __func__);
+ break;
+ /*case CS9:
+ bits |= BITS_DATA_9;
+ dev_dbg(dev, "%s - data bits = 9\n", __func__);
+ break;*/
+ default:
+ dev_dbg(dev, "cp210x driver does not support the number of bits requested, using 8 bit mode\n");
+ bits |= BITS_DATA_8;
+ break;
+ }
+ if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
+ dev_dbg(dev, "Number of data bits requested not supported by device\n");
+ }
+
+ if ((cflag & (PARENB|PARODD|CMSPAR)) !=
+ (old_cflag & (PARENB|PARODD|CMSPAR))) {
+ cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
+ bits &= ~BITS_PARITY_MASK;
+ if (cflag & PARENB) {
+ if (cflag & CMSPAR) {
+ if (cflag & PARODD) {
+ bits |= BITS_PARITY_MARK;
+ dev_dbg(dev, "%s - parity = MARK\n", __func__);
+ } else {
+ bits |= BITS_PARITY_SPACE;
+ dev_dbg(dev, "%s - parity = SPACE\n", __func__);
+ }
+ } else {
+ if (cflag & PARODD) {
+ bits |= BITS_PARITY_ODD;
+ dev_dbg(dev, "%s - parity = ODD\n", __func__);
+ } else {
+ bits |= BITS_PARITY_EVEN;
+ dev_dbg(dev, "%s - parity = EVEN\n", __func__);
+ }
+ }
+ }
+ if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
+ dev_dbg(dev, "Parity mode not supported by device\n");
+ }
+
+ if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) {
+ cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
+ bits &= ~BITS_STOP_MASK;
+ if (cflag & CSTOPB) {
+ bits |= BITS_STOP_2;
+ dev_dbg(dev, "%s - stop bits = 2\n", __func__);
+ } else {
+ bits |= BITS_STOP_1;
+ dev_dbg(dev, "%s - stop bits = 1\n", __func__);
+ }
+ if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
+ dev_dbg(dev, "Number of stop bits requested not supported by device\n");
+ }
+
+ if ((cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
+ cp210x_get_config(port, CP210X_GET_FLOW, modem_ctl, 16);
+ dev_dbg(dev, "%s - read modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x\n",
+ __func__, modem_ctl[0], modem_ctl[1],
+ modem_ctl[2], modem_ctl[3]);
+
+ if (cflag & CRTSCTS) {
+ modem_ctl[0] &= ~0x7B;
+ modem_ctl[0] |= 0x09;
+ modem_ctl[1] = 0x80;
+ dev_dbg(dev, "%s - flow control = CRTSCTS\n", __func__);
+ } else {
+ modem_ctl[0] &= ~0x7B;
+ modem_ctl[0] |= 0x01;
+ modem_ctl[1] |= 0x40;
+ dev_dbg(dev, "%s - flow control = NONE\n", __func__);
+ }
+
+ dev_dbg(dev, "%s - write modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x\n",
+ __func__, modem_ctl[0], modem_ctl[1],
+ modem_ctl[2], modem_ctl[3]);
+ cp210x_set_config(port, CP210X_SET_FLOW, modem_ctl, 16);
+ }
+
+}
+
+static int cp210x_tiocmset(struct tty_struct *tty,
+ unsigned int set, unsigned int clear)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ return cp210x_tiocmset_port(port, set, clear);
+}
+
+static int cp210x_tiocmset_port(struct usb_serial_port *port,
+ unsigned int set, unsigned int clear)
+{
+ unsigned int control = 0;
+
+ if (set & TIOCM_RTS) {
+ control |= CONTROL_RTS;
+ control |= CONTROL_WRITE_RTS;
+ }
+ if (set & TIOCM_DTR) {
+ control |= CONTROL_DTR;
+ control |= CONTROL_WRITE_DTR;
+ }
+ if (clear & TIOCM_RTS) {
+ control &= ~CONTROL_RTS;
+ control |= CONTROL_WRITE_RTS;
+ }
+ if (clear & TIOCM_DTR) {
+ control &= ~CONTROL_DTR;
+ control |= CONTROL_WRITE_DTR;
+ }
+
+ dev_dbg(&port->dev, "%s - control = 0x%.4x\n", __func__, control);
+
+ return cp210x_set_config(port, CP210X_SET_MHS, &control, 2);
+}
+
+static void cp210x_dtr_rts(struct usb_serial_port *p, int on)
+{
+ if (on)
+ cp210x_tiocmset_port(p, TIOCM_DTR|TIOCM_RTS, 0);
+ else
+ cp210x_tiocmset_port(p, 0, TIOCM_DTR|TIOCM_RTS);
+}
+
+static int cp210x_tiocmget(struct tty_struct *tty)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ unsigned int control;
+ int result;
+
+ cp210x_get_config(port, CP210X_GET_MDMSTS, &control, 1);
+
+ result = ((control & CONTROL_DTR) ? TIOCM_DTR : 0)
+ |((control & CONTROL_RTS) ? TIOCM_RTS : 0)
+ |((control & CONTROL_CTS) ? TIOCM_CTS : 0)
+ |((control & CONTROL_DSR) ? TIOCM_DSR : 0)
+ |((control & CONTROL_RING)? TIOCM_RI : 0)
+ |((control & CONTROL_DCD) ? TIOCM_CD : 0);
+
+ dev_dbg(&port->dev, "%s - control = 0x%.2x\n", __func__, control);
+
+ return result;
+}
+
+static void cp210x_break_ctl(struct tty_struct *tty, int break_state)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ unsigned int state;
+
+ if (break_state == 0)
+ state = BREAK_OFF;
+ else
+ state = BREAK_ON;
+ dev_dbg(&port->dev, "%s - turning break %s\n", __func__,
+ state == BREAK_OFF ? "off" : "on");
+ cp210x_set_config(port, CP210X_SET_BREAK, &state, 2);
+}
+
+static int cp210x_startup(struct usb_serial *serial)
+{
+ struct usb_host_interface *cur_altsetting;
+ struct cp210x_serial_private *spriv;
+
+ /* cp210x buffers behave strangely unless device is reset */
+ usb_reset_device(serial->dev);
+
+ spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
+ if (!spriv)
+ return -ENOMEM;
+
+ cur_altsetting = serial->interface->cur_altsetting;
+ spriv->bInterfaceNumber = cur_altsetting->desc.bInterfaceNumber;
+
+ usb_set_serial_data(serial, spriv);
+
+ return 0;
+}
+
+static void cp210x_release(struct usb_serial *serial)
+{
+ struct cp210x_serial_private *spriv;
+
+ spriv = usb_get_serial_data(serial);
+ kfree(spriv);
+}
+
+module_usb_serial_driver(serial_drivers, id_table);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");