diff options
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | decoders/Makefile.am | 4 | ||||
-rw-r--r-- | decoders/usb/usb.py | 12 | ||||
-rw-r--r-- | decoders/usb_protocol/Makefile.am | 26 | ||||
-rw-r--r-- | decoders/usb_protocol/__init__.py | 47 | ||||
-rw-r--r-- | decoders/usb_protocol/usb_protocol.py | 137 | ||||
-rw-r--r-- | decoders/usb_signalling/Makefile.am | 26 | ||||
-rw-r--r-- | decoders/usb_signalling/__init__.py | 55 | ||||
-rw-r--r-- | decoders/usb_signalling/usb_signalling.py | 146 |
9 files changed, 448 insertions, 7 deletions
diff --git a/configure.ac b/configure.ac index be2946e..ec1a14f 100644 --- a/configure.ac +++ b/configure.ac @@ -168,6 +168,8 @@ AC_CONFIG_FILES([Makefile decoders/uart/Makefile decoders/uart_dump/Makefile decoders/usb/Makefile + decoders/usb_signalling/Makefile + decoders/usb_protocol/Makefile ]) AC_OUTPUT diff --git a/decoders/Makefile.am b/decoders/Makefile.am index 60cd853..65a0580 100644 --- a/decoders/Makefile.am +++ b/decoders/Makefile.am @@ -39,5 +39,7 @@ SUBDIRS = \ transitioncounter \ uart \ uart_dump \ - usb + usb \ + usb_signalling \ + usb_protocol diff --git a/decoders/usb/usb.py b/decoders/usb/usb.py index 529868e..c5b6a2e 100644 --- a/decoders/usb/usb.py +++ b/decoders/usb/usb.py @@ -166,12 +166,12 @@ class Decoder(srd.Decoder): if sym == self.sym: continue - if self.scount == 1: - # We ignore single sample width "pulses", i.e., symbol changes - # (D+/D- line changes). I sometimes get these with the OLS. - self.sym = sym - self.scount = 0 - continue + # if self.scount == 1: + # # We ignore single sample width "pulses", i.e., symbol changes + # # (D+/D- line changes). I sometimes get these with the OLS. + # self.sym = sym + # self.scount = 0 + # continue # How many bits since the last transition? if self.packet != '' or self.sym != 'J': diff --git a/decoders/usb_protocol/Makefile.am b/decoders/usb_protocol/Makefile.am new file mode 100644 index 0000000..599092b --- /dev/null +++ b/decoders/usb_protocol/Makefile.am @@ -0,0 +1,26 @@ +## +## This file is part of the sigrok project. +## +## Copyright (C) 2012 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 +## + +pkgdatadir = $(DECODERS_DIR)/usb_protocol + +dist_pkgdata_DATA = __init__.py usb_protocol.py + +CLEANFILES = *.pyc + diff --git a/decoders/usb_protocol/__init__.py b/decoders/usb_protocol/__init__.py new file mode 100644 index 0000000..a19304e --- /dev/null +++ b/decoders/usb_protocol/__init__.py @@ -0,0 +1,47 @@ +## +## This file is part of the sigrok project. +## +## Copyright (C) 2012 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 +## + +''' +USB (low-speed and full-speed) protocol decoder. + +Protocol layer (USB spec, chapter 8): + +Bit/byte ordering: Bits are sent onto the bus LSB-first. Multibyte fields +are transmitted in little-endian order (i.e., LSB to MSB). + +SYNC field: All packets begin with a SYNC field (8 bits). + +Packet field format: Packets start with an SOP (Start Of Packet) delimiter +that is part of the SYNC field, and end with an EOP (End Of Packet). + +PID: A PID (packet identifier) follows the SYNC field of every packet. A PID +consists of a 4-bit packet type field, and a 4 bit check field. +The check field is the one's complement of the packet type field. + +Protocol output format: +TODO + +Details: +https://en.wikipedia.org/wiki/USB +http://www.usb.org/developers/docs/ +''' + +from .usb_protocol import * + diff --git a/decoders/usb_protocol/usb_protocol.py b/decoders/usb_protocol/usb_protocol.py new file mode 100644 index 0000000..b1cd33b --- /dev/null +++ b/decoders/usb_protocol/usb_protocol.py @@ -0,0 +1,137 @@ +## +## This file is part of the sigrok project. +## +## Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz> +## Copyright (C) 2012 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 +## + +# USB (low-speed and full-speed) protocol decoder + +import sigrokdecode as srd + +# Packet IDs (PIDs). +# The first 4 bits are the 'packet type' field, the last 4 bits are the +# 'check field' (each bit in the check field must be the inverse of the resp. +# bit in the 'packet type' field; if not, that's a 'PID error'). +# For the 4-bit strings, the left-most '1' or '0' is the LSB, i.e. it's sent +# to the bus first. +pids = { + # Tokens + '10000111': ['OUT', 'Address & EP number in host-to-function transaction'], + '10010110': ['IN', 'Address & EP number in function-to-host transaction'], + '10100101': ['SOF', 'Start-Of-Frame marker & frame number'], + '10110100': ['SETUP', 'Address & EP number in host-to-function transaction for SETUP to a control pipe'], + + # Data + # Note: DATA2 and MDATA are HS-only. + '11000011': ['DATA0', 'Data packet PID even'], + '11010010': ['DATA1', 'Data packet PID odd'], + '11100001': ['DATA2', 'Data packet PID HS, high bandwidth isosynchronous transaction in a microframe'], + '11110000': ['MDATA', 'Data packet PID HS for split and high-bandwidth isosynchronous transactions'], + + # Handshake + '01001011': ['ACK', 'Receiver accepts error-free packet'], + '01011010': ['NAK', 'Receiver cannot accept or transmitter cannot send'], + '01111000': ['STALL', 'EP halted or control pipe request unsupported'], + '01101001': ['NYET', 'No response yet from receiver'], + + # Special + '00111100': ['PRE', 'Host-issued preamble; enables downstream bus traffic to low-speed devices'], + '00111100': ['ERR', 'Split transaction error handshake'], + '00011110': ['SPLIT', 'HS split transaction token'], + '00101101': ['PING', 'HS flow control probe for a bulk/control EP'], + '00001111': ['Reserved', 'Reserved PID'], +} + +def bitstr_to_num(bitstr): + if not bitstr: + return 0 + l = list(bitstr) + l.reverse() + return int(''.join(l), 2) + +def packet_decode(packet): + sync = packet[:8] + pid = packet[8:16] + pid = pids.get(pid, (pid, ''))[0] + + # Remove CRC. + if pid in ('OUT', 'IN', 'SOF', 'SETUP'): + data = packet[16:-5] + if pid == 'SOF': + data = str(bitstr_to_num(data)) + else: + dev = bitstr_to_num(data[:7]) + ep = bitstr_to_num(data[7:]) + data = 'DEV %d EP %d' % (dev, ep) + elif pid in ('DATA0', 'DATA1'): + data = packet[16:-16] + tmp = '' + while data: + tmp += '%02x ' % bitstr_to_num(data[:8]) + data = data[8:] + data = tmp + else: + data = packet[16:] + + # The SYNC pattern for low-speed/full-speed is KJKJKJKK (0001). + if sync != '00000001': + return 'SYNC INVALID: %s' % sync + + return pid + ' ' + data + +class Decoder(srd.Decoder): + api_version = 1 + id = 'usb_protocol' + name = 'USB protocol' + longname = 'Universal Serial Bus (LS/FS) protocol' + desc = 'USB 1.x (low-speed and full-speed) serial protocol.' + license = 'gplv2+' + inputs = ['usb_signalling'] + outputs = ['usb_protocol'] + probes = [] + optional_probes = [] + options = { + 'signalling': ['Signalling', 'full-speed'], + } + annotations = [ + ['Text', 'Human-readable text'] + ] + + def __init__(self): + self.sym = 'J' + self.samplenum = 0 + self.scount = 0 + self.packet = '' + self.state = 'IDLE' + + def start(self, metadata): + self.samplerate = metadata['samplerate'] + self.out_proto = self.add(srd.OUTPUT_PROTO, 'usb_protocol') + self.out_ann = self.add(srd.OUTPUT_ANN, 'usb_protocol') + + def report(self): + pass + + def decode(self, ss, es, data): + (ptype, pdata) = data + + if ptype == 'PACKET': + self.put(0, 0, self.out_ann, [0, [packet_decode(pdata)]]) + + # TODO. + diff --git a/decoders/usb_signalling/Makefile.am b/decoders/usb_signalling/Makefile.am new file mode 100644 index 0000000..316c62c --- /dev/null +++ b/decoders/usb_signalling/Makefile.am @@ -0,0 +1,26 @@ +## +## This file is part of the sigrok project. +## +## Copyright (C) 2012 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 +## + +pkgdatadir = $(DECODERS_DIR)/usb_signalling + +dist_pkgdata_DATA = __init__.py usb_signalling.py + +CLEANFILES = *.pyc + diff --git a/decoders/usb_signalling/__init__.py b/decoders/usb_signalling/__init__.py new file mode 100644 index 0000000..026875c --- /dev/null +++ b/decoders/usb_signalling/__init__.py @@ -0,0 +1,55 @@ +## +## This file is part of the sigrok project. +## +## Copyright (C) 2012 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 +## + +''' +USB (low-speed and full-speed) signalling protocol decoder. + +Electrical/signalling layer (USB spec, chapter 7): + +USB signalling consists of two signal lines, both driven at 3.3V +logic levels. The signals are DP (D+) and DM (D-), and normally operate in +differential mode. + +Low-speed: The state where DP=1,DM=0 is K, the state DP=0,DM=1 is J. +Full-speed: The state where DP=1,DM=0 is J, the state DP=0,DM=1 is K. + +A state SE0 is defined where DP=DM=0. This common mode signal is used to +signal a reset or end of packet (EOP). A state SE1 is defined where DP=DM=1. + +Data transmitted on the USB is encoded with NRZI. A transition from J to K +or vice-versa indicates a logic 0, while no transition indicates a logic 1. +If 6 ones are transmitted consecutively, a zero is inserted to force a +transition. This is known as bit stuffing. + +Data is transferred at a rate of 1.5Mbit/s (low-speed) / 12Mbit/s (full-speed). + +The SE0 transmitted to signal an end-of-packet is two bit intervals long +(low-speed: 1.25uS - 1.50uS, full-speed: 160ns - 175ns). + +Protocol output format: +TODO + +Details: +https://en.wikipedia.org/wiki/USB +http://www.usb.org/developers/docs/ +''' + +from .usb_signalling import * + diff --git a/decoders/usb_signalling/usb_signalling.py b/decoders/usb_signalling/usb_signalling.py new file mode 100644 index 0000000..512703d --- /dev/null +++ b/decoders/usb_signalling/usb_signalling.py @@ -0,0 +1,146 @@ +## +## This file is part of the sigrok project. +## +## Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz> +## Copyright (C) 2012 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 +## + +# USB signalling (low-speed and full-speed) protocol decoder + +import sigrokdecode as srd + +# Low-/full-speed symbols (used as states of our state machine, too). +# Note: Low-speed J and K are inverted compared to the full-speed J and K! +symbols_ls = { + # (<dp>, <dm>): <symbol/state> + (0, 0): 'SE0', + (1, 0): 'K', + (0, 1): 'J', + (1, 1): 'SE1', +} +symbols_fs = { + # (<dp>, <dm>): <symbol/state> + (0, 0): 'SE0', + (1, 0): 'J', + (0, 1): 'K', + (1, 1): 'SE1', +} + +class Decoder(srd.Decoder): + api_version = 1 + id = 'usb_signalling' + name = 'USB signalling' + longname = 'Universal Serial Bus (LS/FS) signalling' + desc = 'USB 1.x (low-speed and full-speed) signalling protocol.' + license = 'gplv2+' + inputs = ['logic'] + outputs = ['usb_signalling'] + probes = [ + {'id': 'dp', 'name': 'D+', 'desc': 'USB D+ signal'}, + {'id': 'dm', 'name': 'D-', 'desc': 'USB D- signal'}, + ] + optional_probes = [] + options = { + 'signalling': ['Signalling', 'full-speed'], + } + annotations = [ + ['Text', 'Human-readable text'] + ] + + def __init__(self): + self.sym = 'J' # The "idle" state is J. + self.samplenum = 0 + self.scount = 0 + self.packet = '' + self.syms = [] + + def start(self, metadata): + self.samplerate = metadata['samplerate'] + self.out_proto = self.add(srd.OUTPUT_PROTO, 'usb_signalling') + self.out_ann = self.add(srd.OUTPUT_ANN, 'usb_signalling') + + def report(self): + pass + + def decode(self, ss, es, data): + for (self.samplenum, (dp, dm)) in data: + + # Note: self.samplenum is the absolute sample number, whereas + # self.scount only counts the number of samples since the + # last change in the D+/D- lines. + self.scount += 1 + + if self.options['signalling'] == 'low-speed': + sym = symbols_ls[dp, dm] + elif self.options['signalling'] == 'full-speed': + sym = symbols_fs[dp, dm] + + self.put(0, 0, self.out_ann, [0, [sym]]) + self.put(0, 0, self.out_proto, ['SYM', sym]) + + # Wait for a symbol change (i.e., change in D+/D- lines). + if sym == self.sym: + continue + + ## # Debug code: + ## self.syms.append(sym + ' ') + ## if len(self.syms) == 16: + ## self.put(0, 0, self.out_ann, [0, [''.join(self.syms)]]) + ## self.syms = [] + # continue + + # How many bits since the last transition? + if self.packet != '' or self.sym != 'J': + if self.options['signalling'] == 'low-speed': + bitrate = 1500000 # 1.5Mb/s (+/- 1.5%) + elif self.options['signalling'] == 'full-speed': + bitrate = 12000000 # 12Mb/s (+/- 0.25%) + bitcount = int((self.scount - 1) * bitrate / self.samplerate) + else: + bitcount = 0 + + if self.sym == 'SE0': + if bitcount == 1: + # End-Of-Packet (EOP) + # self.put(0, 0, self.out_ann, + # [0, [packet_decode(self.packet), self.packet]]) + if self.packet != '': # FIXME? + self.put(0, 0, self.out_ann, [0, ['PACKET: %s' % self.packet]]) + self.put(0, 0, self.out_proto, ['PACKET', self.packet]) + else: + # Longer than EOP, assume reset. + self.put(0, 0, self.out_ann, [0, ['RESET']]) + self.put(0, 0, self.out_proto, ['RESET', None]) + # self.put(0, 0, self.out_ann, [0, [self.packet]]) + self.scount = 0 + self.sym = sym + self.packet = '' + continue + + # Add bits to the packet string. + self.packet += '1' * bitcount + + # Handle bit stuffing. + if bitcount < 6 and sym != 'SE0': + self.packet += '0' + elif bitcount > 6: + self.put(0, 0, self.out_ann, [0, ['BIT STUFF ERROR']]) + self.put(0, 0, self.out_proto, ['BIT STUFF ERROR', None]) + + self.scount = 0 + self.sym = sym + |