summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorGrey Christoforo2018-04-12 13:42:13 +0100
committerGrey Christoforo2018-04-12 13:48:47 +0100
commit82595db7dfcc1e6c4ec46d0868d48c2bde5fa489 (patch)
tree3e40a612aac0ef83b84273e96229c054031b0468
downloadaur-82595db7dfcc1e6c4ec46d0868d48c2bde5fa489.tar.gz
initial commit
fix typo fix .srcinfo name
-rw-r--r--.SRCINFO19
-rw-r--r--.gitignore4
-rw-r--r--80-spectrometer.rules20
-rw-r--r--PKGBUILD43
-rw-r--r--cyusb-fw-extract-py2.py250
5 files changed, 336 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO
new file mode 100644
index 000000000000..ea8574910f95
--- /dev/null
+++ b/.SRCINFO
@@ -0,0 +1,19 @@
+pkgbase = thorspec
+ pkgdesc = Driver for Thorlabs spectrometers
+ pkgver = 2.8.0
+ pkgrel = 1
+ url = https://www.thorlabs.com/newgrouppage9.cfm?objectgroup_id=3482&pn=CCS175/M
+ arch = any
+ license = custom
+ makedepends = innoextract
+ makedepends = python2
+ noextract = setupblob_2.8.0
+ source = setupblob_2.8.0.exe::https://www.thorlabs.com/software/THO/OSA/V2_80/ThorlabsOSASW_Full_setup.exe
+ source = cyusb-fw-extract-py2.py
+ source = 80-spectrometer.rules
+ md5sums = 1301c516d4bd64aa348a9936ea8ee4ab
+ md5sums = 793c35c7b9b08baa950a63564dfb782f
+ md5sums = 7083e7b40cbd3095640d227190cdd08d
+
+pkgname = thorspec
+
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000000..cb050aac7c01
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+src/
+pkg/
+*.pkg.tar.xz
+*.exe
diff --git a/80-spectrometer.rules b/80-spectrometer.rules
new file mode 100644
index 000000000000..2a4bbfa80a0a
--- /dev/null
+++ b/80-spectrometer.rules
@@ -0,0 +1,20 @@
+# Thor SPx and CCS spectrometers
+ACTION!="add", GOTO="thor_rules_end"
+ATTR{idVendor}!="1313", GOTO="thor_rules_end"
+
+# Thor CCS spectrometers need a firmware upload before they renumerate as the real device
+ATTR{idProduct}=="8080", RUN+="/usr/bin/fxload -t fx2lp -I /lib/firmware/CCS100_2.ihx -D %N"
+ATTR{idProduct}=="8082", RUN+="/usr/bin/fxload -t fx2lp -I /lib/firmware/CCS125_2.ihx -D %N"
+ATTR{idProduct}=="8084", RUN+="/usr/bin/fxload -t fx2lp -I /lib/firmware/CCS150_2.ihx -D %N"
+ATTR{idProduct}=="8086", RUN+="/usr/bin/fxload -t fx2lp -I /lib/firmware/CCS175_2.ihx -D %N"
+ATTR{idProduct}=="8088", RUN+="/usr/bin/fxload -t fx2lp -I /lib/firmware/CCS200_2.ihx -D %N"
+
+# Need libusb to have access for all users
+ATTR{idProduct}=="0111", MODE="0666"
+ATTR{idProduct}=="8081", MODE="0666"
+ATTR{idProduct}=="8083", MODE="0666"
+ATTR{idProduct}=="8085", MODE="0666"
+ATTR{idProduct}=="8087", MODE="0666"
+ATTR{idProduct}=="8089", MODE="0666"
+
+LABEL="thor_rules_end"
diff --git a/PKGBUILD b/PKGBUILD
new file mode 100644
index 000000000000..cde1eeb43bbe
--- /dev/null
+++ b/PKGBUILD
@@ -0,0 +1,43 @@
+# Maintainer: Grey Christoforo <first name at last name dot net>
+pkgname=thorspec
+pkgver=2.8.0
+pkgrel=1
+pkgdesc="Driver for Thorlabs spectrometers"
+arch=('any')
+url="https://www.thorlabs.com/newgrouppage9.cfm?objectgroup_id=3482&pn=CCS175/M"
+license=('custom')
+makedepends=('innoextract' 'python2')
+_dlver=${pkgver//./}
+_dlver=V${_dlver::1}_${_dlver: -2}
+source=(setupblob_${pkgver}.exe::"https://www.thorlabs.com/software/THO/OSA/${_dlver}/ThorlabsOSASW_Full_setup.exe" "cyusb-fw-extract-py2.py" "80-spectrometer.rules")
+noextract=("setupblob_${pkgver}")
+md5sums=('1301c516d4bd64aa348a9936ea8ee4ab'
+ '793c35c7b9b08baa950a63564dfb782f'
+ '7083e7b40cbd3095640d227190cdd08d')
+
+
+_firmware_loc="app/CCS/inf/Loader"
+
+prepare() {
+ # extract the installer
+ innoextract setupblob_${pkgver}.exe
+}
+
+build() {
+ # convert the firmwares
+ _firmwares="$srcdir/${_firmware_loc}/*.spt"
+ for firmware in $_firmwares
+ do
+ python2 cyusb-fw-extract-py2.py -o${firmware//.spt/} $firmware || true
+ done
+}
+
+
+package() {
+ _firmwares="$srcdir/${_firmware_loc}/*_2.ihx"
+ for firmware in $_firmwares
+ do
+ install -D -m644 "${firmware}" "$pkgdir/usr/lib/firmware/$(basename ${firmware})"
+ done
+ install -D -m644 80-spectrometer.rules "$pkgdir/etc/udev/rules.d/80-spectrometer.rules"
+}
diff --git a/cyusb-fw-extract-py2.py b/cyusb-fw-extract-py2.py
new file mode 100644
index 000000000000..9cb25dc3efe7
--- /dev/null
+++ b/cyusb-fw-extract-py2.py
@@ -0,0 +1,250 @@
+#!/usr/bin/python
+# -*- coding: ascii -*-
+########################################################################
+# cyusb-fw-extract.py - Extract firmware from Cypress USB script files
+# Copyright (C) 2008 Dwayne C. Litzenberger <dlitz@dlitz.net>
+#
+# 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 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+########################################################################
+#
+# This program attempts to create fxload-compatible firmware images by
+# analyzing a supplied Cypress USB script file. Cypress USB script files
+# typically have the ".spt" file extension, and are used in devices that
+# incorporate the Cypress Semiconductor EZ-USB (CY7C68xxx) series
+# microcontrollers. These script files are executed on Windows by the Cypress
+# Generic USB Driver (CyUsb.sys).
+#
+# This program is known to work for the following firmware(s):
+# - ADS Tech Instant Video-To-Go RDX-160 (hardware H.264 encoder)
+# + "vtogold.spt" on the CD "Instant Video To-Go CD, Ver. 1.2"
+#
+
+import sys
+import os.path
+import struct
+import getopt
+
+PROGRAM_VERSION = "cyusb-fw-extract 0.1"
+
+def exit_usage():
+ print >>sys.stderr, """Usage: %s [-v] -oPREFIX SPTFILE
+
+Read SPTFILE and output PREFIX_1.ihx, PREFIX_2.ihx...
+Use -v for verbose output.
+""" % (sys.argv[0],)
+ sys.exit(2)
+
+class CSPTChunk(object):
+ @classmethod
+ def fromfile(cls, file):
+ chunk = file.read(4)
+ if not chunk:
+ return None # EOF
+ if chunk[:4] != "CSPT":
+ raise ValueError("Error reading chunk: expected 'CSPT', got %r" % (chunk[:4],))
+ chunk += file.read(4)
+ (length,) = struct.unpack("<L", chunk[4:8])
+ if length < 8:
+ raise ValueError("Error reading chunk: chunk length %d too small" % (length,))
+ chunk += file.read(length - 8)
+ if len(chunk) < length:
+ raise EOFError("Error reading chunk: file truncated")
+
+ fmt = "<LLLLBBHLLL"
+ hdr_len = struct.calcsize(fmt)
+ (magic, # 0: Long word
+ length, # 4: Long word
+ dummy8, # 8: Long word
+ dummy12, # 12: Long word
+ request, # 16: Byte
+ dummy17, # 17: Byte
+ addr, # 18: Half word
+ dummy20, # 20: long word
+ dummy24, # 24: long word
+ data_len, # 28: long word
+ ) = struct.unpack(fmt, chunk[:hdr_len])
+ data = chunk[hdr_len:]
+ if len(data) != data_len:
+ raise ValueError("Error reading chunk: data length mismatch (%d vs %d)" % (len(data), data_len))
+
+ if len(data) < data_len:
+ raise EOFError("Error reading chunk: file truncated")
+
+ obj = cls()
+ obj.raw_chunk = chunk
+ obj.dummies = (dummy8, dummy12, dummy17, dummy20, dummy24)
+ obj.request = request
+ obj.address = addr
+ obj.data = data
+
+ return obj
+
+ def __repr__(self):
+ return """<%s request=0x%02x address=0x%04x length=%d dummies=%r>""" % (
+ self.__class__.__name__, self.request, self.address,
+ len(self.data), self.dummies)
+
+def make_ihx_data_lines(addr, data, width=16):
+ """Generate a list of Intel Hex format lines"""
+ if not 1 <= width < 256:
+ raise ValueError("width must be in range(1,256)")
+ if not 0 <= addr <= 0xffff or not 0 <= addr + len(data) - 1 <= 0xffff:
+ raise NotImplementedError("Addresses greater than 16 bits not supported")
+ for i in xrange(0, len(data), width):
+ a = addr + i
+ d = data[i:i+width]
+ assert 0 <= a <= 0xffff
+ assert 0 <= a+width-1 <= 0xffff
+ octets = []
+ octets.append(chr(len(d))) # Load record length
+ octets.append(chr(a >> 8)) # high address bits
+ octets.append(chr(a & 0xff)) # low address bits
+ octets.append("\x00") # record type = 00 (data record)
+ octets += list(d)
+
+ # checksum
+ checksum = 0
+ for c in octets:
+ checksum = (checksum + ord(c)) & 0xff
+ checksum = (0x100 - checksum) & 0xff
+ octets.append(chr(checksum))
+
+ line = ":%s\n" % ("".join(octets).encode('hex').upper(),)
+ yield line
+
+def main():
+ # Parse arguments
+ verbose = False
+ output_prefix = None
+
+ try:
+ optlist, args = getopt.getopt(sys.argv[1:], 'vo:')
+ except getopt.GetoptError, exc:
+ print >>sys.stderr, "error: %s" % (str(exc),)
+ exit_usage()
+
+ if len(args) != 1:
+ print >>sys.stderr, "error: this program takes exactly one argument"
+ exit_usage()
+ (input_path,) = args
+
+ for (opt, optarg) in optlist:
+ if opt == '-v':
+ verbose = True
+ elif opt == "-o":
+ output_prefix = optarg
+ else:
+ assert 0 # This should never happen
+
+ if output_prefix is None:
+ print >>sys.stderr, "error: no output prefix specified"
+ exit_usage()
+
+ warning_count = 0
+ cpucs_address = None # Address of the CPUCS register
+ cpu_reset = 0 # Value of the 8051RES bit (1 = CPU in RESET)
+ stage_number = 0
+ input_file = open(input_path, "rb")
+ chunk = CSPTChunk.fromfile(input_file)
+ output_file = None
+ while chunk is not None:
+# if verbose:
+# print repr(chunk)
+
+ if chunk.request != 0xa0:
+ print >>sys.stderr, "warning: skipping unrecognized device request 0x%02x" % (chunk.request,)
+ warning_count += 1
+ continue
+
+ # FIXME: What do these bits mean? Do they need to be set this way?
+ if chunk.dummies != (0x20, 0x40000000, 0x75, 0x900000, 0xf):
+ print >>sys.stderr, "warning: file format not entirely understood: %r" % (chunk.dummies,)
+ warning_count += 1
+
+ # The first request in the input file should be to write a single byte
+ # to the CPUCS register. Prior to uploading the firmware, the 8051 CPU
+ # core must be put into a RESET state by setting CPUCS.0 = 1. When the
+ # upload is complete, the 8051 core must be restarted by setting
+ # CPUCS.0 = 0. This process is done once for a single-stage loader, or
+ # twice for a two-stage loader.
+
+ if cpucs_address is None: # First write
+ if len(chunk.data) != 1:
+ print >>sys.stderr, "warning: skipping multi-byte write past CPUCS" % (chunk.request,)
+ warning_count += 1
+ continue
+
+ if not ord(chunk.data[0]) & 1: # The reset bit should be
+ print >>sys.stderr, "warning: first write clears CPUCS.0 instead of setting it" % (chunk.request,)
+ warning_count += 1
+
+ cpucs_address = chunk.address
+ if verbose:
+ print "Detected CPUCS address: 0x%04x" % (cpucs_address,)
+
+ if chunk.address == cpucs_address:
+ if len(chunk.data) != 1:
+ print >>sys.stderr, "warning: skipping multi-byte write past CPUCS" % (chunk.request,)
+ warning_count += 1
+ continue
+
+ new_reset_bit = ord(chunk.data[0]) & 1 # 8051RES = CPUCS.0
+ if new_reset_bit == cpu_reset:
+ print >>sys.stderr, "warning: skipping write to CPUCS that doesn't change CPUCS.0" % (chunk.request,)
+ warning_count += 1
+ continue
+
+ if not cpu_reset:
+ # Open a new file
+ stage_number += 1
+ filename = output_prefix + "_%d.ihx" % (stage_number,)
+ if verbose:
+ print "Writing stage %d to %s" % (stage_number, filename,)
+ output_file = open(filename, "w")
+ output_file.write("# Extracted using %s\n" % (PROGRAM_VERSION,))
+ output_file.write("# Stage %d from %s\n" % (stage_number, os.path.basename(input_path).replace("\n", " ")))
+ else:
+ # Write the end-of-file marker and close current file
+ output_file.write(":00000001FF\n")
+ output_file.close()
+ output_file = None
+
+ cpu_reset = new_reset_bit
+
+ else:
+
+ for line in make_ihx_data_lines(chunk.address, chunk.data):
+ output_file.write(line)
+
+ chunk = CSPTChunk.fromfile(input_file)
+
+ if cpu_reset:
+ print >>sys.stderr, "warning: input file finished without clearing CPUCS.0"
+ warning_count += 1
+
+ input_file.close()
+
+ if warning_count > 0:
+ print >>sys.stderr, "Finished with %d warnings." % (warning_count,)
+ sys.exit(1)
+ else:
+ if verbose:
+ print "Finished with no warnings."
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
+
+
+# vim:set ts=4 sw=4 sts=4 expandtab: