]> sigrok.org Git - libsigrokdecode.git/commitdiff
Backport current PDs from git mainline.
authorUwe Hermann <redacted>
Mon, 26 Dec 2016 21:20:08 +0000 (22:20 +0100)
committerUwe Hermann <redacted>
Mon, 26 Dec 2016 21:20:08 +0000 (22:20 +0100)
88 files changed:
decoders/adns5020/pd.py
decoders/am230x/pd.py
decoders/arm_etmv3/pd.py
decoders/arm_itm/pd.py
decoders/arm_tpiu/pd.py
decoders/aud/__init__.py [new file with mode: 0644]
decoders/aud/pd.py [new file with mode: 0644]
decoders/avr_isp/pd.py
decoders/avr_pdi/__init__.py [new file with mode: 0644]
decoders/avr_pdi/pd.py [new file with mode: 0644]
decoders/can/pd.py
decoders/common/__init__.py [new file with mode: 0644]
decoders/common/plugtrx/__init__.py [new file with mode: 0644]
decoders/common/plugtrx/mod.py [new file with mode: 0644]
decoders/common/sdcard/__init__.py [new file with mode: 0644]
decoders/common/sdcard/mod.py [new file with mode: 0644]
decoders/common/srdhelper/__init__.py [new file with mode: 0644]
decoders/common/srdhelper/mod.py [new file with mode: 0644]
decoders/dcf77/pd.py
decoders/dmx512/__init__.py [new file with mode: 0644]
decoders/dmx512/pd.py [new file with mode: 0644]
decoders/ds1307/pd.py
decoders/edid/pd.py
decoders/eeprom24xx/pd.py
decoders/em4100/pd.py
decoders/em4305/__init__.py [new file with mode: 0644]
decoders/em4305/pd.py [new file with mode: 0644]
decoders/gpib/__init__.py [new file with mode: 0644]
decoders/gpib/pd.py [new file with mode: 0644]
decoders/guess_bitrate/pd.py
decoders/i2c/pd.py
decoders/i2cdemux/pd.py
decoders/i2cfilter/pd.py
decoders/i2s/pd.py
decoders/ir_nec/pd.py
decoders/ir_rc5/pd.py
decoders/jitter/pd.py
decoders/jtag/pd.py
decoders/jtag_stm32/pd.py
decoders/lm75/pd.py
decoders/lpc/pd.py
decoders/maxim_ds28ea00/pd.py
decoders/mdio/__init__.py
decoders/mdio/pd.py
decoders/midi/lists.py
decoders/midi/pd.py
decoders/mlx90614/pd.py
decoders/modbus/pd.py
decoders/mrf24j40/pd.py
decoders/mxc6225xu/pd.py
decoders/nrf24l01/pd.py
decoders/nunchuk/pd.py
decoders/onewire_link/pd.py
decoders/onewire_network/pd.py
decoders/pan1321/pd.py
decoders/ps2/__init__.py [new file with mode: 0644]
decoders/ps2/pd.py [new file with mode: 0644]
decoders/pwm/pd.py
decoders/qi/pd.py
decoders/rfm12/pd.py
decoders/rgb_led_spi/pd.py
decoders/rgb_led_ws281x/__init__.py [new file with mode: 0644]
decoders/rgb_led_ws281x/pd.py [new file with mode: 0644]
decoders/rtc8564/pd.py
decoders/sdcard_sd/__init__.py
decoders/sdcard_sd/pd.py
decoders/sdcard_spi/pd.py
decoders/spdif/pd.py
decoders/spi/pd.py
decoders/spiflash/lists.py
decoders/spiflash/pd.py
decoders/ssi32/__init__.py [new file with mode: 0644]
decoders/ssi32/pd.py [new file with mode: 0644]
decoders/stepper_motor/pd.py
decoders/swd/pd.py
decoders/t55xx/__init__.py [new file with mode: 0644]
decoders/t55xx/pd.py [new file with mode: 0644]
decoders/tca6408a/pd.py
decoders/timing/pd.py
decoders/tlc5620/pd.py
decoders/uart/pd.py
decoders/usb_power_delivery/__init__.py
decoders/usb_power_delivery/pd.py
decoders/usb_request/pd.py
decoders/wiegand/__init__.py [new file with mode: 0644]
decoders/wiegand/pd.py [new file with mode: 0644]
decoders/xfp/pd.py
decoders/z80/pd.py

index bcdb52a0c774e55854d66d7eb4189fd3240e1941..972227e96c93a3c05b1493817ae844179833e131 100644 (file)
@@ -45,7 +45,7 @@ class Decoder(srd.Decoder):
     name = 'ADNS-5020'
     longname = 'Avago ADNS-5020 optical mouse sensor'
     desc = 'Bidirectional command and data over an SPI-like protocol.'
-    license = 'gplv2'
+    license = 'gplv2+'
     inputs = ['spi']
     outputs = ['adns5020']
     annotations = (
@@ -59,7 +59,7 @@ class Decoder(srd.Decoder):
         ('warnings', 'Warnings', (2,)),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.ss_cmd, self.es_cmd = 0, 0
         self.mosi_bytes = []
 
index 2b54cde928f09b3a8f2fdb8257142aa2c5f405c0..483dc9be9792497b471c11c46cefa5fc21445b0d 100644 (file)
@@ -123,7 +123,7 @@ class Decoder(srd.Decoder):
             checksum += self.bits2num(bitlist[i-8:i])
         return checksum % 256
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.samplerate = None
         self.reset()
 
index 0083fdb2873db4b2f869b429f4497a126ae4d1dc..367ceb8aaecf69742e1ef91305def83ea5696fc6 100644 (file)
@@ -170,7 +170,7 @@ class Decoder(srd.Decoder):
             'default': 'alternative', 'values': ('alternative', 'original')},
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.buf = []
         self.syncbuf = []
         self.prevsample = 0
index f6a021226bb93aefc5b8d47c7d58c75e11f23eeb..384999c36dd920b1d7e006981294b4b2f771cbc8 100644 (file)
@@ -80,7 +80,7 @@ class Decoder(srd.Decoder):
         ('function', 'Current function', (11,)),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.buf = []
         self.syncbuf = []
         self.swpackets = {}
index ba01aa08f06f865d41eb17e4acc688f26a1222f7..d111311852aa4624eae86571aad9155dca1d60fc 100644 (file)
@@ -42,12 +42,12 @@ class Decoder(srd.Decoder):
         ('data', 'Stream data', (1,)),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.buf = []
         self.syncbuf = []
         self.prevsample = 0
         self.stream = 0
-        self.stream_ss = None
+        self.ss_stream = None
         self.bytenum = 0
 
     def start(self):
@@ -57,14 +57,14 @@ class Decoder(srd.Decoder):
     def stream_changed(self, ss, stream):
         if self.stream != stream:
             if self.stream != 0:
-                self.put(self.stream_ss, ss, self.out_ann,
-                         [0, ["Stream %d" % self.stream, "S%d" % self.stream]])
+                self.put(self.ss_stream, ss, self.out_ann,
+                         [0, ['Stream %d' % self.stream, 'S%d' % self.stream]])
             self.stream = stream
-            self.stream_ss = ss
+            self.ss_stream = ss
 
     def emit_byte(self, ss, es, byte):
         if self.stream == self.options['stream']:
-            self.put(ss, es, self.out_ann, [1, ["0x%02x" % byte]])
+            self.put(ss, es, self.out_ann, [1, ['0x%02x' % byte]])
             self.put(ss, es, self.out_python, ['DATA', 0, (byte, [])])
 
     def process_frame(self, buf):
diff --git a/decoders/aud/__init__.py b/decoders/aud/__init__.py
new file mode 100644 (file)
index 0000000..aaa31ff
--- /dev/null
@@ -0,0 +1,32 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 fenugrec <fenugrec users.sourceforge.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 2 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+'''
+This protocol decoder decodes the AUD (Advanced User Debugger) interface
+of certain Renesas / Hitachi microcontrollers, when set in Branch Trace mode.
+
+AUD has two modes, this PD currently only supports "Branch Trace" mode.
+
+Details:
+http://www.renesas.eu/products/mpumcu/superh/sh7050/sh7058/Documentation.jsp
+("rej09b0046 - SH7058 Hardware manual")
+'''
+
+from .pd import Decoder
diff --git a/decoders/aud/pd.py b/decoders/aud/pd.py
new file mode 100644 (file)
index 0000000..a5330f9
--- /dev/null
@@ -0,0 +1,114 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 fenugrec <fenugrec users.sourceforge.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 2 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# TODO:
+#  - Annotations are very crude and could be improved.
+#  - Annotate every nibble? Would give insight on interrupted shifts.
+#  - Annotate invalid "command" nibbles while SYNC==1?
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'aud'
+    name = 'AUD'
+    longname = 'Advanced User Debugger'
+    desc = 'Renesas/Hitachi Advanced User Debugger (AUD) protocol.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['aud']
+    channels = (
+        {'id': 'audck', 'name': 'AUDCK', 'desc': 'AUD clock'},
+        {'id': 'naudsync', 'name': 'nAUDSYNC', 'desc': 'AUD sync'},
+        {'id': 'audata3', 'name': 'AUDATA3', 'desc': 'AUD data line 3'},
+        {'id': 'audata2', 'name': 'AUDATA2', 'desc': 'AUD data line 2'},
+        {'id': 'audata1', 'name': 'AUDATA1', 'desc': 'AUD data line 1'},
+        {'id': 'audata0', 'name': 'AUDATA0', 'desc': 'AUD data line 0'},
+    )
+    annotations = (
+        ('dest', 'Destination address'),
+    )
+
+    def __init__(self):
+        self.ncnt = 0
+        self.nmax = 0
+        self.addr = 0
+        self.lastaddr = 0
+        self.samplenum = 0
+        self.oldclk = 0
+        self.ss = 0
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def putx(self, data):
+        self.put(self.ss, self.samplenum, self.out_ann, data)
+
+    def find_clk_edge(self, clk, sync, datapins):
+        # Ignore sample if there's no edge.
+        if clk == self.oldclk:
+            return
+        self.oldclk = clk
+        # Ignore falling edges.
+        if clk == 0:
+            return
+
+        # Reconstruct nibble.
+        nib = 0
+        for i in range(4):
+            nib |= datapins[3-i] << i
+
+        # sync == 1: annotate if finished; update cmd.
+        # TODO: Annotate idle level (nibble = 0x03 && SYNC=1).
+        if sync == 1:
+            if (self.ncnt == self.nmax) and (self.nmax != 0):
+                # Done shifting an address: annotate.
+                self.putx([0, ['0x%08X' % self.addr]])
+                self.lastaddr = self.addr
+
+            self.ncnt = 0
+            self.addr = self.lastaddr
+            self.ss = self.samplenum
+            if nib == 0x08:
+                self.nmax = 1
+            elif nib == 0x09:
+                self.nmax = 2
+            elif nib == 0x0a:
+                self.nmax = 4
+            elif nib == 0x0b:
+                self.nmax = 8
+            else:
+                # Undefined or idle.
+                self.nmax = 0
+        else:
+            # sync == 0, valid cmd: start or continue shifting in nibbles.
+            if (self.nmax > 0):
+                # Clear tgt nibble.
+                self.addr &= ~(0x0F << (self.ncnt * 4))
+                # Set nibble.
+                self.addr |= nib << (self.ncnt * 4)
+                self.ncnt += 1
+
+    def decode(self, ss, es, data):
+        for (self.samplenum, pins) in data:
+            clk = pins[0]
+            sync = pins[1]
+            d = pins[2:]
+            self.find_clk_edge(clk, sync, d)
index 0914b5b347101b1b16f34b7ee75fcf985f580e7e..16e3d70685f3678eb8331547dde04156f8adfcce 100644 (file)
@@ -51,7 +51,7 @@ class Decoder(srd.Decoder):
         ('dev', 'Device', (9,)),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.state = 'IDLE'
         self.mosi_bytes, self.miso_bytes = [], []
         self.ss_cmd, self.es_cmd = 0, 0
diff --git a/decoders/avr_pdi/__init__.py b/decoders/avr_pdi/__init__.py
new file mode 100644 (file)
index 0000000..ebe647b
--- /dev/null
@@ -0,0 +1,41 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Gerhard Sittig <gerhard.sittig@gmx.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 2 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/>.
+##
+
+'''
+PDI (Program and Debug Interface) is an Atmel proprietary interface for
+external programming and on-chip debugging of the device. See the Atmel
+Application Note AVR1612 "PDI programming driver" and the "Program and
+Debug Interface" section in the Xmega A manual for details.
+
+The protocol uses two pins: the RESET pin and one dedicated DATA pin.
+The RESET pin provides a clock, the DATA pin communicates serial frames
+with a start bit, eight data bits, an even parity bit, and two stop bits.
+Data communication is bidirectional and half duplex, the device will
+provide response data after reception of a respective request.
+
+Protocol frames communicate opcodes and their arguments, which provides
+random and sequential access to the device's address space. By accessing
+the registers of internal peripherals, especially the NVM controller,
+it's possible to identify the device, read from and write to several
+kinds of memory (signature rows, fuses and lock bits, internal flash and
+EEPROM, memory mapped peripherals), and to control execution of software
+on the device.
+'''
+
+from .pd import Decoder
diff --git a/decoders/avr_pdi/pd.py b/decoders/avr_pdi/pd.py
new file mode 100644 (file)
index 0000000..1568fdf
--- /dev/null
@@ -0,0 +1,586 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2011-2014 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2016 Gerhard Sittig <gerhard.sittig@gmx.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 2 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/>.
+##
+
+# Note the implementation details:
+#
+# Although the Atmel literature suggests (does not explicitly mandate,
+# but shows in diagrams) that two stop bits are used in the protocol,
+# the decoder loses synchronization with ATxmega generated responses
+# when it expects more than one stop bit. Since the chip's hardware is
+# fixed, this is not an implementation error in some programmer software.
+# Since this is a protocol decoder which does not participate in the
+# communication (does not actively send data), we can read the data
+# stream with one stop bit, and transparently keep working when two
+# are used.
+#
+# Annotations in the UART fields level differ from Atmel literature.
+# Wrong parity bits are referred to as "parity error". Low stop bits are
+# referred to as "frame error".
+#
+# The PDI component in the device starts disabled. Enabling PDI
+# communication is done by raising DATA and clocking RESET with a
+# minimum frequency. PDI communication automatically gets disabled when
+# RESET "is inactive" for a certain period of time. The specific timing
+# conditions are rather fuzzy in the literature (phrased weakly), and
+# are device dependent (refer to the minumum RESET pulse width). This
+# protocol decoder implementation internally prepares for but currently
+# does not support these enable and disable phases. On the one hand it
+# avoids excess external dependencies or wrong results for legal input
+# data. On the other hand the decoder works when input streams start in
+# the middle of an established connection.
+#
+# Communication peers detect physical collisions. The decoder can't.
+# Upon collisions, a peer will cease any subsequent transmission, until
+# a BREAK is seen. Synchronization can get enforced by sending two BREAK
+# conditions. The first will cause a collision, the second will re-enable
+# the peer. The decoder has no concept of physical collisions. It stops
+# the interpretation of instructions when BREAK is seen, and assumes
+# that a new instruction will start after BREAK.
+#
+# This protocol decoder only supports PDI communication over UART frames.
+# It lacks support for PDI over JTAG. This would require separation into
+# multiple protocol decoder layers (UART physical, JTAG physical, PDI
+# instructions, optionally device support on top of PDI. There is some
+# more potential for future extensions:
+# - The JTAG physical has dedicated TX and RX directions. This decoder
+#   only picks up communicated bytes but does not check which "line"
+#   they are communicated on (not applicable to half duplex UART).
+# - PDI over JTAG uses "special frame error" conditions to communicate
+#   additional symbols: BREAK (0xBB with parity 1), DELAY (0xDB with
+#   parity 1), and EMPTY (0xEB with parity 1).
+# - Another "device support" layer might interpret device specific
+#   timings, and might map addresses used in memory access operations
+#   to component names, or even register names and bit fields(?). It's
+#   quite deep a rabbithole though...
+
+import sigrokdecode as srd
+from collections import namedtuple
+
+class Ann:
+    '''Annotation and binary output classes.'''
+    (
+        BIT, START, DATA, PARITY_OK, PARITY_ERR,
+        STOP_OK, STOP_ERR, BREAK,
+        OPCODE, DATA_PROG, DATA_DEV, PDI_BREAK,
+        ENABLE, DISABLE, COMMAND,
+    ) = range(15)
+    (
+        BIN_BYTES,
+    ) = range(1)
+
+Bit = namedtuple('Bit', 'val ss es')
+
+class PDI:
+    '''PDI protocol instruction opcodes, and operand formats.'''
+    (
+        OP_LDS, OP_LD, OP_STS, OP_ST,
+        OP_LDCS, OP_REPEAT, OP_STCS, OP_KEY,
+    ) = range(8)
+    pointer_format_nice = [
+        '*(ptr)',
+        '*(ptr++)',
+        'ptr',
+        'ptr++ (rsv)',
+    ]
+    pointer_format_terse = [
+        '*p',
+        '*p++',
+        'p',
+        '(rsv)',
+    ]
+    ctrl_reg_name = {
+        0: 'status',
+        1: 'reset',
+        2: 'ctrl',
+    }
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'avr_pdi'
+    name = 'AVR PDI'
+    longname = 'Atmel Program and Debug Interface'
+    desc = 'Atmel proprietary interface for the ATxmega MCU.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['pdi']
+    channels = (
+        {'id': 'reset', 'name': 'RESET', 'desc': 'RESET / PDI_CLK'},
+        {'id': 'data', 'name': 'DATA', 'desc': 'PDI_DATA'},
+    )
+    annotations = (
+        ('uart-bit', 'UART bit'),
+        ('start-bit', 'Start bit'),
+        ('data-bit', 'Data bit'),
+        ('parity-ok', 'Parity OK bit'),
+        ('parity-err', 'Parity error bit'),
+        ('stop-ok', 'Stop OK bit'),
+        ('stop-err', 'Stop error bit'),
+        ('break', 'BREAK condition'),
+        ('opcode', 'Instruction opcode'),
+        ('data-prog', 'Programmer data'),
+        ('data-dev', 'Device data'),
+        ('pdi-break', 'BREAK at PDI level'),
+        ('enable', 'Enable PDI'),
+        ('disable', 'Disable PDI'),
+        ('cmd-data', 'PDI command with data'),
+    )
+    annotation_rows = (
+        ('uart_bits', 'UART bits', (Ann.BIT,)),
+        ('uart_fields', 'UART fields', (Ann.START, Ann.DATA, Ann.PARITY_OK,
+            Ann.PARITY_ERR, Ann.STOP_OK, Ann.STOP_ERR, Ann.BREAK)),
+        ('pdi_fields', 'PDI fields', (Ann.OPCODE, Ann.DATA_PROG, Ann.DATA_DEV,
+            Ann.PDI_BREAK)),
+        ('pdi_cmds', 'PDI Cmds', (Ann.ENABLE, Ann.DISABLE, Ann.COMMAND)),
+    )
+    binary = (
+        ('bytes', 'PDI protocol bytes'),
+    )
+
+    def __init__(self):
+        self.samplerate = None
+        # Detect input changes and clock edges.
+        self.prev_pins = None
+        self.prev_clock = None
+        self.clear_state()
+
+    def clear_state(self):
+        # Track bit times and bit values.
+        self.ss_last_fall = None
+        self.data_sample = None
+        self.ss_curr_fall = None
+        # Collect UART frame bits into byte values.
+        self.bits = []
+        self.zero_count = 0
+        self.zero_ss = None
+        self.break_ss = None
+        self.break_es = None
+        self.clear_insn()
+
+    def clear_insn(self):
+        # Collect instructions and their arguments,
+        # properties of the current instructions.
+        self.insn_rep_count = 0
+        self.insn_opcode = None
+        self.insn_wr_counts = []
+        self.insn_rd_counts = []
+        # Accumulation of data items as bytes pass by.
+        self.insn_dat_bytes = []
+        self.insn_dat_count = 0
+        self.insn_ss_data = None
+        # Next layer "commands", instructions plus operands.
+        self.cmd_ss = None
+        self.cmd_insn_parts_nice = []
+        self.cmd_insn_parts_terse = []
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.out_binary = self.register(srd.OUTPUT_BINARY)
+
+    def put_ann_bit(self, bit_nr, ann_idx):
+        b = self.bits[bit_nr]
+        self.put(b.ss, b.es, self.out_ann, [ann_idx, [str(b.val)]])
+
+    def put_ann_data(self, bit_nr, ann_data):
+        b = self.bits[bit_nr]
+        self.put(b.ss, b.es, self.out_ann, ann_data)
+
+    def put_ann_row_val(self, ss, es, row, value):
+        self.put(ss, es, self.out_ann, [row, value])
+
+    def put_bin_bytes(self, ss, es, row, value):
+        self.put(ss, es, self.out_binary, [row, value])
+
+    def handle_byte(self, ss, es, byteval):
+        '''Handle a byte at the PDI protocol layer.'''
+
+        # Handle BREAK conditions, which will abort any
+        # potentially currently executing instruction.
+        is_break = byteval is None
+        if is_break:
+            self.cmd_insn_parts_nice.append('BREAK')
+            self.cmd_insn_parts_terse.append('BRK')
+            self.insn_rep_count = 0
+            # Will FALLTHROUGH to "end of instruction" below.
+
+        # Decode instruction opcodes and argument sizes
+        # from the first byte of a transaction.
+        if self.insn_opcode is None and not is_break:
+            opcode = (byteval & 0xe0) >> 5
+            arg30 = byteval & 0x0f
+            arg32 = (byteval & 0x0c) >> 2
+            arg10 = byteval & 0x03
+            self.insn_opcode = opcode
+            self.cmd_ss = ss
+            mnemonics = None
+            if opcode == PDI.OP_LDS:
+                # LDS: load data, direct addressing.
+                # Writes an address, reads a data item.
+                width_addr = arg32 + 1
+                width_data = arg10 + 1
+                self.insn_wr_counts = [width_addr]
+                self.insn_rd_counts = [width_data]
+                mnemonics = [
+                    'Insn: LDS a{:d}, m{:d}'.format(width_addr, width_data),
+                    'LDS a{:d}, m{:d}'.format(width_addr, width_data), 'LDS',
+                ]
+                self.cmd_insn_parts_nice = ['LDS']
+                self.cmd_insn_parts_terse = ['LDS']
+            elif opcode == PDI.OP_LD:
+                # LD: load data, indirect addressing.
+                # Reads a data item, with optional repeat.
+                ptr_txt = PDI.pointer_format_nice[arg32]
+                ptr_txt_terse = PDI.pointer_format_terse[arg32]
+                width_data = arg10 + 1
+                self.insn_wr_counts = []
+                self.insn_rd_counts = [width_data]
+                if self.insn_rep_count:
+                    self.insn_rd_counts.extend(self.insn_rep_count * [width_data])
+                    self.insn_rep_count = 0
+                mnemonics = [
+                    'Insn: LD {:s} m{:d}'.format(ptr_txt, width_data),
+                    'LD {:s} m{:d}'.format(ptr_txt, width_data), 'LD',
+                ]
+                self.cmd_insn_parts_nice = ['LD', ptr_txt]
+                self.cmd_insn_parts_terse = ['LD', ptr_txt_terse]
+            elif opcode == PDI.OP_STS:
+                # STS: store data, direct addressing.
+                # Writes an address, writes a data item.
+                width_addr = arg32 + 1
+                width_data = arg10 + 1
+                self.insn_wr_counts = [width_addr, width_data]
+                self.insn_rd_counts = []
+                mnemonics = [
+                    'Insn: STS a{:d}, i{:d}'.format(width_addr, width_data),
+                    'STS a{:d}, i{:d}'.format(width_addr, width_data), 'STS',
+                ]
+                self.cmd_insn_parts_nice = ['STS']
+                self.cmd_insn_parts_terse = ['STS']
+            elif opcode == PDI.OP_ST:
+                # ST: store data, indirect addressing.
+                # Writes a data item, with optional repeat.
+                ptr_txt = PDI.pointer_format_nice[arg32]
+                ptr_txt_terse = PDI.pointer_format_terse[arg32]
+                width_data = arg10 + 1
+                self.insn_wr_counts = [width_data]
+                self.insn_rd_counts = []
+                if self.insn_rep_count:
+                    self.insn_wr_counts.extend(self.insn_rep_count * [width_data])
+                    self.insn_rep_count = 0
+                mnemonics = [
+                    'Insn: ST {:s} i{:d}'.format(ptr_txt, width_data),
+                    'ST {:s} i{:d}'.format(ptr_txt, width_data), 'ST',
+                ]
+                self.cmd_insn_parts_nice = ['ST', ptr_txt]
+                self.cmd_insn_parts_terse = ['ST', ptr_txt_terse]
+            elif opcode == PDI.OP_LDCS:
+                # LDCS: load control/status.
+                # Loads exactly one byte.
+                reg_num = arg30
+                reg_txt = PDI.ctrl_reg_name.get(reg_num, 'r{:d}'.format(reg_num))
+                reg_txt_terse = '{:d}'.format(reg_num)
+                self.insn_wr_counts = []
+                self.insn_rd_counts = [1]
+                mnemonics = [
+                    'Insn: LDCS {:s}, m1'.format(reg_txt),
+                    'LDCS {:s}, m1'.format(reg_txt), 'LDCS',
+                ]
+                self.cmd_insn_parts_nice = ['LDCS', reg_txt]
+                self.cmd_insn_parts_terse = ['LDCS', reg_txt_terse]
+            elif opcode == PDI.OP_STCS:
+                # STCS: store control/status.
+                # Writes exactly one byte.
+                reg_num = arg30
+                reg_txt = PDI.ctrl_reg_name.get(reg_num, 'r{:d}'.format(reg_num))
+                reg_txt_terse = '{:d}'.format(reg_num)
+                self.insn_wr_counts = [1]
+                self.insn_rd_counts = []
+                mnemonics = [
+                    'Insn: STCS {:s}, i1'.format(reg_txt),
+                    'STCS {:s}, i1'.format(reg_txt), 'STCS',
+                ]
+                self.cmd_insn_parts_nice = ['STCS', reg_txt]
+                self.cmd_insn_parts_terse = ['STCS', reg_txt_terse]
+            elif opcode == PDI.OP_REPEAT:
+                # REPEAT: sets repeat count for the next instruction.
+                # Reads repeat count from following bytes.
+                width_data = arg10 + 1
+                self.insn_wr_counts = [width_data]
+                self.insn_rd_counts = []
+                mnemonics = [
+                    'Insn: REPEAT i{:d}'.format(width_data),
+                    'REPEAT i{:d}'.format(width_data), 'REP',
+                ]
+                self.cmd_insn_parts_nice = ['REPEAT']
+                self.cmd_insn_parts_terse = ['REP']
+            elif opcode == PDI.OP_KEY:
+                # KEY: set activation key (enables PDIBUS mmap access).
+                # Writes a sequence of 8 bytes, fixed length.
+                width_data = 8
+                self.insn_wr_counts = [width_data]
+                self.insn_rd_counts = []
+                mnemonics = [
+                    'Insn: KEY i{:d}'.format(width_data),
+                    'KEY i{:d}'.format(width_data), 'KEY',
+                ]
+                self.cmd_insn_parts_nice = ['KEY']
+                self.cmd_insn_parts_terse = ['KEY']
+
+            # Emit an annotation for the instruction opcode.
+            self.put_ann_row_val(ss, es, Ann.OPCODE, mnemonics)
+
+            # Prepare to write/read operands/data bytes.
+            self.insn_dat_bytes = []
+            if self.insn_wr_counts:
+                self.insn_dat_count = self.insn_wr_counts[0]
+                return
+            if self.insn_rd_counts:
+                self.insn_dat_count = self.insn_rd_counts[0]
+                return
+            # FALLTHROUGH.
+            # When there are no operands or data bytes to read,
+            # then fall through to the end of the instruction
+            # handling below (which emits annotations).
+
+        # Read bytes which carry operands (addresses, immediates)
+        # or data values for memory access.
+        if self.insn_dat_count and not is_break:
+
+            # Accumulate received bytes until another multi byte
+            # data item is complete.
+            if not self.insn_dat_bytes:
+                self.insn_ss_data = ss
+            self.insn_dat_bytes.append(byteval)
+            self.insn_dat_count -= 1
+            if self.insn_dat_count:
+                return
+
+            # Determine the data item's duration and direction,
+            # "consume" its length spec (to simplify later steps).
+            data_ss = self.insn_ss_data
+            data_es = es
+            if self.insn_wr_counts:
+                data_ann = Ann.DATA_PROG
+                data_width = self.insn_wr_counts.pop(0)
+            elif self.insn_rd_counts:
+                data_ann = Ann.DATA_DEV
+                data_width = self.insn_rd_counts.pop(0)
+
+            # PDI communicates multi-byte data items in little endian
+            # order. Get a nice textual representation of the number,
+            # wide and narrow for several zoom levels.
+            self.insn_dat_bytes.reverse()
+            data_txt_digits = ''.join(['{:02x}'.format(b) for b in self.insn_dat_bytes])
+            data_txt_hex = '0x' + data_txt_digits
+            data_txt_prefix = 'Data: ' + data_txt_hex
+            data_txts = [data_txt_prefix, data_txt_hex, data_txt_digits]
+            self.insn_dat_bytes = []
+
+            # Emit an annotation for the data value.
+            self.put_ann_row_val(data_ss, data_es, data_ann, data_txts)
+
+            # Collect detailled information which describes the whole
+            # command when combined (for a next layer annotation,
+            # spanning the complete command).
+            self.cmd_insn_parts_nice.append(data_txt_hex)
+            self.cmd_insn_parts_terse.append(data_txt_digits)
+
+            # Send out write data first until exhausted,
+            # then drain expected read data.
+            if self.insn_wr_counts:
+                self.insn_dat_count = self.insn_wr_counts[0]
+                return
+            if self.insn_rd_counts:
+                self.insn_dat_count = self.insn_rd_counts[0]
+                return
+
+            # FALLTHROUGH.
+            # When all operands and data bytes were seen,
+            # terminate the inspection of the instruction.
+
+        # Postprocess the instruction after its operands were seen.
+        cmd_es = es
+        cmd_txt_nice = ' '.join(self.cmd_insn_parts_nice)
+        cmd_txt_terse = ' '.join(self.cmd_insn_parts_terse)
+        cmd_txts = [cmd_txt_nice, cmd_txt_terse]
+        self.put_ann_row_val(self.cmd_ss, cmd_es, Ann.COMMAND, cmd_txts)
+        if self.insn_opcode == PDI.OP_REPEAT and not is_break:
+            # The last communicated data item is the repeat
+            # count for the next instruction (i.e. it will
+            # execute N+1 times when "REPEAT N" is specified).
+            count = int(self.cmd_insn_parts_nice[-1], 0)
+            self.insn_rep_count = count
+
+        # Have the state for instruction decoding cleared, but make sure
+        # to carry over REPEAT count specs between instructions. They
+        # start out as zero, will be setup by REPEAT instructions, need
+        # to get passed to the instruction which follows REPEAT. The
+        # instruction which sees a non-zero repeat count which will
+        # consume the counter and drop it to zero, then the counter
+        # remains at zero until the next REPEAT instruction.
+        save_rep_count = self.insn_rep_count
+        self.clear_insn()
+        self.insn_rep_count = save_rep_count
+
+    def handle_bits(self, ss, es, bitval):
+        '''Handle a bit at the UART layer.'''
+
+        # Concentrate annotation literals here for easier maintenance.
+        ann_class_text = {
+            Ann.START: ['Start bit', 'Start', 'S'],
+            Ann.PARITY_OK: ['Parity OK', 'Par OK', 'P'],
+            Ann.PARITY_ERR: ['Parity error', 'Par ERR', 'PE'],
+            Ann.STOP_OK: ['Stop bit', 'Stop', 'T'],
+            Ann.STOP_ERR: ['Stop bit error', 'Stop ERR', 'TE'],
+            Ann.BREAK: ['Break condition', 'BREAK', 'BRK'],
+        }
+        def put_uart_field(bitpos, annclass):
+            self.put_ann_data(bitpos, [annclass, ann_class_text[annclass]])
+
+        # The number of bits which form one UART frame. Note that
+        # the decoder operates with only one stop bit.
+        frame_bitcount = 1 + 8 + 1 + 1
+
+        # Detect adjacent runs of all-zero bits. This is meant
+        # to cope when BREAK conditions appear at any arbitrary
+        # position, it need not be "aligned" to an UART frame.
+        if bitval == 1:
+            self.zero_count = 0
+        elif bitval == 0:
+            if not self.zero_count:
+                self.zero_ss = ss
+            self.zero_count += 1
+            if self.zero_count == frame_bitcount:
+                self.break_ss = self.zero_ss
+
+        # BREAK conditions are _at_minimum_ the length of a UART frame, but
+        # can span an arbitrary number of bit times. Track the "end sample"
+        # value of the last low bit we have seen, and emit the annotation only
+        # after the line went idle (high) again. Pass BREAK to the upper layer
+        # as well. When the line is low, BREAK still is pending. When the line
+        # is high, the current bit cannot be START, thus return from here.
+        if self.break_ss is not None:
+            if bitval == '0':
+                self.break_es = es
+                return
+            self.put(self.break_ss, self.break_es, self.out_ann,
+                 [Ann.BREAK, ann_class_text[Ann.BREAK]])
+            self.handle_byte(self.break_ss, self.break_es, None)
+            self.break_ss = None
+            self.break_es = None
+            self.bits = []
+            return
+
+        # Ignore high bits when waiting for START.
+        if not self.bits and bitval == 1:
+            return
+
+        # Store individual bits and their start/end sample numbers,
+        # until a complete frame was received.
+        self.bits.append(Bit(bitval, ss, es))
+        if len(self.bits) < frame_bitcount:
+            return
+
+        # Get individual fields of the UART frame.
+        bits_num = sum([b.val << pos for pos, b in enumerate(self.bits)])
+        if False:
+            # This logic could detect BREAK conditions which are aligned to
+            # UART frames. Which was obsoleted by the above detection at
+            # arbitrary positions. The code still can be useful to detect
+            # "other kinds of frame errors" which carry valid symbols for
+            # upper layers (the Atmel literature suggests "break", "delay",
+            # and "empty" symbols when PDI is communicated over different
+            # physical layers).
+            if bits_num == 0: # BREAK
+                self.break_ss = self.bits[0].ss
+                self.break_es = es
+                self.bits = []
+                return
+        start_bit = bits_num & 0x01; bits_num >>= 1
+        data_val = bits_num & 0xff; bits_num >>= 8
+        data_text = '{:02x}'.format(data_val)
+        parity_bit = bits_num & 0x01; bits_num >>= 1
+        stop_bit = bits_num & 0x01; bits_num >>= 1
+
+        # Check for frame errors. START _must_ have been low
+        # according to the above accumulation logic.
+        parity_ok = (bin(data_val).count('1') + parity_bit) % 2 == 0
+        stop_ok = stop_bit == 1
+        valid_frame = parity_ok and stop_ok
+
+        # Emit annotations.
+        for idx in range(frame_bitcount):
+            self.put_ann_bit(idx, Ann.BIT)
+        put_uart_field(0, Ann.START)
+        self.put(self.bits[1].ss, self.bits[8].es, self.out_ann,
+             [Ann.DATA, ['Data: ' + data_text, 'D: ' + data_text, data_text]])
+        put_uart_field(9, Ann.PARITY_OK if parity_ok else Ann.PARITY_ERR)
+        put_uart_field(10, Ann.STOP_OK if stop_ok else Ann.STOP_ERR)
+
+        # Emit binary data stream. Have bytes interpreted at higher layers.
+        if valid_frame:
+            byte_ss, byte_es = self.bits[0].ss, self.bits[-1].es
+            self.put_bin_bytes(byte_ss, byte_es, Ann.BIN_BYTES, bytes([data_val]))
+            self.handle_byte(byte_ss, byte_es, data_val)
+
+        # Reset internal state for the next frame.
+        self.bits = []
+
+    def find_clk_edge(self, samplenum, clock_pin, data_pin):
+        # Ignore the sample if the clock pin has not changed.
+        if clock_pin == self.prev_clock:
+            return
+        self.prev_clock = clock_pin
+
+        # Sample the data line on rising clock edges. Always, for TX and for
+        # RX bytes alike.
+        if clock_pin == 1:
+            self.data_sample = data_pin
+            return
+
+        # Falling clock edges are boundaries for bit slots. Inspect previously
+        # sampled bits on falling clock edges, when the start and end sample
+        # numbers were determined. Only inspect bit slots of known clock
+        # periods (avoid interpreting the DATA line when the "enabled" state
+        # has not yet been determined).
+        self.ss_last_fall = self.ss_curr_fall
+        self.ss_curr_fall = samplenum
+        if self.ss_last_fall is None:
+            return
+
+        # Have the past bit slot processed.
+        bit_ss, bit_es = self.ss_last_fall, self.ss_curr_fall
+        bit_val = self.data_sample
+        self.handle_bits(bit_ss, bit_es, bit_val)
+
+    def decode(self, ss, es, data):
+        for samplenum, pins in data:
+
+            # Ignore identical samples.
+            if self.prev_pins == pins:
+                continue
+            self.prev_pins = pins
+
+            # Have DATA processed at appropriate clock edges.
+            clock_pin, data_pin = pins[0], pins[1]
+            self.find_clk_edge(samplenum, clock_pin, data_pin)
index ffc3652d0cb891a863e04408e3eb68d0fb2478a4..c92877b556db18a0de9c05ccce71fded631549af 100644 (file)
@@ -64,7 +64,7 @@ class Decoder(srd.Decoder):
         ('fields', 'Fields', tuple(range(15)) + (16,)),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.samplerate = None
         self.reset_variables()
 
diff --git a/decoders/common/__init__.py b/decoders/common/__init__.py
new file mode 100644 (file)
index 0000000..c597431
--- /dev/null
@@ -0,0 +1,20 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
diff --git a/decoders/common/plugtrx/__init__.py b/decoders/common/plugtrx/__init__.py
new file mode 100644 (file)
index 0000000..8dd0822
--- /dev/null
@@ -0,0 +1,20 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Bert Vermeulen <bert@biot.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 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/>.
+##
+
+from .mod import *
diff --git a/decoders/common/plugtrx/mod.py b/decoders/common/plugtrx/mod.py
new file mode 100644 (file)
index 0000000..3d1b66d
--- /dev/null
@@ -0,0 +1,192 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Bert Vermeulen <bert@biot.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 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 module contains definitions for use by pluggable network adapters,
+# such as SFP, XFP etc.
+
+MODULE_ID = {
+    0x01: 'GBIC',
+    0x02: 'Integrated module/connector',
+    0x03: 'SFP',
+    0x04: '300-pin XBI',
+    0x05: 'XENPAK',
+    0x06: 'XFP',
+    0x07: 'XFF',
+    0x08: 'XFP-E',
+    0x09: 'XPAK',
+    0x0a: 'X2',
+}
+
+ALARM_THRESHOLDS = {
+    0:  'Temp high alarm',
+    2:  'Temp low alarm',
+    4:  'Temp high warning',
+    6:  'Temp low warning',
+    16: 'Bias high alarm',
+    18: 'Bias low alarm',
+    20: 'Bias high warning',
+    22: 'Bias low warning',
+    24: 'TX power high alarm',
+    26: 'TX power low alarm',
+    28: 'TX power high warning',
+    30: 'TX power low warning',
+    32: 'RX power high alarm',
+    34: 'RX power low alarm',
+    36: 'RX power high warning',
+    38: 'RX power low warning',
+    40: 'AUX 1 high alarm',
+    42: 'AUX 1 low alarm',
+    44: 'AUX 1 high warning',
+    46: 'AUX 1 low warning',
+    48: 'AUX 2 high alarm',
+    50: 'AUX 2 low alarm',
+    52: 'AUX 2 high warning',
+    54: 'AUX 2 low warning',
+}
+
+AD_READOUTS = {
+    0:  'Module temperature',
+    4:  'TX bias current',
+    6:  'Measured TX output power',
+    8:  'Measured RX input power',
+    10: 'AUX 1 measurement',
+    12: 'AUX 2 measurement',
+}
+
+GCS_BITS = [
+    'TX disable',
+    'Soft TX disable',
+    'MOD_NR',
+    'P_Down',
+    'Soft P_Down',
+    'Interrupt',
+    'RX_LOS',
+    'Data_Not_Ready',
+    'TX_NR',
+    'TX_Fault',
+    'TX_CDR not locked',
+    'RX_NR',
+    'RX_CDR not locked',
+]
+
+CONNECTOR = {
+    0x01:   'SC',
+    0x02:   'Fibre Channel style 1 copper',
+    0x03:   'Fibre Channel style 2 copper',
+    0x04:   'BNC/TNC',
+    0x05:   'Fibre Channel coax',
+    0x06:   'FiberJack',
+    0x07:   'LC',
+    0x08:   'MT-RJ',
+    0x09:   'MU',
+    0x0a:   'SG',
+    0x0b:   'Optical pigtail',
+    0x20:   'HSSDC II',
+    0x21:   'Copper pigtail',
+}
+
+TRANSCEIVER = [
+    # 10GB Ethernet
+    ['10GBASE-SR', '10GBASE-LR', '10GBASE-ER', '10GBASE-LRM', '10GBASE-SW',
+        '10GBASE-LW',   '10GBASE-EW'],
+    # 10GB Fibre Channel
+    ['1200-MX-SN-I', '1200-SM-LL-L', 'Extended Reach 1550 nm',
+        'Intermediate reach 1300 nm FP'],
+    # 10GB Copper
+    [],
+    # 10GB low speed
+    ['1000BASE-SX / 1xFC MMF', '1000BASE-LX / 1xFC SMF', '2xFC MMF',
+        '2xFC SMF', 'OC48-SR', 'OC48-IR', 'OC48-LR'],
+    # 10GB SONET/SDH interconnect
+    ['I-64.1r', 'I-64.1', 'I-64.2r', 'I-64.2', 'I-64.3', 'I-64.5'],
+    # 10GB SONET/SDH short haul
+    ['S-64.1', 'S-64.2a', 'S-64.2b', 'S-64.3a', 'S-64.3b', 'S-64.5a', 'S-64.5b'],
+    # 10GB SONET/SDH long haul
+    ['L-64.1', 'L-64.2a', 'L-64.2b', 'L-64.2c', 'L-64.3', 'G.959.1 P1L1-2D2'],
+    # 10GB SONET/SDH very long haul
+    ['V-64.2a', 'V-64.2b', 'V-64.3'],
+]
+
+SERIAL_ENCODING = [
+    '64B/66B',
+    '8B/10B',
+    'SONET scrambled',
+    'NRZ',
+    'RZ',
+]
+
+XMIT_TECH = [
+    '850 nm VCSEL',
+    '1310 nm VCSEL',
+    '1550 nm VCSEL',
+    '1310 nm FP',
+    '1310 nm DFB',
+    '1550 nm DFB',
+    '1310 nm EML'
+    '1550 nm EML'
+    'copper',
+]
+
+CDR = [
+    '9.95Gb/s',
+    '10.3Gb/s',
+    '10.5Gb/s',
+    '10.7Gb/s',
+    '11.1Gb/s',
+    '(unknown)',
+    'lineside loopback mode',
+    'XFI loopback mode',
+]
+
+DEVICE_TECH = [
+    ['no wavelength control', 'sctive wavelength control'],
+    ['uncooled transmitter device', 'cooled transmitter'],
+    ['PIN detector', 'APD detector'],
+    ['transmitter not tunable', 'transmitter tunable'],
+]
+
+ENHANCED_OPTS = [
+    'VPS',
+    'soft TX_DISABLE',
+    'soft P_Down',
+    'VPS LV regulator mode',
+    'VPS bypassed regulator mode',
+    'active FEC control',
+    'wavelength tunability',
+    'CMU',
+]
+
+AUX_TYPES = [
+    'not implemented',
+    'APD bias voltage',
+    '(unknown)',
+    'TEC current',
+    'laser temperature',
+    'laser wavelength',
+    '5V supply voltage',
+    '3.3V supply voltage',
+    '1.8V supply voltage',
+    '-5.2V supply voltage',
+    '5V supply current',
+    '(unknown)',
+    '(unknown)',
+    '3.3V supply current',
+    '1.8V supply current',
+    '-5.2V supply current',
+]
diff --git a/decoders/common/sdcard/__init__.py b/decoders/common/sdcard/__init__.py
new file mode 100644 (file)
index 0000000..cf09d9d
--- /dev/null
@@ -0,0 +1,21 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2012-2014 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+from .mod import *
diff --git a/decoders/common/sdcard/mod.py b/decoders/common/sdcard/mod.py
new file mode 100644 (file)
index 0000000..f553cf1
--- /dev/null
@@ -0,0 +1,186 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2012-2014 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# Normal commands (CMD)
+# Unlisted items are 'Reserved' as per SD spec. The 'Unknown' items don't
+# seem to be mentioned in the spec, but aren't marked as reserved either.
+cmd_names = {
+    0:  'GO_IDLE_STATE',
+    1:  'SEND_OP_COND', # Reserved in SD mode
+    2:  'ALL_SEND_CID',
+    3:  'SEND_RELATIVE_ADDR',
+    4:  'SET_DSR',
+    5:  'IO_SEND_OP_COND', # SDIO-only
+    6:  'SWITCH_FUNC', # New since spec 1.10
+    7:  'SELECT/DESELECT_CARD',
+    8:  'SEND_IF_COND',
+    9:  'SEND_CSD',
+    10: 'SEND_CID',
+    11: 'VOLTAGE_SWITCH',
+    12: 'STOP_TRANSMISSION',
+    13: 'SEND_STATUS',
+    # 14: Reserved
+    15: 'GO_INACTIVE_STATE',
+    16: 'SET_BLOCKLEN',
+    17: 'READ_SINGLE_BLOCK',
+    18: 'READ_MULTIPLE_BLOCK',
+    19: 'SEND_TUNING_BLOCK',
+    20: 'SPEED_CLASS_CONTROL',
+    # 21-22: Reserved
+    23: 'SET_BLOCK_COUNT',
+    24: 'WRITE_BLOCK',
+    25: 'WRITE_MULTIPLE_BLOCK',
+    26: 'Reserved for manufacturer',
+    27: 'PROGRAM_CSD',
+    28: 'SET_WRITE_PROT',
+    29: 'CLR_WRITE_PROT',
+    30: 'SEND_WRITE_PROT',
+    # 31: Reserved
+    32: 'ERASE_WR_BLK_START', # SPI mode: ERASE_WR_BLK_START_ADDR
+    33: 'ERASE_WR_BLK_END', # SPI mode: ERASE_WR_BLK_END_ADDR
+    34: 'Reserved for CMD6', # New since spec 1.10
+    35: 'Reserved for CMD6', # New since spec 1.10
+    36: 'Reserved for CMD6', # New since spec 1.10
+    37: 'Reserved for CMD6', # New since spec 1.10
+    38: 'ERASE',
+    # 39: Reserved
+    40: 'Reserved for security specification',
+    # 41: Reserved
+    42: 'LOCK_UNLOCK',
+    # 43-49: Reserved
+    50: 'Reserved for CMD6', # New since spec 1.10
+    # 51: Reserved
+    52: 'IO_RW_DIRECT', # SDIO-only
+    53: 'IO_RW_EXTENDED', # SDIO-only
+    54: 'Unknown',
+    55: 'APP_CMD',
+    56: 'GEN_CMD',
+    57: 'Reserved for CMD6', # New since spec 1.10
+    58: 'READ_OCR', # Reserved in SD mode
+    59: 'CRC_ON_OFF', # Reserved in SD mode
+    60: 'Reserved for manufacturer',
+    61: 'Reserved for manufacturer',
+    62: 'Reserved for manufacturer',
+    63: 'Reserved for manufacturer',
+}
+
+# Application-specific commands (ACMD)
+# Unlisted items are 'Reserved' as per SD spec. The 'Unknown' items don't
+# seem to be mentioned in the spec, but aren't marked as reserved either.
+acmd_names = {
+    # 1-5: Reserved
+    6:  'SET_BUS_WIDTH',
+    # 7-12: Reserved
+    13: 'SD_STATUS',
+    14: 'Reserved for Security Application',
+    15: 'Reserved for Security Application',
+    16: 'Reserved for Security Application',
+    # 17: Reserved
+    18: 'Reserved for SD security applications',
+    # 19-21: Reserved
+    22: 'SEND_NUM_WR_BLOCKS',
+    23: 'SET_WR_BLK_ERASE_COUNT',
+    # 24: Reserved
+    25: 'Reserved for SD security applications',
+    26: 'Reserved for SD security applications',
+    27: 'Reserved for security specification',
+    28: 'Reserved for security specification',
+    # 29: Reserved
+    30: 'Reserved for security specification',
+    31: 'Reserved for security specification',
+    32: 'Reserved for security specification',
+    33: 'Reserved for security specification',
+    34: 'Reserved for security specification',
+    35: 'Reserved for security specification',
+    # 36-37: Reserved
+    38: 'Reserved for SD security applications',
+    # 39-40: Reserved
+    41: 'SD_SEND_OP_COND',
+    42: 'SET_CLR_CARD_DETECT',
+    43: 'Reserved for SD security applications',
+    44: 'Reserved for SD security applications',
+    45: 'Reserved for SD security applications',
+    46: 'Reserved for SD security applications',
+    47: 'Reserved for SD security applications',
+    48: 'Reserved for SD security applications',
+    49: 'Reserved for SD security applications',
+    50: 'Unknown',
+    51: 'SEND_SCR',
+    52: 'Reserved for security specification',
+    53: 'Reserved for security specification',
+    54: 'Reserved for security specification',
+    55: 'Non-existant', # Doesn't exist (equivalent to CMD55)
+    56: 'Reserved for security specification',
+    57: 'Reserved for security specification',
+    58: 'Reserved for security specification',
+    59: 'Reserved for security specification',
+    60: 'Unknown',
+    61: 'Unknown',
+    62: 'Unknown',
+    63: 'Unknown',
+}
+
+accepted_voltages = {
+    0b0001: '2.7-3.6V',
+    0b0010: 'reserved for low voltage range',
+    0b0100: 'reserved',
+    0b1000: 'reserved',
+    # All other values: "not defined".
+}
+
+card_status = {
+    0:  'Reserved for manufacturer test mode',
+    1:  'Reserved for manufacturer test mode',
+    2:  'Reserved for application specific commands',
+    3:  'AKE_SEQ_ERROR',
+    4:  'Reserved for SDIO card',
+    5:  'APP_CMD',
+    6:  'Unknown',
+    7:  'Unknown',
+    8:  'READY_FOR_DATA',
+    9:  'CURRENT_STATE', # CURRENT_STATE is a 4-bit value (decimal: 0..15).
+    10: 'CURRENT_STATE',
+    11: 'CURRENT_STATE',
+    12: 'CURRENT_STATE',
+    13: 'ERASE_RESET',
+    14: 'CARD_ECC_DISABLED',
+    15: 'WP_ERASE_SKIP',
+    16: 'CSD_OVERWRITE',
+    17: 'Reserved for DEFERRED_RESPONSE', # See eSD addendum
+    18: 'Reserved',
+    19: 'ERROR',
+    20: 'CC_ERROR',
+    21: 'CARD_ECC_FAILED',
+    22: 'ILLEGAL_COMMAND',
+    23: 'COM_CRC_ERROR',
+    24: 'LOCK_UNLOCK_FAILED',
+    25: 'CARD_IS_LOCKED',
+    26: 'WP_VIOLATION',
+    27: 'ERASE_PARAM',
+    28: 'ERASE_SEQ_ERROR',
+    29: 'BLOCK_LEN_ERROR',
+    30: 'ADDRESS_ERROR',
+    31: 'OUT_OF_RANGE',
+}
+
+sd_status = {
+    # 311:0: Reserved for manufacturer
+    # 391:312: Reserved
+}
diff --git a/decoders/common/srdhelper/__init__.py b/decoders/common/srdhelper/__init__.py
new file mode 100644 (file)
index 0000000..cf09d9d
--- /dev/null
@@ -0,0 +1,21 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2012-2014 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+from .mod import *
diff --git a/decoders/common/srdhelper/mod.py b/decoders/common/srdhelper/mod.py
new file mode 100644 (file)
index 0000000..e65ab17
--- /dev/null
@@ -0,0 +1,23 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2012-2014 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+# Return the specified BCD number (max. 8 bits) as integer.
+def bcd2int(b):
+    return (b & 0x0f) + ((b >> 4) * 10)
index adee4037323df77428d4ed8c8e409d8c3348287f..0f1d3d1910cf2dddba62f29395451448a4c9dc26 100644 (file)
 
 import sigrokdecode as srd
 import calendar
-
-# Return the specified BCD number (max. 8 bits) as integer.
-def bcd2int(b):
-    return (b & 0x0f) + ((b >> 4) * 10)
+from common.srdhelper import bcd2int
 
 class SamplerateError(Exception):
     pass
@@ -68,7 +65,7 @@ class Decoder(srd.Decoder):
         ('warnings', 'Warnings', (19,)),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.samplerate = None
         self.state = 'WAIT FOR RISING EDGE'
         self.oldpins = None
diff --git a/decoders/dmx512/__init__.py b/decoders/dmx512/__init__.py
new file mode 100644 (file)
index 0000000..9761dd5
--- /dev/null
@@ -0,0 +1,26 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Fabian J. Stumpf <sigrok@fabianstumpf.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+'''
+DMX512 (Digital MultipleX 512) is a protocol based on RS485, used to control
+professional lighting fixtures.
+'''
+
+from .pd import Decoder
diff --git a/decoders/dmx512/pd.py b/decoders/dmx512/pd.py
new file mode 100644 (file)
index 0000000..e684b13
--- /dev/null
@@ -0,0 +1,170 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Fabian J. Stumpf <sigrok@fabianstumpf.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'dmx512'
+    name = 'DMX512'
+    longname = 'Digital MultipleX 512'
+    desc = 'Professional lighting control protocol.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['dmx512']
+    channels = (
+        {'id': 'dmx', 'name': 'DMX data', 'desc': 'Any DMX data line'},
+    )
+    annotations = (
+        ('bit', 'Bit'),
+        ('break', 'Break'),
+        ('mab', 'Mark after break'),
+        ('startbit', 'Start bit'),
+        ('stopbits', 'Stop bit'),
+        ('startcode', 'Start code'),
+        ('channel', 'Channel'),
+        ('interframe', 'Interframe'),
+        ('interpacket', 'Interpacket'),
+        ('data', 'Data'),
+        ('error', 'Error'),
+    )
+    annotation_rows = (
+        ('name', 'Logical', (1, 2, 5, 6, 7, 8)),
+        ('data', 'Data', (9,)),
+        ('bits', 'Bits', (0, 3, 4)),
+        ('errors', 'Errors', (10,)),
+    )
+
+    def __init__(self):
+        self.samplerate = None
+        self.sample_usec = None
+        self.samplenum = -1
+        self.run_start = -1
+        self.run_bit = 0
+        self.state = 'FIND BREAK'
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+            self.sample_usec = 1 / value * 1000000
+            self.skip_per_bit = int(4 / self.sample_usec)
+
+    def putr(self, data):
+        self.put(self.run_start, self.samplenum, self.out_ann, data)
+
+    def decode(self, ss, es, data):
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
+        for (self.samplenum, pins) in data:
+            # Seek for an interval with no state change with a length between
+            # 88 and 1000000 us (BREAK).
+            if self.state == 'FIND BREAK':
+                if self.run_bit == pins[0]:
+                    continue
+                runlen = (self.samplenum - self.run_start) * self.sample_usec
+                if runlen > 88 and runlen < 1000000:
+                    self.putr([1, ['Break']])
+                    self.bit_break = self.run_bit
+                    self.state = 'MARK MAB'
+                    self.channel = 0
+                elif runlen >= 1000000:
+                    # Error condition.
+                    self.putr([10, ['Invalid break length']])
+                self.run_bit = pins[0]
+                self.run_start = self.samplenum
+            # Directly following the BREAK is the MARK AFTER BREAK.
+            elif self.state == 'MARK MAB':
+                if self.run_bit == pins[0]:
+                    continue
+                self.putr([2, ['MAB']])
+                self.state = 'READ BYTE'
+                self.channel = 0
+                self.bit = 0
+                self.aggreg = pins[0]
+                self.run_start = self.samplenum
+            # Mark and read a single transmitted byte
+            # (start bit, 8 data bits, 2 stop bits).
+            elif self.state == 'READ BYTE':
+                self.next_sample = self.run_start + (self.bit + 1) * self.skip_per_bit
+                self.aggreg += pins[0]
+                if self.samplenum != self.next_sample:
+                    continue
+                bit_value = 0 if round(self.aggreg/self.skip_per_bit) == self.bit_break else 1
+
+                if self.bit == 0:
+                    self.byte = 0
+                    self.putr([3, ['Start bit']])
+                    if bit_value != 0:
+                        # (Possibly) invalid start bit, mark but don't fail.
+                        self.put(self.samplenum, self.samplenum,
+                                 self.out_ann, [10, ['Invalid start bit']])
+                elif self.bit >= 9:
+                    self.put(self.samplenum - self.skip_per_bit,
+                        self.samplenum, self.out_ann, [4, ['Stop bit']])
+                    if bit_value != 1:
+                        # Invalid stop bit, mark.
+                        self.put(self.samplenum, self.samplenum,
+                            self.out_ann, [10, ['Invalid stop bit']])
+                        if self.bit == 10:
+                            # On invalid 2nd stop bit, search for new break.
+                            self.run_bit = pins[0]
+                            self.state = 'FIND BREAK'
+                else:
+                    # Label and process one bit.
+                    self.put(self.samplenum - self.skip_per_bit,
+                        self.samplenum, self.out_ann, [0, [str(bit_value)]])
+                    self.byte |= bit_value << (self.bit - 1)
+
+                # Label a complete byte.
+                if self.bit == 10:
+                    if self.channel == 0:
+                        d = [5, ['Start code']]
+                    else:
+                        d = [6, ['Channel ' + str(self.channel)]]
+                    self.put(self.run_start, self.next_sample, self.out_ann, d)
+                    self.put(self.run_start + self.skip_per_bit,
+                        self.next_sample - 2 * self.skip_per_bit,
+                        self.out_ann, [9, [str(self.byte) + ' / ' + \
+                        str(hex(self.byte))]])
+                    # Continue by scanning the IFT.
+                    self.channel += 1
+                    self.run_start = self.samplenum
+                    self.run_bit = pins[0]
+                    self.state = 'MARK IFT'
+
+                self.aggreg = pins[0]
+                self.bit += 1
+            # Mark the INTERFRAME-TIME between bytes / INTERPACKET-TIME between packets.
+            elif self.state == 'MARK IFT':
+                if self.run_bit == pins[0]:
+                    continue
+                if self.channel > 512:
+                    self.putr([8, ['Interpacket']])
+                    self.state = 'FIND BREAK'
+                    self.run_bit = pins[0]
+                    self.run_start = self.samplenum
+                else:
+                    self.putr([7, ['Interframe']])
+                    self.state = 'READ BYTE'
+                    self.bit = 0
+                    self.run_start = self.samplenum
index b784b6dd225bfcb6e51a4607cd0ad6d70bfa10c3..f181fd9952c359784ae868d00e84fd5130ff7f51 100644 (file)
@@ -21,6 +21,7 @@
 
 import re
 import sigrokdecode as srd
+from common.srdhelper import bcd2int
 
 days_of_week = (
     'Sunday', 'Monday', 'Tuesday', 'Wednesday',
@@ -51,10 +52,6 @@ def regs_and_bits():
     l += [('bit-' + re.sub('\/| ', '-', b).lower(), b + ' bit') for b in bits]
     return tuple(l)
 
-# Return the specified BCD number (max. 8 bits) as integer.
-def bcd2int(b):
-    return (b & 0x0f) + ((b >> 4) * 10)
-
 class Decoder(srd.Decoder):
     api_version = 2
     id = 'ds1307'
@@ -78,7 +75,7 @@ class Decoder(srd.Decoder):
         ('warnings', 'Warnings', (28,)),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.state = 'IDLE'
         self.hours = -1
         self.minutes = -1
index b154de7e4e0ad82204afa32b4618646df69ec290..389fbda9f48a4cdbf9196428d86533e6adfad673 100644 (file)
@@ -90,7 +90,7 @@ class Decoder(srd.Decoder):
         ('fields', 'Fields', (0,)),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.state = None
         # Received data items, used as an index into samplenum/data
         self.cnt = 0
@@ -140,10 +140,10 @@ class Decoder(srd.Decoder):
                         [ANN_SECTIONS, ['EDID Version']])
                 self.put(self.sn[OFF_VERSION][0], self.sn[OFF_VERSION][1],
                         self.out_ann, [ANN_FIELDS,
-                            ["Version %d" % self.cache[-2]]])
+                            ['Version %d' % self.cache[-2]]])
                 self.put(self.sn[OFF_VERSION+1][0], self.sn[OFF_VERSION+1][1],
                         self.out_ann, [ANN_FIELDS,
-                            [ "Revision %d" % self.cache[-1]]])
+                            ['Revision %d' % self.cache[-1]]])
             elif self.cnt == OFF_CHROM:
                 self.put(self.sn[OFF_BASIC][0], es, self.out_ann,
                         [ANN_SECTIONS, ['Basic display']])
index 386431e12419144f53e7c7359e9e968a04d489e4..73269a6daf67979c3c8e71282c828d4326167253 100644 (file)
@@ -73,7 +73,7 @@ class Decoder(srd.Decoder):
         ('binary', 'Binary'),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.reset()
 
     def start(self):
index d1a7ba327de8c4467fa7c6ef3198edd314ce441b..0768f9d74e832337cbb71b8f2653a9241569d13b 100644 (file)
@@ -5,8 +5,8 @@
 ##
 ## 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 data 2 of the License, or
-## (at your option) any later data.
+## the Free Software Foundation; either version 2 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
@@ -62,7 +62,7 @@ class Decoder(srd.Decoder):
         ('tags', 'Tags', (9,)),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.samplerate = None
         self.oldpin = None
         self.last_samplenum = None
@@ -74,12 +74,12 @@ class Decoder(srd.Decoder):
         self.oldpl = 0
         self.oldsamplenum = 0
         self.last_bit_pos = 0
-        self.first_ss = 0
+        self.ss_first = 0
         self.first_one = 0
         self.state = 'HEADER'
         self.data = 0
         self.data_bits = 0
-        self.data_ss = 0
+        self.ss_data = 0
         self.data_parity = 0
         self.payload_cnt = 0
         self.data_col_parity = [0, 0, 0, 0, 0, 0]
@@ -105,14 +105,14 @@ class Decoder(srd.Decoder):
                 if self.first_one > 0:
                     self.first_one += 1
                 if self.first_one == 9:
-                    self.put(self.first_ss, es, self.out_ann,
+                    self.put(self.ss_first, es, self.out_ann,
                              [1, ['Header', 'Head', 'He', 'H']])
                     self.first_one = 0
                     self.state = 'PAYLOAD'
                     return
                 if self.first_one == 0:
                     self.first_one = 1
-                    self.first_ss = ss
+                    self.ss_first = ss
 
             if bit == 0:
                 self.first_one = 0
@@ -121,14 +121,14 @@ class Decoder(srd.Decoder):
         if self.state == 'PAYLOAD':
             self.payload_cnt += 1
             if self.data_bits == 0:
-                self.data_ss = ss
+                self.ss_data = ss
                 self.data = 0
                 self.data_parity = 0
             self.data_bits += 1
             if self.data_bits == 5:
                 s = 'Version/customer' if self.payload_cnt <= 10 else 'Data'
                 c = 2 if self.payload_cnt <= 10 else 3
-                self.put(self.data_ss, ss, self.out_ann,
+                self.put(self.ss_data, ss, self.out_ann,
                          [c, [s + ': %X' % self.data, '%X' % self.data]])
                 s = 'OK' if self.data_parity == bit else 'ERROR'
                 c = 4 if s == 'OK' else 5
@@ -150,7 +150,7 @@ class Decoder(srd.Decoder):
         if self.state == 'TRAILER':
             self.payload_cnt += 1
             if self.data_bits == 0:
-                self.data_ss = ss
+                self.ss_data = ss
                 self.data = 0
                 self.data_parity = 0
             self.data_bits += 1
@@ -172,7 +172,7 @@ class Decoder(srd.Decoder):
                 # Emit an annotation for valid-looking tags.
                 all_col_parity_ok = (self.data_col_parity[1:5] == self.col_parity[1:5])
                 if all_col_parity_ok and self.all_row_parity_ok:
-                    self.put(self.first_ss, es, self.out_ann,
+                    self.put(self.ss_first, es, self.out_ann,
                              [9, ['Tag: %010X' % self.tag, 'Tag', 'T']])
 
                 self.tag = 0
diff --git a/decoders/em4305/__init__.py b/decoders/em4305/__init__.py
new file mode 100644 (file)
index 0000000..1c1896a
--- /dev/null
@@ -0,0 +1,25 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2015 Benjamin Larsson <benjamin@southpole.se>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+'''
+EM4305 is a 100-150kHz RFID protocol.
+'''
+
+from .pd import Decoder
diff --git a/decoders/em4305/pd.py b/decoders/em4305/pd.py
new file mode 100644 (file)
index 0000000..73ed955
--- /dev/null
@@ -0,0 +1,396 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Benjamin Larsson <benjamin@southpole.se>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+
+class SamplerateError(Exception):
+    pass
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'em4305'
+    name = 'EM4305'
+    longname = 'RFID EM4205/EM4305'
+    desc = 'EM4205/EM4305 100-150kHz RFID protocol.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['em4305']
+    channels = (
+        {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
+    )
+    options = (
+        {'id': 'coilfreq', 'desc': 'Coil frequency', 'default': 125000},
+        {'id': 'first_field_stop', 'desc': 'First field stop min', 'default': 40},
+        {'id': 'w_gap', 'desc': 'Write gap min', 'default': 12},
+        {'id': 'w_one_max', 'desc': 'Write one max', 'default': 32},
+        {'id': 'w_zero_on_min', 'desc': 'Write zero on min', 'default': 15},
+        {'id': 'w_zero_off_max', 'desc': 'Write zero off max', 'default': 27},
+        {'id': 'em4100_decode', 'desc': 'EM4100 decode', 'default': 'on',
+            'values': ('on', 'off')},
+    )
+    annotations = (
+        ('bit_value', 'Bit value'),
+        ('first_field_stop', 'First field stop'),
+        ('write_gap', 'Write gap'),
+        ('write_mode_exit', 'Write mode exit'),
+        ('bit', 'Bit'),
+        ('opcode', 'Opcode'),
+        ('lock', 'Lock'),
+        ('data', 'Data'),
+        ('password', 'Password'),
+        ('address', 'Address'),
+        ('bitrate', 'Bitrate'),
+    )
+    annotation_rows = (
+        ('bits', 'Bits', (0,)),
+        ('structure', 'Structure', (1, 2, 3, 4)),
+        ('fields', 'Fields', (5, 6, 7, 8, 9)),
+        ('decode', 'Decode', (10,)),
+    )
+
+    def __init__(self):
+        self.samplerate = None
+        self.oldpin = None
+        self.last_samplenum = None
+        self.state = 'FFS_SEARCH'
+        self.bits_pos = [[0 for col in range(3)] for row in range(70)]
+        self.br_string = ['RF/8', 'RF/16', 'Unused', 'RF/32', 'RF/40',
+                          'Unused', 'Unused', 'RF/64',]
+        self.encoder = ['not used', 'Manchester', 'Bi-phase', 'not used']
+        self.delayed_on = ['No delay', 'Delayed on - BP/8', 'Delayed on - BP/4', 'No delay']
+        self.em4100_decode1_partial = 0
+        self.cmds = ['Invalid', 'Login', 'Write word', 'Invalid', 'Read word', 'Disable', 'Protect', 'Invalid']
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+        self.field_clock = self.samplerate / self.options['coilfreq']
+        self.wzmax = self.options['w_zero_off_max'] * self.field_clock
+        self.wzmin = self.options['w_zero_on_min'] * self.field_clock
+        self.womax = self.options['w_one_max'] * self.field_clock
+        self.ffs = self.options['first_field_stop'] * self.field_clock
+        self.writegap = self.options['w_gap'] * self.field_clock
+        self.nogap = 300 * self.field_clock
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def decode_config(self, idx):
+        bitrate = self.get_3_bits(idx+2)
+        self.put(self.bits_pos[idx][1], self.bits_pos[idx+5][2],
+                 self.out_ann, [10, ['Data rate: ' + \
+                 self.br_string[bitrate], self.br_string[bitrate]]])
+        encoding = self.bits_pos[idx+6][0]<<0 | self.bits_pos[idx+7][0]<<1
+        self.put(self.bits_pos[idx+6][1], self.bits_pos[idx+10][2],
+                 self.out_ann, [10, ['Encoder: ' + \
+                 self.encoder[encoding], self.encoder[encoding]]])
+        self.put(self.bits_pos[idx+11][1], self.bits_pos[idx+12][2], self.out_ann,
+                 [10, ['Zero bits', 'ZB']])
+        delay_on = self.bits_pos[idx+13][0]<<0 | self.bits_pos[idx+14][0]<<1
+        self.put(self.bits_pos[idx+13][1], self.bits_pos[idx+14][2],
+                 self.out_ann, [10, ['Delayed on: ' + \
+                 self.delayed_on[delay_on], self.delayed_on[delay_on]]])
+        lwr = self.bits_pos[idx+15][0]<<3 | self.bits_pos[idx+16][0]<<2 | \
+                 self.bits_pos[idx+18][0]<<1 | self.bits_pos[idx+19][0]<<0
+        self.put(self.bits_pos[idx+15][1], self.bits_pos[idx+19][2],
+                 self.out_ann, [10, ['Last default read word: %d' % lwr, 'LWR: %d' % lwr, '%d' % lwr]])
+        self.put(self.bits_pos[idx+20][1], self.bits_pos[idx+20][2],
+                 self.out_ann, [10, ['Read login: %d' % self.bits_pos[idx+20][0], '%d' % self.bits_pos[idx+20][0]]])
+        self.put(self.bits_pos[idx+21][1], self.bits_pos[idx+21][2], self.out_ann,
+                 [10, ['Zero bits', 'ZB']])
+        self.put(self.bits_pos[idx+22][1], self.bits_pos[idx+22][2],
+                 self.out_ann, [10, ['Write login: %d' % self.bits_pos[idx+22][0], '%d' % self.bits_pos[idx+22][0]]])
+        self.put(self.bits_pos[idx+23][1], self.bits_pos[idx+24][2], self.out_ann,
+                 [10, ['Zero bits', 'ZB']])
+        self.put(self.bits_pos[idx+25][1], self.bits_pos[idx+25][2],
+                 self.out_ann, [10, ['Disable: %d' % self.bits_pos[idx+25][0], '%d' % self.bits_pos[idx+25][0]]])
+        self.put(self.bits_pos[idx+27][1], self.bits_pos[idx+27][2],
+                 self.out_ann, [10, ['Reader talk first: %d' % self.bits_pos[idx+27][0], 'RTF: %d' % self.bits_pos[idx+27][0]]])
+        self.put(self.bits_pos[idx+28][1], self.bits_pos[idx+28][2], self.out_ann,
+                 [10, ['Zero bits', 'ZB']])
+        self.put(self.bits_pos[idx+29][1], self.bits_pos[idx+29][2],
+                 self.out_ann, [10, ['Pigeon mode: %d' % self.bits_pos[idx+29][0], '%d' % self.bits_pos[idx+29][0]]])
+        self.put(self.bits_pos[idx+30][1], self.bits_pos[idx+34][2],
+                 self.out_ann, [10, ['Reserved', 'Res', 'R']])
+
+    def put4bits(self, idx):
+        bits = self.bits_pos[idx][0]<<3 | self.bits_pos[idx+1][0]<<2 | \
+               self.bits_pos[idx+2][0]<<1 | self.bits_pos[idx+3][0]
+        self.put(self.bits_pos[idx][1], self.bits_pos[idx+3][2], self.out_ann,
+                 [10, ['%X' % bits]])
+
+    def em4100_decode1(self, idx):
+        self.put(self.bits_pos[idx][1], self.bits_pos[idx+9][2], self.out_ann,
+                 [10, ['EM4100 header', 'EM header', 'Header', 'H']])
+        self.put4bits(idx+10)
+        bits = self.bits_pos[idx+15][0]<<3 | self.bits_pos[idx+16][0]<<2 | \
+               self.bits_pos[idx+18][0]<<1 | self.bits_pos[idx+19][0]<<0
+        self.put(self.bits_pos[idx+15][1], self.bits_pos[idx+19][2], self.out_ann,
+               [10, ['%X' % bits]])
+        self.put4bits(idx+21)
+        self.put4bits(idx+27)
+        self.em4100_decode1_partial = self.bits_pos[idx+32][0]<<3 | \
+            self.bits_pos[idx+33][0]<<2 | self.bits_pos[idx+34][0]<<1
+        self.put(self.bits_pos[idx+32][1], self.bits_pos[idx+34][2],
+                 self.out_ann, [10, ['Partial nibble']])
+
+    def em4100_decode2(self, idx):
+        if self.em4100_decode1_partial != 0:
+            bits = self.em4100_decode1_partial + self.bits_pos[idx][0]
+            self.put(self.bits_pos[idx][1], self.bits_pos[idx][2],
+                     self.out_ann, [10, ['%X' % bits]])
+            self.em4100_decode1_partial = 0
+        else:
+            self.put(self.bits_pos[idx][1], self.bits_pos[idx][2],
+                     self.out_ann, [10, ['Partial nibble']])
+
+        self.put4bits(idx+2)
+        bits = self.bits_pos[idx+7][0]<<3 | self.bits_pos[idx+9][0]<<2 | \
+               self.bits_pos[idx+10][0]<<1 | self.bits_pos[idx+11][0]<<0
+        self.put(self.bits_pos[idx+7][1], self.bits_pos[idx+11][2], self.out_ann,
+               [10, ['%X' % bits]])
+        self.put4bits(idx+13)
+        self.put4bits(idx+19)
+        bits = self.bits_pos[idx+24][0]<<3 | self.bits_pos[idx+25][0]<<2 | \
+               self.bits_pos[idx+27][0]<<1 | self.bits_pos[idx+28][0]<<0
+        self.put(self.bits_pos[idx+24][1], self.bits_pos[idx+28][2], self.out_ann,
+               [10, ['%X' % bits]])
+        self.put(self.bits_pos[idx+30][1], self.bits_pos[idx+34][2],
+                 self.out_ann, [10, ['EM4100 trailer']])
+
+    def get_32_bits(self, idx):
+        return self.get_8_bits(idx+27)<<24 | self.get_8_bits(idx+18)<<16 | \
+               self.get_8_bits(idx+9)<<8 | self.get_8_bits(idx)
+
+    def get_8_bits(self, idx):
+        retval = 0
+        for i in range(0, 8):
+            retval <<= 1
+            retval |= self.bits_pos[i+idx][0]
+        return retval
+
+    def get_3_bits(self, idx):
+        return self.bits_pos[idx][0]<<2 | self.bits_pos[idx+1][0]<<1 | \
+               self.bits_pos[idx+2][0]
+
+    def get_4_bits(self, idx):
+        return self.bits_pos[idx][0]<<0 | self.bits_pos[idx+1][0]<<1 | \
+               self.bits_pos[idx+2][0]<<2 | self.bits_pos[idx+3][0]<<3
+
+    def print_row_parity(self, idx, length):
+        parity = 0
+        for i in range(0, length):
+            parity += self.bits_pos[i+idx][0]
+        parity = parity & 0x1
+        if parity == self.bits_pos[idx+length][0]:
+            self.put(self.bits_pos[idx+length][1], self.bits_pos[idx+length][2], self.out_ann,
+                [5, ['Row parity OK', 'Parity OK', 'OK']])
+        else:
+            self.put(self.bits_pos[idx+length][1], self.bits_pos[idx+length][2], self.out_ann,
+                [5, ['Row parity failed', 'Parity failed', 'Fail']])
+
+    def print_col_parity(self, idx):
+        data_1 = self.get_8_bits(idx)
+        data_2 = self.get_8_bits(idx+9)
+        data_3 = self.get_8_bits(idx+9+9)
+        data_4 = self.get_8_bits(idx+9+9+9)
+        col_par = self.get_8_bits(idx+9+9+9+9)
+        col_par_calc = data_1^data_2^data_3^data_4
+
+        if col_par == col_par_calc:
+            self.put(self.bits_pos[idx+9+9+9+9][1], self.bits_pos[idx+9+9+9+9+7][2], self.out_ann,
+                [5, ['Column parity OK', 'Parity OK', 'OK']])
+        else:
+            self.put(self.bits_pos[idx+9+9+9+9][1], self.bits_pos[idx+9+9+9+9+7][2], self.out_ann,
+                [5, ['Column parity failed', 'Parity failed', 'Fail']])
+
+    def print_8bit_data(self, idx):
+        data = self.get_8_bits(idx)
+        self.put(self.bits_pos[idx][1], self.bits_pos[idx+7][2], self.out_ann,
+                     [9, ['Data' + ': %X' % data, '%X' % data]])
+
+    def put_fields(self):
+        if self.bit_nr == 50:
+            self.put(self.bits_pos[0][1], self.bits_pos[0][2], self.out_ann,
+                     [4, ['Logic zero']])
+            self.put(self.bits_pos[1][1], self.bits_pos[4][2], self.out_ann,
+                     [4, ['Command', 'Cmd', 'C']])
+            self.put(self.bits_pos[5][1], self.bits_pos[49][2], self.out_ann,
+                     [4, ['Password', 'Passwd', 'Pass', 'P']])
+            # Get command.
+            cmd = self.get_3_bits(1)
+            self.put(self.bits_pos[1][1], self.bits_pos[3][2], self.out_ann,
+                     [5, [self.cmds[cmd]]])
+            self.print_row_parity(1, 3)
+
+            # Print data.
+            self.print_8bit_data(5)
+            self.print_row_parity(5, 8)
+            self.print_8bit_data(14)
+            self.print_row_parity(14, 8)
+            self.print_8bit_data(23)
+            self.print_row_parity(23, 8)
+            self.print_8bit_data(32)
+            self.print_row_parity(32, 8)
+            self.print_col_parity(5)
+            if self.bits_pos[49][0] == 0:
+                self.put(self.bits_pos[49][1], self.bits_pos[49][2], self.out_ann,
+                         [5, ['Stop bit', 'Stop', 'SB']])
+            else:
+                self.put(self.bits_pos[49][1], self.bits_pos[49][2], self.out_ann,
+                         [5, ['Stop bit error', 'Error']])
+
+            if cmd == 1:
+                password = self.get_32_bits(5)
+                self.put(self.bits_pos[12][1], self.bits_pos[46][2], self.out_ann,
+                     [10, ['Login password: %X' % password]])
+
+        if self.bit_nr == 57:
+            self.put(self.bits_pos[0][1], self.bits_pos[0][2], self.out_ann,
+                     [4, ['Logic zero', 'LZ']])
+            self.put(self.bits_pos[1][1], self.bits_pos[4][2], self.out_ann,
+                     [4, ['Command', 'Cmd', 'C']])
+            self.put(self.bits_pos[5][1], self.bits_pos[11][2], self.out_ann,
+                     [4, ['Address', 'Addr', 'A']])
+            self.put(self.bits_pos[12][1], self.bits_pos[56][2], self.out_ann,
+                     [4, ['Data', 'Da', 'D']])
+
+            # Get command.
+            cmd = self.get_3_bits(1)
+            self.put(self.bits_pos[1][1], self.bits_pos[3][2], self.out_ann,
+                     [5, [self.cmds[cmd]]])
+            self.print_row_parity(1, 3)
+
+            # Get address.
+            addr = self.get_4_bits(5)
+            self.put(self.bits_pos[5][1], self.bits_pos[8][2], self.out_ann,
+                     [9, ['Addr' + ': %d' % addr, '%d' % addr]])
+            self.put(self.bits_pos[9][1], self.bits_pos[10][2], self.out_ann,
+                     [5, ['Zero bits', 'ZB']])
+            self.print_row_parity(5, 6)
+            # Print data.
+            self.print_8bit_data(12)
+            self.print_row_parity(12, 8)
+            self.print_8bit_data(21)
+            self.print_row_parity(21, 8)
+            self.print_8bit_data(30)
+            self.print_row_parity(30, 8)
+            self.print_8bit_data(39)
+            self.print_row_parity(39, 8)
+            self.print_col_parity(12)
+            if self.bits_pos[56][0] == 0:
+                self.put(self.bits_pos[56][1], self.bits_pos[56][2], self.out_ann,
+                         [5, ['Stop bit', 'Stop', 'SB']])
+            else:
+                self.put(self.bits_pos[56][1], self.bits_pos[56][2], self.out_ann,
+                         [5, ['Stop bit error', 'Error']])
+
+            if addr == 4:
+                self.decode_config(12)
+
+            if addr == 2:
+                password = self.get_32_bits(12)
+                self.put(self.bits_pos[12][1], self.bits_pos[46][2], self.out_ann,
+                     [10, ['Write password: %X' % password]])
+
+            # If we are programming EM4100 data we can decode it halfway.
+            if addr == 5 and self.options['em4100_decode'] == 'on':
+                self.em4100_decode1(12)
+            if addr == 6 and self.options['em4100_decode'] == 'on':
+                self.em4100_decode2(12)
+
+        self.bit_nr = 0
+
+    def add_bits_pos(self, bit, ss_bit, es_bit):
+        if self.bit_nr < 70:
+            self.bits_pos[self.bit_nr][0] = bit
+            self.bits_pos[self.bit_nr][1] = ss_bit
+            self.bits_pos[self.bit_nr][2] = es_bit
+            self.bit_nr += 1
+
+    def decode(self, ss, es, data):
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
+        for (self.samplenum, (pin,)) in data:
+            # Ignore identical samples early on (for performance reasons).
+            if self.oldpin == pin:
+                continue
+
+            if self.oldpin is None:
+                self.oldpin = pin
+                self.last_samplenum = self.samplenum
+                self.oldsamplenum = 0
+                self.old_gap_end = 0
+                self.gap_detected = 0
+                self.bit_nr = 0
+                continue
+
+            if self.oldpin != pin:
+                pl = self.samplenum - self.oldsamplenum
+                pp = pin
+                samples = self.samplenum - self.last_samplenum
+
+                if self.state == 'FFS_DETECTED':
+                    if pl > self.writegap:
+                        self.gap_detected = 1
+                    if (self.last_samplenum - self.old_gap_end) > self.nogap:
+                        self.gap_detected = 0
+                        self.state = 'FFS_SEARCH'
+                        self.put(self.old_gap_end, self.last_samplenum,
+                                 self.out_ann, [3, ['Write mode exit']])
+                        self.put_fields()
+
+                if self.state == 'FFS_SEARCH':
+                    if pl > self.ffs:
+                        self.gap_detected = 1
+                        self.put(self.last_samplenum, self.samplenum,
+                                 self.out_ann, [1, ['First field stop', 'Field stop', 'FFS']])
+                        self.state = 'FFS_DETECTED'
+
+                if self.gap_detected == 1:
+                    self.gap_detected = 0
+                    if (self.last_samplenum - self.old_gap_end) > self.wzmin \
+                            and (self.last_samplenum - self.old_gap_end) < self.wzmax:
+                        self.put(self.old_gap_end, self.samplenum,
+                                 self.out_ann, [0, ['0']])
+                        self.add_bits_pos(0, self.old_gap_end, self.samplenum)
+                    if (self.last_samplenum - self.old_gap_end) > self.womax \
+                            and (self.last_samplenum-self.old_gap_end) < self.nogap:
+                        # One or more 1 bits
+                        one_bits = (int)((self.last_samplenum - self.old_gap_end) / self.womax)
+                        for ox in range(0, one_bits):
+                            bs = (int)(self.old_gap_end+ox*self.womax)
+                            be = (int)(self.old_gap_end+ox*self.womax + self.womax)
+                            self.put(bs, be, self.out_ann, [0, ['1']])
+                            self.add_bits_pos(1, bs, be)
+                        if (self.samplenum - self.last_samplenum) > self.wzmin \
+                                and (self.samplenum - self.last_samplenum) < self.wzmax:
+                            bs = (int)(self.old_gap_end+one_bits*self.womax)
+                            self.put(bs, self.samplenum, self.out_ann, [0, ['0']])
+                            self.add_bits_pos(0, bs, self.samplenum)
+
+                    self.old_gap_end = self.samplenum
+
+                if self.state == 'SKIP':
+                    self.state = 'FFS_SEARCH'
+
+                self.oldsamplenum = self.samplenum
+                self.last_samplenum = self.samplenum
+                self.oldpin = pin
diff --git a/decoders/gpib/__init__.py b/decoders/gpib/__init__.py
new file mode 100644 (file)
index 0000000..3dae563
--- /dev/null
@@ -0,0 +1,25 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Rudolf Reuter <reuterru@arcor.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+'''
+This protocol decoder can decode the GPIB (IEEE-488) protocol.
+'''
+
+from .pd import Decoder
diff --git a/decoders/gpib/pd.py b/decoders/gpib/pd.py
new file mode 100644 (file)
index 0000000..0712966
--- /dev/null
@@ -0,0 +1,189 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Rudolf Reuter <reuterru@arcor.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'gpib'
+    name = 'GPIB'
+    longname = 'General Purpose Interface Bus'
+    desc = 'IEEE-488 GPIB / HPIB protocol.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['gpib']
+    channels = (
+        {'id': 'dio1' , 'name': 'DIO1', 'desc': 'Data I/O bit 1'},
+        {'id': 'dio2' , 'name': 'DIO2', 'desc': 'Data I/O bit 2'},
+        {'id': 'dio3' , 'name': 'DIO3', 'desc': 'Data I/O bit 3'},
+        {'id': 'dio4' , 'name': 'DIO4', 'desc': 'Data I/O bit 4'},
+        {'id': 'dio5' , 'name': 'DIO5', 'desc': 'Data I/O bit 5'},
+        {'id': 'dio6' , 'name': 'DIO6', 'desc': 'Data I/O bit 6'},
+        {'id': 'dio7' , 'name': 'DIO7', 'desc': 'Data I/O bit 7'},
+        {'id': 'dio8' , 'name': 'DIO8', 'desc': 'Data I/O bit 8'},
+        {'id': 'eoi', 'name': 'EOI', 'desc': 'End or identify'},
+        {'id': 'dav', 'name': 'DAV', 'desc': 'Data valid'},
+        {'id': 'nrfd', 'name': 'NRFD', 'desc': 'Not ready for data'},
+        {'id': 'ndac', 'name': 'NDAC', 'desc': 'Not data accepted'},
+        {'id': 'ifc', 'name': 'IFC', 'desc': 'Interface clear'},
+        {'id': 'srq', 'name': 'SRQ', 'desc': 'Service request'},
+        {'id': 'atn', 'name': 'ATN', 'desc': 'Attention'},
+        {'id': 'ren', 'name': 'REN', 'desc': 'Remote enable'},
+    )
+    options = (
+        {'id': 'sample_total', 'desc': 'Total number of samples', 'default': 0},
+    )
+    annotations = (
+        ('items', 'Items'),
+        ('gpib', 'DAT/CMD'),
+        ('eoi', 'EOI'),
+    )
+    annotation_rows = (
+        ('bytes', 'Bytes', (0,)),
+        ('gpib', 'DAT/CMD', (1,)),
+        ('eoi', 'EOI', (2,)),
+    )
+
+    def __init__(self):
+        self.olddav = None
+        self.items = []
+        self.itemcount = 0
+        self.saved_item = None
+        self.saved_ATN = False
+        self.saved_EOI = False
+        self.samplenum = 0
+        self.oldpins = None
+        self.ss_item = self.es_item = None
+        self.first = True
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def putb(self, data):
+        self.put(self.ss_item, self.es_item, self.out_ann, data)
+
+    def handle_bits(self, datapins):
+        dbyte = 0x20
+        dATN = False
+        item2 = False
+        dEOI = False
+        item3 = False
+        # If this is the first item in a word, save its sample number.
+        if self.itemcount == 0:
+            self.ss_word = self.samplenum
+
+        # Get the bits for this item.
+        item = 0
+        for i in range(8):
+            item |= datapins[i] << i
+
+        item = item ^ 0xff # Invert data byte.
+        self.items.append(item)
+        self.itemcount += 1
+
+        if datapins[14] == 0:
+            item2 = True
+        if datapins[8] == 0:
+            item3 = True
+
+        if self.first:
+            # Save the start sample and item for later (no output yet).
+            self.ss_item = self.samplenum
+            self.first = False
+            self.saved_item = item
+            self.saved_ATN = item2
+            self.saved_EOI = item3
+        else:
+            # Output the saved item.
+            dbyte = self.saved_item
+            dATN = self.saved_ATN
+            dEOI = self.saved_EOI
+            self.es_item = self.samplenum
+            self.putb([0, ['%02X' % self.saved_item]])
+
+            # Encode item byte to GPIB convention.
+            self.strgpib = ' '
+            if dATN: # ATN, decode commands.
+                if dbyte == 0x01: self.strgpib = 'GTL'
+                if dbyte == 0x04: self.strgpib = 'SDC'
+                if dbyte == 0x05: self.strgpib = 'PPC'
+                if dbyte == 0x08: self.strgpib = 'GET'
+                if dbyte == 0x09: self.strgpib = 'TCT'
+                if dbyte == 0x11: self.strgpib = 'LLO'
+                if dbyte == 0x14: self.strgpib = 'DCL'
+                if dbyte == 0x15: self.strgpib = 'PPU'
+                if dbyte == 0x18: self.strgpib = 'SPE'
+                if dbyte == 0x19: self.strgpib = 'SPD'
+                if dbyte == 0x3f: self.strgpib = 'UNL'
+                if dbyte == 0x5f: self.strgpib = 'UNT'
+                if dbyte > 0x1f and dbyte < 0x3f: # Address Listener.
+                    self.strgpib = 'L' + chr(dbyte + 0x10)
+                if dbyte > 0x3f and dbyte < 0x5f: # Address Talker
+                    self.strgpib = 'T' + chr(dbyte - 0x10)
+            else:
+                if dbyte > 0x1f and dbyte < 0x7f:
+                    self.strgpib = chr(dbyte)
+                if dbyte == 0x0a:
+                    self.strgpib = 'LF'
+                if dbyte == 0x0d:
+                    self.strgpib = 'CR'
+
+            self.putb([1, [self.strgpib]])
+            self.strEOI = ' '
+            if dEOI:
+                self.strEOI = 'EOI'
+            self.putb([2, [self.strEOI]])
+
+            self.ss_item = self.samplenum
+            self.saved_item = item
+            self.saved_ATN = item2
+            self.saved_EOI = item3
+
+        if self.itemcount < 16:
+            return
+
+        self.itemcount, self.items = 0, []
+
+    def find_falling_dav_edge(self, dav, datapins):
+        # Ignore sample if the DAV pin hasn't changed.
+        if dav == self.olddav:
+            return
+        self.olddav = dav
+        # Sample on falling DAV edge.
+        if dav == 1:
+            return
+
+        # Found the correct DAV edge, now get the bits.
+        self.handle_bits(datapins)
+
+    def decode(self, ss, es, data):
+        lsn = self.options['sample_total']
+
+        for (self.samplenum, pins) in data:
+            if lsn > 0:
+                if (lsn - self.samplenum) == 1: # Show the last data word.
+                    self.handle_bits(pins)
+
+            # Ignore identical samples early on (for performance reasons).
+            if self.oldpins == pins:
+                continue
+            self.oldpins = pins
+
+            self.find_falling_dav_edge(pins[9], pins)
index 250d519977430f08721deaa5d4548f29e9aff4f8..92d35208628dfcbd6fbb648db70e463246048ae2 100644 (file)
@@ -42,7 +42,7 @@ class Decoder(srd.Decoder):
     def putx(self, data):
         self.put(self.ss_edge, self.samplenum, self.out_ann, data)
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.olddata = None
         self.ss_edge = None
         self.first_transition = True
index 2b8b302e086184f1952c58cf0fef44d6dba8fab2..a056ef513449e968554c70a537ca35e50a6d1b1a 100644 (file)
@@ -108,7 +108,7 @@ class Decoder(srd.Decoder):
         ('data-write', 'Data write'),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.samplerate = None
         self.ss = self.es = self.ss_byte = -1
         self.samplenum = None
index 68b75a0d66fc70c9854cd3ece0520eaac279c075..5e83a214282cfcc7fb39e5cf0dc171ad4f807034 100644 (file)
@@ -30,7 +30,7 @@ class Decoder(srd.Decoder):
     inputs = ['i2c']
     outputs = [] # TODO: Only known at run-time.
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.packets = [] # Local cache of I²C packets
         self.slaves = [] # List of known slave addresses
         self.stream = -1 # Current output stream
index 3c02a2e40ec46aabc115f4b4ed94ac3fc81e01a6..c3f148fc07e8ae78550b7afb9765cd20a363a04b 100644 (file)
@@ -38,7 +38,7 @@ class Decoder(srd.Decoder):
             'values': ('read', 'write', 'both')}
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.curslave = -1
         self.curdirection = None
         self.packets = [] # Local cache of I²C packets
index 6b94c1052ed031a223d42a9ec33ce5068ebcfa8a..3287a7258ced6181b2c25aeaab21d1751a50e7b5 100644 (file)
@@ -59,7 +59,7 @@ class Decoder(srd.Decoder):
         ('wav', 'WAV file'),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.samplerate = None
         self.oldsck = 1
         self.oldws = 1
index d14c7d33215b093fe0b74a6a23eb7d7c877d1eb3..8c589fa480c8426a1b38fa524135e84c35c65b3e 100644 (file)
@@ -99,7 +99,7 @@ class Decoder(srd.Decoder):
                  [11, ['%s: %s' % (dev, btn[0]), '%s: %s' % (dev, btn[1]),
                  '%s' % btn[1]]])
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.state = 'IDLE'
         self.ss_bit = self.ss_start = self.ss_other_edge = self.ss_remote = 0
         self.data = self.count = self.active = self.old_ir = None
index e1dd42f3c0a0b60f382232146fc6b468bff23201..ae20d3a70d11911ce7c7b615e8add19c2f065c75 100644 (file)
@@ -56,7 +56,7 @@ class Decoder(srd.Decoder):
         ('fields', 'Fields', (1, 2, 3, 4, 5, 6)),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.samplerate = None
         self.samplenum = None
         self.edges, self.bits, self.ss_es_bits = [], [], []
index ba1bffacb48b55c2925143f201446d59ff71864e..c3579a81ddc6809e76a3d01fbcf991a9f3b52e5a 100644 (file)
@@ -63,7 +63,7 @@ class Decoder(srd.Decoder):
         ('ascii-float', 'Jitter values as newline-separated ASCII floats'),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.state = 'CLK'
         self.samplerate = None
         self.oldpin = None
@@ -91,17 +91,17 @@ class Decoder(srd.Decoder):
     def putx(self, delta):
         # Adjust granularity.
         if delta == 0 or delta >= 1:
-            delta_s = "%.1fs" % (delta)
+            delta_s = '%.1fs' % (delta)
         elif delta <= 1e-12:
-            delta_s = "%.1ffs" % (delta * 1e15)
+            delta_s = '%.1ffs' % (delta * 1e15)
         elif delta <= 1e-9:
-            delta_s = "%.1fps" % (delta * 1e12)
+            delta_s = '%.1fps' % (delta * 1e12)
         elif delta <= 1e-6:
-            delta_s = "%.1fns" % (delta * 1e9)
+            delta_s = '%.1fns' % (delta * 1e9)
         elif delta <= 1e-3:
-            delta_s = "%.1fμs" % (delta * 1e6)
+            delta_s = '%.1fμs' % (delta * 1e6)
         else:
-            delta_s = "%.1fms" % (delta * 1e3)
+            delta_s = '%.1fms' % (delta * 1e3)
 
         self.put(self.clk_start, self.sig_start, self.out_ann, [0, [delta_s]])
 
index aaa9b6a3cbc91dc2efbe8a4e8566fe5a6a2dcd04..83d4b28b3f204bc127e019a2ba7c2a87aba79dfe 100644 (file)
@@ -88,7 +88,7 @@ class Decoder(srd.Decoder):
         ('states', 'States', tuple(range(15 + 1))),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         # self.state = 'TEST-LOGIC-RESET'
         self.state = 'RUN-TEST/IDLE'
         self.oldstate = None
index bc3a4c7e168d9dc75a15aab4bb1f37d2c0d9d07a..ff5bb770666090a60d5f322b03e81379f2434209 100644 (file)
@@ -156,7 +156,7 @@ class Decoder(srd.Decoder):
         ('warnings', 'Warnings', (3,)),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.state = 'IDLE'
         self.samplenums = None
 
index 6b2bfa8105f32bc18f5752be2cb31addc33c4f38..50e3c91372b241a78acc8b6cc598eaf4982d7d59 100644 (file)
@@ -62,7 +62,7 @@ class Decoder(srd.Decoder):
         ('warnings', 'Human-readable warnings'),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.state = 'IDLE'
         self.reg = 0x00 # Currently selected register
         self.databytes = []
index 5e25db4779ba886070fecd752ecf848926eec966..936839442346a5b609d87c11b88347a2c6798205 100644 (file)
@@ -136,7 +136,7 @@ class Decoder(srd.Decoder):
         ('warnings', 'Warnings', (0,)),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.state = 'IDLE'
         self.oldlclk = -1
         self.samplenum = 0
index 229331fd393d0e917ab5bc0624df7218f9d6a3ce..c7ff7dfa112e227b0f278bf6e0ea05120415f616 100644 (file)
@@ -48,7 +48,7 @@ class Decoder(srd.Decoder):
         ('text', 'Human-readable text'),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.trn_beg = 0
         self.trn_end = 0
         self.state = 'ROM'
index d9028a3998c2e68962801bb40eb6d13c5c318270..98213b978d73cfdae90099d9ec298fed64efd6bb 100644 (file)
@@ -1,29 +1,39 @@
 ##
 ## This file is part of the libsigrokdecode project.
 ##
-## Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
+## Copyright (C) 2016 Elias Oenal <sigrok@eliasoenal.com>
+## All rights reserved.
 ##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are met:
 ##
-## 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.
+## 1. Redistributions of source code must retain the above copyright notice,
+##    this list of conditions and the following disclaimer.
+## 2. Redistributions in binary form must reproduce the above copyright notice,
+##    this list of conditions and the following disclaimer in the documentation
+##    and/or other materials provided with the distribution.
 ##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+## POSSIBILITY OF SUCH DAMAGE.
 ##
 
 '''
 The MDIO (Management Data Input/Output) protocol decoder supports the
-MII Management serial bus, with a clock line (MDC) and a bi-directional
-data line (MDIO).
+MII Management serial bus (a bidirectional bus between the PHY and the STA),
+with a clock line (MDC) and a bi-directional data line (MDIO).
 
 MDIO is also known as SMI (Serial Management Interface).
+
+It's part of the Ethernet standard.
 '''
 
 from .pd import Decoder
index bb1f53f82cc1c54813afc069f0c90d98b0cd2ef6..873079ab879eeb86508bc4f43b5672bc9250edd0 100644 (file)
@@ -1,21 +1,29 @@
 ##
 ## This file is part of the libsigrokdecode project.
 ##
-## Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
+## Copyright (C) 2016 Elias Oenal <sigrok@eliasoenal.com>
+## All rights reserved.
 ##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are met:
 ##
-## 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.
+## 1. Redistributions of source code must retain the above copyright notice,
+##    this list of conditions and the following disclaimer.
+## 2. Redistributions in binary form must reproduce the above copyright notice,
+##    this list of conditions and the following disclaimer in the documentation
+##    and/or other materials provided with the distribution.
 ##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+## POSSIBILITY OF SUCH DAMAGE.
 ##
 
 import sigrokdecode as srd
@@ -26,235 +34,297 @@ class Decoder(srd.Decoder):
     name = 'MDIO'
     longname = 'Management Data Input/Output'
     desc = 'Half-duplex sync serial bus for MII management between MAC and PHY.'
-    license = 'gplv2+'
+    license = 'bsd'
     inputs = ['logic']
     outputs = ['mdio']
     channels = (
         {'id': 'mdc', 'name': 'MDC', 'desc': 'Clock'},
         {'id': 'mdio', 'name': 'MDIO', 'desc': 'Data'},
     )
+    options = (
+        {'id': 'show_debug_bits', 'desc': 'Show debug bits',
+            'default': 'no', 'values': ('yes', 'no')},
+    )
     annotations = (
-        ('mdio-data', 'MDIO data'),
-        ('mdio-bits', 'MDIO bits'),
-        ('errors', 'Human-readable errors'),
+        ('bit-val', 'Bit value'),
+        ('bit-num', 'Bit number'),
+        ('frame', 'Frame'),
+        ('frame-idle', 'Bus idle state'),
+        ('frame-error', 'Frame error'),
+        ('decode', 'Decode'),
     )
     annotation_rows = (
-        ('mdio-data', 'MDIO data', (0,)),
-        ('mdio-bits', 'MDIO bits', (1,)),
-        ('other', 'Other', (2,)),
+        ('bit-val', 'Bit value', (0,)),
+        ('bit-num', 'Bit number', (1,)),
+        ('frame', 'Frame', (2, 3)),
+        ('frame-error', 'Frame error', (4,)),
+        ('decode', 'Decode', (5,)),
     )
 
     def __init__(self):
-        self.oldmdc = 0
-        self.ss_block = -1
+        self.last_mdc = 1
+        self.illegal_bus = 0
         self.samplenum = -1
-        self.oldpins = None
+        self.clause45_addr = -1 # Clause 45 is context sensitive.
         self.reset_decoder_state()
 
     def start(self):
         self.out_python = self.register(srd.OUTPUT_PYTHON)
         self.out_ann = self.register(srd.OUTPUT_ANN)
 
-    def putw(self, data):
-        self.put(self.ss_block, self.samplenum, self.out_ann, data)
+    def putbit(self, mdio, ss, es):
+        self.put(ss, es, self.out_ann, [0, ['%d' % mdio]])
+        if self.options['show_debug_bits'] == 'yes':
+            self.put(ss, es, self.out_ann, [1, ['%d' % (self.bitcount - 1), '%d' % ((self.bitcount - 1) % 10)]])
 
-    def putbit(self, mdio, start, stop):
-        # Bit annotations.
-        self.put(start, stop, self.out_ann, [1, ['%d' % mdio]])
+    def putff(self, data):
+        self.put(self.ss_frame_field, self.samplenum, self.out_ann, data)
 
     def putdata(self):
-        # FIXME: Only pass data, no bits.
-        # Pass MDIO bits and then data to the next PD up the stack.
-        ss, es = self.mdiobits[-1][1], self.mdiobits[0][2]
-
-        # self.put(ss, es, self.out_python, ['BITS', self.mdiobits])
-        self.put(ss, es, self.out_python, ['DATA', self.mdiodata])
-
-        # Bit annotations.
-        for bit in self.mdiobits:
-            self.put(bit[1], bit[2], self.out_ann, [1, ['%d' % bit[0]]])
-
-        # Error annotation if an error happened.
-        if self.error:
-            self.put(self.ss_bit, self.es_error, self.out_ann, [2, [self.error]])
-            return
-
-        op = 'READ' if self.operation else 'WRITE'
-
-        # Dataword annotations.
-        if self.ss_preamble != -1:
-            self.put(self.ss_preamble, self.ss_start, self.out_ann, [0, ['PREAMBLE']])
-        self.put(self.ss_start, self.ss_operation, self.out_ann, [0, ['START']])
-        self.put(self.ss_operation, self.ss_phy, self.out_ann, [0, [op]])
-        self.put(self.ss_phy, self.ss_reg, self.out_ann, [0, ['PHY: %d' % self.phy]])
-        self.put(self.ss_reg, self.ss_turnaround, self.out_ann, [0, ['REG: %d' % self.reg]])
-        self.put(self.ss_turnaround, self.ss_data, self.out_ann, [0, ['TURNAROUND']])
-        self.put(self.ss_data, self.es_data, self.out_ann, [0, ['DATA: %04X' % self.data]])
+        self.put(self.ss_frame_field, self.mdiobits[0][2], self.out_ann,
+                 [2, ['DATA: %04X' % self.data, 'DATA', 'D']])
+
+        if self.clause45 and self.opcode == 0:
+            self.clause45_addr = self.data
+
+        # Decode data.
+        if self.opcode > 0 or not self.clause45:
+            decoded_min = ''
+            if self.clause45 and self.clause45_addr != -1:
+                decoded_min += str.format('ADDR: %04X ' % self.clause45_addr)
+            elif self.clause45:
+                decoded_min += str.format('ADDR: UKWN ' % self.clause45_addr)
+
+            if self.clause45 and self.opcode > 1 \
+            or (not self.clause45 and self.opcode):
+                decoded_min += str.format('READ:  %04X' % self.data)
+                is_read = 1
+            else:
+                decoded_min += str.format('WRITE: %04X' % self.data)
+                is_read = 0
+            decoded_ext = str.format(' %s: %02d' % \
+                        ('PRTAD' if self.clause45 else 'PHYAD', self.portad))
+            decoded_ext += str.format(' %s: %02d' % \
+                        ('DEVAD' if self.clause45 else 'REGAD', self.devad))
+            if self.ta_invalid or self.op_invalid:
+                decoded_ext += ' ERROR'
+            self.put(self.ss_frame, self.mdiobits[0][2], self.out_ann,
+                     [5, [decoded_min + decoded_ext, decoded_min]])
+
+            self.put(self.ss_frame, self.mdiobits[0][2], self.out_python,
+                     [(bool(self.clause45), int(self.clause45_addr), \
+                       bool(is_read), int(self.portad), int(self.devad), \
+                       int(self.data))])
+
+        # Post read increment address.
+        if self.clause45 and self.opcode == 2 and self.clause45_addr != -1:
+            self.clause45_addr += 1
 
     def reset_decoder_state(self):
-        self.mdiodata = 0
         self.mdiobits = []
-        self.bitcount = 0
-        self.ss_preamble = -1
-        self.ss_start = -1
-        self.ss_operation = -1
-        self.ss_phy = -1
-        self.ss_reg = -1
-        self.ss_turnaround = -1
-        self.ss_data = -1
-        self.phy = 0
-        self.phy_bits = 0
-        self.reg = 0
-        self.reg_bits = 0
-        self.data = 0
-        self.data_bits = 0
-        self.state = 'PREAMBLE'
-        self.error = None
-
-    def parse_preamble(self, mdio):
-        if self.ss_preamble == -1:
-            self.ss_preamble = self.samplenum
-        if mdio != 1:
-            self.error = 'Invalid preamble: could not find 32 consecutive bits set to 1'
-            self.state = 'ERROR'
-        elif self.bitcount == 31:
-            self.state = 'START'
+        self.bitcount = -1
+        self.opcode = -1
+        self.clause45 = 0
+        self.ss_frame = -1
+        self.ss_frame_field = -1
+        self.preamble_len = 0
+        self.ta_invalid = -1
+        self.op_invalid = ''
+        self.portad = -1
+        self.portad_bits = 5
+        self.devad = -1
+        self.devad_bits = 5
+        self.data = -1
+        self.data_bits = 16
+        self.state = 'PRE'
+
+    def state_PRE(self, mdio):
+        if self.illegal_bus:
+            if mdio == 0:   # Stay in illegal bus state.
+                return
+            else:           # Leave and continue parsing.
+                self.illegal_bus = 0
+                self.put(self.ss_illegal, self.samplenum, self.out_ann,
+                         [4, ['ILLEGAL BUS STATE', 'ILL']])
+                self.ss_frame = self.samplenum
+
+        if self.ss_frame == -1:
+            self.ss_frame = self.samplenum
+
+        if mdio == 1:
+            self.preamble_len += 1
+
+        # Valid MDIO can't clock more than 16 succeeding ones without being
+        # in either IDLE or PRE.
+        if self.preamble_len > 16:
+            if self.preamble_len >= 10000 + 32:
+                self.put(self.ss_frame, self.mdiobits[32][1], self.out_ann,
+                    [3, ['IDLE #%d' % (self.preamble_len - 32), 'IDLE', 'I']])
+                self.ss_frame = self.mdiobits[32][1]
+                self.preamble_len = 32
+                # This is getting out of hand, free some memory.
+                del self.mdiobits[33:-1]
+            if mdio == 0:
+                if self.preamble_len < 32:
+                    self.ss_frame = self.mdiobits[self.preamble_len][1]
+                    self.put(self.ss_frame, self.samplenum, self.out_ann,
+                             [4, ['SHORT PREAMBLE', 'SHRT PRE']])
+                elif self.preamble_len > 32:
+                    self.ss_frame = self.mdiobits[32][1]
+                    self.put(self.mdiobits[self.preamble_len][1],
+                             self.mdiobits[32][1], self.out_ann,
+                             [3, ['IDLE #%d' % (self.preamble_len - 32),
+                             'IDLE', 'I']])
+                    self.preamble_len = 32
+                else:
+                    self.ss_frame = self.mdiobits[32][1]
+                self.put(self.ss_frame, self.samplenum, self.out_ann,
+                         [2, ['PRE #%d' % self.preamble_len, 'PRE', 'P']])
+                self.ss_frame_field = self.samplenum
+                self.state = 'ST'
+        elif mdio == 0:
+                self.ss_illegal = self.ss_frame
+                self.illegal_bus = 1
+
+    def state_ST(self, mdio):
+        if mdio == 0:
+            self.clause45 = 1
+        self.state = 'OP'
+
+    def state_OP(self, mdio):
+        if self.opcode == -1:
+            if self.clause45:
+                st = ['ST (Clause 45)', 'ST 45']
+            else:
+                st = ['ST (Clause 22)', 'ST 22']
+            self.putff([2, st + ['ST', 'S']])
+            self.ss_frame_field = self.samplenum
 
-    def parse_start(self, mdio):
-        if self.ss_start == -1:
-            if mdio != 0:
-                self.error = 'Invalid start bits: should be 01'
-                self.state = 'ERROR'
+            if mdio:
+                self.opcode = 2
             else:
-                self.ss_start = self.samplenum
+                self.opcode = 0
         else:
-            if mdio != 1:
-                self.error = 'Invalid start bits: should be 01'
-                self.state = 'ERROR'
+            if self.clause45:
+                self.state = 'PRTAD'
+                self.opcode += mdio
             else:
-                self.state = 'OPERATION'
-
-    def parse_operation(self, mdio):
-        if self.ss_operation == -1:
-            self.ss_operation = self.samplenum
-            self.operation = mdio
-        else:
-            if mdio == self.operation:
-                self.error = 'Invalid operation bits'
-                self.state = 'ERROR'
+                if mdio == self.opcode:
+                    self.op_invalid = 'invalid for Clause 22'
+                self.state = 'PRTAD'
+
+    def state_PRTAD(self, mdio):
+        if self.portad == -1:
+            self.portad = 0
+            if self.clause45:
+                if self.opcode == 0:
+                    op = ['OP: ADDR', 'OP: A']
+                elif self.opcode == 1:
+                    op = ['OP: WRITE', 'OP: W']
+                elif self.opcode == 2:
+                    op = ['OP: READINC', 'OP: RI']
+                elif self.opcode == 3:
+                    op = ['OP: READ', 'OP: R']
             else:
-                self.state = 'PHY'
-
-    def parse_phy(self, mdio):
-        if self.ss_phy == -1:
-            self.ss_phy = self.samplenum
-        self.phy_bits += 1
-        self.phy |= mdio << (5 - self.phy_bits)
-        if self.phy_bits == 5:
-            self.state = 'REG'
-
-    def parse_reg(self, mdio):
-        if self.ss_reg == -1:
-            self.ss_reg = self.samplenum
-        self.reg_bits += 1
-        self.reg |= mdio << (5 - self.reg_bits)
-        if self.reg_bits == 5:
-            self.state = 'TURNAROUND'
-
-    def parse_turnaround(self, mdio):
-        if self.ss_turnaround == -1:
-            if self.operation == 0 and mdio != 1:
-                self.error = 'Invalid turnaround bits'
-                self.state = 'ERROR'
+                op = ['OP: READ', 'OP: R'] if self.opcode else ['OP: WRITE', 'OP: W']
+            self.putff([2, op + ['OP', 'O']])
+            if self.op_invalid:
+                self.putff([4, ['OP %s' % self.op_invalid, 'OP', 'O']])
+            self.ss_frame_field = self.samplenum
+        self.portad_bits -= 1
+        self.portad |= mdio << self.portad_bits
+        if not self.portad_bits:
+            self.state = 'DEVAD'
+
+    def state_DEVAD(self, mdio):
+        if self.devad == -1:
+            self.devad = 0
+            if self.clause45:
+                prtad = ['PRTAD: %02d' % self.portad, 'PRT', 'P']
+            else:
+                prtad = ['PHYAD: %02d' % self.portad, 'PHY', 'P']
+            self.putff([2, prtad])
+            self.ss_frame_field = self.samplenum
+        self.devad_bits -= 1
+        self.devad |= mdio << self.devad_bits
+        if not self.devad_bits:
+            self.state = 'TA'
+
+    def state_TA(self, mdio):
+        if self.ta_invalid == -1:
+            self.ta_invalid = ''
+            if self.clause45:
+                regad = ['DEVAD: %02d' % self.devad, 'DEV', 'D']
             else:
-                self.ss_turnaround = self.samplenum
+                regad = ['REGAD: %02d' % self.devad, 'REG', 'R']
+            self.putff([2, regad])
+            self.ss_frame_field = self.samplenum
+            if mdio != 1 and ((self.clause45 and self.opcode < 2)
+            or (not self.clause45 and self.opcode == 0)):
+                self.ta_invalid = ' invalid (bit1)'
         else:
             if mdio != 0:
-                self.error = 'Invalid turnaround bits'
-                self.state = 'ERROR'
-            else:
-                self.state = 'DATA'
-
-    def parse_data(self, mdio):
-        if self.ss_data == -1:
-            self.ss_data = self.samplenum
-        self.data_bits += 1
-        self.data |= mdio << (16 - self.data_bits)
-        if self.data_bits == 16:
-            self.es_data = self.samplenum + int((self.samplenum - self.ss_data) / 15)
-            self.state = 'DONE'
-
-    def parse_error(self, mdio):
-        if self.bitcount == 63:
-            self.es_error = self.samplenum + int((self.samplenum - self.ss_bit) / 63)
-            self.state = 'DONE'
-
-    def handle_bit(self, mdio):
-        # If this is the first bit of a command, save its sample number.
-        if self.bitcount == 0:
-            self.ss_bit = self.samplenum
-            # No preamble?
-            if mdio == 0:
-                self.state = 'START'
-
-        # Guesstimate the endsample for this bit (can be overridden below).
-        es = self.samplenum
-        if self.bitcount > 0:
-            es += self.samplenum - self.mdiobits[0][1]
-
-        self.mdiobits.insert(0, [mdio, self.samplenum, es])
-
-        if self.bitcount > 0:
-            self.bitsamples = (self.samplenum - self.ss_bit) / self.bitcount
-            self.mdiobits[1][2] = self.samplenum
-
-        if self.state == 'PREAMBLE':
-            self.parse_preamble(mdio)
-        elif self.state == 'START':
-            self.parse_start(mdio)
-        elif self.state == 'OPERATION':
-            self.parse_operation(mdio)
-        elif self.state == 'PHY':
-            self.parse_phy(mdio)
-        elif self.state == 'REG':
-            self.parse_reg(mdio)
-        elif self.state == 'TURNAROUND':
-            self.parse_turnaround(mdio)
-        elif self.state == 'DATA':
-            self.parse_data(mdio)
-        elif self.state == 'ERROR':
-            self.parse_error(mdio)
-
-        self.bitcount += 1
-        if self.state == 'DONE':
-            self.putdata()
-            self.reset_decoder_state()
-
-    def find_mdc_edge(self, mdc, mdio):
-        # Output the current error annotation if the clock stopped running
-        if self.state == 'ERROR' and self.samplenum - self.clocksample > (1.5 * self.bitsamples):
-            self.es_error = self.clocksample + int((self.clocksample - self.ss_bit) / self.bitcount)
+                if self.ta_invalid:
+                    self.ta_invalid = ' invalid (bit1 and bit2)'
+                else:
+                    self.ta_invalid = ' invalid (bit2)'
+            self.state = 'DATA'
+
+    def state_DATA(self, mdio):
+        if self.data == -1:
+            self.data = 0
+            self.putff([2, ['TURNAROUND', 'TA', 'T']])
+            if self.ta_invalid:
+                self.putff([4, ['TURNAROUND%s' % self.ta_invalid,
+                                'TA%s' % self.ta_invalid, 'TA', 'T']])
+            self.ss_frame_field = self.samplenum
+        self.data_bits -= 1
+        self.data |= mdio << self.data_bits
+        if not self.data_bits:
+            # Output final bit.
+            self.mdiobits[0][2] = self.mdiobits[0][1] + self.quartile_cycle_length()
+            self.bitcount += 1
+            self.putbit(self.mdiobits[0][0], self.mdiobits[0][1], self.mdiobits[0][2])
             self.putdata()
             self.reset_decoder_state()
 
-        # Ignore sample if the clock pin hasn't changed.
-        if mdc == self.oldmdc:
-            return
+    def process_state(self, argument, mdio):
+        method_name = 'state_' + str(argument)
+        method = getattr(self, method_name)
+        return method(mdio)
+
+    # Returns the first quartile point of the frames cycle lengths. This is a
+    # conservative guess for the end of the last cycle. On average it will be
+    # more likely to fall short, than being too long, which makes for better
+    # readability in GUIs.
+    def quartile_cycle_length(self):
+        # 48 is the minimum number of samples we have to have at the end of a
+        # frame. The last sample only has a leading clock edge and is ignored.
+        bitlen = []
+        for i in range(1, 49):
+            bitlen.append(self.mdiobits[i][2] - self.mdiobits[i][1])
+        bitlen = sorted(bitlen)
+        return bitlen[12]
 
-        self.oldmdc = mdc
+    def handle_bit(self, mdio):
+        self.bitcount += 1
+        self.mdiobits.insert(0, [mdio, self.samplenum, -1])
 
-        if mdc == 0:   # Sample on rising clock edge.
-            return
+        if self.bitcount > 0:
+            self.mdiobits[1][2] = self.samplenum # Note end of last cycle.
+            # Output the last bit we processed.
+            self.putbit(self.mdiobits[1][0], self.mdiobits[1][1], self.mdiobits[1][2])
 
-        # Found the correct clock edge, now get/handle the bit(s).
-        self.clocksample = self.samplenum
-        self.handle_bit(mdio)
+        self.process_state(self.state, mdio)
 
     def decode(self, ss, es, data):
         for (self.samplenum, pins) in data:
             # Ignore identical samples early on (for performance reasons).
-            if self.oldpins == pins:
+            if self.last_mdc == pins[0]:
+                continue
+            self.last_mdc = pins[0]
+            if pins[0] == 0: # Check for rising edge.
                 continue
-            self.oldpins, (mdc, mdio) = pins, pins
 
-            self.find_mdc_edge(mdc, mdio)
+            # Found the correct clock edge, now get/handle the bit(s).
+            self.handle_bit(pins[1])
index c72f5c9c1627f9883da844eb986c55ae60ca2925..ce4c1aebf4a2e95d404384f86d2ce82fa77e140c 100644 (file)
@@ -1,7 +1,8 @@
 ##
 ## This file is part of the libsigrokdecode project.
 ##
-## Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2013-2016 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2016 Chris Dreher <chrisdreher@hotmail.com>
 ##
 ## This program is free software; you can redistribute it and/or modify
 ## it under the terms of the GNU General Public License as published by
 ## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 ##
 
+# Each status byte has 3 string names, each shorter than the previous
 status_bytes = {
     # Channel voice messages
-    0x80: 'note off',
-    0x90: 'note on', # However, velocity = 0 means "note off".
-    0xa0: 'polyphonic key pressure / aftertouch',
-    0xb0: 'control change',
-    0xc0: 'program change',
-    0xd0: 'channel pressure / aftertouch',
-    0xe0: 'pitch bend change',
+    0x80: ['note off', 'note off', 'N off'],
+    0x90: ['note on', 'note on', 'N on'], # However, velocity = 0 means "note off".
+    0xa0: ['polyphonic key pressure / aftertouch', 'key pressure', 'KP' ],
+    0xb0: ['control change', 'ctrl chg', 'CC'],
+    0xc0: ['program change', 'prgm chg', 'PC'],
+    0xd0: ['channel pressure / aftertouch', 'channel pressure', 'CP'],
+    0xe0: ['pitch bend change', 'pitch bend', 'PB'],
 
     # Channel mode messages
     # 0xb0: 'select channel mode', # Note: Same as 'control change'.
 
     # System exclusive messages
-    0xf0: 'system exclusive (SysEx)',
+    0xf0: ['system exclusive', 'SysEx', 'SE'],
 
     # System common messages
-    0xf1: 'MIDI time code quarter frame',
-    0xf2: 'song position pointer',
-    0xf3: 'song select',
-    0xf4: 'undefined',
-    0xf5: 'undefined',
-    0xf6: 'tune request',
-    0xf7: 'end of system exclusive (EOX)',
+    0xf1: ['MIDI time code quarter frame', 'MIDI time code', 'MIDI time'],
+    0xf2: ['song position pointer', 'song position', 'song pos'],
+    0xf3: ['song select', 'song select', 'song sel'],
+    0xf4: ['undefined 0xf4', 'undef 0xf4', 'undef'],
+    0xf5: ['undefined 0xf5', 'undef 0xf5', 'undef'],
+    0xf6: ['tune request', 'tune request', 'tune req'],
+    0xf7: ['end of system exclusive (EOX)', 'end of SysEx', 'EOX'],
 
     # System real time messages
-    0xf8: 'timing clock',
-    0xf9: 'undefined',
-    0xfa: 'start',
-    0xfb: 'continue',
-    0xfc: 'stop',
-    0xfd: 'undefined',
-    0xfe: 'active sensing',
-    0xff: 'system reset',
+    0xf8: ['timing clock', 'timing clock', 'clock'],
+    0xf9: ['undefined 0xf9', 'undef 0xf9', 'undef'],
+    0xfa: ['start', 'start', 's'],
+    0xfb: ['continue', 'continue', 'cont'],
+    0xfc: ['stop', 'stop', 'st'],
+    0xfd: ['undefined 0xfd', 'undef 0xfd', 'undef'],
+    0xfe: ['active sensing', 'active sensing', 'sensing'],
+    0xff: ['system reset', 'reset', 'rst'],
 }
 
 # Universal system exclusive (SysEx) messages, non-realtime (0x7e)
@@ -156,37 +158,37 @@ universal_sysex_realtime = {
 
 # Note: Not all IDs are used/listed, i.e. there are some "holes".
 sysex_manufacturer_ids = {
-    # American group
-    (0x01): 'Sequential',
-    (0x02): 'IDP',
-    (0x03): 'Voyetra/Octave-Plateau',
-    (0x04): 'Moog',
-    (0x05): 'Passport Designs',
-    (0x06): 'Lexicon',
-    (0x07): 'Kurzweil',
-    (0x08): 'Fender',
-    (0x09): 'Gulbransen',
-    (0x0a): 'AKG Acoustics',
-    (0x0b): 'Voyce Music',
-    (0x0c): 'Waveframe Corp',
-    (0x0d): 'ADA Signal Processors',
-    (0x0e): 'Garfield Electronics',
-    (0x0f): 'Ensoniq',
-    (0x10): 'Oberheim',
-    (0x11): 'Apple Computer',
-    (0x12): 'Grey Matter Response',
-    (0x13): 'Digidesign',
-    (0x14): 'Palm Tree Instruments',
-    (0x15): 'JLCooper Electronics',
-    (0x16): 'Lowrey',
-    (0x17): 'Adams-Smith',
-    (0x18): 'Emu Systems',
-    (0x19): 'Harmony Systems',
-    (0x1a): 'ART',
-    (0x1b): 'Baldwin',
-    (0x1c): 'Eventide',
-    (0x1d): 'Inventronics',
-    (0x1f): 'Clarity',
+    # American group (range 01-1f, 000001-001f7f)
+    (0x01,): 'Sequential',
+    (0x02,): 'IDP',
+    (0x03,): 'Voyetra/Octave-Plateau',
+    (0x04,): 'Moog',
+    (0x05,): 'Passport Designs',
+    (0x06,): 'Lexicon',
+    (0x07,): 'Kurzweil',
+    (0x08,): 'Fender',
+    (0x09,): 'Gulbransen',
+    (0x0a,): 'AKG Acoustics',
+    (0x0b,): 'Voyce Music',
+    (0x0c,): 'Waveframe Corp',
+    (0x0d,): 'ADA Signal Processors',
+    (0x0e,): 'Garfield Electronics',
+    (0x0f,): 'Ensoniq',
+    (0x10,): 'Oberheim',
+    (0x11,): 'Apple Computer',
+    (0x12,): 'Grey Matter Response',
+    (0x13,): 'Digidesign',
+    (0x14,): 'Palm Tree Instruments',
+    (0x15,): 'JLCooper Electronics',
+    (0x16,): 'Lowrey',
+    (0x17,): 'Adams-Smith',
+    (0x18,): 'Emu Systems',
+    (0x19,): 'Harmony Systems',
+    (0x1a,): 'ART',
+    (0x1b,): 'Baldwin',
+    (0x1c,): 'Eventide',
+    (0x1d,): 'Inventronics',
+    (0x1f,): 'Clarity',
 
     (0x00, 0x00, 0x01): 'Time Warner Interactive',
     (0x00, 0x00, 0x07): 'Digital Music Corp.',
@@ -306,32 +308,32 @@ sysex_manufacturer_ids = {
     (0x00, 0x01, 0x02): 'Crystal Semiconductor',
     (0x00, 0x01, 0x03): 'Rockwell Semiconductor',
 
-    # European group
-    (0x20): 'Passac',
-    (0x21): 'SIEL',
-    (0x22): 'Synthaxe',
-    (0x24): 'Hohner',
-    (0x25): 'Twister',
-    (0x26): 'Solton',
-    (0x27): 'Jellinghaus MS',
-    (0x28): 'Southworth Music Systems',
-    (0x29): 'PPG',
-    (0x2a): 'JEN',
-    (0x2b): 'SSL Limited',
-    (0x2c): 'Audio Veritrieb',
-    (0x2f): 'Elka',
-
-    (0x30): 'Dynacord',
-    (0x31): 'Viscount',
-    (0x33): 'Clavia Digital Instruments',
-    (0x34): 'Audio Architecture',
-    (0x35): 'GeneralMusic Corp.',
-    (0x39): 'Soundcraft Electronics',
-    (0x3b): 'Wersi',
-    (0x3c): 'Avab Elektronik Ab',
-    (0x3d): 'Digigram',
-    (0x3e): 'Waldorf Electronics',
-    (0x3f): 'Quasimidi',
+    # European group (range 20-3f, 002000-003f7f)
+    (0x20,): 'Passac',
+    (0x21,): 'SIEL',
+    (0x22,): 'Synthaxe',
+    (0x24,): 'Hohner',
+    (0x25,): 'Twister',
+    (0x26,): 'Solton',
+    (0x27,): 'Jellinghaus MS',
+    (0x28,): 'Southworth Music Systems',
+    (0x29,): 'PPG',
+    (0x2a,): 'JEN',
+    (0x2b,): 'SSL Limited',
+    (0x2c,): 'Audio Veritrieb',
+    (0x2f,): 'Elka',
+
+    (0x30,): 'Dynacord',
+    (0x31,): 'Viscount',
+    (0x33,): 'Clavia Digital Instruments',
+    (0x34,): 'Audio Architecture',
+    (0x35,): 'GeneralMusic Corp.',
+    (0x39,): 'Soundcraft Electronics',
+    (0x3b,): 'Wersi',
+    (0x3c,): 'Avab Elektronik Ab',
+    (0x3d,): 'Digigram',
+    (0x3e,): 'Waldorf Electronics',
+    (0x3f,): 'Quasimidi',
 
     (0x00, 0x20, 0x00): 'Dream',
     (0x00, 0x20, 0x01): 'Strand Lighting',
@@ -378,92 +380,462 @@ sysex_manufacturer_ids = {
     (0x00, 0x20, 0x2d): 'Blue Chip Music Tech',
     (0x00, 0x20, 0x2e): 'BEE OH Corp',
 
-    # Japanese group
-    (0x40): 'Kawai',
-    (0x41): 'Roland',
-    (0x42): 'Korg',
-    (0x43): 'Yamaha',
-    (0x44): 'Casio',
-    (0x46): 'Kamiya Studio',
-    (0x47): 'Akai',
-    (0x48): 'Japan Victor',
-    (0x49): 'Mesosha',
-    (0x4a): 'Hoshino Gakki',
-    (0x4b): 'Fujitsu Elect',
-    (0x4c): 'Sony',
-    (0x4d): 'Nisshin Onpa',
-    (0x4e): 'TEAC',
-    (0x50): 'Matsushita Electric',
-    (0x51): 'Fostex',
-    (0x52): 'Zoom',
-    (0x53): 'Midori Electronics',
-    (0x54): 'Matsushita Communication Industrial',
-    (0x55): 'Suzuki Musical Inst. Mfg.',
+    # Japanese group (range 40-5f, 004000-005f7f)
+    (0x40,): 'Kawai',
+    (0x41,): 'Roland',
+    (0x42,): 'Korg',
+    (0x43,): 'Yamaha',
+    (0x44,): 'Casio',
+    (0x46,): 'Kamiya Studio',
+    (0x47,): 'Akai',
+    (0x48,): 'Japan Victor',
+    (0x49,): 'Mesosha',
+    (0x4a,): 'Hoshino Gakki',
+    (0x4b,): 'Fujitsu Elect',
+    (0x4c,): 'Sony',
+    (0x4d,): 'Nisshin Onpa',
+    (0x4e,): 'TEAC',
+    (0x50,): 'Matsushita Electric',
+    (0x51,): 'Fostex',
+    (0x52,): 'Zoom',
+    (0x53,): 'Midori Electronics',
+    (0x54,): 'Matsushita Communication Industrial',
+    (0x55,): 'Suzuki Musical Inst. Mfg.',
+
+    # Other (range 60-7c, 006000-007f7f)
+
+    # Special (7d-7f)
+    (0x7d,): 'Non-Commercial',
+    (0x7e,): 'Universal Non-Realtime',
+    (0x7f,): 'Universal Realtime',
 }
 
 control_functions = {
-    0x00: 'bank select',
-    0x01: 'modulation wheel/lever',
-    0x02: 'breath controller',
-    # 0x03: undefined
-    0x04: 'foot controller',
-    0x05: 'portamento time',
-    0x06: 'data entry MSB',
-    0x07: 'channel volume (formerly main volume)',
-    0x08: 'balance',
-    # 0x09: undefined
-    0x0a: 'pan',
-    0x0b: 'expression controller',
-    0x0c: 'effect control 1',
-    0x0d: 'effect control 2',
-    # 0x0e-0x0f: undefined
-    0x10: 'general purpose controller 1',
-    0x11: 'general purpose controller 2',
-    0x12: 'general purpose controller 3',
-    0x13: 'general purpose controller 4',
-    # 0x14-0x1f: undefined
-    # 0x20-0x3f: LSB for values 0x00-0x1f
-    0x40: 'damper pedal (sustain)',
-    0x41: 'portamento on/off',
-    0x42: 'sostenuto',
-    0x43: 'soft pedal',
-    0x44: 'legato footswitch', # vv: 00-3f = normal, 40-7f = legato
-    0x45: 'hold 2',
-    0x46: 'sound controller 1 (default: sound variation)',
-    0x47: 'sound controller 2 (default: timbre / harmonic intensity)',
-    0x48: 'sound controller 3 (default: release time)',
-    0x49: 'sound controller 4 (default: attack time)',
-    0x4a: 'sound controller 5 (default: brightness)',
-    0x4b: 'sound controller 6 (GM2 default: decay time)',
-    0x4c: 'sound controller 7 (GM2 default: vibrato rate)',
-    0x4d: 'sound controller 8 (GM2 default: vibrato depth)',
-    0x4e: 'sound controller 9 (GM2 default: vibrato delay)',
-    0x4f: 'sound controller 10',
-    0x50: 'general purpose controller 5',
-    0x51: 'general purpose controller 6',
-    0x52: 'general purpose controller 7',
-    0x53: 'general purpose controller 8',
-    0x54: 'portamento control',
+    0x00: ['bank select MSB', 'bank MSB', 'bank-M'],
+    0x01: ['modulation wheel/lever MSB', 'modulation MSB', 'mod-M'],
+    0x02: ['breath controller MSB', 'breath MSB', 'breath-M'],
+    # 0x03: undefined MSB
+    0x04: ['foot controller MSB', 'foot MSB', 'foot-M'],
+    0x05: ['portamento time MSB', 'portamento MSB', 'porta-M'],
+    0x06: ['data entry MSB', 'data entry MSB', 'data-M'],
+    0x07: ['channel volume MSB (formerly main volume)', 'channel volume MSB', 'ch vol-M'],
+    0x08: ['balance MSB', 'bal MSB', 'bal-M'],
+    # 0x09: undefined MSB
+    0x0a: ['pan MSB', 'pan MSB', 'pan-M'],
+    0x0b: ['expression controller MSB', 'expression MSB', 'expr-M'],
+    0x0c: ['effect control 1 MSB', 'effect 1 MSB', 'eff-1-M'],
+    0x0d: ['effect control 2 MSB', 'effect 2 MSB', 'eff-2-M'],
+    # 0x0e-0x0f: undefined MSB
+    0x10: ['general purpose controller 1 MSB', 'GP ctrl 1 MSB', 'GPC-1-M'],
+    0x11: ['general purpose controller 2 MSB', 'GP ctrl 2 MSB', 'GPC-2-M'],
+    0x12: ['general purpose controller 3 MSB', 'GP ctrl 3 MSB', 'GPC-3-M'],
+    0x13: ['general purpose controller 4 MSB', 'GP ctrl 4 MSB', 'GPC-4-M'],
+    # 0x14-0x1f: undefined MSB
+    0x20: ['bank select LSB', 'bank LSB', 'bank-L'],
+    0x21: ['modulation wheel/lever LSB', 'modulation LSB', 'mod-L'],
+    0x22: ['breath controller LSB', 'breath LSB', 'breath-L'],
+    # 0x23: undefined LSB
+    0x24: ['foot controller LSB', 'foot LSB', 'foot-L'],
+    0x25: ['portamento time LSB', 'portamento LSB', 'porta-L'],
+    0x26: ['data entry LSB', 'data entry LSB', 'data-L'],
+    0x27: ['channel volume LSB (formerly main volume)', 'channel volume LSB', 'ch vol-L'],
+    0x28: ['balance LSB', 'bal LSB', 'bal-L'],
+    # 0x29: undefined LSB
+    0x2a: ['pan LSB', 'pan LSB', 'pan-L'],
+    0x2b: ['expression controller LSB', 'expression LSB', 'expr-L'],
+    0x2c: ['effect control 1 LSB', 'effect 1 LSB', 'eff-1-L'],
+    0x2d: ['effect control 2 LSB', 'effect 2 LSB', 'eff-2-L'],
+    # 0x2e-0x2f: undefined LSB
+    0x30: ['general purpose controller 1 LSB', 'GP ctrl 1 LSB', 'GPC-1-L'],
+    0x31: ['general purpose controller 2 LSB', 'GP ctrl 2 LSB', 'GPC-2-L'],
+    0x32: ['general purpose controller 3 LSB', 'GP ctrl 3 LSB', 'GPC-3-L'],
+    0x33: ['general purpose controller 4 LSB', 'GP ctrl 4 LSB', 'GPC-4-L'],
+    # 0x34-0x3f: undefined LSB
+    0x40: ['damper pedal (sustain)', 'sustain', 'sust'],
+    0x41: ['portamento on/off', 'porta on/off', 'porta on/off'],
+    0x42: ['sostenuto', 'sostenuto', 'sostenuto'],
+    0x43: ['soft pedal', 'soft pedal', 'soft pedal'],
+    0x44: ['legato footswitch', 'legato switch', 'legato'], # vv: 00-3f = normal, 40-7f = legato
+    0x45: ['hold 2', 'hold 2', 'hold 2'],
+    0x46: ['sound controller 1 (default: sound variation)', 'sound ctrl 1', 'snd ctrl 1'],
+    0x47: ['sound controller 2 (default: timbre / harmonic intensity)', 'sound ctrl 2', 'snd ctrl 2'],
+    0x48: ['sound controller 3 (default: release time)', 'sound ctrl 3', 'snd ctrl 3'],
+    0x49: ['sound controller 4 (default: attack time)', 'sound ctrl 4', 'snd ctrl 4'],
+    0x4a: ['sound controller 5 (default: brightness)', 'sound ctrl 5', 'snd ctrl 5'],
+    0x4b: ['sound controller 6 (GM2 default: decay time)', 'sound ctrl 6', 'snd ctrl 6'],
+    0x4c: ['sound controller 7 (GM2 default: vibrato rate)', 'sound ctrl 7', 'snd ctrl 7'],
+    0x4d: ['sound controller 8 (GM2 default: vibrato depth)', 'sound ctrl 8', 'snd ctrl 8'],
+    0x4e: ['sound controller 9 (GM2 default: vibrato delay)', 'sound ctrl 9', 'snd ctrl 9'],
+    0x4f: ['sound controller 10', 'sound ctrl 10', 'snd ctrl 10'],
+    0x50: ['general purpose controller 5', 'GP controller 5', 'GPC-5'],
+    0x51: ['general purpose controller 6', 'GP controller 6', 'GPC-6'],
+    0x52: ['general purpose controller 7', 'GP controller 7', 'GPC-7'],
+    0x53: ['general purpose controller 8', 'GP controller 8', 'GPC-8'],
+    0x54: ['portamento control', 'portamento ctrl', 'porta ctrl'],
     # 0x55-0x5a: undefined
-    0x5b: 'effects 1 depth (formerly external effects depth)',
-    0x5c: 'effects 2 depth (formerly tremolo depth)',
-    0x5d: 'effects 3 depth (formerly chorus depth)',
-    0x5e: 'effects 4 depth (formerly celeste/detune depth)',
-    0x5f: 'effects 5 depth (formerly phaser depth)',
-    0x60: 'data increment',
-    0x61: 'data decrement',
-    0x62: 'non-registered parameter number LSB',
-    0x63: 'non-registered parameter number MSB',
-    0x64: 'registered parameter number LSB',
-    0x65: 'registered parameter number MSB',
+    0x5b: ['effects 1 depth (formerly external effects depth)', 'effects 1 depth', 'eff 1 depth'],
+    0x5c: ['effects 2 depth (formerly tremolo depth)', 'effects 2 depth', 'eff 2 depth'],
+    0x5d: ['effects 3 depth (formerly chorus depth)', 'effects 3 depth', 'eff 3 depth'],
+    0x5e: ['effects 4 depth (formerly celeste/detune depth)', 'effects 4 depth', 'eff 4 depth'],
+    0x5f: ['effects 5 depth (formerly phaser depth)', 'effects 5 depth', 'eff 5 depth'],
+    0x60: ['data increment', 'data inc', 'data++'],
+    0x61: ['data decrement', 'data dec', 'data--'],
+    0x62: ['Non-Registered Parameter Number LSB', 'NRPN LSB', 'NRPN-L'],
+    0x63: ['Non-Registered Parameter Number MSB', 'NRPN MSB', 'NRPN-M'],
+    0x64: ['Registered Parameter Number LSB', 'RPN LSB', 'RPN-L'],
+    0x65: ['Registered Parameter Number MSB', 'RPN MSB', 'RPN-M'],
     # 0x66-0x77: undefined
     # 0x78-0x7f: reserved for channel mode messages
-    0x78: 'all sound off',
-    0x79: 'reset all controllers',
-    0x7a: 'local control on/off',
-    0x7b: 'all notes off',
-    0x7c: 'omni mode off', # all notes off
-    0x7d: 'omni mode on', # all notes off
-    0x7e: 'poly mode off', # mono mode on, all notes off
-    0x7f: 'poly mode on', # mono mode off, all notes off
+    0x78: ['all sound off', 'all snd off', 'snd off'],
+    0x79: ['reset all controllers', 'reset all ctrls', 'reset ctrls'],
+    0x7a: ['local control', 'local ctrl', 'local ctrl'],
+    0x7b: ['all notes off', 'notes off', 'notes off'],
+    0x7c: ['omni mode off', 'omni off', 'omni off'], # all notes off
+    0x7d: ['omni mode on', 'omni on', 'omni on'], # all notes off
+    0x7e: ['mono mode on', 'mono on', 'mono'], # mono mode on, all notes off
+    0x7f: ['poly mode on', 'poly on', 'poly'], # mono mode off, all notes off
+}
+
+gm_instruments = {
+    1:   'Acoustic Grand Piano',
+    2:   'Bright Acoustic Piano',
+    3:   'Electric Grand Piano',
+    4:   'Honky-tonk Piano',
+    5:   'Electric Piano 1',
+    6:   'Electric Piano 2',
+    7:   'Harpsichord',
+    8:   'Clavi',
+    9:   'Celesta',
+    10:  'Glockenspiel',
+    11:  'Music Box',
+    12:  'Vibraphone',
+    13:  'Marimba',
+    14:  'Xylophone',
+    15:  'Tubular Bells',
+    16:  'Dulcimer',
+    17:  'Drawbar Organ',
+    18:  'Percussive Organ',
+    19:  'Rock Organ',
+    20:  'Church Organ',
+    21:  'Reed Organ',
+    22:  'Accordion',
+    23:  'Harmonica',
+    24:  'Tango Accordion',
+    25:  'Acoustic Guitar (nylon)',
+    26:  'Acoustic Guitar (steel)',
+    27:  'Electric Guitar (jazz)',
+    28:  'Electric Guitar (clean)',
+    29:  'Electric Guitar (muted)',
+    30:  'Overdriven Guitar',
+    31:  'Distortion Guitar',
+    32:  'Guitar harmonics',
+    33:  'Acoustic Bass',
+    34:  'Electric Bass (finger)',
+    35:  'Electric Bass (pick)',
+    36:  'Fretless Bass',
+    37:  'Slap Bass 1',
+    38:  'Slap Bass 2',
+    39:  'Synth Bass 1',
+    40:  'Synth Bass 2',
+    41:  'Violin',
+    42:  'Viola',
+    43:  'Cello',
+    44:  'Contrabass',
+    45:  'Tremolo Strings',
+    46:  'Pizzicato Strings',
+    47:  'Orchestral Harp',
+    48:  'Timpani',
+    49:  'String Ensemble 1',
+    50:  'String Ensemble 2',
+    51:  'SynthStrings 1',
+    52:  'SynthStrings 2',
+    53:  'Choir Aahs',
+    54:  'Voice Oohs',
+    55:  'Synth Voice',
+    56:  'Orchestra Hit',
+    57:  'Trumpet',
+    58:  'Trombone',
+    59:  'Tuba',
+    60:  'Muted Trumpet',
+    61:  'French Horn',
+    62:  'Brass Section',
+    63:  'SynthBrass 1',
+    64:  'SynthBrass 2',
+    65:  'Soprano Sax',
+    66:  'Alto Sax',
+    67:  'Tenor Sax',
+    68:  'Baritone Sax',
+    69:  'Oboe',
+    70:  'English Horn',
+    71:  'Bassoon',
+    72:  'Clarinet',
+    73:  'Piccolo',
+    74:  'Flute',
+    75:  'Recorder',
+    76:  'Pan Flute',
+    77:  'Blown Bottle',
+    78:  'Shakuhachi',
+    79:  'Whistle',
+    80:  'Ocarina',
+    81:  'Lead 1 (square)',
+    82:  'Lead 2 (sawtooth)',
+    83:  'Lead 3 (calliope)',
+    84:  'Lead 4 (chiff)',
+    85:  'Lead 5 (charang)',
+    86:  'Lead 6 (voice)',
+    87:  'Lead 7 (fifths)',
+    88:  'Lead 8 (bass + lead)',
+    89:  'Pad 1 (new age)',
+    90:  'Pad 2 (warm)',
+    91:  'Pad 3 (polysynth)',
+    92:  'Pad 4 (choir)',
+    93:  'Pad 5 (bowed)',
+    94:  'Pad 6 (metallic)',
+    95:  'Pad 7 (halo)',
+    96:  'Pad 8 (sweep)',
+    97:  'FX 1 (rain)',
+    98:  'FX 2 (soundtrack)',
+    99:  'FX 3 (crystal)',
+    100: 'FX 4 (atmosphere)',
+    101: 'FX 5 (brightness)',
+    102: 'FX 6 (goblins)',
+    103: 'FX 7 (echoes)',
+    104: 'FX 8 (sci-fi)',
+    105: 'Sitar',
+    106: 'Banjo',
+    107: 'Shamisen',
+    108: 'Koto',
+    109: 'Kalimba',
+    110: 'Bag pipe',
+    111: 'Fiddle',
+    112: 'Shanai',
+    113: 'Tinkle Bell',
+    114: 'Agogo',
+    115: 'Steel Drums',
+    116: 'Woodblock',
+    117: 'Taiko Drum',
+    118: 'Melodic Tom',
+    119: 'Synth Drum',
+    120: 'Reverse Cymbal',
+    121: 'Guitar Fret Noise',
+    122: 'Breath Noise',
+    123: 'Seashore',
+    124: 'Bird Tweet',
+    125: 'Telephone Ring',
+    126: 'Helicopter',
+    127: 'Applause',
+    128: 'Gunshot',
+}
+
+drum_kit = {
+    1:   'GM Standard Kit',
+    9:   'GS Room Kit',
+    17:  'GS Power Kit',
+    25:  'GS Power Kit',
+    26:  'GS TR-808 Kit',
+    33:  'GS Jazz Kit',
+    41:  'GS Brush Kit',
+    49:  'GS Orchestra Kit',
+    57:  'GS Sound FX Kit',
+    128: 'GS CM-64/CM-32 Kit',
+}
+
+# Each quarter frame type has 2 string names, each shorter than the previous
+quarter_frame_type = {
+    0: ['frame count LS nibble', 'frame LSN'],
+    1: ['frame count MS nibble', 'frame MSN'],
+    2: ['seconds LS nibble', 'sec LSN'],
+    3: ['seconds MS nibble', 'sec MSN'],
+    4: ['minutes LS nibble', 'min LSN'],
+    5: ['minutes MS nibble', 'min MSN'],
+    6: ['hours LS nibble', 'hrs LSN'],
+    7: ['hours MS nibble and SMPTE type', 'hrs MSN'],
+}
+
+smpte_type = {
+    0: '24 fps',
+    1: '25 fps',
+    2: '30 fps (drop-frame)',
+    3: '30 fps (non-drop)',
+}
+
+chromatic_notes = {
+    0: 'C-1',
+    1: 'C#-1',
+    2: 'D-1',
+    3: 'D#-1',
+    4: 'E-1',
+    5: 'F-1',
+    6: 'F#-1',
+    7: 'G-1',
+    8: 'G#-1',
+    9: 'A-1',
+    10: 'A#-1',
+    11: 'B-1',
+    12: 'C0',
+    13: 'C#0',
+    14: 'D0',
+    15: 'D#0',
+    16: 'E0',
+    17: 'F0',
+    18: 'F#0',
+    19: 'G0',
+    20: 'G#0',
+    21: 'A0',
+    22: 'A#0',
+    23: 'B0',
+    24: 'C1',
+    25: 'C#1',
+    26: 'D1',
+    27: 'D#1',
+    28: 'E1',
+    29: 'F1',
+    30: 'F#1',
+    31: 'G1',
+    32: 'G#1',
+    33: 'A1',
+    34: 'A#1',
+    35: 'B1',
+    36: 'C2',
+    37: 'C#2',
+    38: 'D2',
+    39: 'D#2',
+    40: 'E2',
+    41: 'F2',
+    42: 'F#2',
+    43: 'G2',
+    44: 'G#2',
+    45: 'A2',
+    46: 'A#2',
+    47: 'B2',
+    48: 'C3',
+    49: 'C#3',
+    50: 'D3',
+    51: 'D#3',
+    52: 'E3',
+    53: 'F3',
+    54: 'F#3',
+    55: 'G3',
+    56: 'G#3',
+    57: 'A3',
+    58: 'A#3',
+    59: 'B3',
+    60: 'C4',
+    61: 'C#4',
+    62: 'D4',
+    63: 'D#4',
+    64: 'E4',
+    65: 'F4',
+    66: 'F#4',
+    67: 'G4',
+    68: 'G#4',
+    69: 'A4',
+    70: 'A#4',
+    71: 'B4',
+    72: 'C5',
+    73: 'C#5',
+    74: 'D5',
+    75: 'D#5',
+    76: 'E5',
+    77: 'F5',
+    78: 'F#5',
+    79: 'G5',
+    80: 'G#5',
+    81: 'A5',
+    82: 'A#5',
+    83: 'B5',
+    84: 'C6',
+    85: 'C#6',
+    86: 'D6',
+    87: 'D#6',
+    88: 'E6',
+    89: 'F6',
+    90: 'F#6',
+    91: 'G6',
+    92: 'G#6',
+    93: 'A6',
+    94: 'A#6',
+    95: 'B6',
+    96: 'C7',
+    97: 'C#7',
+    98: 'D7',
+    99: 'D#7',
+    100: 'E7',
+    101: 'F7',
+    102: 'F#7',
+    103: 'G7',
+    104: 'G#7',
+    105: 'A7',
+    106: 'A#7',
+    107: 'B7',
+    108: 'C8',
+    109: 'C#8',
+    110: 'D8',
+    111: 'D#8',
+    112: 'E8',
+    113: 'F8',
+    114: 'F#8',
+    115: 'G8',
+    116: 'G#8',
+    117: 'A8',
+    118: 'A#8',
+    119: 'B8',
+    120: 'C9',
+    121: 'C#9',
+    122: 'D9',
+    123: 'D#9',
+    124: 'E9',
+    125: 'F9',
+    126: 'F#9',
+    127: 'G9',
+}
+
+percussion_notes = {
+    35: 'Acoustic Bass Drum',
+    36: 'Bass Drum 1',
+    37: 'Side Stick',
+    38: 'Acoustic Snare',
+    39: 'Hand Clap',
+    40: 'Electric Snare',
+    41: 'Low Floor Tom',
+    42: 'Closed Hi Hat',
+    43: 'High Floor Tom',
+    44: 'Pedal Hi-Hat',
+    45: 'Low Tom',
+    46: 'Open Hi-Hat',
+    47: 'Low-Mid Tom',
+    48: 'Hi Mid Tom',
+    49: 'Crash Cymbal 1',
+    50: 'High Tom',
+    51: 'Ride Cymbal 1',
+    52: 'Chinese Cymbal',
+    53: 'Ride Bell',
+    54: 'Tambourine',
+    55: 'Splash Cymbal',
+    56: 'Cowbell',
+    57: 'Crash Cymbal 2',
+    58: 'Vibraslap',
+    59: 'Ride Cymbal 2',
+    60: 'Hi Bongo',
+    61: 'Low Bongo',
+    62: 'Mute Hi Conga',
+    63: 'Open Hi Conga',
+    64: 'Low Conga',
+    65: 'High Timbale',
+    66: 'Low Timbale',
+    67: 'High Agogo',
+    68: 'Low Agogo',
+    69: 'Cabasa',
+    70: 'Maracas',
+    71: 'Short Whistle',
+    72: 'Long Whistle',
+    73: 'Short Guiro',
+    74: 'Long Guiro',
+    75: 'Claves',
+    76: 'Hi Wood Block',
+    77: 'Low Wood Block',
+    78: 'Mute Cuica',
+    79: 'Open Cuica',
+    80: 'Mute Triangle',
+    81: 'Open Triangle',
 }
index 5915976c164535d42c10d29b920f9a81795777a3..18c439f48ea695665de0472c82648dce60aa08ac 100644 (file)
@@ -1,7 +1,8 @@
 ##
 ## This file is part of the libsigrokdecode project.
 ##
-## Copyright (C) 2013 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2013-2016 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2016 Chris Dreher <chrisdreher@hotmail.com> 
 ##
 ## This program is free software; you can redistribute it and/or modify
 ## it under the terms of the GNU General Public License as published by
@@ -35,11 +36,19 @@ class Decoder(srd.Decoder):
     outputs = ['midi']
     annotations = (
         ('text-verbose', 'Human-readable text (verbose)'),
+        ('text-sysreal-verbose', 'Human-readable SysReal text (verbose)'),
+        ('text-error', 'Human-readable Error text'),
+    )
+    annotation_rows = (
+        ('normal', 'Normal', (0, 2)),
+        ('sys-real', 'SysReal', (1,)),
     )
 
-    def __init__(self, **kwargs):
-        self.cmd = []
+    def __init__(self):
         self.state = 'IDLE'
+        self.status_byte = 0
+        self.explicit_status_byte = False
+        self.cmd = []
         self.ss = None
         self.es = None
         self.ss_block = None
@@ -51,130 +60,524 @@ class Decoder(srd.Decoder):
     def putx(self, data):
         self.put(self.ss_block, self.es_block, self.out_ann, data)
 
-    def handle_channel_msg_0x80(self):
+    def get_note_name(self, channel, note):
+        if channel != 10:
+            return chromatic_notes[note]
+        else:
+            return 'assuming ' + percussion_notes.get(note, 'undefined')
+
+    def check_for_garbage_flush(self, is_flushed):
+        if is_flushed:
+            if self.explicit_status_byte:
+                self.cmd.insert(0, self.status_byte)
+            self.handle_garbage_msg(None)
+
+    def soft_clear_status_byte(self):
+        self.explicit_status_byte = False
+
+    def hard_clear_status_byte(self):
+        self.status_byte = 0
+        self.explicit_status_byte = False
+
+    def set_status_byte(self, newbyte):
+        self.status_byte = newbyte
+        self.explicit_status_byte = True
+
+    def handle_channel_msg_0x80(self, is_flushed):
         # Note off: 8n kk vv
         # n = channel, kk = note, vv = velocity
         c = self.cmd
-        if len(c) < 3:
+        if len(c) < 2:
+            self.check_for_garbage_flush(is_flushed)
             return
         self.es_block = self.es
-        msg, chan, note, velocity = c[0] & 0xf0, (c[0] & 0x0f) + 1, c[1], c[2]
-        self.putx([0, ['Channel %d: %s (note = %d, velocity = %d)' % \
-                  (chan, status_bytes[msg], note, velocity)]])
+        msg, chan = self.status_byte & 0xf0, (self.status_byte & 0x0f) + 1
+        note, velocity = c[0], c[1]
+        note_name = self.get_note_name(chan, note)
+        self.putx([0, ['Channel %d: %s (note = %d \'%s\', velocity = %d)' % \
+                  (chan, status_bytes[msg][0], note, note_name, velocity),
+                  'ch %d: %s %d, velocity = %d' % \
+                  (chan, status_bytes[msg][1], note, velocity),
+                  '%d: %s %d, vel %d' % \
+                  (chan, status_bytes[msg][2], note, velocity)]])
         self.cmd, self.state = [], 'IDLE'
+        self.soft_clear_status_byte()
 
-    def handle_channel_msg_0x90(self):
+    def handle_channel_msg_0x90(self, is_flushed):
         # Note on: 9n kk vv
         # n = channel, kk = note, vv = velocity
         # If velocity == 0 that actually means 'note off', though.
         c = self.cmd
-        if len(c) < 3:
+        if len(c) < 2:
+            self.check_for_garbage_flush(is_flushed)
             return
         self.es_block = self.es
-        msg, chan, note, velocity = c[0] & 0xf0, (c[0] & 0x0f) + 1, c[1], c[2]
-        s = 'note off' if (velocity == 0) else status_bytes[msg]
-        self.putx([0, ['Channel %d: %s (note = %d, velocity = %d)' % \
-                  (chan, s, note, velocity)]])
+        msg, chan = self.status_byte & 0xf0, (self.status_byte & 0x0f) + 1
+        note, velocity = c[0], c[1]
+        s = status_bytes[0x80] if (velocity == 0) else status_bytes[msg]
+        note_name = self.get_note_name(chan, note)
+        self.putx([0, ['Channel %d: %s (note = %d \'%s\', velocity = %d)' % \
+                  (chan, s[0], note, note_name, velocity),
+                  'ch %d: %s %d, velocity = %d' % \
+                  (chan, s[1], note, velocity),
+                  '%d: %s %d, vel %d' % \
+                  (chan, s[2], note, velocity)]])
         self.cmd, self.state = [], 'IDLE'
+        self.soft_clear_status_byte()
 
-    def handle_channel_msg_0xa0(self):
+    def handle_channel_msg_0xa0(self, is_flushed):
         # Polyphonic key pressure / aftertouch: An kk vv
         # n = channel, kk = polyphonic key pressure, vv = pressure value
-        pass # TODO
+        c = self.cmd
+        if len(c) < 2:
+            self.check_for_garbage_flush(is_flushed)
+            return
+        self.es_block = self.es
+        msg, chan = self.status_byte & 0xf0, (self.status_byte & 0x0f) + 1
+        note, pressure = c[0], c[1]
+        note_name = self.get_note_name(chan, note)
+        self.putx([0, ['Channel %d: %s of %d for note = %d \'%s\'' % \
+                  (chan, status_bytes[msg][0], pressure, note, note_name),
+                  'ch %d: %s %d for note %d' % \
+                  (chan, status_bytes[msg][1], pressure, note),
+                  '%d: %s %d, N %d' % \
+                  (chan, status_bytes[msg][2], pressure, note)]])
+        self.cmd, self.state = [], 'IDLE'
+        self.soft_clear_status_byte()
 
     def handle_controller_0x44(self):
         # Legato footswitch: Bn 44 vv
         # n = channel, vv = value (<= 0x3f: normal, > 0x3f: legato)
-        chan, vv = (self.cmd[0] & 0x0f) + 1, self.cmd[2]
-        t = 'normal' if vv <= 0x3f else 'legato'
-        self.putx([0, ['Channel %d: control function \'%s\' = %s' % \
-                  (chan, control_functions[0x44], t)]])
+        c = self.cmd
+        msg, chan = self.status_byte & 0xf0, (self.status_byte & 0x0f) + 1
+        vv = c[1]
+        t = ('normal', 'no') if vv <= 0x3f else ('legato', 'yes')
+        self.putx([0, ['Channel %d: %s \'%s\' = %s' % \
+                  (chan, status_bytes[msg][0],
+                  control_functions[0x44][0], t[0]),
+                  'ch %d: %s \'%s\' = %s' % \
+                  (chan, status_bytes[msg][1],
+                  control_functions[0x44][1], t[0]),
+                  '%d: %s \'%s\' = %s' % \
+                  (chan, status_bytes[msg][2],
+                  control_functions[0x44][2], t[1])]])
 
     def handle_controller_0x54(self):
         # Portamento control (PTC): Bn 54 kk
         # n = channel, kk = source note for pitch reference
-        chan, kk = (self.cmd[0] & 0x0f) + 1, self.cmd[2]
-        self.putx([0, ['Channel %d: control function \'%s\' (source note ' \
-                  '= %d)' % (chan, control_functions[0x54], kk)]])
+        c = self.cmd
+        msg, chan = self.status_byte & 0xf0, (self.status_byte & 0x0f) + 1
+        kk = c[1]
+        kk_name = self.get_note_name(chan, kk)
+        self.putx([0, ['Channel %d: %s \'%s\' (source note = %d / %s)' % \
+                  (chan, status_bytes[msg][0],
+                  control_functions[0x54][0], kk, kk_name),
+                  'ch %d: %s \'%s\' (source note = %d)' % \
+                  (chan, status_bytes[msg][1],
+                  control_functions[0x54][1], kk),
+                  '%d: %s \'%s\' (src N %d)' % \
+                  (chan, status_bytes[msg][2],
+                  control_functions[0x54][2], kk)]])
 
     def handle_controller_generic(self):
         c = self.cmd
-        chan, fn, param = (c[0] & 0x0f) + 1, c[1], c[2]
-        ctrl_fn = control_functions.get(fn, 'undefined')
-        self.putx([0, ['Channel %d: control change to function \'%s\' ' \
-                  '(param = 0x%02x)' % (chan, ctrl_fn, param)]])
+        msg, chan = self.status_byte & 0xf0, (self.status_byte & 0x0f) + 1
+        fn, param = c[0], c[1]
+        default_name = 'undefined'
+        ctrl_fn = control_functions.get(fn, default_name)
+        if ctrl_fn == default_name:
+            ctrl_fn = ('undefined 0x%02x' % fn, 'undef 0x%02x' % fn, '0x%02x' % fn)
+        self.putx([0, ['Channel %d: %s \'%s\' (param = 0x%02x)' % \
+                  (chan, status_bytes[msg][0], ctrl_fn[0], param),
+                  'ch %d: %s \'%s\' (param = 0x%02x)' % \
+                  (chan, status_bytes[msg][1], ctrl_fn[1], param),
+                  '%d: %s \'%s\' is 0x%02x' % \
+                  (chan, status_bytes[msg][2], ctrl_fn[2], param)]])
+
+    def handle_channel_mode(self):
+        # Channel Mode: Bn mm vv
+        # n = channel, mm = mode number (120 - 127), vv = value
+        c = self.cmd
+        msg, chan = self.status_byte & 0xf0, (self.status_byte & 0x0f) + 1
+        mm, vv = c[0], c[1]
+        mode_fn = control_functions.get(mm, ('undefined', 'undef', 'undef'))
+        # Decode the value based on the mode number.
+        vv_string = ('', '')
+        if mm == 122:           # mode = local control?
+            if vv == 0:
+                vv_string = ('off', 'off')
+            elif vv == 127:     # mode = poly mode on?
+                vv_string = ('on', 'on')
+            else:
+                vv_string = ('(non-standard param value of 0x%02x)' % vv,
+                            '0x%02x' % vv)
+        elif mm == 126:         # mode = mono mode on?
+            if vv != 0:
+                vv_string = ('(%d channels)' % vv, '(%d ch)' % vv)
+            else:
+                vv_string = ('(channels \'basic\' through 16)',
+                            '(ch \'basic\' thru 16)')
+        elif vv != 0: # All other channel mode messages expect vv == 0.
+            vv_string = ('(non-standard param value of 0x%02x)' % vv,
+                        '0x%02x' % vv)
+        self.putx([0, ['Channel %d: %s \'%s\' %s' % \
+                      (chan, status_bytes[msg][0], mode_fn[0], vv_string[0]),
+                      'ch %d: %s \'%s\' %s' % \
+                      (chan, status_bytes[msg][1], mode_fn[1], vv_string[1]),
+                      '%d: %s \'%s\' %s' % \
+                      (chan, status_bytes[msg][2], mode_fn[2], vv_string[1])]])
+        self.cmd, self.state = [], 'IDLE'
+        self.soft_clear_status_byte()
 
-    def handle_channel_msg_0xb0(self):
+    def handle_channel_msg_0xb0(self, is_flushed):
         # Control change (or channel mode messages): Bn cc vv
         # n = channel, cc = control number (0 - 119), vv = control value
         c = self.cmd
-        if (len(c) >= 2) and (c[1] in range(0x78, 0x7f + 1)):
-            # This is not a control change, but rather a channel mode message.
-            # TODO: Handle channel mode messages.
-            return
-        if len(c) < 3:
+        if len(c) < 2:
+            self.check_for_garbage_flush(is_flushed)
             return
         self.es_block = self.es
-        handle_ctrl = getattr(self, 'handle_controller_0x%02x' % c[1],
+        if c[0] in range(0x78, 0x7f + 1):
+            self.handle_channel_mode()
+            return
+        handle_ctrl = getattr(self, 'handle_controller_0x%02x' % c[0],
                               self.handle_controller_generic)
         handle_ctrl()
         self.cmd, self.state = [], 'IDLE'
+        self.soft_clear_status_byte()
 
-    def handle_channel_msg_0xc0(self):
+    def handle_channel_msg_0xc0(self, is_flushed):
         # Program change: Cn pp
         # n = channel, pp = program number (0 - 127)
-        pass # TODO
+        c = self.cmd
+        if len(c) < 1:
+            self.check_for_garbage_flush(is_flushed)
+            return
+        self.es_block = self.es
+        msg, chan = self.status_byte & 0xf0, (self.status_byte & 0x0f) + 1
+        pp = self.cmd[0] + 1
+        change_type = 'instrument'
+        name = ''
+        if chan != 10:  # channel != percussion
+            name = gm_instruments.get(pp, 'undefined')
+        else:
+            change_type = 'drum kit'
+            name = drum_kit.get(pp, 'undefined')
+        self.putx([0, ['Channel %d: %s to %s %d (assuming %s)' % \
+            (chan, status_bytes[msg][0], change_type, pp, name),
+            'ch %d: %s to %s %d' % \
+            (chan, status_bytes[msg][1], change_type, pp),
+            '%d: %s %d' % \
+            (chan, status_bytes[msg][2], pp)]])
+        self.cmd, self.state = [], 'IDLE'
+        self.soft_clear_status_byte()
 
-    def handle_channel_msg_0xd0(self):
+    def handle_channel_msg_0xd0(self, is_flushed):
         # Channel pressure / aftertouch: Dn vv
         # n = channel, vv = pressure value
-        pass # TODO
+        c = self.cmd
+        if len(c) < 1:
+            self.check_for_garbage_flush(is_flushed)
+            return
+        self.es_block = self.es
+        msg, chan = self.status_byte & 0xf0, (self.status_byte & 0x0f) + 1
+        vv = self.cmd[0]
+        self.putx([0, ['Channel %d: %s %d' % (chan, status_bytes[msg][0], vv),
+                      'ch %d: %s %d' % (chan, status_bytes[msg][1], vv),
+                      '%d: %s %d' % (chan, status_bytes[msg][2], vv)]])
+        self.cmd, self.state = [], 'IDLE'
+        self.soft_clear_status_byte()
 
-    def handle_channel_msg_0xe0(self):
+    def handle_channel_msg_0xe0(self, is_flushed):
         # Pitch bend change: En ll mm
         # n = channel, ll = pitch bend change LSB, mm = pitch bend change MSB
-        pass # TODO
+        c = self.cmd
+        if len(c) < 2:
+            self.check_for_garbage_flush(is_flushed)
+            return
+        self.es_block = self.es
+        msg, chan = self.status_byte & 0xf0, (self.status_byte & 0x0f) + 1
+        ll, mm = self.cmd[0], self.cmd[1]
+        decimal = (mm << 7) + ll
+        self.putx([0, ['Channel %d: %s 0x%02x 0x%02x (%d)' % \
+                      (chan, status_bytes[msg][0], ll, mm, decimal),
+                      'ch %d: %s 0x%02x 0x%02x (%d)' % \
+                      (chan, status_bytes[msg][1], ll, mm, decimal),
+                      '%d: %s (%d)' % \
+                      (chan, status_bytes[msg][2], decimal)]])
+        self.cmd, self.state = [], 'IDLE'
+        self.soft_clear_status_byte()
 
-    def handle_channel_msg_generic(self):
-        msg_type = self.cmd[0] & 0xf0
-        self.putx([0, ['Unknown channel message type: 0x%02x' % msg_type]])
-        # TODO: Handle properly.
+    def handle_channel_msg_generic(self, is_flushed):
+        # TODO: It should not be possible to hit this code.
+        # It currently can not be unit tested.
+        msg_type = self.status_byte & 0xf0
+        self.es_block = self.es
+        self.putx([2, ['Unknown channel message type: 0x%02x' % msg_type]])
+        self.cmd, self.state = [], 'IDLE'
+        self.soft_clear_status_byte()
 
     def handle_channel_msg(self, newbyte):
-        self.cmd.append(newbyte)
-        msg_type = self.cmd[0] & 0xf0
+        if newbyte is not None:
+            if newbyte >= 0x80:
+                self.set_status_byte(newbyte)
+            else:
+                self.cmd.append(newbyte)
+        msg_type = self.status_byte & 0xf0
         handle_msg = getattr(self, 'handle_channel_msg_0x%02x' % msg_type,
                              self.handle_channel_msg_generic)
-        handle_msg()
+        handle_msg(newbyte is None)
 
     def handle_sysex_msg(self, newbyte):
-        # SysEx message: 1 status byte, x data bytes, EOX byte
-        self.cmd.append(newbyte)
-        if newbyte != 0xf7: # EOX
+        # SysEx message: 1 status byte, 1-3 manuf. bytes, x data bytes, EOX byte
+        #
+        # SysEx messages are variable length, can be terminated by EOX or
+        # by any non-SysReal status byte, and it clears self.status_byte.
+        #
+        # Note: All System message codes don't utilize self.status_byte.
+        self.hard_clear_status_byte()
+        if newbyte != 0xf7 and newbyte is not None: # EOX
+            self.cmd.append(newbyte)
+            return
+        self.es_block = self.es
+        # Note: Unlike other methods, this code pops bytes out of self.cmd
+        # to isolate the data.
+        msg = self.cmd.pop(0)
+        if len(self.cmd) < 1:
+            self.putx([2, ['%s: truncated manufacturer code (<1 bytes)' % \
+                          status_bytes[msg][0],
+                          '%s: truncated manufacturer (<1 bytes)' % \
+                          status_bytes[msg][1],
+                          '%s: trunc. manu.' % status_bytes[msg][2]]])
+            self.cmd, self.state = [], 'IDLE'
+            return
+        # Extract the manufacturer name (or SysEx realtime or non-realtime).
+        m1 = self.cmd.pop(0)
+        manu = (m1,)
+        if m1 == 0x00:  # If byte == 0, then 2 more manufacturer bytes follow.
+            if len(self.cmd) < 2:
+                self.putx([2, ['%s: truncated manufacturer code (<3 bytes)' % \
+                          status_bytes[msg][0],
+                          '%s: truncated manufacturer (<3 bytes)' % \
+                          status_bytes[msg][1],
+                          '%s: trunc. manu.' % status_bytes[msg][2]]])
+                self.cmd, self.state = [], 'IDLE'
+                return
+            manu = (m1, self.cmd.pop(0), self.cmd.pop(0))
+        default_name = 'undefined'
+        manu_name = sysex_manufacturer_ids.get(manu, default_name)
+        if manu_name == default_name:
+            if len(manu) == 3:
+                manu_name = ('%s (0x%02x 0x%02x 0x%02x)' % \
+                            (default_name, manu[0], manu[1], manu[2]),
+                            default_name)
+            else:
+                manu_name = ('%s (0x%02x)' % (default_name, manu[0]),
+                            default_name)
+        else:
+            manu_name = (manu_name, manu_name)
+        # Extract the payload, display in 1 of 2 formats
+        # TODO: Write methods to decode SysEx realtime & non-realtime payloads.
+        payload0 = ''
+        payload1 = ''
+        while len(self.cmd) > 0:
+            byte = self.cmd.pop(0)
+            payload0 += '0x%02x ' % (byte)
+            payload1 += '%02x ' % (byte)
+        if payload0 == '':
+            payload0 = '<empty>'
+            payload1 = '<>'
+        payload = (payload0, payload1)
+        self.putx([0, ['%s: for \'%s\' with payload %s' % \
+                      (status_bytes[msg][0], manu_name[0], payload[0]),
+                      '%s: \'%s\', payload %s' % \
+                      (status_bytes[msg][1], manu_name[1], payload[1]),
+                      '%s: \'%s\', payload %s' % \
+                      (status_bytes[msg][2], manu_name[1], payload[1])]])
+        self.cmd, self.state = [], 'IDLE'
+
+    def handle_syscommon_midi_time_code_quarter_frame_msg(self, newbyte):
+        # MIDI time code quarter frame: F1 nd
+        # n = message type
+        # d = values
+        #
+        # Note: All System message codes don't utilize self.status_byte,
+        # and System Exclusive and System Common clear it.
+        c = self.cmd
+        if len(c) < 2:
+            if newbyte is None:
+                self.handle_garbage_msg(None)
             return
+        msg = c[0]
+        nn, dd = (c[1] & 0x70) >> 4, c[1] & 0x0f
+        group = ('System Common', 'SysCom', 'SC')
         self.es_block = self.es
-        # TODO: Get message ID, vendor ID, message contents, etc.
-        self.putx([0, ['SysEx message']])
+        if nn != 7: # If message type does not contain SMPTE type.
+            self.putx([0, ['%s: %s of %s, value 0x%01x' % \
+                          (group[0], status_bytes[msg][0],
+                          quarter_frame_type[nn][0], dd),
+                          '%s: %s of %s, value 0x%01x' % \
+                          (group[1], status_bytes[msg][1],
+                          quarter_frame_type[nn][1], dd),
+                          '%s: %s of %s, value 0x%01x' % \
+                          (group[2], status_bytes[msg][2],
+                          quarter_frame_type[nn][1], dd)]])
+            self.cmd, self.state = [], 'IDLE'
+            return
+        tt = (dd & 0x6) >> 1
+        self.putx([0, ['%s: %s of %s, value 0x%01x for %s' % \
+                      (group[0], status_bytes[msg][0], \
+                      quarter_frame_type[nn][0], dd, smpte_type[tt]),
+                      '%s: %s of %s, value 0x%01x for %s' % \
+                      (group[1], status_bytes[msg][1], \
+                      quarter_frame_type[nn][1], dd, smpte_type[tt]),
+                      '%s: %s of %s, value 0x%01x for %s' % \
+                      (group[2], status_bytes[msg][2], \
+                      quarter_frame_type[nn][1], dd, smpte_type[tt])]])
         self.cmd, self.state = [], 'IDLE'
 
     def handle_syscommon_msg(self, newbyte):
-        pass # TODO
+        # System common messages
+        #
+        # There are 5 simple formats (which are directly handled here) and
+        # 1 complex one called MIDI time code quarter frame.
+        #
+        # Note: While the MIDI lists 0xf7 as a "system common" message, it
+        # is actually only used with SysEx messages so it is processed there.
+        #
+        # Note: All System message codes don't utilize self.status_byte.
+        self.hard_clear_status_byte()
+        if newbyte is not None:
+            self.cmd.append(newbyte)
+        c = self.cmd
+        msg = c[0]
+        group = ('System Common', 'SysCom', 'SC')
+        if msg == 0xf1:
+            # MIDI time code quarter frame
+            self.handle_syscommon_midi_time_code_quarter_frame_msg(newbyte)
+            return
+        elif msg == 0xf2:
+            # Song position pointer: F2 ll mm
+            # ll = LSB position, mm = MSB position
+            if len(c) < 3:
+                if newbyte is None:
+                    self.handle_garbage_msg(None)
+                return
+            ll, mm = c[1], c[2]
+            decimal = (mm << 7) + ll
+            self.es_block = self.es
+            self.putx([0, ['%s: %s 0x%02x 0x%02x (%d)' % \
+                          (group[0], status_bytes[msg][0], ll, mm, decimal),
+                          '%s: %s 0x%02x 0x%02x (%d)' % \
+                          (group[1], status_bytes[msg][1], ll, mm, decimal),
+                          '%s: %s (%d)' % \
+                          (group[2], status_bytes[msg][2], decimal)]])
+        elif msg == 0xf3:
+            # Song select: F3 ss
+            # ss = song selection number
+            if len(c) < 2:
+                if newbyte is None:
+                    self.handle_garbage_msg(None)
+                return
+            ss = c[1]
+            self.es_block = self.es
+            self.putx([0, ['%s: %s number %d' % \
+                          (group[0], status_bytes[msg][0], ss),
+                          '%s: %s number %d' % \
+                          (group[1], status_bytes[msg][1], ss),
+                          '%s: %s # %d' % \
+                          (group[2], status_bytes[msg][2], ss)]])
+        elif msg == 0xf4 or msg == 0xf5 or msg == 0xf6:
+            # Undefined 0xf4, Undefined 0xf5, and Tune Request (respectively).
+            # All are only 1 byte long with no data bytes.
+            self.es_block = self.es
+            self.putx([0, ['%s: %s' % (group[0], status_bytes[msg][0]),
+                          '%s: %s' % (group[1], status_bytes[msg][1]),
+                          '%s: %s' % (group[2], status_bytes[msg][2])]])
+        self.cmd, self.state = [], 'IDLE'
 
     def handle_sysrealtime_msg(self, newbyte):
         # System realtime message: 0b11111ttt (t = message type)
+        #
+        # Important: These messages are handled differently from all others
+        # because they are allowed to temporarily interrupt other messages.
+        # The interrupted messages resume after the realtime message is done.
+        # Thus, they mostly leave 'self' the way it was found.
+        #
+        # Note: All System message codes don't utilize self.status_byte.
+        old_ss_block, old_es_block = self.ss_block, self.es_block
+        self.ss_block, self.es_block = self.ss, self.es
+        group = ('System Realtime', 'SysReal', 'SR')
+        self.putx([1, ['%s: %s' % (group[0], status_bytes[newbyte][0]),
+                      '%s: %s' % (group[1], status_bytes[newbyte][1]),
+                      '%s: %s' % (group[2], status_bytes[newbyte][2])]])
+        self.ss_block, self.es_block = old_ss_block, old_es_block
+        # Deliberately not resetting self.cmd or self.state.
+
+    def handle_garbage_msg(self, newbyte):
+        # Handle messages that are either not handled or are corrupt.
         self.es_block = self.es
-        self.putx([0, ['System realtime message: %s' % status_bytes[newbyte]]])
+        if newbyte is not None:
+            self.cmd.append(newbyte)
+            return
+        payload = '<empty>'
+        max_bytes = 16 # Put a limit on the length on the hex dump.
+        for index in range(len(self.cmd)):
+            if index == max_bytes:
+                payload += ' ...'
+                break
+            if index == 0:
+                payload = '0x%02x' % self.cmd[index]
+            else:
+                payload += ' 0x%02x' % self.cmd[index]
+        self.putx([2, ['UNHANDLED DATA: %s' % payload,
+                      'UNHANDLED', '???', '?']])
         self.cmd, self.state = [], 'IDLE'
+        self.hard_clear_status_byte()
+
+    def handle_state(self, state, newbyte):
+        # 'newbyte' can either be:
+        # 1. Value between 0x00-0xff, deal with the byte normally.
+        # 2. Value of 'None' which means "flush any buffered data".
+        if state == 'HANDLE CHANNEL MSG':
+            self.handle_channel_msg(newbyte)
+        elif state == 'HANDLE SYSEX MSG':
+            self.handle_sysex_msg(newbyte)
+        elif state == 'HANDLE SYSCOMMON MSG':
+            self.handle_syscommon_msg(newbyte)
+        elif state == 'HANDLE SYSREALTIME MSG':
+            self.handle_sysrealtime_msg(newbyte)
+        elif state == 'BUFFER GARBAGE MSG':
+            self.handle_garbage_msg(newbyte)
+
+    def get_next_state(self, newbyte):
+        # 'newbyte' must be a valid byte between 0x00 and 0xff.
+        #
+        # Try to determine the state based off of the 'newbyte' parameter.
+        if newbyte in range(0x80, 0xef + 1):
+            return 'HANDLE CHANNEL MSG'
+        if newbyte == 0xf0:
+            return 'HANDLE SYSEX MSG'
+        if newbyte in range(0xf1, 0xf7):
+            return'HANDLE SYSCOMMON MSG'
+        if newbyte in range(0xf8, 0xff + 1):
+            return 'HANDLE SYSREALTIME MSG'
+        # Passing 0xf7 is an error; messages don't start with 0xf7.
+        if newbyte == 0xf7:
+            return 'BUFFER GARBAGE MSG'
+        # Next, base the state off of self.status_byte.
+        if self.status_byte < 0x80:
+            return 'BUFFER GARBAGE MSG'
+        return self.get_next_state(self.status_byte)
 
     def decode(self, ss, es, data):
         ptype, rxtx, pdata = data
+        state = 'IDLE'
 
         # For now, ignore all UART packets except the actual data packets.
         if ptype != 'DATA':
             return
 
-        self.ss, self.es = ss, es
-
         # We're only interested in the byte value (not individual bits).
         pdata = pdata[0]
 
@@ -183,29 +586,39 @@ class Decoder(srd.Decoder):
         #  - Most messages: 1 status byte, 1-2 data bytes.
         #  - Real-time system messages: always 1 byte.
         #  - SysEx messages: 1 status byte, n data bytes, EOX byte.
+        #
+        # Aspects of the MIDI protocol that complicate decoding:
+        #  - MIDI System Realtime messages can briefly interrupt other
+        #    messages already in progress.
+        #  - "Running Status" allows for omitting the status byte in most
+        #    scenarios if sequential messages have the same status byte.
+        #  - System Exclusive (SysEx) messages can be terminated by ANY
+        #    status byte (not limited to EOX byte).
 
         # State machine.
-        if self.state == 'IDLE':
-            # Wait until we see a status byte (bit 7 must be set).
-            if pdata < 0x80:
-                return # TODO: How to handle? Ignore?
+        if pdata >= 0x80 and pdata != 0xf7:
+            state = self.get_next_state(pdata)
+            if state != 'HANDLE SYSREALTIME MSG' and self.state != 'IDLE':
+                # Flush the previous data since a new message is starting.
+                self.handle_state(self.state, None)
+            # Cache ss and es -after- flushing previous data.
+            self.ss, self.es = ss, es
             # This is a status byte, remember the start sample.
-            self.ss_block = ss
-            if pdata in range(0x80, 0xef + 1):
-                self.state = 'HANDLE CHANNEL MSG'
-            elif pdata == 0xf0:
-                self.state = 'HANDLE SYSEX MSG'
-            elif pdata in range(0xf1, 0xf7 + 1):
-                self.state = 'HANDLE SYSCOMMON MSG'
-            elif pdata in range(0xf8, 0xff + 1):
-                self.state = 'HANDLE SYSREALTIME MSG'
+            if state != 'HANDLE SYSREALTIME MSG':
+                self.ss_block = ss
+        elif self.state == 'IDLE' or self.state == 'BUFFER GARBAGE MSG':
+            # Deal with "running status" or that we're buffering garbage.
+            self.ss, self.es = ss, es
+            if self.state == 'IDLE':
+                self.ss_block = ss
+            state = self.get_next_state(pdata)
+        else:
+            self.ss, self.es = ss, es
+            state = self.state
 
         # Yes, this is intentionally _not_ an 'elif' here.
-        if self.state == 'HANDLE CHANNEL MSG':
-            self.handle_channel_msg(pdata)
-        elif self.state == 'HANDLE SYSEX MSG':
-            self.handle_sysex_msg(pdata)
-        elif self.state == 'HANDLE SYSCOMMON MSG':
-            self.handle_syscommon_msg(pdata)
-        elif self.state == 'HANDLE SYSREALTIME MSG':
-            self.handle_sysrealtime_msg(pdata)
+        if state != 'HANDLE SYSREALTIME MSG':
+            self.state = state
+        if state == 'BUFFER GARBAGE MSG':
+            self.status_byte = 0
+        self.handle_state(state, pdata)
index aa1ead548fe0b4a9878a1a4b83f027f20311b634..0a3abbaf62daf1aef006cdc285b89bb131b96f86 100644 (file)
@@ -34,7 +34,7 @@ class Decoder(srd.Decoder):
         ('kelvin', 'Temperature in Kelvin'),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.state = 'IGNORE START REPEAT'
         self.data = []
 
index d427844ca490892151899e3d8bf8d94db128b9c6..3420f99db419f33afdc873082b6d79376c50daf7 100644 (file)
@@ -816,7 +816,7 @@ class Decoder(srd.Decoder):
     name = 'Modbus'
     longname = 'Modbus RTU over RS232/RS485'
     desc = 'Modbus RTU protocol for industrial applications.'
-    license = 'gplv2+'
+    license = 'gplv3+'
     inputs = ['uart']
     outputs = ['modbus']
     annotations = (
@@ -846,7 +846,7 @@ class Decoder(srd.Decoder):
             'values': ('RX', 'TX')},
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.ADUSc = None # Start off with empty slave -> client ADU.
         self.ADUCs = None # Start off with empty client -> slave ADU.
 
index 286fa52403a4bd78d0c638cea2176a63a66de39e..8ef7017ec0cfb762a5a7697773ec6af91d1c2742 100644 (file)
@@ -27,7 +27,7 @@ class Decoder(srd.Decoder):
     name = 'MRF24J40'
     longname = 'Microchip MRF24J40'
     desc = 'IEEE 802.15.4 2.4 GHz RF tranceiver chip.'
-    license = 'gplv2'
+    license = 'gplv2+'
     inputs = ['spi']
     outputs = ['mrf24j40']
     annotations = (
@@ -43,7 +43,7 @@ class Decoder(srd.Decoder):
         ('warnings', 'Warnings', (4,)),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.ss_cmd, self.es_cmd = 0, 0
         self.mosi_bytes = []
         self.miso_bytes = []
index 962f963fa63e137cb976872978b5f51017f58d41..832eb06d61092d0f8a27a2bbf9e3a2dcc512b336 100644 (file)
@@ -72,7 +72,7 @@ class Decoder(srd.Decoder):
         ('text', 'Human-readable text'),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.state = 'IDLE'
 
     def start(self):
index 2337d4b6d01faed86c778e4b49ff89cbdebb9b4b..d46eea45a80783af28a6688544610a6dfda9e9cd 100644 (file)
@@ -94,7 +94,7 @@ class Decoder(srd.Decoder):
         ('warnings', 'Warnings', (ann_warn,)),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.next()
         self.requirements_met = True
         self.cs_was_released = False
index 0861cba3574cdbb393fe63cba590f5b7c1adcdb1..48a70991624232fdc882d8b8e3d1da376111df49 100644 (file)
@@ -47,7 +47,7 @@ class Decoder(srd.Decoder):
         ('warnings', 'Warnings', (14,)),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.state = 'IDLE'
         self.sx = self.sy = self.ax = self.ay = self.az = self.bz = self.bc = -1
         self.databytecount = 0
index 2be02426aa593ad835e049413e09030abf485e1d..bd64aa1e25a85da6938ff0a4e9c8de2f1f0f5276 100644 (file)
@@ -93,7 +93,7 @@ class Decoder(srd.Decoder):
     def putrs(self, data):
         self.put(self.rise, self.samplenum, self.out_ann, data)
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.samplerate = None
         self.samplenum = 0
         self.state = 'WAIT FOR FALLING EDGE'
index bddc4a8434246d12cbef28623c19ab191be57350..1a7567a144d76e5f8c24b28b060779d9dfc8781a 100644 (file)
@@ -45,7 +45,7 @@ class Decoder(srd.Decoder):
         ('text', 'Human-readable text'),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.ss_block = 0
         self.es_block = 0
         self.state = 'COMMAND'
index b70defc4f852b70b9831ef38529c38488bbb8fa8..a8938c60f6bd961f6972bf275be58004d75de025 100644 (file)
@@ -39,7 +39,7 @@ class Decoder(srd.Decoder):
         ('warnings', 'Human-readable warnings'),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.cmd = ['', '']
         self.ss_block = None
 
diff --git a/decoders/ps2/__init__.py b/decoders/ps2/__init__.py
new file mode 100644 (file)
index 0000000..75c4768
--- /dev/null
@@ -0,0 +1,26 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Daniel Schulte <trilader@schroedingers-bit.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 2 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 protocol decoder can decode PS/2 device -> host communication.
+
+Host -> device communication is currently not supported.
+'''
+
+from .pd import Decoder
diff --git a/decoders/ps2/pd.py b/decoders/ps2/pd.py
new file mode 100644 (file)
index 0000000..6392f1a
--- /dev/null
@@ -0,0 +1,144 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Daniel Schulte <trilader@schroedingers-bit.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 2 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+from collections import namedtuple
+
+class Ann:
+    BIT, START, STOP, PARITY_OK, PARITY_ERR, DATA, WORD = range(7)
+
+Bit = namedtuple('Bit', 'val ss es')
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'ps2'
+    name = 'PS/2'
+    longname = 'PS/2'
+    desc = 'PS/2 keyboard/mouse interface.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['ps2']
+    channels = (
+        {'id': 'clk', 'name': 'Clock', 'desc': 'Clock line'},
+        {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
+    )
+    annotations = (
+        ('bit', 'Bit'),
+        ('start-bit', 'Start bit'),
+        ('stop-bit', 'Stop bit'),
+        ('parity-ok', 'Parity OK bit'),
+        ('parity-err', 'Parity error bit'),
+        ('data-bit', 'Data bit'),
+        ('word', 'Word'),
+    )
+    annotation_rows = (
+        ('bits', 'Bits', (0,)),
+        ('fields', 'Fields', (1, 2, 3, 4, 5, 6)),
+    )
+
+    def __init__(self):
+        self.bits = []
+        self.prev_pins = None
+        self.prev_clock = None
+        self.samplenum = 0
+        self.clock_was_high = False
+        self.bitcount = 0
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def putb(self, bit, ann_idx):
+        b = self.bits[bit]
+        self.put(b.ss, b.es, self.out_ann, [ann_idx, [str(b.val)]])
+
+    def putx(self, bit, ann):
+        self.put(self.bits[bit].ss, self.bits[bit].es, self.out_ann, ann)
+
+    def handle_bits(self, datapin):
+        # Ignore non start condition bits (useful during keyboard init).
+        if self.bitcount == 0 and datapin == 1:
+            return
+
+        # Store individual bits and their start/end samplenumbers.
+        self.bits.append(Bit(datapin, self.samplenum, self.samplenum))
+
+        # Fix up end sample numbers of the bits.
+        if self.bitcount > 0:
+            b = self.bits[self.bitcount - 1]
+            self.bits[self.bitcount - 1] = Bit(b.val, b.ss, self.samplenum)
+        if self.bitcount == 11:
+            self.bitwidth = self.bits[1].es - self.bits[2].es
+            b = self.bits[-1]
+            self.bits[-1] = Bit(b.val, b.ss, b.es + self.bitwidth)
+
+        # Find all 11 bits. Start + 8 data + odd parity + stop.
+        if self.bitcount < 11:
+            self.bitcount += 1
+            return
+
+        # Extract data word.
+        word = 0
+        for i in range(8):
+            word |= (self.bits[i + 1].val << i)
+
+        # Calculate parity.
+        parity_ok = (bin(word).count('1') + self.bits[9].val) % 2 == 1
+
+        # Emit annotations.
+        for i in range(11):
+            self.putb(i, Ann.BIT)
+        self.putx(0, [Ann.START, ['Start bit', 'Start', 'S']])
+        self.put(self.bits[1].ss, self.bits[8].es, self.out_ann, [Ann.WORD,
+                 ['Data: %02x' % word, 'D: %02x' % word, '%02x' % word]])
+        if parity_ok:
+            self.putx(9, [Ann.PARITY_OK, ['Parity OK', 'Par OK', 'P']])
+        else:
+            self.putx(9, [Ann.PARITY_ERR, ['Parity error', 'Par err', 'PE']])
+        self.putx(10, [Ann.STOP, ['Stop bit', 'Stop', 'St', 'T']])
+
+        self.bits, self.bitcount = [], 0
+
+    def find_clk_edge(self, clock_pin, data_pin):
+        # Ignore sample if the clock pin hasn't changed.
+        if clock_pin == self.prev_clock:
+            return
+        self.prev_clock = clock_pin
+
+        # Sample on falling clock edge.
+        if clock_pin == 1:
+            return
+
+        # Found the correct clock edge, now get the bits.
+        self.handle_bits(data_pin)
+
+    def decode(self, ss, es, data):
+        for (self.samplenum, pins) in data:
+            clock_pin, data_pin = pins[0], pins[1]
+
+            # Ignore identical samples.
+            if self.prev_pins == pins:
+                continue
+            self.prev_pins = pins
+
+            if clock_pin == 0 and not self.clock_was_high:
+                continue
+            self.clock_was_high = True
+
+            self.find_clk_edge(clock_pin, data_pin)
index 7bf21f4637aec242f21758b161ec6705c4c210eb..6ca46b17d8ddfa4a7558843b2c5eaddad01fd2be 100644 (file)
@@ -49,8 +49,8 @@ class Decoder(srd.Decoder):
         ('raw', 'RAW file'),
     )
 
-    def __init__(self, **kwargs):
-        self.ss = self.es = None
+    def __init__(self):
+        self.ss_block = self.es_block = None
         self.first_transition = True
         self.first_samplenum = None
         self.start_samplenum = None
@@ -72,7 +72,7 @@ class Decoder(srd.Decoder):
                           meta=(float, 'Average', 'PWM base (cycle) frequency'))
 
     def putx(self, data):
-        self.put(self.ss, self.es, self.out_ann, data)
+        self.put(self.ss_block, self.es_block, self.out_ann, data)
 
     def putp(self, period_t):
         # Adjust granularity.
@@ -89,7 +89,7 @@ class Decoder(srd.Decoder):
         else:
             period_s = '%.1f ms' % (period_t * 1e3)
 
-        self.put(self.ss, self.es, self.out_ann, [1, [period_s]])
+        self.put(self.ss_block, self.es_block, self.out_ann, [1, [period_s]])
 
     def putb(self, data):
         self.put(self.num_cycles, self.num_cycles, self.out_binary, data)
@@ -122,10 +122,10 @@ class Decoder(srd.Decoder):
                     ratio = float(duty / period)
 
                     # This interval starts at this edge.
-                    self.ss = self.start_samplenum
+                    self.ss_block = self.start_samplenum
                     # Store the new rising edge position and the ending
                     # edge interval.
-                    self.start_samplenum = self.es = self.samplenum
+                    self.start_samplenum = self.es_block = self.samplenum
 
                     # Report the duty cycle in percent.
                     percent = float(ratio * 100)
@@ -141,10 +141,10 @@ class Decoder(srd.Decoder):
                     # Update and report the new duty cycle average.
                     self.num_cycles += 1
                     self.average += percent
-                    self.put(self.first_samplenum, self.es, self.out_average,
+                    self.put(self.first_samplenum, self.es_block, self.out_average,
                              float(self.average / self.num_cycles))
                 else:
                     # Falling edge
-                    self.end_samplenum = self.ss = self.samplenum
+                    self.end_samplenum = self.ss_block = self.samplenum
 
             self.oldpin = pins[0]
index cba4ce983fc93eec664dfd434a1ecdef61e44dc4..6db4bf52c662f07fd11f52355c016c149fc39d92 100644 (file)
@@ -73,7 +73,7 @@ class Decoder(srd.Decoder):
         ('packets', 'Packets', (5, 6, 7)),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.samplerate = None
         self.reset_variables()
 
index 9bcbd9d8c21439c75cdf295892fb4f38ec7857dc..46e5b07bfe7d7489cbf344445d09107b0e028383 100644 (file)
@@ -43,7 +43,7 @@ class Decoder(srd.Decoder):
         ('interpretation', 'Interpretation', (5,)),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.mosi_bytes, self.miso_bytes = [], []
         self.mosi_bits, self.miso_bits = [], []
         self.row_pos = [0, 0, 0]
index c6e1032fbe08487df1431f242562289b83a40a37..9a465ad066c654d4bcdb399910a51b96a57ea4b5 100644 (file)
@@ -26,14 +26,14 @@ class Decoder(srd.Decoder):
     name = 'RGB LED (SPI)'
     longname = 'RGB LED string decoder (SPI)'
     desc = 'RGB LED string protocol (RGB values clocked over SPI).'
-    license = 'gplv2'
+    license = 'gplv2+'
     inputs = ['spi']
     outputs = ['rgb_led_spi']
     annotations = (
         ('rgb', 'RGB values'),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.ss_cmd, self.es_cmd = 0, 0
         self.mosi_bytes = []
 
diff --git a/decoders/rgb_led_ws281x/__init__.py b/decoders/rgb_led_ws281x/__init__.py
new file mode 100644 (file)
index 0000000..63135a4
--- /dev/null
@@ -0,0 +1,28 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Vladimir Ermakov <vooon341@gmail.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+'''
+WS281x RGB LED protocol decoder.
+
+Details:
+https://cpldcpu.wordpress.com/2014/01/14/light_ws2812-library-v2-0-part-i-understanding-the-ws2812/
+'''
+
+from .pd import Decoder
diff --git a/decoders/rgb_led_ws281x/pd.py b/decoders/rgb_led_ws281x/pd.py
new file mode 100644 (file)
index 0000000..6f65960
--- /dev/null
@@ -0,0 +1,128 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Vladimir Ermakov <vooon341@gmail.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+from functools import reduce
+
+class SamplerateError(Exception):
+    pass
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'rgb_led_ws281x'
+    name = 'RGB LED (WS281x)'
+    longname = 'RGB LED string decoder (WS281x)'
+    desc = 'RGB LED string protocol (WS281x).'
+    license = 'gplv3+'
+    inputs = ['logic']
+    outputs = ['rgb_led_ws281x']
+    channels = (
+        {'id': 'din', 'name': 'DIN', 'desc': 'DIN data line'},
+    )
+    annotations = (
+        ('bit', 'Bit'),
+        ('reset', 'RESET'),
+        ('rgb', 'RGB'),
+    )
+    annotation_rows = (
+        ('bit', 'Bits', (0, 1)),
+        ('rgb', 'RGB', (2,)),
+    )
+
+    def __init__(self):
+        self.samplerate = None
+        self.oldpin = None
+        self.ss_packet = None
+        self.ss = None
+        self.es = None
+        self.bits = []
+        self.inreset = False
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+
+    def handle_bits(self, samplenum):
+        if len(self.bits) == 24:
+            grb = reduce(lambda a, b: (a << 1) | b, self.bits)
+            rgb = (grb & 0xff0000) >> 8 | (grb & 0x00ff00) << 8 | (grb & 0x0000ff)
+            self.put(self.ss_packet, samplenum, self.out_ann,
+                     [2, ['#%06x' % rgb]])
+            self.bits = []
+            self.ss_packet = None
+
+    def decode(self, ss, es, data):
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
+
+        for (samplenum, (pin, )) in data:
+            if self.oldpin is None:
+                self.oldpin = pin
+                continue
+
+            # Check RESET condition (manufacturer recommends 50 usec minimal,
+            # but real minimum is ~10 usec).
+            if not self.inreset and not pin and self.es is not None and \
+                    (samplenum - self.es) / self.samplerate > 50e-6:
+
+                # Decode last bit value.
+                tH = (self.es - self.ss) / self.samplerate
+                bit_ = True if tH >= 625e-9 else False
+
+                self.bits.append(bit_)
+                self.handle_bits(self.es)
+
+                self.put(self.ss, self.es, self.out_ann, [0, ['%d' % bit_]])
+                self.put(self.es, samplenum, self.out_ann,
+                         [1, ['RESET', 'RST', 'R']])
+
+                self.inreset = True
+                self.bits = []
+                self.ss_packet = None
+                self.ss = None
+
+            if not self.oldpin and pin:
+                # Rising edge.
+                if self.ss and self.es:
+                    period = samplenum - self.ss
+                    duty = self.es - self.ss
+                    # Ideal duty for T0H: 33%, T1H: 66%.
+                    bit_ = (duty / period) > 0.5
+
+                    self.put(self.ss, samplenum, self.out_ann,
+                             [0, ['%d' % bit_]])
+
+                    self.bits.append(bit_)
+                    self.handle_bits(samplenum)
+
+                if self.ss_packet is None:
+                    self.ss_packet = samplenum
+
+                self.ss = samplenum
+
+            elif self.oldpin and not pin:
+                # Falling edge.
+                self.inreset = False
+                self.es = samplenum
+
+            self.oldpin = pin
index 24a68fbe28da6f50c8d30f4a57ddf1de8f0e8d9f..4ecc15a615d56eda4960ceebce7569b21c52ac4e 100644 (file)
 ##
 
 import sigrokdecode as srd
-
-# Return the specified BCD number (max. 8 bits) as integer.
-def bcd2int(b):
-    return (b & 0x0f) + ((b >> 4) * 10)
+from common.srdhelper import bcd2int
 
 def reg_list():
     l = []
@@ -55,7 +52,7 @@ class Decoder(srd.Decoder):
         ('date-time', 'Date/time', (9, 10)),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.state = 'IDLE'
         self.hours = -1
         self.minutes = -1
index a05f21aa1f4e401c8b478bfe4985f8ad0da97f30..c43f6237d1128b4d1b2dafa236992ff78db97106 100644 (file)
@@ -1,5 +1,5 @@
 ##
-## This file is part of the sigrok project.
+## This file is part of the libsigrokdecode project.
 ##
 ## Copyright (C) 2015 Uwe Hermann <uwe@hermann-uwe.de>
 ##
index a1eac2be2cc8de33305dcbcbf6db494a2cb2a741..79c20740d4c9b99890f1503e742927df6f023640 100644 (file)
@@ -1,5 +1,5 @@
 ##
-## This file is part of the sigrok project.
+## This file is part of the libsigrokdecode project.
 ##
 ## Copyright (C) 2015 Uwe Hermann <uwe@hermann-uwe.de>
 ##
@@ -19,7 +19,7 @@
 ##
 
 import sigrokdecode as srd
-from .lists import *
+from common.sdcard import (cmd_names, acmd_names, accepted_voltages, card_status, sd_status)
 
 class Decoder(srd.Decoder):
     api_version = 2
@@ -61,7 +61,7 @@ class Decoder(srd.Decoder):
         ('cmd', 'Commands', tuple(range(128))),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.state = 'GET COMMAND TOKEN'
         self.token = []
         self.oldpins = None
index bc761ee822914ad0fe67dbd873d18f3c50128d8a..45490adb03cd80e03783bad11d13984cbb1c0b58 100644 (file)
 ##
 
 import sigrokdecode as srd
-
-# Normal commands (CMD)
-cmd_names = {
-    0:  'GO_IDLE_STATE',
-    1:  'SEND_OP_COND',
-    6:  'SWITCH_FUNC',
-    8:  'SEND_IF_COND',
-    9:  'SEND_CSD',
-    10: 'SEND_CID',
-    12: 'STOP_TRANSMISSION',
-    13: 'SEND_STATUS',
-    16: 'SET_BLOCKLEN',
-    17: 'READ_SINGLE_BLOCK',
-    18: 'READ_MULTIPLE_BLOCK',
-    24: 'WRITE_BLOCK',
-    25: 'WRITE_MULTIPLE_BLOCK',
-    27: 'PROGRAM_CSD',
-    28: 'SET_WRITE_PROT',
-    29: 'CLR_WRITE_PROT',
-    30: 'SEND_WRITE_PROT',
-    32: 'ERASE_WR_BLK_START_ADDR',
-    33: 'ERASE_WR_BLK_END_ADDR',
-    38: 'ERASE',
-    42: 'LOCK_UNLOCK',
-    55: 'APP_CMD',
-    56: 'GEN_CMD',
-    58: 'READ_OCR',
-    59: 'CRC_ON_OFF',
-    # CMD60-63: Reserved for manufacturer
-}
-
-# Application-specific commands (ACMD)
-acmd_names = {
-    13: 'SD_STATUS',
-    18: 'Reserved for SD security applications',
-    22: 'SEND_NUM_WR_BLOCKS',
-    23: 'SET_WR_BLK_ERASE_COUNT',
-    25: 'Reserved for SD security applications',
-    26: 'Reserved for SD security applications',
-    38: 'Reserved for SD security applications',
-    41: 'SD_SEND_OP_COND',
-    42: 'SET_CLR_CARD_DETECT',
-    43: 'Reserved for SD security applications',
-    44: 'Reserved for SD security applications',
-    45: 'Reserved for SD security applications',
-    46: 'Reserved for SD security applications',
-    47: 'Reserved for SD security applications',
-    48: 'Reserved for SD security applications',
-    49: 'Reserved for SD security applications',
-    51: 'SEND_SCR',
-}
+from common.sdcard import (cmd_names, acmd_names)
 
 class Decoder(srd.Decoder):
     api_version = 2
@@ -96,7 +46,7 @@ class Decoder(srd.Decoder):
         ('cmd-reply', 'Commands/replies', tuple(range(134))),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.state = 'IDLE'
         self.ss, self.es = 0, 0
         self.ss_bit, self.es_bit = 0, 0
@@ -122,7 +72,12 @@ class Decoder(srd.Decoder):
 
     def cmd_name(self, cmd):
         c = acmd_names if self.is_acmd else cmd_names
-        return c.get(cmd, 'Unknown')
+        s = c.get(cmd, 'Unknown')
+        # SD mode names for CMD32/33: ERASE_WR_BLK_{START,END}.
+        # SPI mode names for CMD32/33: ERASE_WR_BLK_{START,END}_ADDR.
+        if cmd in (32, 33):
+            s += '_ADDR'
+        return s
 
     def handle_command_token(self, mosi, miso):
         # Command tokens (6 bytes) are sent (MSB-first) by the host.
index e6977e26858725a55c6c1998a65a578b1099f513..da3ca283ca87e8761ed05288c1bfc7fae3d58382 100644 (file)
@@ -58,7 +58,7 @@ class Decoder(srd.Decoder):
     def puty(self, data):
         self.put(self.ss_edge, self.samplenum, self.out_ann, data)
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.state = 'GET FIRST PULSE WIDTH'
         self.olddata = None
         self.ss_edge = None
index fc8c7d0024bf8169c431566cb87245211b6d2ed0..ce007e231b0866d11d7a44bfce9432d1cd5effdf 100644 (file)
@@ -152,6 +152,7 @@ class Decoder(srd.Decoder):
         self.out_binary = self.register(srd.OUTPUT_BINARY)
         self.out_bitrate = self.register(srd.OUTPUT_META,
                 meta=(int, 'Bitrate', 'Bitrate during transfers'))
+        self.bw = (self.options['wordsize'] + 7) // 8
 
     def putw(self, data):
         self.put(self.ss_block, self.samplenum, self.out_ann, data)
@@ -165,10 +166,12 @@ class Decoder(srd.Decoder):
 
         if self.have_miso:
             ss, es = self.misobits[-1][1], self.misobits[0][2]
-            self.put(ss, es, self.out_binary, [0, bytes([so])])
+            bdata = so.to_bytes(self.bw, byteorder='big')
+            self.put(ss, es, self.out_binary, [0, bdata])
         if self.have_mosi:
             ss, es = self.mosibits[-1][1], self.mosibits[0][2]
-            self.put(ss, es, self.out_binary, [1, bytes([si])])
+            bdata = si.to_bytes(self.bw, byteorder='big')
+            self.put(ss, es, self.out_binary, [1, bdata])
 
         self.put(ss, es, self.out_python, ['BITS', si_bits, so_bits])
         self.put(ss, es, self.out_python, ['DATA', si, so])
index 4ed6aaf37691e3d02c0ccfe130bb6791c6c21f8a..ba9f2c2b5a6dd5fff6e37aba4558184132990fc7 100644 (file)
 ## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 ##
 
-# Dict which maps command IDs to their names and descriptions.
-cmds = {
-    0x06: ('WREN', 'Write enable'),
-    0x04: ('WRDI', 'Write disable'),
-    0x9f: ('RDID', 'Read identification'),
-    0x05: ('RDSR', 'Read status register'),
-    0x01: ('WRSR', 'Write status register'),
-    0x03: ('READ', 'Read data'),
-    0x0b: ('FAST/READ', 'Fast read data'),
-    0xbb: ('2READ', '2x I/O read'),
-    0x20: ('SE', 'Sector erase'),
-    0xd8: ('BE', 'Block erase'),
-    0x60: ('CE', 'Chip erase'),
-    0xc7: ('CE2', 'Chip erase'), # Alternative command ID
-    0x02: ('PP', 'Page program'),
-    0xad: ('CP', 'Continuously program mode'),
-    0xb9: ('DP', 'Deep power down'),
-    0xab: ('RDP/RES', 'Release from deep powerdown / Read electronic ID'),
-    0x90: ('REMS', 'Read electronic manufacturer & device ID'),
-    0xef: ('REMS2', 'Read ID for 2x I/O mode'),
-    0xb1: ('ENSO', 'Enter secured OTP'),
-    0xc1: ('EXSO', 'Exit secured OTP'),
-    0x2b: ('RDSCUR', 'Read security register'),
-    0x2f: ('WRSCUR', 'Write security register'),
-    0x70: ('ESRY', 'Enable SO to output RY/BY#'),
-    0x80: ('DSRY', 'Disable SO to output RY/BY#'),
-}
+from collections import OrderedDict
+
+# OrderedDict which maps command IDs to their names and descriptions.
+# Please keep this sorted by command ID.
+# Don't forget to update 'Ann' in pd.py if you add/remove items here.
+cmds = OrderedDict([
+    (0x01, ('WRSR', 'Write status register')),
+    (0x02, ('PP', 'Page program')),
+    (0x03, ('READ', 'Read data')),
+    (0x04, ('WRDI', 'Write disable')),
+    (0x05, ('RDSR', 'Read status register')),
+    (0x06, ('WREN', 'Write enable')),
+    (0x0b, ('FAST/READ', 'Fast read data')),
+    (0x20, ('SE', 'Sector erase')),
+    (0x2b, ('RDSCUR', 'Read security register')),
+    (0x2f, ('WRSCUR', 'Write security register')),
+    (0x35, ('RDSR2', 'Read status register 2')),
+    (0x60, ('CE', 'Chip erase')),
+    (0x70, ('ESRY', 'Enable SO to output RY/BY#')),
+    (0x80, ('DSRY', 'Disable SO to output RY/BY#')),
+    (0x90, ('REMS', 'Read electronic manufacturer & device ID')),
+    (0x9f, ('RDID', 'Read identification')),
+    (0xab, ('RDP/RES', 'Release from deep powerdown / Read electronic ID')),
+    (0xad, ('CP', 'Continuously program mode')),
+    (0xb1, ('ENSO', 'Enter secured OTP')),
+    (0xb9, ('DP', 'Deep power down')),
+    (0xbb, ('2READ', '2x I/O read')), # a.k.a. "Fast read dual I/O".
+    (0xc1, ('EXSO', 'Exit secured OTP')),
+    (0xc7, ('CE2', 'Chip erase')), # Alternative command ID
+    (0xd8, ('BE', 'Block erase')),
+    (0xef, ('REMS2', 'Read ID for 2x I/O mode')),
+])
 
 device_name = {
-    0x14: 'MX25L1605D',
-    0x15: 'MX25L3205D',
-    0x16: 'MX25L6405D',
+    'fidelix': {
+        0x15: 'FM25Q32',
+    },
+    'macronix': {
+        0x14: 'MX25L1605D',
+        0x15: 'MX25L3205D',
+        0x16: 'MX25L6405D',
+    },
 }
 
 chips = {
+    # FIDELIX
+    'fidelix_fm25q32': {
+        'vendor': 'FIDELIX',
+        'model': 'FM25Q32',
+        'res_id': 0x15,
+        'rems_id': 0xa115,
+        'rems2_id': 0xa115,
+        'rdid_id': 0xa14016,
+        'page_size': 256,
+        'sector_size': 4 * 1024,
+        'block_size': 64 * 1024,
+    },
     # Macronix
     'macronix_mx25l1605d': {
         'vendor': 'Macronix',
index 0dfa035c03d07df4a11b08244ddd5487506c2d8d..bc5f9ca8075828e0bad0e3bb538c82349a44c770 100644 (file)
@@ -1,7 +1,7 @@
 ##
 ## This file is part of the libsigrokdecode project.
 ##
-## Copyright (C) 2011-2015 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2011-2016 Uwe Hermann <uwe@hermann-uwe.de>
 ##
 ## 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
 import sigrokdecode as srd
 from .lists import *
 
+L = len(cmds)
+
+# Don't forget to keep this in sync with 'cmds' is lists.py.
+class Ann:
+    WRSR, PP, READ, WRDI, RDSR, WREN, FAST_READ, SE, RDSCUR, WRSCUR, \
+    RDSR2, CE, ESRY, DSRY, REMS, RDID, RDP_RES, CP, ENSO, DP, READ2X, \
+    EXSO, CE2, BE, REMS2, \
+    BIT, FIELD, WARN = range(L + 3)
+
 def cmd_annotation_classes():
     return tuple([tuple([cmd[0].lower(), cmd[1]]) for cmd in cmds.values()])
 
+def decode_dual_bytes(sio0, sio1):
+    # Given a byte in SIO0 (MOSI) of even bits and a byte in
+    # SIO1 (MISO) of odd bits, return a tuple of two bytes.
+    def combine_byte(even, odd):
+        result = 0
+        for bit in range(4):
+            if even & (1 << bit):
+                result |= 1 << (bit*2)
+            if odd & (1 << bit):
+                result |= 1 << ((bit*2) + 1)
+        return result
+    return (combine_byte(sio0 >> 4, sio1 >> 4), combine_byte(sio0, sio1))
+
 def decode_status_reg(data):
     # TODO: Additional per-bit(s) self.put() calls with correct start/end.
 
@@ -59,21 +81,40 @@ class Decoder(srd.Decoder):
     inputs = ['spi']
     outputs = ['spiflash']
     annotations = cmd_annotation_classes() + (
-        ('bits', 'Bits'),
-        ('bits2', 'Bits2'),
-        ('warnings', 'Warnings'),
+        ('bit', 'Bit'),
+        ('field', 'Field'),
+        ('warning', 'Warning'),
     )
     annotation_rows = (
-        ('bits', 'Bits', (24, 25)),
-        ('commands', 'Commands', tuple(range(23 + 1))),
-        ('warnings', 'Warnings', (26,)),
+        ('bits', 'Bits', (L + 0,)),
+        ('fields', 'Fields', (L + 1,)),
+        ('commands', 'Commands', tuple(range(len(cmds)))),
+        ('warnings', 'Warnings', (L + 2,)),
     )
     options = (
         {'id': 'chip', 'desc': 'Chip', 'default': tuple(chips.keys())[0],
             'values': tuple(chips.keys())},
+        {'id': 'format', 'desc': 'Data format', 'default': 'hex',
+            'values': ('hex', 'ascii')},
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
+        self.device_id = -1
+        self.on_end_transaction = None
+        self.end_current_transaction()
+
+        # Build dict mapping command keys to handler functions. Each
+        # command in 'cmds' (defined in lists.py) has a matching
+        # handler self.handle_<shortname>.
+        def get_handler(cmd):
+            s = 'handle_%s' % cmds[cmd][0].lower().replace('/', '_')
+            return getattr(self, s)
+        self.cmd_handlers = dict((cmd, get_handler(cmd)) for cmd in cmds.keys())
+
+    def end_current_transaction(self):
+        if self.on_end_transaction is not None: # Callback for CS# transition.
+            self.on_end_transaction()
+            self.on_end_transaction = None
         self.state = None
         self.cmdstate = 1
         self.addr = 0
@@ -82,43 +123,78 @@ class Decoder(srd.Decoder):
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
         self.chip = chips[self.options['chip']]
+        self.vendor = self.options['chip'].split('_')[0]
 
     def putx(self, data):
         # Simplification, most annotations span exactly one SPI byte/packet.
         self.put(self.ss, self.es, self.out_ann, data)
 
-    def putb(self, data):
-        self.put(self.block_ss, self.block_es, self.out_ann, data)
+    def putf(self, data):
+        self.put(self.ss_field, self.es_field, self.out_ann, data)
+
+    def putc(self, data):
+        self.put(self.ss_cmd, self.es_cmd, self.out_ann, data)
+
+    def device(self):
+        return device_name[self.vendor].get(self.device_id, 'Unknown')
+
+    def vendor_device(self):
+        return '%s %s' % (self.chip['vendor'], self.device())
+
+    def cmd_ann_list(self):
+        x, s = cmds[self.state][0], cmds[self.state][1]
+        return ['Command: %s (%s)' % (s, x), 'Command: %s' % s,
+                'Cmd: %s' % s, 'Cmd: %s' % x, x]
+
+    def cmd_vendor_dev_list(self):
+        c, d = cmds[self.state], 'Device = %s' % self.vendor_device()
+        return ['%s (%s): %s' % (c[1], c[0], d), '%s: %s' % (c[1], d),
+                '%s: %s' % (c[0], d), d, self.vendor_device()]
+
+    def emit_cmd_byte(self):
+        self.ss_cmd = self.ss
+        self.putx([Ann.FIELD, self.cmd_ann_list()])
+        self.addr = 0
+
+    def emit_addr_bytes(self, mosi):
+        self.addr |= (mosi << ((4 - self.cmdstate) * 8))
+        b = ((3 - (self.cmdstate - 2)) * 8) - 1
+        self.putx([Ann.BIT,
+            ['Address bits %d..%d: 0x%02x' % (b, b - 7, mosi),
+             'Addr bits %d..%d: 0x%02x' % (b, b - 7, mosi),
+             'Addr bits %d..%d' % (b, b - 7), 'A%d..A%d' % (b, b - 7)]])
+        if self.cmdstate == 2:
+            self.ss_field = self.ss
+        if self.cmdstate == 4:
+            self.es_field = self.es
+            self.putf([Ann.FIELD, ['Address: 0x%06x' % self.addr,
+                'Addr: 0x%06x' % self.addr, '0x%06x' % self.addr]])
 
     def handle_wren(self, mosi, miso):
-        self.putx([0, ['Command: %s' % cmds[self.state][1]]])
+        self.putx([Ann.WREN, self.cmd_ann_list()])
         self.state = None
 
     def handle_wrdi(self, mosi, miso):
         pass # TODO
 
-    # TODO: Check/display device ID / name
     def handle_rdid(self, mosi, miso):
         if self.cmdstate == 1:
             # Byte 1: Master sends command ID.
-            self.ss_block = self.ss
-            self.putx([2, ['Command: %s' % cmds[self.state][1]]])
+            self.emit_cmd_byte()
         elif self.cmdstate == 2:
             # Byte 2: Slave sends the JEDEC manufacturer ID.
-            self.putx([2, ['Manufacturer ID: 0x%02x' % miso]])
+            self.putx([Ann.FIELD, ['Manufacturer ID: 0x%02x' % miso]])
         elif self.cmdstate == 3:
-            # Byte 3: Slave sends the memory type (0x20 for this chip).
-            self.putx([2, ['Memory type: 0x%02x' % miso]])
+            # Byte 3: Slave sends the memory type.
+            self.putx([Ann.FIELD, ['Memory type: 0x%02x' % miso]])
         elif self.cmdstate == 4:
             # Byte 4: Slave sends the device ID.
             self.device_id = miso
-            self.putx([2, ['Device ID: 0x%02x' % miso]])
+            self.putx([Ann.FIELD, ['Device ID: 0x%02x' % miso]])
 
         if self.cmdstate == 4:
-            # TODO: Check self.device_id is valid & exists in device_names.
-            # TODO: Same device ID? Check!
-            d = 'Device: Macronix %s' % device_name[self.device_id]
-            self.put(self.ss_block, self.es, self.out_ann, [0, [d]])
+            self.es_cmd = self.es
+            self.putc([Ann.RDID, self.cmd_vendor_dev_list()])
             self.state = None
         else:
             self.cmdstate += 1
@@ -130,52 +206,69 @@ class Decoder(srd.Decoder):
         # When done, the master de-asserts CS# again.
         if self.cmdstate == 1:
             # Byte 1: Master sends command ID.
-            self.putx([3, ['Command: %s' % cmds[self.state][1]]])
+            self.emit_cmd_byte()
         elif self.cmdstate >= 2:
             # Bytes 2-x: Slave sends status register as long as master clocks.
-            if self.cmdstate <= 3: # TODO: While CS# asserted.
-                self.putx([24, ['Status register: 0x%02x' % miso]])
-                self.putx([25, [decode_status_reg(miso)]])
-
-            if self.cmdstate == 3: # TODO: If CS# got de-asserted.
-                self.state = None
-                return
+            self.es_cmd = self.es
+            self.putx([Ann.BIT, [decode_status_reg(miso)]])
+            self.putx([Ann.FIELD, ['Status register']])
+            self.putc([Ann.RDSR, self.cmd_ann_list()])
+        self.cmdstate += 1
 
+    def handle_rdsr2(self, mosi, miso):
+        # Read status register 2: Master asserts CS#, sends RDSR2 command,
+        # reads status register 2 byte. If CS# is kept asserted, the status
+        # register 2 can be read continuously / multiple times in a row.
+        # When done, the master de-asserts CS# again.
+        if self.cmdstate == 1:
+            # Byte 1: Master sends command ID.
+            self.emit_cmd_byte()
+        elif self.cmdstate >= 2:
+            # Bytes 2-x: Slave sends status register 2 as long as master clocks.
+            self.es_cmd = self.es
+            # TODO: Decode status register 2 correctly.
+            self.putx([Ann.BIT, [decode_status_reg(miso)]])
+            self.putx([Ann.FIELD, ['Status register 2']])
+            self.putc([Ann.RDSR2, self.cmd_ann_list()])
         self.cmdstate += 1
 
     def handle_wrsr(self, mosi, miso):
-        pass # TODO
+        # Write status register: Master asserts CS#, sends WRSR command,
+        # writes 1 or 2 status register byte(s).
+        # When done, the master de-asserts CS# again. If this doesn't happen
+        # the WRSR command will not be executed.
+        if self.cmdstate == 1:
+            # Byte 1: Master sends command ID.
+            self.emit_cmd_byte()
+        elif self.cmdstate == 2:
+            # Byte 2: Master sends status register 1.
+            self.putx([Ann.BIT, [decode_status_reg(miso)]])
+            self.putx([Ann.FIELD, ['Status register 1']])
+        elif self.cmdstate == 3:
+            # Byte 3: Master sends status register 2.
+            # TODO: Decode status register 2 correctly.
+            self.putx([Ann.BIT, [decode_status_reg(miso)]])
+            self.putx([Ann.FIELD, ['Status register 2']])
+            self.es_cmd = self.es
+            self.putc([Ann.WRSR, self.cmd_ann_list()])
+        self.cmdstate += 1
 
     def handle_read(self, mosi, miso):
         # Read data bytes: Master asserts CS#, sends READ command, sends
         # 3-byte address, reads >= 1 data bytes, de-asserts CS#.
         if self.cmdstate == 1:
             # Byte 1: Master sends command ID.
-            self.putx([5, ['Command: %s' % cmds[self.state][1]]])
+            self.emit_cmd_byte()
         elif self.cmdstate in (2, 3, 4):
             # Bytes 2/3/4: Master sends read address (24bits, MSB-first).
-            self.addr |= (mosi << ((4 - self.cmdstate) * 8))
-            # self.putx([0, ['Read address, byte %d: 0x%02x' % \
-            #                (4 - self.cmdstate, mosi)]])
-            if self.cmdstate == 4:
-                self.putx([24, ['Read address: 0x%06x' % self.addr]])
-                self.addr = 0
+            self.emit_addr_bytes(mosi)
         elif self.cmdstate >= 5:
             # Bytes 5-x: Master reads data bytes (until CS# de-asserted).
-            # TODO: For now we hardcode 256 bytes per READ command.
-            if self.cmdstate <= 256 + 4: # TODO: While CS# asserted.
-                self.data.append(miso)
-                # self.putx([0, ['New read byte: 0x%02x' % miso]])
-
-            if self.cmdstate == 256 + 4: # TODO: If CS# got de-asserted.
-                # s = ', '.join(map(hex, self.data))
-                s = ''.join(map(chr, self.data))
-                self.putx([24, ['Read data']])
-                self.putx([25, ['Read data: %s' % s]])
-                self.data = []
-                self.state = None
-                return
-
+            self.es_field = self.es # Will be overwritten for each byte.
+            if self.cmdstate == 5:
+                self.ss_field = self.ss
+                self.on_end_transaction = lambda: self.output_data_block('Data', Ann.READ)
+            self.data.append(miso)
         self.cmdstate += 1
 
     def handle_fast_read(self, mosi, miso):
@@ -183,60 +276,70 @@ class Decoder(srd.Decoder):
         # 3-byte address + 1 dummy byte, reads >= 1 data bytes, de-asserts CS#.
         if self.cmdstate == 1:
             # Byte 1: Master sends command ID.
-            self.putx([5, ['Command: %s' % cmds[self.state][1]]])
+            self.emit_cmd_byte()
         elif self.cmdstate in (2, 3, 4):
             # Bytes 2/3/4: Master sends read address (24bits, MSB-first).
-            self.putx([24, ['AD%d: 0x%02x' % (self.cmdstate - 1, mosi)]])
-            if self.cmdstate == 2:
-                self.block_ss = self.ss
-            self.addr |= (mosi << ((4 - self.cmdstate) * 8))
+            self.emit_addr_bytes(mosi)
         elif self.cmdstate == 5:
-            self.putx([24, ['Dummy byte: 0x%02x' % mosi]])
-            self.block_es = self.es
-            self.putb([5, ['Read address: 0x%06x' % self.addr]])
-            self.addr = 0
+            self.putx([Ann.BIT, ['Dummy byte: 0x%02x' % mosi]])
         elif self.cmdstate >= 6:
             # Bytes 6-x: Master reads data bytes (until CS# de-asserted).
-            # TODO: For now we hardcode 32 bytes per FAST READ command.
+            self.es_field = self.es # Will be overwritten for each byte.
             if self.cmdstate == 6:
-                self.block_ss = self.ss
-            if self.cmdstate <= 32 + 5: # TODO: While CS# asserted.
-                self.data.append(miso)
-            if self.cmdstate == 32 + 5: # TODO: If CS# got de-asserted.
-                self.block_es = self.es
-                s = ' '.join([hex(b)[2:] for b in self.data])
-                self.putb([25, ['Read data: %s' % s]])
-                self.data = []
-                self.state = None
-                return
-
+                self.ss_field = self.ss
+                self.on_end_transaction = lambda: self.output_data_block('Data', Ann.FAST_READ)
+            self.data.append(miso)
         self.cmdstate += 1
 
     def handle_2read(self, mosi, miso):
-        pass # TODO
+        # 2x I/O read (fast read dual I/O): Master asserts CS#, sends 2READ
+        # command, sends 3-byte address + 1 dummy byte, reads >= 1 data bytes,
+        # de-asserts CS#. All data after the command is sent via two I/O pins.
+        # MOSI = SIO0 = even bits, MISO = SIO1 = odd bits.
+        if self.cmdstate != 1:
+            b1, b2 = decode_dual_bytes(mosi, miso)
+        if self.cmdstate == 1:
+            # Byte 1: Master sends command ID.
+            self.emit_cmd_byte()
+        elif self.cmdstate == 2:
+            # Bytes 2/3(/4): Master sends read address (24bits, MSB-first).
+            # Handle bytes 2 and 3 here.
+            self.emit_addr_bytes(b1)
+            self.cmdstate = 3
+            self.emit_addr_bytes(b2)
+        elif self.cmdstate == 4:
+            # Byte 5: Dummy byte. Also handle byte 4 (address LSB) here.
+            self.emit_addr_bytes(b1)
+            self.cmdstate = 5
+            self.putx([Ann.BIT, ['Dummy byte: 0x%02x' % b2]])
+        elif self.cmdstate >= 6:
+            # Bytes 6-x: Master reads data bytes (until CS# de-asserted).
+            self.es_field = self.es # Will be overwritten for each byte.
+            if self.cmdstate == 6:
+                self.ss_field = self.ss
+                self.on_end_transaction = lambda: self.output_data_block('Data', Ann.READ2X)
+            self.data.append(b1)
+            self.data.append(b2)
+        self.cmdstate += 1
 
     # TODO: Warn/abort if we don't see the necessary amount of bytes.
     # TODO: Warn if WREN was not seen before.
     def handle_se(self, mosi, miso):
         if self.cmdstate == 1:
             # Byte 1: Master sends command ID.
-            self.addr = 0
-            self.ss_block = self.ss
-            self.putx([8, ['Command: %s' % cmds[self.state][1]]])
+            self.emit_cmd_byte()
         elif self.cmdstate in (2, 3, 4):
             # Bytes 2/3/4: Master sends sector address (24bits, MSB-first).
-            self.addr |= (mosi << ((4 - self.cmdstate) * 8))
-            # self.putx([0, ['Sector address, byte %d: 0x%02x' % \
-            #                (4 - self.cmdstate, mosi)]])
+            self.emit_addr_bytes(mosi)
 
         if self.cmdstate == 4:
+            self.es_cmd = self.es
             d = 'Erase sector %d (0x%06x)' % (self.addr, self.addr)
-            self.put(self.ss_block, self.es, self.out_ann, [24, [d]])
+            self.putc([Ann.SE, [d]])
             # TODO: Max. size depends on chip, check that too if possible.
             if self.addr % 4096 != 0:
                 # Sector addresses must be 4K-aligned (same for all 3 chips).
-                d = 'Warning: Invalid sector address!'
-                self.put(self.ss_block, self.es, self.out_ann, [101, [d]])
+                self.putc([Ann.WARN, ['Warning: Invalid sector address!']])
             self.state = None
         else:
             self.cmdstate += 1
@@ -255,31 +358,17 @@ class Decoder(srd.Decoder):
         # page address, sends >= 1 data bytes, de-asserts CS#.
         if self.cmdstate == 1:
             # Byte 1: Master sends command ID.
-            self.putx([12, ['Command: %s' % cmds[self.state][1]]])
+            self.emit_cmd_byte()
         elif self.cmdstate in (2, 3, 4):
             # Bytes 2/3/4: Master sends page address (24bits, MSB-first).
-            self.addr |= (mosi << ((4 - self.cmdstate) * 8))
-            # self.putx([0, ['Page address, byte %d: 0x%02x' % \
-            #                (4 - self.cmdstate, mosi)]])
-            if self.cmdstate == 4:
-                self.putx([24, ['Page address: 0x%06x' % self.addr]])
-                self.addr = 0
+            self.emit_addr_bytes(mosi)
         elif self.cmdstate >= 5:
             # Bytes 5-x: Master sends data bytes (until CS# de-asserted).
-            # TODO: For now we hardcode 256 bytes per page / PP command.
-            if self.cmdstate <= 256 + 4: # TODO: While CS# asserted.
-                self.data.append(mosi)
-                # self.putx([0, ['New data byte: 0x%02x' % mosi]])
-
-            if self.cmdstate == 256 + 4: # TODO: If CS# got de-asserted.
-                # s = ', '.join(map(hex, self.data))
-                s = ''.join(map(chr, self.data))
-                self.putx([24, ['Page data']])
-                self.putx([25, ['Page data: %s' % s]])
-                self.data = []
-                self.state = None
-                return
-
+            self.es_field = self.es # Will be overwritten for each byte.
+            if self.cmdstate == 5:
+                self.ss_field = self.ss
+                self.on_end_transaction = lambda: self.output_data_block('Data', Ann.PP)
+            self.data.append(mosi)
         self.cmdstate += 1
 
     def handle_cp(self, mosi, miso):
@@ -289,38 +378,52 @@ class Decoder(srd.Decoder):
         pass # TODO
 
     def handle_rdp_res(self, mosi, miso):
-        pass # TODO
+        if self.cmdstate == 1:
+            # Byte 1: Master sends command ID.
+            self.emit_cmd_byte()
+        elif self.cmdstate in (2, 3, 4):
+            # Bytes 2/3/4: Master sends three dummy bytes.
+            self.putx([Ann.FIELD, ['Dummy byte: %02x' % mosi]])
+        elif self.cmdstate == 5:
+            # Byte 5: Slave sends device ID.
+            self.es_cmd = self.es
+            self.device_id = miso
+            self.putx([Ann.FIELD, ['Device ID: %s' % self.device()]])
+            d = 'Device = %s' % self.vendor_device()
+            self.putc([Ann.RDP_RES, self.cmd_vendor_dev_list()])
+            self.state = None
+        self.cmdstate += 1
 
     def handle_rems(self, mosi, miso):
         if self.cmdstate == 1:
             # Byte 1: Master sends command ID.
-            self.ss_block = self.ss
-            self.putx([16, ['Command: %s' % cmds[self.state][1]]])
+            self.emit_cmd_byte()
         elif self.cmdstate in (2, 3):
             # Bytes 2/3: Master sends two dummy bytes.
-            # TODO: Check dummy bytes? Check reply from device?
-            self.putx([24, ['Dummy byte: %s' % mosi]])
+            self.putx([Ann.FIELD, ['Dummy byte: 0x%02x' % mosi]])
         elif self.cmdstate == 4:
             # Byte 4: Master sends 0x00 or 0x01.
             # 0x00: Master wants manufacturer ID as first reply byte.
             # 0x01: Master wants device ID as first reply byte.
             self.manufacturer_id_first = True if (mosi == 0x00) else False
             d = 'manufacturer' if (mosi == 0x00) else 'device'
-            self.putx([24, ['Master wants %s ID first' % d]])
+            self.putx([Ann.FIELD, ['Master wants %s ID first' % d]])
         elif self.cmdstate == 5:
             # Byte 5: Slave sends manufacturer ID (or device ID).
             self.ids = [miso]
             d = 'Manufacturer' if self.manufacturer_id_first else 'Device'
-            self.putx([24, ['%s ID' % d]])
+            self.putx([Ann.FIELD, ['%s ID: 0x%02x' % (d, miso)]])
         elif self.cmdstate == 6:
             # Byte 6: Slave sends device ID (or manufacturer ID).
             self.ids.append(miso)
-            d = 'Manufacturer' if self.manufacturer_id_first else 'Device'
-            self.putx([24, ['%s ID' % d]])
+            d = 'Device' if self.manufacturer_id_first else 'Manufacturer'
+            self.putx([Ann.FIELD, ['%s ID: 0x%02x' % (d, miso)]])
 
         if self.cmdstate == 6:
             id = self.ids[1] if self.manufacturer_id_first else self.ids[0]
-            self.putx([24, ['Device: Macronix %s' % device_name[id]]])
+            self.device_id = id
+            self.es_cmd = self.es
+            self.putc([Ann.REMS, self.cmd_vendor_dev_list()])
             self.state = None
         else:
             self.cmdstate += 1
@@ -346,34 +449,37 @@ class Decoder(srd.Decoder):
     def handle_dsry(self, mosi, miso):
         pass # TODO
 
-    def decode(self, ss, es, data):
+    def output_data_block(self, label, idx):
+        # Print accumulated block of data
+        # (called on CS# de-assert via self.on_end_transaction callback).
+        self.es_cmd = self.es # End on the CS# de-assert sample.
+        if self.options['format'] == 'hex':
+            s = ' '.join([('%02x' % b) for b in self.data])
+        else:
+            s = ''.join(map(chr, self.data))
+        self.putf([Ann.FIELD, ['%s (%d bytes)' % (label, len(self.data))]])
+        self.putc([idx, ['%s (addr 0x%06x, %d bytes): %s' % \
+                   (cmds[self.state][1], self.addr, len(self.data), s)]])
 
+    def decode(self, ss, es, data):
         ptype, mosi, miso = data
 
-        # if ptype == 'DATA':
-        #     self.putx([0, ['MOSI: 0x%02x, MISO: 0x%02x' % (mosi, miso)]])
+        self.ss, self.es = ss, es
 
-        # if ptype == 'CS-CHANGE':
-        #     if mosi == 1 and miso == 0:
-        #         self.putx([0, ['Asserting CS#']])
-        #     elif mosi == 0 and miso == 1:
-        #         self.putx([0, ['De-asserting CS#']])
+        if ptype == 'CS-CHANGE':
+            self.end_current_transaction()
 
         if ptype != 'DATA':
             return
 
-        self.ss, self.es = ss, es
-
         # If we encountered a known chip command, enter the resp. state.
         if self.state is None:
             self.state = mosi
             self.cmdstate = 1
 
         # Handle commands.
-        if self.state in cmds:
-            s = 'handle_%s' % cmds[self.state][0].lower().replace('/', '_')
-            handle_reg = getattr(self, s)
-            handle_reg(mosi, miso)
-        else:
-            self.putx([24, ['Unknown command: 0x%02x' % mosi]])
+        try:
+            self.cmd_handlers[self.state](mosi, miso)
+        except KeyError:
+            self.putx([Ann.BIT, ['Unknown command: 0x%02x' % mosi]])
             self.state = None
diff --git a/decoders/ssi32/__init__.py b/decoders/ssi32/__init__.py
new file mode 100644 (file)
index 0000000..fb7b4ed
--- /dev/null
@@ -0,0 +1,29 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Robert Bosch Car Multimedia GmbH
+## Authors: Oleksij Rempel
+##              <fixed-term.Oleksij.Rempel@de.bosch.com>
+##              <linux@rempel-privat.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+'''
+This decoder stacks on top of the 'spi' PD and decodes the Bosch
+SSI32 protocol.
+'''
+
+from .pd import Decoder
diff --git a/decoders/ssi32/pd.py b/decoders/ssi32/pd.py
new file mode 100644 (file)
index 0000000..8c68e0d
--- /dev/null
@@ -0,0 +1,124 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Robert Bosch Car Multimedia GmbH
+## Authors: Oleksij Rempel
+##              <fixed-term.Oleksij.Rempel@de.bosch.com>
+##              <linux@rempel-privat.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'ssi32'
+    name = 'SSI32'
+    longname = 'Synchronous Serial Interface (32bit)'
+    desc = 'Synchronous Serial Interface (32bit) protocol.'
+    license = 'gplv2+'
+    inputs = ['spi']
+    outputs = ['ssi32']
+    options = (
+        {'id': 'msgsize', 'desc': 'Message size', 'default': 64},
+    )
+    annotations = (
+        ('ctrl-tx', 'CTRL TX'),
+        ('ack-tx', 'ACK TX'),
+        ('ctrl-rx', 'CTRL RX'),
+        ('ack-rx', 'ACK RX'),
+    )
+    annotation_rows = (
+        ('tx', 'TX', (0, 1)),
+        ('rx', 'RX', (2, 3)),
+    )
+
+    def __init__(self):
+        self.ss_cmd, self.es_cmd = 0, 0
+        self.mosi_bytes = []
+        self.miso_bytes = []
+        self.es_array = []
+        self.rx_size = 0
+        self.tx_size = 0
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def putx(self, data):
+        self.put(self.ss_cmd, self.es_cmd, self.out_ann, data)
+
+    def reset(self):
+        self.mosi_bytes = []
+        self.miso_bytes = []
+        self.es_array = []
+
+    def handle_ack(self):
+        # Only first byte should have ACK data, other 3 bytes are reserved.
+        self.es_cmd = self.es_array[0]
+        self.putx([1, ['> ACK:0x%02x' % (self.mosi_bytes[0])]])
+        self.putx([3, ['< ACK:0x%02x' % (self.miso_bytes[0])]])
+
+    def handle_ctrl(self):
+        mosi = miso = ''
+        self.tx_size = self.mosi_bytes[2]
+        self.rx_size = self.miso_bytes[2]
+
+        if self.tx_size > 0:
+            mosi = ', DATA:0x' + ''.join(format(x, '02x') for x in self.mosi_bytes[4:self.tx_size + 4])
+        if self.rx_size > 0:
+            miso = ', DATA:0x' + ''.join(format(x, '02x') for x in self.miso_bytes[4:self.rx_size + 4])
+
+        self.es_cmd = self.es_array[self.tx_size + 3]
+        self.putx([0, ['> CTRL:0x%02x, LUN:0x%02x, SIZE:0x%02x, CRC:0x%02x%s'
+                   % (self.mosi_bytes[0], self.mosi_bytes[1],
+                      self.mosi_bytes[2], self.mosi_bytes[3], mosi)]])
+
+        self.es_cmd = self.es_array[self.rx_size + 3]
+        self.putx([2, ['< CTRL:0x%02x, LUN:0x%02x, SIZE:0x%02x, CRC:0x%02x%s'
+                   % (self.miso_bytes[0], self.miso_bytes[1],
+                      self.miso_bytes[2], self.miso_bytes[3], miso)]])
+
+    def decode(self, ss, es, data):
+        ptype = data[0]
+        if ptype == 'CS-CHANGE':
+            self.reset()
+            return
+
+        # Don't care about anything else.
+        if ptype != 'DATA':
+            return
+        mosi, miso = data[1:]
+
+        self.ss, self.es = ss, es
+
+        if len(self.mosi_bytes) == 0:
+            self.ss_cmd = ss
+        self.mosi_bytes.append(mosi)
+        self.miso_bytes.append(miso)
+        self.es_array.append(es)
+
+        if self.mosi_bytes[0] & 0x80:
+            if len(self.mosi_bytes) < 4:
+                return
+
+            self.handle_ack()
+            self.reset()
+        else:
+            if len(self.mosi_bytes) < self.options['msgsize']:
+                return
+
+            self.handle_ctrl()
+            self.reset()
index 1527d4813b67957dc71deec851a9ee53f45a9f60..7664fc4290bc1836343ff633dc924af070acc37d 100644 (file)
@@ -50,9 +50,9 @@ class Decoder(srd.Decoder):
         ('position', 'Position', (1,)),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.oldstep = None
-        self.prev_step_ss = None
+        self.ss_prev_step = None
         self.pos = 0
         self.prev_speed = None
         self.prev_pos = None
@@ -70,18 +70,18 @@ class Decoder(srd.Decoder):
             self.unit = 'mm'
 
     def step(self, ss, direction):
-        if self.prev_step_ss is not None:
-            delta = ss - self.prev_step_ss
+        if self.ss_prev_step is not None:
+            delta = ss - self.ss_prev_step
             speed = self.samplerate / delta / self.scale
             speed_txt = self.format % speed
             pos_txt = self.format % (self.pos / self.scale)
-            self.put(self.prev_step_ss, ss, self.out_ann,
+            self.put(self.ss_prev_step, ss, self.out_ann,
                 [0, [speed_txt + ' ' + self.unit + '/s', speed_txt]])
-            self.put(self.prev_step_ss, ss, self.out_ann,
+            self.put(self.ss_prev_step, ss, self.out_ann,
                 [1, [pos_txt + ' ' + self.unit, pos_txt]])
 
         self.pos += (1 if direction else -1)
-        self.prev_step_ss = ss
+        self.ss_prev_step = ss
 
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
index d53d1493f59d7f9e6fb867dede91a4cae71dbffa..3414c35f7cf3a17eff4548522f32e5a1fa57d406 100644 (file)
@@ -92,7 +92,7 @@ class Decoder(srd.Decoder):
         ('parity', 'PARITY'),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         # SWD data/clock state
         self.state = 'UNKNOWN'
         self.oldclk = -1
diff --git a/decoders/t55xx/__init__.py b/decoders/t55xx/__init__.py
new file mode 100644 (file)
index 0000000..8f0f8a4
--- /dev/null
@@ -0,0 +1,26 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2015 Benjamin Larsson <benjamin@southpole.se>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+'''
+T55xx is a 100-150kHz RFID protocol according to the Atmel e555x
+downlink/write protocol (pulse interval coding).
+'''
+
+from .pd import Decoder
diff --git a/decoders/t55xx/pd.py b/decoders/t55xx/pd.py
new file mode 100644 (file)
index 0000000..57838f0
--- /dev/null
@@ -0,0 +1,331 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2015 Benjamin Larsson <benjamin@southpole.se>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+
+class SamplerateError(Exception):
+    pass
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 't55xx'
+    name = 'T55xx'
+    longname = 'RFID T55xx'
+    desc = 'T55xx 100-150kHz RFID protocol.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['t55xx']
+    channels = (
+        {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
+    )
+    options = (
+        {'id': 'coilfreq', 'desc': 'Coil frequency', 'default': 125000},
+        {'id': 'start_gap', 'desc': 'Start gap min', 'default': 20},
+        {'id': 'w_gap', 'desc': 'Write gap min', 'default': 20},
+        {'id': 'w_one_min', 'desc': 'Write one min', 'default': 48},
+        {'id': 'w_one_max', 'desc': 'Write one max', 'default': 63},
+        {'id': 'w_zero_min', 'desc': 'Write zero min', 'default': 16},
+        {'id': 'w_zero_max', 'desc': 'Write zero max', 'default': 31},
+        {'id': 'em4100_decode', 'desc': 'EM4100 decode', 'default': 'on',
+            'values': ('on', 'off')},
+    )
+    annotations = (
+        ('bit_value', 'Bit value'),
+        ('start_gap', 'Start gap'),
+        ('write_gap', 'Write gap'),
+        ('write_mode_exit', 'Write mode exit'),
+        ('bit', 'Bit'),
+        ('opcode', 'Opcode'),
+        ('lock', 'Lock'),
+        ('data', 'Data'),
+        ('password', 'Password'),
+        ('address', 'Address'),
+        ('bitrate', 'Bitrate'),
+    )
+    annotation_rows = (
+        ('bits', 'Bits', (0,)),
+        ('structure', 'Structure', (1, 2, 3, 4)),
+        ('fields', 'Fields', (5, 6, 7, 8, 9)),
+        ('decode', 'Decode', (10,)),
+    )
+
+    def __init__(self):
+        self.samplerate = None
+        self.oldpin = None
+        self.last_samplenum = None
+        self.lastlast_samplenum = None
+        self.state = 'START_GAP'
+        self.bits_pos = [[0 for col in range(3)] for row in range(70)]
+        self.br_string = ['RF/8', 'RF/16', 'RF/32', 'RF/40',
+                          'RF/50', 'RF/64', 'RF/100', 'RF/128']
+        self.mod_str1 = ['Direct', 'Manchester', 'Biphase', 'Reserved']
+        self.mod_str2 = ['Direct', 'PSK1', 'PSK2', 'PSK3', 'FSK1', 'FSK2',
+                         'FSK1a', 'FSK2a']
+        self.pskcf_str = ['RF/2', 'RF/4', 'RF/8', 'Reserved']
+        self.em4100_decode1_partial = 0
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+        self.field_clock = self.samplerate / self.options['coilfreq']
+        self.wzmax = self.options['w_zero_max'] * self.field_clock
+        self.wzmin = self.options['w_zero_min'] * self.field_clock
+        self.womax = self.options['w_one_max'] * self.field_clock
+        self.womin = self.options['w_one_min'] * self.field_clock
+        self.startgap = self.options['start_gap'] * self.field_clock
+        self.writegap = self.options['w_gap'] * self.field_clock
+        self.nogap = 64 * self.field_clock
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def decode_config(self, idx):
+        safer_key = self.bits_pos[idx][0]<<3 | self.bits_pos[idx+1][0]<<2 | \
+                    self.bits_pos[idx+2][0]<<1 | self.bits_pos[idx+3][0]
+        self.put(self.bits_pos[idx][1], self.bits_pos[idx+3][2], self.out_ann,
+                 [10, ['Safer Key' + ': %X' % safer_key,'%X' % safer_key]])
+        bitrate = self.bits_pos[idx+11][0]<<2 | self.bits_pos[idx+12][0]<<1 | \
+                  self.bits_pos[idx+13][0]
+        self.put(self.bits_pos[idx+11][1], self.bits_pos[idx+13][2],
+                 self.out_ann, [10, ['Data Bit Rate: ' + \
+                 self.br_string[bitrate], self.br_string[bitrate]]])
+        modulation1 = self.bits_pos[idx+15][0]<<1 | self.bits_pos[idx+16][0]
+        modulation2 = self.bits_pos[idx+17][0]<<2 | \
+                      self.bits_pos[idx+18][0]<<1 | self.bits_pos[idx+19][0]
+        if modulation1 == 0:
+            mod_string = self.mod_str2[modulation2]
+        else:
+            mod_string = self.mod_str1[modulation1]
+        self.put(self.bits_pos[idx+15][1], self.bits_pos[idx+19][2],
+                 self.out_ann, [10, ['Modulation: ' + mod_string, mod_string]])
+        psk_cf = self.bits_pos[idx+20][0]<<1 | self.bits_pos[idx+21][0]
+        self.put(self.bits_pos[idx+20][1], self.bits_pos[idx+21][2],
+                 self.out_ann, [10, ['PSK-CF: ' + self.pskcf_str[psk_cf],
+                 self.pskcf_str[psk_cf]]])
+        self.put(self.bits_pos[idx+22][1], self.bits_pos[idx+22][2],
+                 self.out_ann, [10, ['AOR' + ': %d' % \
+                 (self.bits_pos[idx+22][0]),'%d' % (self.bits_pos[idx+22][0])]])
+        maxblock = self.bits_pos[idx+24][0]<<2 | self.bits_pos[idx+25][0]<<1 | \
+                   self.bits_pos[idx+26][0]
+        self.put(self.bits_pos[idx+24][1], self.bits_pos[idx+26][2],
+                 self.out_ann, [10, ['Max-Block' + ': %d' % maxblock,
+                 '%d' % maxblock]])
+        self.put(self.bits_pos[idx+27][1], self.bits_pos[idx+27][2],
+                 self.out_ann, [10, ['PWD' + ': %d' % \
+                 (self.bits_pos[idx+27][0]),'%d' % (self.bits_pos[idx+27][0])]])
+        self.put(self.bits_pos[idx+28][1], self.bits_pos[idx+28][2],
+                 self.out_ann, [10, ['ST-sequence terminator' + ': %d' % \
+                 (self.bits_pos[idx+28][0]),'%d' % (self.bits_pos[idx+28][0])]])
+        self.put(self.bits_pos[idx+31][1], self.bits_pos[idx+31][2],
+                 self.out_ann, [10, ['POR delay' + ': %d' % \
+                 (self.bits_pos[idx+31][0]),'%d' % (self.bits_pos[idx+31][0])]])
+
+    def put4bits(self, idx):
+        bits = self.bits_pos[idx][0]<<3 | self.bits_pos[idx+1][0]<<2 | \
+               self.bits_pos[idx+2][0]<<1 | self.bits_pos[idx+3][0]
+        self.put(self.bits_pos[idx][1], self.bits_pos[idx+3][2], self.out_ann,
+                 [10, ['%X' % bits]])
+
+    def em4100_decode1(self, idx):
+        self.put(self.bits_pos[idx][1], self.bits_pos[idx+8][2], self.out_ann,
+                 [10, ['EM4100 header', 'EM header', 'Header', 'H']])
+        self.put4bits(idx+9)
+        self.put4bits(idx+14)
+        self.put4bits(idx+19)
+        self.put4bits(idx+24)
+        self.em4100_decode1_partial = self.bits_pos[idx+29][0]<<3 | \
+            self.bits_pos[idx+30][0]<<2 | self.bits_pos[idx+31][0]<<1
+        self.put(self.bits_pos[idx+29][1], self.bits_pos[idx+31][2],
+                 self.out_ann, [10, ['Partial nibble']])
+
+    def em4100_decode2(self, idx):
+        if self.em4100_decode1_partial != 0:
+            bits = self.em4100_decode1_partial + self.bits_pos[idx][0]
+            self.put(self.bits_pos[idx][1], self.bits_pos[idx][2],
+                     self.out_ann, [10, ['%X' % bits]])
+            self.em4100_decode1_partial = 0
+        else:
+            self.put(self.bits_pos[idx][1], self.bits_pos[idx][2],
+                     self.out_ann, [10, ['Partial nibble']])
+
+        self.put4bits(idx+2)
+        self.put4bits(idx+7)
+        self.put4bits(idx+12)
+        self.put4bits(idx+17)
+        self.put4bits(idx+22)
+        self.put(self.bits_pos[idx+27][1], self.bits_pos[idx+31][2],
+                 self.out_ann, [10, ['EM4100 trailer']])
+
+    def get_32_bits(self, idx):
+        retval = 0
+        for i in range(0, 32):
+            retval <<= 1
+            retval |= self.bits_pos[i+idx][0]
+        return retval
+
+    def get_3_bits(self, idx):
+        retval = self.bits_pos[idx][0]<<2 | self.bits_pos[idx+1][0]<<1 | \
+                 self.bits_pos[idx+2][0]
+        return retval
+
+    def put_fields(self):
+        if (self.bit_nr == 70):
+            self.put(self.bits_pos[0][1], self.bits_pos[1][2], self.out_ann,
+                     [5, ['Opcode' + ': %d%d' % (self.bits_pos[0][0],
+                     self.bits_pos[1][0]), '%d%d' % (self.bits_pos[0][0],
+                     self.bits_pos[1][0])]])
+            password = self.get_32_bits(2)
+            self.put(self.bits_pos[2][1], self.bits_pos[33][2], self.out_ann,
+                     [8, ['Password' + ': %X' % password, '%X' % password]])
+            self.put(self.bits_pos[34][1], self.bits_pos[34][2], self.out_ann,
+                     [6, ['Lock' + ': %X' % self.bits_pos[34][0],
+                     '%X' % self.bits_pos[34][0]]])
+            data = self.get_32_bits(35)
+            self.put(self.bits_pos[35][1], self.bits_pos[66][2], self.out_ann,
+                     [7, ['Data' + ': %X' % data, '%X' % data]])
+            addr = self.get_3_bits(67)
+            self.put(self.bits_pos[67][1], self.bits_pos[69][2], self.out_ann,
+                     [9, ['Addr' + ': %X' % addr, '%X' % addr]])
+            if addr == 0:
+                self.decode_config(35)
+            if addr == 7:
+                self.put(self.bits_pos[35][1], self.bits_pos[66][2],
+                         self.out_ann, [10, ['Password' + ': %X' % data,
+                         '%X' % data]])
+            # If we are programming EM4100 data we can decode it halfway.
+            if addr == 1 and self.options['em4100_decode'] == 'on':
+                self.em4100_decode1(35)
+            if addr == 2 and self.options['em4100_decode'] == 'on':
+                self.em4100_decode2(35)
+
+        if (self.bit_nr == 38):
+            self.put(self.bits_pos[0][1], self.bits_pos[1][2], self.out_ann,
+                     [5, ['Opcode' + ': %d%d' % (self.bits_pos[0][0],
+                     self.bits_pos[1][0]), '%d%d' % (self.bits_pos[0][0],
+                     self.bits_pos[1][0])]])
+            self.put(self.bits_pos[2][1], self.bits_pos[2][2], self.out_ann,
+                     [6, ['Lock' + ': %X' % self.bits_pos[2][0],
+                     '%X' % self.bits_pos[2][0]]])
+            data = self.get_32_bits(3)
+            self.put(self.bits_pos[3][1], self.bits_pos[34][2], self.out_ann,
+                     [7, ['Data' + ': %X' % data, '%X' % data]])
+            addr = self.get_3_bits(35)
+            self.put(self.bits_pos[35][1], self.bits_pos[37][2], self.out_ann,
+                     [9, ['Addr' + ': %X' % addr, '%X' % addr]])
+            if addr == 0:
+                self.decode_config(3)
+            if addr == 7:
+                self.put(self.bits_pos[3][1], self.bits_pos[34][2],
+                         self.out_ann, [10, ['Password' + ': %X' % data,
+                         '%X' % data]])
+            # If we are programming EM4100 data we can decode it halfway.
+            if addr == 1 and self.options['em4100_decode'] == 'on':
+                self.em4100_decode1(3)
+            if addr == 2 and self.options['em4100_decode'] == 'on':
+                self.em4100_decode2(3)
+
+        if (self.bit_nr == 2):
+            self.put(self.bits_pos[0][1], self.bits_pos[1][2], self.out_ann,
+                     [5, ['Opcode' + ': %d%d' % (self.bits_pos[0][0],
+                     self.bits_pos[1][0]), '%d%d' % (self.bits_pos[0][0],
+                     self.bits_pos[1][0])]])
+        self.bit_nr = 0
+
+    def add_bits_pos(self, bit, bit_start, bit_end):
+        if self.bit_nr < 70:
+            self.bits_pos[self.bit_nr][0] = bit
+            self.bits_pos[self.bit_nr][1] = bit_start
+            self.bits_pos[self.bit_nr][2] = bit_end
+            self.bit_nr += 1
+
+    def decode(self, ss, es, data):
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
+        for (self.samplenum, (pin,)) in data:
+            # Ignore identical samples early on (for performance reasons).
+            if self.oldpin == pin:
+                continue
+
+            if self.oldpin is None:
+                self.oldpin = pin
+                self.last_samplenum = self.samplenum
+                self.lastlast_samplenum = self.samplenum
+                self.last_edge = self.samplenum
+                self.oldpl = 0
+                self.oldpp = 0
+                self.oldsamplenum = 0
+                self.last_bit_pos = 0
+
+                self.old_gap_start = 0
+                self.old_gap_end = 0
+                self.gap_detected = 0
+                self.bit_nr = 0
+                continue
+
+            if self.oldpin != pin:
+                pl = self.samplenum - self.oldsamplenum
+                pp = pin
+                samples = self.samplenum - self.last_samplenum
+
+                if self.state == 'WRITE_GAP':
+                    if pl > self.writegap:
+                        self.gap_detected = 1
+                        self.put(self.last_samplenum, self.samplenum,
+                                 self.out_ann, [2, ['Write gap']])
+                    if (self.last_samplenum-self.old_gap_end) > self.nogap:
+                        self.gap_detected = 0
+                        self.state = 'START_GAP'
+                        self.put(self.old_gap_end, self.last_samplenum,
+                                 self.out_ann, [3, ['Write mode exit']])
+                        self.put_fields()
+
+                if self.state == 'START_GAP':
+                    if pl > self.startgap:
+                        self.gap_detected = 1
+                        self.put(self.last_samplenum, self.samplenum,
+                                 self.out_ann, [1, ['Start gap']])
+                        self.state = 'WRITE_GAP'
+
+                if self.gap_detected == 1:
+                    self.gap_detected = 0
+                    if (self.last_samplenum - self.old_gap_end) > self.wzmin \
+                            and (self.last_samplenum - self.old_gap_end) < self.wzmax:
+                        self.put(self.old_gap_end, self.last_samplenum,
+                                 self.out_ann, [0, ['0']])
+                        self.put(self.old_gap_end, self.last_samplenum,
+                                 self.out_ann, [4, ['Bit']])
+                        self.add_bits_pos(0, self.old_gap_end,
+                                          self.last_samplenum)
+                    if (self.last_samplenum - self.old_gap_end) > self.womin \
+                            and (self.last_samplenum - self.old_gap_end) < self.womax:
+                        self.put(self.old_gap_end, self.last_samplenum,
+                                 self.out_ann, [0, ['1']])
+                        self.put(self.old_gap_end, self.last_samplenum,
+                                 self.out_ann, [4, ['Bit']])
+                        self.add_bits_pos(1, self.old_gap_end, self.last_samplenum)
+
+                    self.old_gap_start = self.last_samplenum
+                    self.old_gap_end = self.samplenum
+
+                self.oldpl = pl
+                self.oldpp = pp
+                self.oldsamplenum = self.samplenum
+                self.last_samplenum = self.samplenum
+                self.oldpin = pin
index 1f90245c8f725093b757121d2b19cabb466afc69..a794547c3b772b3d0dafafb5a654ea32dd355980 100644 (file)
@@ -41,7 +41,7 @@ class Decoder(srd.Decoder):
         ('warnings', 'Warnings', (2,)),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.state = 'IDLE'
         self.chip = -1
 
index 6bca12085e7843383ec7c03d4b8de66a17503171..98677b9f20d0e7d3529834fef808c8665b0bc8f9 100644 (file)
 ##
 
 import sigrokdecode as srd
+from collections import deque
 
 class SamplerateError(Exception):
     pass
 
 def normalize_time(t):
     if t >= 1.0:
-        return '%.3f s' % t
+        return '%.3f s  (%.3f Hz)' % (t, (1/t))
     elif t >= 0.001:
-        return '%.3f ms' % (t * 1000.0)
+        if 1/t/1000 < 1:
+            return '%.3f ms (%.3f Hz)' % (t * 1000.0, (1/t))
+        else:
+            return '%.3f ms (%.3f kHz)' % (t * 1000.0, (1/t)/1000)
     elif t >= 0.000001:
-        return '%.3f Î¼s' % (t * 1000.0 * 1000.0)
+        if 1/t/1000/1000 < 1:
+            return '%.3f Î¼s (%.3f kHz)' % (t * 1000.0 * 1000.0, (1/t)/1000)
+        else:
+            return '%.3f Î¼s (%.3f MHz)' % (t * 1000.0 * 1000.0, (1/t)/1000/1000)
     elif t >= 0.000000001:
-        return '%.3f ns' % (t * 1000.0 * 1000.0 * 1000.0)
+        if 1/t/1000/1000/1000:
+            return '%.3f ns (%.3f MHz)' % (t * 1000.0 * 1000.0 * 1000.0, (1/t)/1000/1000)
+        else:
+            return '%.3f ns (%.3f GHz)' % (t * 1000.0 * 1000.0 * 1000.0, (1/t)/1000/1000/1000)
     else:
         return '%f' % t
 
@@ -40,7 +50,7 @@ class Decoder(srd.Decoder):
     api_version = 2
     id = 'timing'
     name = 'Timing'
-    longname = 'Timing calculation'
+    longname = 'Timing calculation with frequency and averaging'
     desc = 'Calculate time between edges.'
     license = 'gplv2+'
     inputs = ['logic']
@@ -50,15 +60,22 @@ class Decoder(srd.Decoder):
     )
     annotations = (
         ('time', 'Time'),
+        ('average', 'Average'),
     )
     annotation_rows = (
         ('time', 'Time', (0,)),
+        ('average', 'Average', (1,)),
+    )
+    options = (
+        { 'id': 'avg_period', 'desc': 'Averaging period', 'default': 100 },
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.samplerate = None
         self.oldpin = None
         self.last_samplenum = None
+        self.last_n = deque()
+        self.chunks = 0
 
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
@@ -71,24 +88,36 @@ class Decoder(srd.Decoder):
         if not self.samplerate:
             raise SamplerateError('Cannot decode without samplerate.')
 
-        for (samplenum, (pin,)) in data:
-            # Ignore identical samples early on (for performance reasons).
-            if self.oldpin == pin:
-                continue
-
+        for (self.samplenum, (pin,)) in data:
             if self.oldpin is None:
                 self.oldpin = pin
-                self.last_samplenum = samplenum
+                self.last_samplenum = self.samplenum
                 continue
 
             if self.oldpin != pin:
-                samples = samplenum - self.last_samplenum
+                samples = self.samplenum - self.last_samplenum
                 t = samples / self.samplerate
+                self.chunks += 1
+
+                # Don't insert the first chunk into the averaging as it is
+                # not complete probably.
+                if self.last_samplenum is None or self.chunks < 2:
+                    # Report the timing normalized.
+                    self.put(self.last_samplenum, self.samplenum, self.out_ann,
+                             [0, [normalize_time(t)]])
+                else:
+                    if t > 0:
+                        self.last_n.append(t)
+
+                    if len(self.last_n) > self.options['avg_period']:
+                        self.last_n.popleft()
 
-                # Report the timing normalized.
-                self.put(self.last_samplenum, samplenum, self.out_ann,
-                         [0, [normalize_time(t)]])
+                    # Report the timing normalized.
+                    self.put(self.last_samplenum, self.samplenum, self.out_ann,
+                             [0, [normalize_time(t)]])
+                    self.put(self.last_samplenum, self.samplenum, self.out_ann,
+                             [1, [normalize_time(sum(self.last_n) / len(self.last_n))]])
 
                 # Store data for next round.
-                self.last_samplenum = samplenum
+                self.last_samplenum = self.samplenum
                 self.oldpin = pin
index df42bfbe10613c786c98108adb805cdb82e80155..317e4ed665cd00be887425ed4989b032653b724a 100644 (file)
@@ -71,7 +71,7 @@ class Decoder(srd.Decoder):
         ('errors', 'Errors', (9,)),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.oldpins = self.oldclk = self.oldload = self.oldldac = None
         self.bits = []
         self.ss_dac_first = None
index db1065dac4f42e1ec03e8f953518cd3ad435eeb4..0fa0e7ff442b8e307a883cbd9711b646be9372cd 100644 (file)
@@ -31,7 +31,7 @@ This is the list of <ptype>s and their respective <pdata> values:
  - 'STARTBIT': The data is the (integer) value of the start bit (0/1).
  - 'DATA': This is always a tuple containing two items:
    - 1st item: the (integer) value of the UART data. Valid values
-     range from 0 to 512 (as the data can be up to 9 bits in size).
+     range from 0 to 511 (as the data can be up to 9 bits in size).
    - 2nd item: the list of individual data bits and their ss/es numbers.
  - 'PARITYBIT': The data is the (integer) value of the parity bit (0/1).
  - 'STOPBIT': The data is the (integer) value of the stop bit (0 or 1).
@@ -102,7 +102,7 @@ class Decoder(srd.Decoder):
             'values': (0.0, 0.5, 1.0, 1.5)},
         {'id': 'bit_order', 'desc': 'Bit order', 'default': 'lsb-first',
             'values': ('lsb-first', 'msb-first')},
-        {'id': 'format', 'desc': 'Data format', 'default': 'ascii',
+        {'id': 'format', 'desc': 'Data format', 'default': 'hex',
             'values': ('ascii', 'dec', 'hex', 'oct', 'bin')},
         {'id': 'invert_rx', 'desc': 'Invert RX?', 'default': 'no',
             'values': ('yes', 'no')},
@@ -160,13 +160,13 @@ class Decoder(srd.Decoder):
         s, halfbit = self.startsample[rxtx], self.bit_width / 2.0
         self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_binary, data)
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.samplerate = None
         self.samplenum = 0
         self.frame_start = [-1, -1]
         self.startbit = [-1, -1]
         self.cur_data_bit = [0, 0]
-        self.databyte = [0, 0]
+        self.datavalue = [0, 0]
         self.paritybit = [-1, -1]
         self.stopbit1 = [-1, -1]
         self.startsample = [-1, -1]
@@ -179,6 +179,7 @@ class Decoder(srd.Decoder):
         self.out_python = self.register(srd.OUTPUT_PYTHON)
         self.out_binary = self.register(srd.OUTPUT_BINARY)
         self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.bw = (self.options['num_data_bits'] + 7) // 8
 
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
@@ -223,14 +224,16 @@ class Decoder(srd.Decoder):
 
         self.startbit[rxtx] = signal
 
-        # The startbit must be 0. If not, we report an error.
+        # The startbit must be 0. If not, we report an error and wait
+        # for the next start bit (assuming this one was spurious).
         if self.startbit[rxtx] != 0:
             self.putp(['INVALID STARTBIT', rxtx, self.startbit[rxtx]])
             self.putg([rxtx + 10, ['Frame error', 'Frame err', 'FE']])
-            # TODO: Abort? Ignore rest of the frame?
+            self.state[rxtx] = 'WAIT FOR START BIT'
+            return
 
         self.cur_data_bit[rxtx] = 0
-        self.databyte[rxtx] = 0
+        self.datavalue[rxtx] = 0
         self.startsample[rxtx] = -1
 
         self.state[rxtx] = 'GET DATA BITS'
@@ -249,12 +252,12 @@ class Decoder(srd.Decoder):
 
         # Get the next data bit in LSB-first or MSB-first fashion.
         if self.options['bit_order'] == 'lsb-first':
-            self.databyte[rxtx] >>= 1
-            self.databyte[rxtx] |= \
+            self.datavalue[rxtx] >>= 1
+            self.datavalue[rxtx] |= \
                 (signal << (self.options['num_data_bits'] - 1))
         else:
-            self.databyte[rxtx] <<= 1
-            self.databyte[rxtx] |= (signal << 0)
+            self.datavalue[rxtx] <<= 1
+            self.datavalue[rxtx] |= (signal << 0)
 
         self.putg([rxtx + 12, ['%d' % signal]])
 
@@ -270,25 +273,60 @@ class Decoder(srd.Decoder):
         self.state[rxtx] = 'GET PARITY BIT'
 
         self.putpx(rxtx, ['DATA', rxtx,
-            (self.databyte[rxtx], self.databits[rxtx])])
-
-        b, f = self.databyte[rxtx], self.options['format']
-        if f == 'ascii':
-            c = chr(b) if b in range(30, 126 + 1) else '[%02X]' % b
-            self.putx(rxtx, [rxtx, [c]])
-        elif f == 'dec':
-            self.putx(rxtx, [rxtx, [str(b)]])
-        elif f == 'hex':
-            self.putx(rxtx, [rxtx, [hex(b)[2:].zfill(2).upper()]])
-        elif f == 'oct':
-            self.putx(rxtx, [rxtx, [oct(b)[2:].zfill(3)]])
-        elif f == 'bin':
-            self.putx(rxtx, [rxtx, [bin(b)[2:].zfill(8)]])
-
-        self.putbin(rxtx, [rxtx, bytes([b])])
-        self.putbin(rxtx, [2, bytes([b])])
+            (self.datavalue[rxtx], self.databits[rxtx])])
+
+        b = self.datavalue[rxtx]
+        formatted = self.format_value(b)
+        if formatted is not None:
+            self.putx(rxtx, [rxtx, [formatted]])
+
+        bdata = b.to_bytes(self.bw, byteorder='big')
+        self.putbin(rxtx, [rxtx, bdata])
+        self.putbin(rxtx, [2, bdata])
+
+        self.databits[rxtx] = []
+
+    def format_value(self, v):
+        # Format value 'v' according to configured options.
+        # Reflects the user selected kind of representation, as well as
+        # the number of data bits in the UART frames.
+
+        fmt, bits = self.options['format'], self.options['num_data_bits']
+
+        # Assume "is printable" for values from 32 to including 126,
+        # below 32 is "control" and thus not printable, above 127 is
+        # "not ASCII" in its strict sense, 127 (DEL) is not printable,
+        # fall back to hex representation for non-printables.
+        if fmt == 'ascii':
+            if v in range(32, 126 + 1):
+                return chr(v)
+            hexfmt = "[{:02X}]" if bits <= 8 else "[{:03X}]"
+            return hexfmt.format(v)
+
+        # Mere number to text conversion without prefix and padding
+        # for the "decimal" output format.
+        if fmt == 'dec':
+            return "{:d}".format(v)
+
+        # Padding with leading zeroes for hex/oct/bin formats, but
+        # without a prefix for density -- since the format is user
+        # specified, there is no ambiguity.
+        if fmt == 'hex':
+            digits = (bits + 4 - 1) // 4
+            fmtchar = "X"
+        elif fmt == 'oct':
+            digits = (bits + 3 - 1) // 3
+            fmtchar = "o"
+        elif fmt == 'bin':
+            digits = bits
+            fmtchar = "b"
+        else:
+            fmtchar = None
+        if fmtchar is not None:
+            fmt = "{{:0{:d}{:s}}}".format(digits, fmtchar)
+            return fmt.format(v)
 
-        self.databits = [[], []]
+        return None
 
     def get_parity_bit(self, rxtx, signal):
         # If no parity is used/configured, skip to the next state immediately.
@@ -305,7 +343,7 @@ class Decoder(srd.Decoder):
         self.state[rxtx] = 'GET STOP BITS'
 
         if parity_ok(self.options['parity_type'], self.paritybit[rxtx],
-                     self.databyte[rxtx], self.options['num_data_bits']):
+                     self.datavalue[rxtx], self.options['num_data_bits']):
             self.putp(['PARITYBIT', rxtx, self.paritybit[rxtx]])
             self.putg([rxtx + 4, ['Parity bit', 'Parity', 'P']])
         else:
index 669fb48234e425fe647c8e2a88cabf747591c0fc..4ba626a86010a373d32e3cb468401865da992260 100644 (file)
@@ -18,8 +18,8 @@
 ## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 ##
 
-"""
-USB Power Delivery - baseband protocol decoder / checker
-"""
+'''
+USB Power Delivery - baseband protocol decoder / checker.
+'''
 
 from .pd import *
index ad28d97a3c34c45aaafa5e41572043bfd72fa64e..48db41b1a2155741b2d13aa662f1039e9d1db568 100644 (file)
@@ -439,7 +439,7 @@ class Decoder(srd.Decoder):
         self.putwarn('No start of packet found', 'XXX')
         return -1   # No Start Of Packet
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.samplerate = None
         self.idx = 0
         self.packet_seq = 0
@@ -471,8 +471,6 @@ class Decoder(srd.Decoder):
         )
 
     def us2samples(self, us):
-        if not self.samplerate:
-            raise SamplerateError('Need the samplerate.')
         return int(us * self.samplerate / 1000000)
 
     def decode_packet(self):
index 79539c6b9deea853caefccb2eac8ca0fe1631e98..bb7aedc5dd8e483f4bbad36d53cca989fd572a83 100644 (file)
@@ -137,13 +137,12 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
-        self.samplerate = 8e6 # None
-        self.secs_per_sample = float(1) / float(self.samplerate)
+        self.samplerate = None
         self.request = {}
         self.request_id = 0
         self.transaction_state = 'IDLE'
-        self.transaction_ss = None
-        self.transaction_es = None
+        self.ss_transaction = None
+        self.es_transaction = None
         self.transaction_ep = None
         self.transaction_addr = None
         self.wrote_pcap_header = False
@@ -183,7 +182,7 @@ class Decoder(srd.Decoder):
         addr = self.transaction_addr
         if not (addr, ep) in self.request:
             self.request[(addr, ep)] = {'setup_data': [], 'data': [],
-                'type': None, 'ss': self.transaction_ss, 'es': None,
+                'type': None, 'ss': self.ss_transaction, 'es': None,
                 'id': self.request_id, 'addr': addr, 'ep': ep}
             self.request_id += 1
             request_started = 1
@@ -193,16 +192,16 @@ class Decoder(srd.Decoder):
         if request['type'] in (None, 'BULK IN') and self.transaction_type == 'IN':
             request['type'] = 'BULK IN'
             request['data'] += self.transaction_data
-            request['es'] = self.transaction_es
+            request['es'] = self.es_transaction
             self.handle_request(request_started, request_end)
         elif request['type'] in (None, 'BULK OUT') and self.transaction_type == 'OUT':
             request['type'] = 'BULK OUT'
             request['data'] += self.transaction_data
-            request['es'] = self.transaction_es
+            request['es'] = self.es_transaction
             self.handle_request(request_started, request_end)
 
         # CONTROL, SETUP stage
-        elif request['type'] == None and self.transaction_type == 'SETUP':
+        elif request['type'] is None and self.transaction_type == 'SETUP':
             request['setup_data'] = self.transaction_data
             request['wLength'] = struct.unpack('<H',
                 bytes(self.transaction_data[6:8]))[0]
@@ -224,11 +223,11 @@ class Decoder(srd.Decoder):
 
         # CONTROL, STATUS stage
         elif request['type'] == 'SETUP IN' and self.transaction_type == 'OUT':
-            request['es'] = self.transaction_es
+            request['es'] = self.es_transaction
             self.handle_request(0, request_end)
 
         elif request['type'] == 'SETUP OUT' and self.transaction_type == 'IN':
-            request['es'] = self.transaction_es
+            request['es'] = self.es_transaction
             self.handle_request(0, request_end)
 
         else:
@@ -307,12 +306,12 @@ class Decoder(srd.Decoder):
             if pname == 'SOF':
                 return
             if self.transaction_state == 'TOKEN RECEIVED':
-                transaction_timeout = self.transaction_es
+                transaction_timeout = self.es_transaction
                 # Token length is 35 bits, timeout is 16..18 bit times
                 # (USB 2.0 7.1.19.1).
-                transaction_timeout += int((self.transaction_es - self.transaction_ss) / 2)
+                transaction_timeout += int((self.es_transaction - self.ss_transaction) / 2)
                 if ss > transaction_timeout:
-                    self.transaction_es = transaction_timeout
+                    self.es_transaction = transaction_timeout
                     self.handshake = 'timeout'
                     self.handle_transfer()
                     self.transaction_state = 'IDLE'
@@ -324,8 +323,8 @@ class Decoder(srd.Decoder):
 
             sync, pid, addr, ep, crc5 = pinfo
             self.transaction_data = []
-            self.transaction_ss = ss
-            self.transaction_es = es
+            self.ss_transaction = ss
+            self.es_transaction = es
             self.transaction_state = 'TOKEN RECEIVED'
             self.transaction_ep = ep
             self.transaction_addr = addr
@@ -348,7 +347,7 @@ class Decoder(srd.Decoder):
 
             self.handshake = pname
             self.transaction_state = 'IDLE'
-            self.transaction_es = es
+            self.es_transaction = es
             self.handle_transfer()
 
         elif pname == 'PRE':
diff --git a/decoders/wiegand/__init__.py b/decoders/wiegand/__init__.py
new file mode 100644 (file)
index 0000000..20f51f8
--- /dev/null
@@ -0,0 +1,29 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Sean Burford <sburford@google.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+'''
+The Wiegand interface is a de facto wiring standard commonly used to connect
+a card swipe mechanism to the rest of an electronic entry system.
+
+Details:
+https://en.wikipedia.org/wiki/Wiegand_interface
+'''
+
+from .pd import Decoder
diff --git a/decoders/wiegand/pd.py b/decoders/wiegand/pd.py
new file mode 100644 (file)
index 0000000..a42c9d2
--- /dev/null
@@ -0,0 +1,134 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Sean Burford <sburford@google.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## 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, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'wiegand'
+    name = 'Wiegand'
+    longname = 'Wiegand interface'
+    desc = 'Wiegand interface for electronic entry systems.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['wiegand']
+    channels = (
+        {'id': 'd0', 'name': 'D0', 'desc': 'Data 0 line'},
+        {'id': 'd1', 'name': 'D1', 'desc': 'Data 1 line'},
+    )
+    options = (
+        {'id': 'active', 'desc': 'Data lines active level',
+         'default': 'low', 'values': ('low', 'high')},
+        {'id': 'bitwidth_ms', 'desc': 'Single bit width in milliseconds',
+         'default': 4, 'values': (1, 2, 4, 8, 16, 32)},
+    )
+    annotations = (
+        ('bits', 'Bits'),
+        ('state', 'State'),
+    )
+    annotation_rows = (
+        ('bits', 'Binary value', (0,)),
+        ('state', 'Stream state', (1,)),
+    )
+
+    def __init__(self):
+        self._samples_per_bit = 10
+
+        self._d0_prev = None
+        self._d1_prev = None
+
+        self._state = None
+        self.ss_state = None
+
+        self.ss_bit = None
+        self.es_bit = None
+        self._bit = None
+        self._bits = []
+
+    def start(self):
+        'Register output types and verify user supplied decoder values.'
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self._active = self.options['active'] == 'high' and 1 or 0
+        self._inactive = 1 - self._active
+
+    def metadata(self, key, value):
+        'Receive decoder metadata about the data stream.'
+        if key == srd.SRD_CONF_SAMPLERATE:
+            ms_per_sample = 1000 * (1.0 / value)
+            ms_per_bit = float(self.options['bitwidth_ms'])
+            self._samples_per_bit = int(max(1, int(ms_per_bit / ms_per_sample)))
+
+    def _update_state(self, state, bit=None):
+        'Update state and bit values when they change.'
+        if self._bit is not None:
+            self._bits.append(self._bit)
+            self.put(self.ss_bit, self.samplenum, self.out_ann,
+                     [0, [str(self._bit)]])
+        self._bit = bit
+        self.ss_bit = self.samplenum
+        if bit is not None:
+            # Set a timeout so that the final bit ends.
+            self.es_bit = self.samplenum + self._samples_per_bit
+        else:
+            self.es_bit = None
+
+        if state != self._state:
+            ann = None
+            if self._state == 'data':
+                accum_bits = ''.join(str(x) for x in self._bits)
+                ann = [1, ['%d bits %s' % (len(self._bits), accum_bits),
+                           '%d bits' % len(self._bits)]]
+            elif self._state == 'invalid':
+                ann = [1, [self._state]]
+            if ann:
+                self.put(self.ss_state, self.samplenum, self.out_ann, ann)
+            self.ss_state = self.samplenum
+            self._state = state
+            self._bits = []
+
+    def decode(self, ss, es, data):
+        for self.samplenum, (d0, d1) in data:
+            if d0 == self._d0_prev and d1 == self._d1_prev:
+                if self.es_bit and self.samplenum >= self.es_bit:
+                    if (d0, d1) == (self._inactive, self._inactive):
+                        self._update_state('idle')
+                    else:
+                        self._update_state('invalid')
+                continue
+
+            if self._state in (None, 'idle', 'data'):
+                if (d0, d1) == (self._active, self._inactive):
+                    self._update_state('data', 0)
+                elif (d0, d1) == (self._inactive, self._active):
+                    self._update_state('data', 1)
+                elif (d0, d1) == (self._active, self._active):
+                    self._update_state('invalid')
+            elif self._state == 'invalid':
+                # Wait until we see an idle state before leaving invalid.
+                # This prevents inverted lines from being misread.
+                if (d0, d1) == (self._inactive, self._inactive):
+                    self._update_state('idle')
+
+            self._d0_prev, self._d1_prev = d0, d1
+
+    def report(self):
+        return '%s: %s D0 %d D1 %d (active on %d), %d samples per bit' % (
+            self.name, self._state, self._d0_prev, self._d1_prev,
+            self._active, self._samples_per_bit)
index 41aca0dbb4ed3f67d4de5b7d28cd295925bc176d..27135e585a1c267a7990af2dc56469b9222bf1a6 100644 (file)
 ##
 
 import sigrokdecode as srd
-
-MODULE_ID = {
-    0x01: 'GBIC',
-    0x02: 'Integrated module/connector',
-    0x03: 'SFP',
-    0x04: '300-pin XBI',
-    0x05: 'XENPAK',
-    0x06: 'XFP',
-    0x07: 'XFF',
-    0x08: 'XFP-E',
-    0x09: 'XPAK',
-    0x0a: 'X2',
-}
-
-ALARM_THRESHOLDS = {
-    0:  "Temp high alarm",
-    2:  "Temp low alarm",
-    4:  "Temp high warning",
-    6:  "Temp low warning",
-    16: "Bias high alarm",
-    18: "Bias low alarm",
-    20: "Bias high warning",
-    22: "Bias low warning",
-    24: "TX power high alarm",
-    26: "TX power low alarm",
-    28: "TX power high warning",
-    30: "TX power low warning",
-    32: "RX power high alarm",
-    34: "RX power low alarm",
-    36: "RX power high warning",
-    38: "RX power low warning",
-    40: "AUX 1 high alarm",
-    42: "AUX 1 low alarm",
-    44: "AUX 1 high warning",
-    46: "AUX 1 low warning",
-    48: "AUX 2 high alarm",
-    50: "AUX 2 low alarm",
-    52: "AUX 2 high warning",
-    54: "AUX 2 low warning",
-}
-
-AD_READOUTS = {
-    0:  "Module temperature",
-    4:  "TX bias current",
-    6:  "Measured TX output power",
-    8:  "Measured RX input power",
-    10: "AUX 1 measurement",
-    12: "AUX 2 measurement",
-}
-
-GCS_BITS = [
-    "TX disable",
-    "Soft TX disable",
-    "MOD_NR",
-    "P_Down",
-    "Soft P_Down",
-    "Interrupt",
-    "RX_LOS",
-    "Data_Not_Ready",
-    "TX_NR",
-    "TX_Fault",
-    "TX_CDR not locked",
-    "RX_NR",
-    "RX_CDR not locked",
-]
-
-CONNECTOR = {
-    0x01:   "SC",
-    0x02:   "Fibre Channel style 1 copper",
-    0x03:   "Fibre Channel style 2 copper",
-    0x04:   "BNC/TNC",
-    0x05:   "Fibre Channel coax",
-    0x06:   "FiberJack",
-    0x07:   "LC",
-    0x08:   "MT-RJ",
-    0x09:   "MU",
-    0x0a:   "SG",
-    0x0b:   "Optical pigtail",
-    0x20:   "HSSDC II",
-    0x21:   "Copper pigtail",
-}
-
-TRANSCEIVER = [
-    # 10GB Ethernet
-    ["10GBASE-SR", "10GBASE-LR", "10GBASE-ER", "10GBASE-LRM", "10GBASE-SW",
-        "10GBASE-LW",   "10GBASE-EW"],
-    # 10GB Fibre Channel
-    ["1200-MX-SN-I", "1200-SM-LL-L", "Extended Reach 1550 nm",
-        "Intermediate reach 1300 nm FP"],
-    # 10GB Copper
-    [],
-    # 10GB low speed
-    ["1000BASE-SX / 1xFC MMF", "1000BASE-LX / 1xFC SMF", "2xFC MMF",
-        "2xFC SMF", "OC48-SR", "OC48-IR", "OC48-LR"],
-    # 10GB SONET/SDH interconnect
-    ["I-64.1r", "I-64.1", "I-64.2r", "I-64.2", "I-64.3", "I-64.5"],
-    # 10GB SONET/SDH short haul
-    ["S-64.1", "S-64.2a", "S-64.2b", "S-64.3a", "S-64.3b", "S-64.5a", "S-64.5b"],
-    # 10GB SONET/SDH long haul
-    ["L-64.1", "L-64.2a", "L-64.2b", "L-64.2c", "L-64.3", "G.959.1 P1L1-2D2"],
-    # 10GB SONET/SDH very long haul
-    ["V-64.2a", "V-64.2b", "V-64.3"],
-]
-
-SERIAL_ENCODING = [
-    "64B/66B",
-    "8B/10B",
-    "SONET scrambled",
-    "NRZ",
-    "RZ",
-]
-
-XMIT_TECH = [
-    "850 nm VCSEL",
-    "1310 nm VCSEL",
-    "1550 nm VCSEL",
-    "1310 nm FP",
-    "1310 nm DFB",
-    "1550 nm DFB",
-    "1310 nm EML"
-    "1550 nm EML"
-    "copper",
-]
-
-CDR = [
-    "9.95Gb/s",
-    "10.3Gb/s",
-    "10.5Gb/s",
-    "10.7Gb/s",
-    "11.1Gb/s",
-    "(unknown)",
-    "lineside loopback mode",
-    "XFI loopback mode",
-]
-
-DEVICE_TECH = [
-    ["no wavelength control", "sctive wavelength control"],
-    ["uncooled transmitter device", "cooled transmitter"],
-    ["PIN detector", "APD detector"],
-    ["transmitter not tunable", "transmitter tunable"],
-]
-
-ENHANCED_OPTS = [
-    "VPS",
-    "soft TX_DISABLE",
-    "soft P_Down",
-    "VPS LV regulator mode",
-    "VPS bypassed regulator mode",
-    "active FEC control",
-    "wavelength tunability",
-    "CMU",
-]
-
-AUX_TYPES = [
-    "not implemented",
-    "APD bias voltage",
-    "(unknown)",
-    "TEC current",
-    "laser temperature",
-    "laser wavelength",
-    "5V supply voltage",
-    "3.3V supply voltage",
-    "1.8V supply voltage",
-    "-5.2V supply voltage",
-    "5V supply current",
-    "(unknown)",
-    "(unknown)",
-    "3.3V supply current",
-    "1.8V supply current",
-    "-5.2V supply current",
-]
+from common.plugtrx import (MODULE_ID, ALARM_THRESHOLDS, AD_READOUTS, GCS_BITS,
+        CONNECTOR, TRANSCEIVER, SERIAL_ENCODING, XMIT_TECH, CDR, DEVICE_TECH,
+        ENHANCED_OPTS, AUX_TYPES)
 
 class Decoder(srd.Decoder):
     api_version = 2
@@ -204,7 +36,7 @@ class Decoder(srd.Decoder):
         ('fields', 'XFP structure fields'),
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         # Received data items, used as an index into samplenum/data
         self.cnt = -1
         # Start/end sample numbers per data item
index e0d7ae4958c0a2a67f46f3e7dd2893f3f804e4cc..a8acf53c3aab2e327d544a7c4a765d7504db1afb 100644 (file)
@@ -110,7 +110,7 @@ class Decoder(srd.Decoder):
         ('warnings', 'Warnings', (Ann.WARN,))
     )
 
-    def __init__(self, **kwargs):
+    def __init__(self):
         self.prev_cycle = Cycle.NONE
         self.op_state   = self.state_IDLE