Beispiel #1
0
 def _exchange_full_duplex(self, frequency, out,
                           cs_prolog, cs_epilog, cpol, cpha):
     if not self._ftdi:
         raise SpiIOError("FTDI controller not initialized")
     if len(out) > SpiController.PAYLOAD_MAX_LENGTH:
         raise SpiIOError("Output payload is too large")
     if cpha:
         # to enable CPHA, we need to use a workaround with FTDI device,
         # that is enable 3-phase clocking (which is usually dedicated to
         # I2C support). This mode use use 3 clock period instead of 2,
         # which implies the FTDI frequency should be fixed to match the
         # requested one.
         frequency = (3*frequency)//2
     if self._frequency != frequency:
         self._ftdi.set_frequency(frequency)
         # store the requested value, not the actual one (best effort),
         # to avoid setting unavailable values on each call.
         self._frequency = frequency
     direction = self.direction & 0xFF  # low bits only
     cmd = array('B')
     for ctrl in cs_prolog or []:
         ctrl &= self._spi_mask
         ctrl |= self._gpio_low
         cmd.extend((Ftdi.SET_BITS_LOW, ctrl, direction))
     epilog = array('B')
     if cs_epilog:
         for ctrl in cs_epilog:
             ctrl &= self._spi_mask
             ctrl |= self._gpio_low
             epilog.extend((Ftdi.SET_BITS_LOW, ctrl, direction))
         # Restore idle state
         cs_high = [Ftdi.SET_BITS_LOW, self._cs_bits | self._gpio_low,
                    direction]
         if not self._turbo:
             cs_high.append(Ftdi.SEND_IMMEDIATE)
         epilog.extend(cs_high)
     writelen = len(out)
     if self._clock_phase != cpha:
         self._ftdi.enable_3phase_clock(cpha)
         self._clock_phase = cpha
     wcmd = (cpol ^ cpha) and \
         Ftdi.RW_BYTES_NVE_PVE_MSB or Ftdi.RW_BYTES_PVE_NVE_MSB
     write_cmd = spack('<BH', wcmd, writelen-1)
     cmd.frombytes(write_cmd)
     cmd.extend(out)
     cmd.extend(self._immediate)
     if self._turbo:
         if epilog:
             cmd.extend(epilog)
         self._ftdi.write_data(cmd)
     else:
         self._ftdi.write_data(cmd)
         if epilog:
             self._ftdi.write_data(epilog)
     # USB read cycle may occur before the FTDI device has actually
     # sent the data, so try to read more than once if no data is
     # actually received
     data = self._ftdi.read_data_bytes(len(out), 4)
     return data
Beispiel #2
0
 def _make_buffer(self, regaddr: int,
                  out: Union[bytes, bytearray, Iterable[int], None] = None)\
                  -> bytes:
     data = bytearray()
     data.extend(spack('%s%s' % (self._endian, self._format), regaddr))
     if out:
         data.extend(out)
     return bytes(data)
Beispiel #3
0
    def set_property(self, name: str, value: Union[str, int, bool],
                     out: Optional[TextIO] = None) -> None:
        """Change the value of a stored property.

           :see: :py:meth:`properties` for a list of valid property names.
                 Note that for now, only a small subset of properties can be
                 changed.
           :param name: the property to change
           :param value: the new value (supported values depend on property)
           :param out: optional output stream to report hints
        """
        mobj = match(r'cbus_func_(\d)', name)
        if mobj:
            if not isinstance(value, str):
                raise ValueError("'%s' should be specified as a string" % name)
            self._set_cbus_func(int(mobj.group(1)), value, out)
            self._dirty.add(name)
            return
        hwords = {
            'vendor_id': 0x02,
            'product_id': 0x04,
            'type': 0x06,
            'usb_version': 0x0c
        }
        if name in hwords:
            val = to_int(value)
            if not 0 <= val <= 0xFFFF:
                raise ValueError('Invalid value for %s' % name)
            offset = hwords[name]
            self._eeprom[offset:offset+2] = spack('<H', val)
            return
        confs = {
            'remote_wakeup': (0, 5),
            'self_powered': (0, 6),
            'in_isochronous': (2, 0),
            'out_isochronous': (2, 1),
            'suspend_pull_down': (2, 2),
            'has_serial': (2, 3),
            'has_usb_version': (2, 4),
        }
        if name in confs:
            val = to_bool(value, permissive=False, allow_int=True)
            offset, bit = confs[name]
            mask = 1 << bit
            if val:
                self._eeprom[0x08+offset] |= mask
            else:
                self._eeprom[0x0a+offset] &= ~mask
            return
        if name == 'power_max':
            val = to_int(value) >> 1
            self._eeprom[0x09] = val
            return
        if name in self.properties:
            raise NotImplementedError("Change to '%s' is not yet supported" %
                                      name)
        raise ValueError("Unknown property '%s'" % name)
Beispiel #4
0
    def write(self, value):
        """Set the GPIO output pin electrical level.

           :param int value: a bitfield of GPIO pins.
        """
        if not self.is_connected:
            raise GpioException('Not connected')
        if value > self.MASK:
            raise GpioException("Invalid value")
        self._ftdi.write_data(spack('<B', value))
Beispiel #5
0
    def write(self, value):
        """Set the GPIO output pin electrical level.

           :param int value: a bitfield of GPIO pins.
        """
        if not self.is_connected:
            raise GpioException('Not connected')
        if value > self.MASK:
            raise GpioException("Invalid value")
        self._ftdi.write_data(spack('<B', value))
Beispiel #6
0
 def get_string(self, type_: int, index: int) -> str:
     if index == 0:
         if self.desc.get('noaccess', False):
             # simulate unauthorized access to the USB device
             return b''
         # request for list of supported languages
         # only support one
         fmt = '<BBH'
         size = scalc(fmt)
         buf = spack(fmt, size, type_, self.DEFAULT_LANGUAGE)
         return buf
     try:
         value = self.strings[index]
     except IndexError:
         return b''
     ms_str = value.encode('utf-16-le')
     fmt = '<BB'
     size = scalc(fmt) + len(ms_str)
     buf = bytearray(spack('<BB', size, type_))
     buf.extend(ms_str)
     return buf
Beispiel #7
0
 def _stream_source(cls, port, chunk, size, results):
     pos = 0
     tx_size = 0
     start = now()
     while tx_size < size:
         samples = spack('>%dI' % chunk, *range(pos, pos + chunk))
         pos += chunk
         port.write(samples)
         tx_size += len(samples)
         if results[1] is not None:
             break
     delta = now() - start
     results[0] = tx_size, delta
Beispiel #8
0
    def write_port(self, value):
        """Set the GPIO output pin electrical level.

           :param value: a bitfield of GPIO pins. Each bit represent a GPIO
                         pin, where '1' reports a high level on the matching
                         pin and '0' reports a low level. GPIO pins that are
                         configured as Output should be ignored.
        """
        if not self.is_connected:
            raise GpioException('Not connected')
        if value > self._direction or (value & ~self._direction):
            raise GpioException("Invalid value")
        self._ftdi.write_data(spack('<B', value))
Beispiel #9
0
def main():
    length = 4 * 4096 - 32
    size = length // scalc('<I')
    chunk_size = min((size, 256))
    hash_ = sha512()
    print(size, chunk_size)
    for segment in range(0, size, chunk_size):
        chunk = list(range(segment, min((segment + chunk_size, size))))
        buf = spack(f'<{len(chunk)}I', *chunk)
        # print(hexlify(buf).decode())
        hash_.update(buf)
    print("//", hash_.hexdigest())
    print_c_array("long_buf_hash", hash_.digest())
Beispiel #10
0
 def _create_data(cls, offset, segment):
     data = segment.data
     address = offset + segment.baseaddr
     high_addr = None
     for pos in range(0, len(data), 16):
         high = address >> 16
         if high != high_addr:
             hi_bytes = spack('>H', high)
             yield cls._create_line(4, 0, hi_bytes)
             high_addr = high
         chunk = data[pos:pos + 16]
         yield cls._create_line(0, address & 0xffff, chunk)
         address += 16
Beispiel #11
0
    def write_port(self, value):
        """Set the GPIO output pin electrical level.

           :param value: a bitfield of GPIO pins. Each bit represent a GPIO
                         pin, where '1' reports a high level on the matching
                         pin and '0' reports a low level. GPIO pins that are
                         configured as Output should be ignored.
        """
        if not self.is_connected:
            raise GpioException('Not connected')
        if value > self._direction or (value & ~self._direction):
            raise GpioException("Invalid value")
        self._ftdi.write_data(spack('<B', value))
Beispiel #12
0
def flowlet_to_packet(flowlet):
    if hasattr(flowlet, "origpkt"):
        return getattr(flowlet, "origpkt")

    ident = flowlet.ident.key

    etherhdr = pktlib.ethernet()
    etherhdr.src = EthAddr(flowlet.srcmac)
    etherhdr.dst = EthAddr(flowlet.dstmac)
    etherhdr.type = pktlib.ethernet.IP_TYPE

    ipv4 = pktlib.ipv4()
    ipv4.srcip = IPAddr(ident.srcip)
    ipv4.dstip = IPAddr(ident.dstip)
    ipv4.protocol = ident.ipproto
    ipv4.tos = flowlet.iptos
    iplen = flowlet.bytes / flowlet.pkts
    ipv4.iplen = iplen
    payloadlen = 0

    etherhdr.payload = ipv4

    if ident.ipproto == IPPROTO_ICMP:
        layer4 = pktlib.icmp()
        layer4.type = ident.dport >> 8
        layer4.code = ident.dport & 0x00FF
        payloadlen = max(iplen - 28, 0)
    elif ident.ipproto == IPPROTO_UDP:
        layer4 = pktlib.udp()
        layer4.srcport = ident.sport
        layer4.dstport = ident.dport
    elif ident.ipproto == IPPROTO_TCP:
        layer4 = pktlib.tcp()
        layer4.srcport = ident.sport
        layer4.dstport = ident.dport
        layer4.flags = flowlet.tcpflags
        layer4.off = 5
        payloadlen = max(iplen - 40, 0)
        layer4.tcplen = payloadlen
        layer4.payload = spack("{}x".format(payloadlen))
    else:
        raise UnhandledPoxPacketFlowletTranslation(
            "Can't translate IP protocol {} from flowlet to POX packet".format(fident.ipproto)
        )
    ipv4.payload = layer4
    etherhdr.origflet = flowlet
    return etherhdr
Beispiel #13
0
 def _control_write_eeprom(self, wValue: int, wIndex: int,
                           data: array) -> None:
     self.log.info('> ftdi write_eeprom @ 0x%04x', wIndex * 2)
     if not self._eeprom:
         self.log.warning('Missing EEPROM')
         return
     address = abs(wIndex * 2)
     if address + 1 > len(self._eeprom):
         # out of bound
         self.log.warning('Invalid EEPROM address: 0x%04x', wValue)
         return
     if self._version == 0x1000:
         if 0x80 <= address < 0xA0:
             # those address are R/O on FT230x
             self.log.warning('Protected EEPROM address: 0x%04x', wValue)
             return
     self._eeprom[address:address + 2] = spack('<H', wValue)
Beispiel #14
0
def flowlet_to_packet(flowlet):
    '''Translate an fs flowlet to a POX packet'''
    if hasattr(flowlet, "origpkt"):
        return getattr(flowlet, "origpkt")

    ident = flowlet.ident.key

    etherhdr = pktlib.ethernet()
    etherhdr.src = EthAddr(flowlet.srcmac)
    etherhdr.dst = EthAddr(flowlet.dstmac)
    etherhdr.type = pktlib.ethernet.IP_TYPE

    ipv4 = pktlib.ipv4() 
    ipv4.srcip = IPAddr(ident.srcip)
    ipv4.dstip = IPAddr(ident.dstip)
    ipv4.protocol = ident.ipproto
    ipv4.tos = flowlet.iptos
    iplen = flowlet.bytes / flowlet.pkts
    ipv4.iplen = iplen
    payloadlen = 0

    etherhdr.payload = ipv4

    if ident.ipproto == IPPROTO_ICMP:
        layer4 = pktlib.icmp()
        layer4.type = ident.dport >> 8
        layer4.code = ident.dport & 0x00FF
        payloadlen = max(iplen-28,0)
    elif ident.ipproto == IPPROTO_UDP:
        layer4 = pktlib.udp()
        layer4.srcport = ident.sport 
        layer4.dstport = ident.dport 
    elif ident.ipproto == IPPROTO_TCP:
        layer4 = pktlib.tcp()
        layer4.srcport = ident.sport 
        layer4.dstport = ident.dport 
        layer4.flags = flowlet.tcpflags
        layer4.off = 5
        payloadlen = max(iplen-40,0)
        layer4.tcplen = payloadlen
        layer4.payload = spack('{}x'.format(payloadlen))
    else:
        raise UnhandledPoxPacketFlowletTranslation("Can't translate IP protocol {} from flowlet to POX packet".format(fident.ipproto))
    ipv4.payload = layer4
    etherhdr.origflet = flowlet
    return etherhdr
Beispiel #15
0
 def serialize(self):
     if self.msgdict is None:
         raise LLRPError('No message dict to serialize.')
     name = list(self.msgdict.keys())[0]
     logger.debug('serializing %s command', name)
     ver = self.msgdict[name]['Ver'] & BITMASK(3)
     msgtype = self.msgdict[name]['Type'] & BITMASK(10)
     msgid = self.msgdict[name]['ID']
     try:
         encoder = Message_struct[name]['encode']
     except KeyError:
         raise LLRPError('Cannot find encoder for message type '
                         '{}'.format(name))
     data = encoder(self.msgdict[name])
     self.msgbytes = spack(self.full_hdr_fmt, (ver << 10) | msgtype,
                           len(data) + self.full_hdr_len, msgid) + data
     logger.debug('serialized bytes: %s', hexlify(self.msgbytes))
     logger.debug('done serializing %s command', name)
Beispiel #16
0
    def _write_raw(self, data, write_high):
        direction = self.direction
        low_data = data & 0xFF
        low_dir = direction & 0xFF
        if write_high:
            high_data = (data >> 8) & 0xFF
            high_dir = (direction >> 8) & 0xFF
            cmd = array('B', [
                Ftdi.SET_BITS_LOW, low_data, low_dir, Ftdi.SET_BITS_HIGH,
                high_data, high_dir
            ])
        else:
            # If not using read_high method, then also means this is
            # BIT BANG and not MPSSE, so just write the data - no CMD
            # needed
            cmd = spack('<B', low_data)

        self._ftdi.write_data(cmd)
Beispiel #17
0
 def _generate_var_strings(self, fill=True) -> None:
     stream = bytearray()
     dynpos = self._PROPERTIES[self.device_version].dynoff
     data_pos = dynpos
     tbl_pos = 0x0e
     for name in self.VAR_STRINGS:
         ustr = self._config[name].encode('utf-16le')
         length = len(ustr) + 2
         stream.append(length)
         stream.append(0x03)  # no idea what this constant means
         stream.extend(ustr)
         self._eeprom[tbl_pos] = data_pos
         tbl_pos += 1
         self._eeprom[tbl_pos] = length
         tbl_pos += 1
         data_pos += length
     self._eeprom[dynpos:dynpos + len(stream)] = stream
     crc_size = scalc('<H')
     if fill:
         rem = len(self._eeprom) - (dynpos + len(stream)) - crc_size
         self._eeprom[dynpos + len(stream):-crc_size] = bytes(rem)
     crc = self._ftdi.calc_eeprom_checksum(self._eeprom[:-crc_size])
     self._eeprom[-crc_size:] = spack('<H', crc)
Beispiel #18
0
def packf64(x):
    return spack('<d', x)
Beispiel #19
0
 def _make_buffer(self, regaddr, out=None):
     data = array('B')
     data.extend(spack('%s%s' % (self._endian, self._format), regaddr))
     if out:
         data.extend(out)
     return data.tobytes()
Beispiel #20
0
 def _make_buffer(self, regaddr, out=None):
     data = bytearray()
     data.extend(spack('%s%s' % (self._endian, self._format), regaddr))
     if out:
         data.extend(out)
     return bytes(data)
Beispiel #21
0
 def _update_crc(self):
     crc, crc_pos, crc_size = self._compute_crc(self._eeprom, False)
     self._eeprom[crc_pos:crc_pos + crc_size] = spack('<H', crc)
Beispiel #22
0
    def set_property(self, name: str, value: Union[str, int, bool],
                     out: Optional[TextIO] = None) -> None:
        """Change the value of a stored property.

           :see: :py:meth:`properties` for a list of valid property names.
                 Note that for now, only a small subset of properties can be
                 changed.
           :param name: the property to change
           :param value: the new value (supported values depend on property)
           :param out: optional output stream to report hints
        """
        mobj = match(r'cbus_func_(\d)', name)
        if mobj:
            if not isinstance(value, str):
                raise ValueError("'%s' should be specified as a string" % name)
            self._set_cbus_func(int(mobj.group(1)), value, out)
            self._dirty.add(name)
            return
        mobj = match(r'([abcd])bus_(drive|slow_slew|schmitt)', name)
        if mobj:
            self._set_bus_control(mobj.group(1), mobj.group(2), value, out)
            self._dirty.add(name)
            return
        mobj = match(r'group_(\d)_(drive|schmitt|slow_slew)', name)
        if mobj:
            self._set_group(int(mobj.group(1)), mobj.group(2), value, out)
            return
        confs = {
            'remote_wakeup': (0, 5),
            'self_powered': (0, 6),
            'in_isochronous': (2, 0),
            'out_isochronous': (2, 1),
            'suspend_pull_down': (2, 2),
            'has_serial': (2, 3),
        }
        hwords = {
            'vendor_id': 0x02,
            'product_id': 0x04,
            'type': 0x06,
        }
        if self.device_version in (0x0400, 0x0500):
            # Type BM and 2232C/D use 0xc to encode the USB version to expose
            # H device use this location to encode bus/group properties
            hwords['usb_version'] = 0x0c
            confs['use_usb_version'] = (2, 4)
        if name in hwords:
            val = to_int(value)
            if not 0 <= val <= 0xFFFF:
                raise ValueError('Invalid value for %s' % name)
            offset = hwords[name]
            self._eeprom[offset:offset+2] = spack('<H', val)
            return
        if name in confs:
            val = to_bool(value, permissive=False, allow_int=True)
            offset, bit = confs[name]
            mask = 1 << bit
            if val:
                self._eeprom[0x08+offset] |= mask
            else:
                self._eeprom[0x0a+offset] &= ~mask
            return
        if name == 'power_max':
            val = to_int(value) >> 1
            self._eeprom[0x09] = val
            return
        if name.startswith('invert_'):
            if not self.device_version in (0x600, 0x1000):
                raise ValueError('UART control line inversion not available '
                                 'with this device')
            self._set_invert(name[len('invert_'):], value, out)
            self._dirty.add(name)
            return
        if name in self.properties:
            if name not in self._config:
                raise NotImplementedError("change is not supported")
            curval = self._config[name]
            try:
                curtype = type(curval)
                value = curtype(value)
            except (ValueError, TypeError) as exc:
                raise ValueError("cannot be converted to "
                    "the proper type '%s'" % curtype) from exc
            if value != curval:
                raise NotImplementedError("not yet supported")
            # no-op change is silently ignored
            return
        raise ValueError(f"unknown property: {name}")
Beispiel #23
0
                                make_patch(
                                    imr.convert("P",
                                                palette=pal,
                                                dither=Image.NONE)))

                        outwad.flats[tf] = omg.Lump(
                            imr.resize(
                                (64, 64)).convert("P",
                                                  palette=pal,
                                                  dither=Image.NONE).tobytes())

                        if not ZDOOM:
                            pnames.append(
                                spack(
                                    "{}s{}c".format(len(tp),
                                                    max(0, 8 - len(tp))),
                                    tp[:8].encode('utf-8'),
                                    *[0 for _ in range(max(0, 8 - len(tp)))]))
                            texture1.append(
                                spack("=8sH2BhHI6h", tt.encode('utf-8'), 0, 8,
                                      8, *imr.size, 0, 1, 0, 0, i, 0, 0))

                        i += 1

    if ANIM:
        if ZDOOM:
            print("\nCreating ANIMDEFS for color/light oscillations...")

            animations = {}

            for g in texnames:
Beispiel #24
0
 def _exchange_half_duplex(self, frequency, out, readlen, cs_prolog,
                           cs_epilog, cpol, cpha):
     if not self._ftdi:
         raise SpiIOError("FTDI controller not initialized")
     if len(out) > SpiController.PAYLOAD_MAX_LENGTH:
         raise SpiIOError("Output payload is too large")
     if readlen > SpiController.PAYLOAD_MAX_LENGTH:
         raise SpiIOError("Input payload is too large")
     if cpha:
         # to enable CPHA, we need to use a workaround with FTDI device,
         # that is enable 3-phase clocking (which is usually dedicated to
         # I2C support). This mode use use 3 clock period instead of 2,
         # which implies the FTDI frequency should be fixed to match the
         # requested one.
         frequency = (3 * frequency) // 2
     if self._frequency != frequency:
         self._ftdi.set_frequency(frequency)
         # store the requested value, not the actual one (best effort),
         # to avoid setting unavailable values on each call.
         self._frequency = frequency
     direction = self.direction & 0xFF  # low bits only
     cmd = array('B')
     for ctrl in cs_prolog or []:
         ctrl &= self._spi_mask
         ctrl |= self._gpio_low
         cmd.extend((Ftdi.SET_BITS_LOW, ctrl, direction))
     epilog = array('B')
     if cs_epilog:
         for ctrl in cs_epilog:
             ctrl &= self._spi_mask
             ctrl |= self._gpio_low
             epilog.extend((Ftdi.SET_BITS_LOW, ctrl, direction))
         # Restore idle state
         cs_high = [
             Ftdi.SET_BITS_LOW, self._cs_bits | self._gpio_low, direction
         ]
         if not self._turbo:
             cs_high.append(Ftdi.SEND_IMMEDIATE)
         epilog.extend(cs_high)
     writelen = len(out)
     if self._clock_phase != cpha:
         self._ftdi.enable_3phase_clock(cpha)
         self._clock_phase = cpha
     if writelen:
         wcmd = (cpol ^ cpha) and \
             Ftdi.WRITE_BYTES_PVE_MSB or Ftdi.WRITE_BYTES_NVE_MSB
         write_cmd = spack('<BH', wcmd, writelen - 1)
         cmd.frombytes(write_cmd)
         cmd.extend(out)
     if readlen:
         rcmd = (cpol ^ cpha) and \
             Ftdi.READ_BYTES_PVE_MSB or Ftdi.READ_BYTES_NVE_MSB
         read_cmd = spack('<BH', rcmd, readlen - 1)
         cmd.frombytes(read_cmd)
         cmd.extend(self._immediate)
         if self._turbo:
             if epilog:
                 cmd.extend(epilog)
             self._ftdi.write_data(cmd)
         else:
             self._ftdi.write_data(cmd)
             if epilog:
                 self._ftdi.write_data(epilog)
         # USB read cycle may occur before the FTDI device has actually
         # sent the data, so try to read more than once if no data is
         # actually received
         data = self._ftdi.read_data_bytes(readlen, 4)
     else:
         if writelen:
             if self._turbo:
                 if epilog:
                     cmd.extend(epilog)
                 self._ftdi.write_data(cmd)
             else:
                 self._ftdi.write_data(cmd)
                 if epilog:
                     self._ftdi.write_data(epilog)
         data = array('B')
     return data
Beispiel #25
0
def inttoip(ipval):
    return inet_ntoa(spack('!I', ipval))
Beispiel #26
0
    def _pack(self, obj):
        w = self.writer

        if obj is None:
            w.write(b"\xC0")
        elif isinstance(obj, bool):
            w.write(b"\xC3" if obj else b"\xC2")
        elif isinstance(obj, int):
            if obj > 0:
                if obj < MAX_UINT7:
                    w.write(bytes([obj]))
                elif obj < MAX_UINT8:
                    w.write(bytes([0xCC, obj]))
                elif obj < MAX_UINT16:
                    w.write(b"\xCD")
                    w.write(spack("!H", obj))
                elif obj < MAX_UINT32:
                    w.write(b"\xCE")
                    w.write(spack("!I", obj))
                elif obj < MAX_UINT64:
                    w.write(b"\xCF")
                    w.write(spack("!Q", obj))
                else:
                    raise ValueError("Integer value >= 2^64 too large.")
            else:
                if obj >= MAX_NINT5:
                    w.write(spack("b", obj))
                elif obj >= MAX_UINT8:
                    w.write(b"\xD0" + spack("!b", obj))
                elif obj >= MAX_NINT16:
                    w.write(b"\xD1" + spack("!h", obj))
                elif obj >= MAX_NINT32:
                    w.write(b"\xD2" + spack("!i", obj))
                elif obj >= MAX_NINT64:
                    w.write(b"\xD3" + spack("!q", obj))
                else:
                    raise ValueError("Integer value < -2^64/2 too small.")
        elif isinstance(obj, float):
            w.write(b"\xCA" if self.use_f32 else b"\xCB")
            w.write(spack("!f" if self.use_f32 else "!d", obj))
        elif isinstance(obj, (bytearray, bytes, str)):
            if isinstance(obj, (bytearray, bytes)) and self.use_bin_type:
                olen = len(obj)

                if olen < MAX_UINT8:
                    w.write(bytes([0xC4, olen]))
                elif olen < MAX_UINT16:
                    w.write(b"\xC5")
                    w.write(spack("!H", olen))
                elif olen < MAX_UINT32:
                    w.write(b"\xC6")
                    w.write(spack("!I", olen))
                else:
                    raise ValueError("Bytes object len >= 2^32 too big.")

                w.write(bytes(obj) if isinstance(obj, bytearray) else obj)
            else:
                if isinstance(obj, str):
                    obj = bytes(obj,
                                encoding="utf-8",
                                errors=self.unicode_errors)

                olen = len(obj)

                if olen < 32:
                    w.write(bytes([olen | 0xA0]))
                elif olen < MAX_UINT8:
                    w.write(bytes([0xD9, olen]))
                elif olen < MAX_UINT16:
                    w.write(b"\xDA")
                    w.write(spack("!H", olen))
                elif olen < MAX_UINT32:
                    w.write(b"\xDB")
                    w.write(spack("!I", olen))
                else:
                    raise ValueError("Bytes object len >= 2^32 too big.")

                w.write(obj)
        elif isinstance(obj, (tuple, list)):
            olen = len(obj)

            if olen < 16:
                w.write(bytes([olen | 0x90]))
            elif olen < MAX_UINT16:
                w.write(b"\xDC")
                w.write(spack("!H", olen))
            elif olen < MAX_UINT32:
                w.write(b"\xDD")
                w.write(spack("!H", olen))
            else:
                raise ValueError(
                    "Sequence object has too many (>= 2^32) elements.")

            for elem in obj:
                self._pack(elem)
        elif isinstance(obj, dict):
            olen = len(obj)

            if olen < 16:
                w.write(bytes([olen | 0x80]))
            elif olen < MAX_UINT16:
                w.write(b"\xDE")
                w.write(spack("!H", olen))
            elif olen < MAX_UINT32:
                w.write(b"\xDF")
                w.write(spack("!H", olen))
            else:
                raise ValueError(
                    "Dict object has too many (>= 2^32) elements.")

            for elem in obj:
                self._pack(elem)
                self._pack(obj[elem])
        else:
            raise TypeError("Cannot serialize instance of %s." % type(obj))
Beispiel #27
0
 def build_dhcp_options(self, clientname):
     if not clientname:
         return b''
     return spack('!BB%ds' % len(clientname),
                  12, len(clientname), clientname)
Beispiel #28
0
    def handle(self, sock, addr, data):
        self.log.info('Sender: %s on socket %s' % (addr, sock.getsockname()))
        if len(data) < DHCPFORMATSIZE:
            self.log.error('Cannot be a DHCP or BOOTP request - too small!')
        tail = data[DHCPFORMATSIZE:]
        buf = list(sunpack(DHCPFORMAT, data[:DHCPFORMATSIZE]))
        if buf[BOOTP_OP] != BOOTREQUEST:
            self.log.warn('Not a BOOTREQUEST')
            return
        options = self.parse_options(tail)
        if options is None:
            self.log.warn('Error in option parsing, ignore request')
            return

        # Extras (DHCP options)
        try:
            dhcp_msg_type = options[53][0]
        except KeyError:
            dhcp_msg_type = None

        server_addr = self.netconfig['server']
        mac_addr = buf[BOOTP_CHADDR][:6]
        mac_str = ':'.join(['%02X' % x for x in mac_addr])
        # is the UUID received (PXE mode)
        if 97 in options and len(options[97]) == 17:
            uuid = options[97][1:]
            pxe = True
            self.log.info('PXE UUID has been received')
        # or retrieved from the cache (DHCP mode)
        else:
            uuid = self.uuidpool.get(mac_addr, None)
            pxe = False
            self.log.info('PXE UUID not present in request')
        uuid_str = uuid and ('%s-%s-%s-%s-%s' % tuple([hexlify(x)
            for x in (uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:16])
            ])).upper()
        if uuid_str:
            self.log.info('UUID is %s for MAC %s' % (uuid_str, mac_str))

        hostname = ''
        filename = ''

        # Basic state machine
        currentstate = self.states.setdefault(mac_str, self.ST_IDLE)
        newstate = currentstate
        if currentstate == self.ST_IDLE:
            if pxe and (dhcp_msg_type == DHCP_DISCOVER):
                # BIOS is booting up, and try to locate a DHCP server
                newstate = self.ST_PXE
        elif currentstate == self.ST_PXE:
            if not pxe and (dhcp_msg_type == DHCP_REQUEST):
                # OS is booting up, and confirm a previous DHCP dicovery
                newstate = self.ST_DHCP
        else:  # currentstate == self.ST_DHCP
            if pxe:
                # OS was running but the BIOS is performing a DHCP request:
                # board has been restarted
                newstate = self.ST_PXE

        # if the state has not evolved from idle, there is nothing to do
        if newstate == self.ST_IDLE:
            sdhcp = 'allow_simple_dhcp'
            simple_dhcp = \
                self.config.has_option(self.BOOTP_SECTION, sdhcp) and \
                to_bool(self.config.get(self.BOOTP_SECTION, sdhcp))
            if not simple_dhcp:
                self.log.info('Request from %s ignored (idle state)' % mac_str)
                return
            if not dhcp_msg_type:
                # Legacy DHCP: assuming discover by default
                dhcp_msg_type = DHCP_DISCOVER

        # if access control is enable
        if self.access:
            # remote access is always validated on each request
            if self.access in self.ACCESS_REMOTE:
                # need to query a host to grant or reject access
                netloc = self.config.get(self.access, 'location')
                path = self.config.get(self.access, pxe and 'pxe' or 'dhcp')
                timeout = int(self.config.get(self.access, 'timeout', '5'))
                always_check = self.config.get(self.access, 'always_check')
                parameters = {'mac': mac_str}
                if uuid:
                    parameters['uuid'] = uuid_str
                if not pxe and mac_str in self.ippool:
                    parameters['ip'] = self.ippool[mac_str]
                item = uuid_str or mac_str
                # only bother the authentication host when a state change is
                # required.
                checkhost = currentstate != newstate
                if to_bool(always_check):
                    checkhost = True
                if checkhost:
                    query = urlencode(parameters)
                    urlparts = (self.access, netloc, path, query, '')
                    url = urlunsplit(urlparts)
                    self.log.info('Requesting URL: %s' % url)
                    try:
                        up = urlopen(url, timeout=timeout)
                        for l in up:
                            try:
                                # Look for extra definition within the reply
                                k, v = [x.strip() for x in l.split(':')]
                                k = k.lower()
                                if k == 'client':
                                    hostname = v
                                if k == 'file':
                                    filename = v
                            except ValueError:
                                pass
                    except HTTPError as exc:
                        self.log.error('HTTP Error: %s' % exc)
                        self.states[mac_str] = self.ST_IDLE
                        return
                    except URLError as exc:
                        self.log.critical('Internal error: %s' % exc)
                        self.states[mac_str] = self.ST_IDLE
                        return
            # local access is only validated if mac address is not yet known
            elif mac_str not in self.ippool:
                item = locals()['%s_str' % self.access]
                if not item:
                    self.log.info('Missing %s identifier, '
                                  'ignoring %s request' %
                                  (self.access, mac_str))
                    return
                if item not in self.acl:
                    self.log.info('%s is not in ACL list, '
                                  'ignoring %s request' % (item, mac_str))
                    return
                if not self.acl[item]:
                    self.log.info('%s access is disabled, '
                                  'ignoring %s request' % (item, mac_str))
                    return
            else:
                item = locals()['%s_str' % self.access]
            self.log.info('%s access is authorized, '
                          'request will be satisfied' % item)
        # construct reply
        buf[BOOTP_HOPS] = 0
        buf[BOOTP_OP] = BOOTREPLY
        ciaddr = buf[BOOTP_CIADDR]
        if not sunpack('!I', ciaddr)[0]:
            self.log.info('Client needs its address')
            ipaddr = iptoint(self.pool_start)
            ip = None
            if mac_str in self.ippool:
                ip = self.ippool[mac_str]
                self.log.info('Lease for MAC %s already defined as IP %s' %
                              (mac_str, ip))
            else:
                for idx in range(self.pool_count):
                    ipkey = inttoip(ipaddr+idx)
                    self.log.debug('Check for IP %s' % ipkey)
                    if ipkey not in self.ippool.values():
                        self.ippool[mac_str] = ipkey
                        ip = ipkey
                        break
            if not ip:
                raise BootpError('No more IP available in definined pool')

            mask = iptoint(self.config.get(
                self.BOOTP_SECTION, 'netmask', self.netconfig['mask']))
            reply_broadcast = iptoint(ip) & mask
            reply_broadcast |= (~mask) & ((1 << 32)-1)
            buf[BOOTP_YIADDR] = inet_aton(ip)
            buf[BOOTP_SECS] = 0
            buf[BOOTP_FLAGS] = BOOTP_FLAGS_BROADCAST

            relay = buf[BOOTP_GIADDR]
            if sunpack('!I', relay)[0]:
                addr = (inet_ntoa(relay), addr[1])
            else:
                addr = (inttoip(reply_broadcast), addr[1])
            self.log.info('Reply to: %s:%s' % addr)
        else:
            self.log.info('Client IP: %s' % inet_ntoa(ciaddr))
            buf[BOOTP_YIADDR] = ciaddr
            ip = inet_ntoa(buf[BOOTP_YIADDR])
        buf[BOOTP_SIADDR] = inet_aton(server_addr)
        # sname
        buf[BOOTP_SNAME] = \
            '.'.join([self.config.get(self.BOOTP_SECTION,
                                      'servername', 'unknown'),
                      self.config.get(self.BOOTP_SECTION,
                                      'domain', 'localdomain')]).encode()
        # file
        buf[BOOTP_FILE] = self.config.get(self.BOOTP_SECTION,
                                          'boot_file', '\x00').encode()

        if not dhcp_msg_type:
            self.log.warn('No DHCP message type found, discarding request')
            return
        if dhcp_msg_type == DHCP_DISCOVER:
            self.log.debug('DHCP DISCOVER')
            dhcp_reply = DHCP_OFFER
            self.log.info('Offering lease for MAC %s: IP %s' %
                          (mac_str, ip))
        elif dhcp_msg_type == DHCP_REQUEST:
            self.log.debug('DHCP REQUEST')
            dhcp_reply = DHCP_ACK
            self.log.info('New lease for MAC %s: IP %s' %
                          (mac_str, ip))
        elif dhcp_msg_type == DHCP_RELEASE:
            self.log.info('DHCP RELEASE')
            if not self.notify:
                return
        elif dhcp_msg_type == DHCP_INFORM:
            self.log.info('DHCP INFORM')
            return
        else:
            self.log.error('Unmanaged DHCP message: %d' % dhcp_msg_type)
            return

        # notify the sequencer
        if self.notify:
            if DHCP_REQUEST == dhcp_msg_type:
                if 97 in options:
                    self._notify('BOOT', uuid_str, mac_str, ip)
                else:
                    self._notify('LEASE', uuid_str, mac_str, ip)
            elif DHCP_RELEASE == dhcp_msg_type:
                self._notify('RELEASE', uuid_str, mac_str, ip)
                return

        # Store the filename
        if filename:
            self.log.info("Filename for IP %s is '%s'" % (ip, filename))
            self.filepool[ip] = filename
        else:
            self.log.debug('No filename defined for IP %s' % ip)

        pkt = spack(DHCPFORMAT, *buf)
        pkt += spack('!BBB', DHCP_MSG, 1, dhcp_reply)
        server = inet_aton(server_addr)
        pkt += spack('!BB4s', DHCP_SERVER, 4, server)

        mask = inet_aton(self.config.get(
            self.BOOTP_SECTION, 'netmask', self.netconfig['mask']))

        pkt += spack('!BB4s', DHCP_IP_MASK, 4, mask)

        gateway_addr = self.config.get(self.BOOTP_SECTION, 'gateway', '')
        if gateway_addr:
            gateway = inet_aton(gateway_addr)
        else:
            gateway = server
        pkt += spack('!BB4s', DHCP_IP_GATEWAY, 4, gateway)

        dns = self.config.get(self.BOOTP_SECTION,
                              'dns', None)
        if dns:
            if dns.lower() == 'auto':
                dns_list = self.get_dns_servers() or [inet_ntoa(server)]
            else:
                dns_list = dns.split(';')
            for dns_str in dns_list:
                dns_ip = inet_aton(dns_str)
                pkt += spack('!BB4s', DHCP_IP_DNS, 4, dns_ip)
        pkt += spack('!BBI', DHCP_LEASE_TIME, 4,
                           int(self.config.get(self.BOOTP_SECTION,
                                               'lease_time',
                                               str(24*3600))))

        # do not attempt to produce a PXE-augmented response for
        # regular DHCP requests
        if pxe:
            extra_buf = self.build_pxe_options(options, server)
            if not extra_buf:
                return
        else:
            extra_buf = self.build_dhcp_options(hostname)

        pkt += extra_buf
        pkt += spack('!BB', DHCP_END, 0)

        # update the UUID cache
        if pxe:
            self.uuidpool[mac_addr] = uuid

        # send the response
        sock.sendto(pkt, addr)

        # update the current state
        if currentstate != newstate:
            self.log.info('Moving from state %d to state %d' %
                          (currentstate, newstate))
            self.states[mac_str] = newstate
Beispiel #29
0
def packu32(x):
    return spack('<I', x)
Beispiel #30
0
 def build_pxe_options(self, options, server, bootp_buf):
     try:
         client_params = options[55]
     except IndexError:
         client_params = b''
     buf = b''
     try:
         if 97 in client_params:
             uuid = options[97]
             buf += spack('!BB%ds' % len(uuid), 97, len(uuid), uuid)
         if 13 in client_params:
             bootfile_size = 0
             path = self.config.get(TftpServer.TFTP_SECTION, 'root', '')
             bootfile_name = bootp_buf[BOOTP_FILE].decode()
             if not self.is_url(path):
                 pathname = realpath(joinpath(path, bootfile_name))
                 try:
                     bootfile_size = stat(pathname).st_size
                 except OSError as exc:
                     self.log.error('Cannot get size of %s: %s', pathname,
                                    exc)
             else:
                 url = joinpath(path, bootp_buf[BOOTP_FILE].decode())
                 try:
                     resource = urlopen(url)
                     bootfile_size = int(resource.info()['Content-Length'])
                 except Exception as exc:
                     self.log.error('Cannot retrieve size of %s: %s', url,
                                    exc)
         if bootfile_size:
             self.log.debug('Bootfile %s is %d byte long', bootfile_name,
                            bootfile_size)
             bootfile_block = (bootfile_size + 511) // 512
             buf += spack('!BBH', 13, scalc('!H'), bootfile_block)
         if 60 in client_params:
             clientclass = options[60]
             clientclass = clientclass[:clientclass.find(b':')]
             buf += spack('!BB%ds' % len(clientclass), 60, len(clientclass),
                          clientclass)
         if 66 in client_params:
             tftp_server = bootp_buf[BOOTP_SNAME]
             buf += spack('!BB%ds' % len(tftp_server), 66, len(tftp_server),
                          tftp_server)
         if 67 in client_params:
             boot_file = bootp_buf[BOOTP_FILE]
             buf += spack('!BB%ds' % len(boot_file), 67, len(boot_file),
                          boot_file)
         # Vendor specific (PXE extension)
         vendor = b''
         vendor += spack('!BBB', PXE_DISCOVERY_CONTROL, 1, 0x0A)
         vendor += spack('!BBHB4s', PXE_BOOT_SERVERS, 2 + 1 + 4, 0, 1,
                         server)
         srvstr = b'Python'
         vendor += spack('!BBHB%ds' % len(srvstr), PXE_BOOT_MENU,
                         2 + 1 + len(srvstr), 0, len(srvstr), srvstr)
         prompt = b'Stupid PXE'
         vendor += spack('!BBB%ds' % len(prompt), PXE_MENU_PROMPT,
                         1 + len(prompt), len(prompt), prompt)
         buf += spack('!BB%ds' % len(vendor), 43, len(vendor), vendor)
         buf += spack('!BBB', 255, 0, 0)
         return buf
     except KeyError as exc:
         self.log.error('Missing options, cancelling: %s' % exc)
         return b''
Beispiel #31
0
 def _exchange_full_duplex(self, frequency: float,
                           out: Union[bytes, bytearray, Iterable[int]],
                           cs_prolog: bool, cs_epilog: bool, cpol: bool,
                           cpha: bool, droptail: int) -> bytes:
     if not self._ftdi.is_connected:
         raise SpiIOError("FTDI controller not initialized")
     if len(out) > SpiController.PAYLOAD_MAX_LENGTH:
         raise SpiIOError("Output payload is too large")
     if cpha:
         # to enable CPHA, we need to use a workaround with FTDI device,
         # that is enable 3-phase clocking (which is usually dedicated to
         # I2C support). This mode use use 3 clock period instead of 2,
         # which implies the FTDI frequency should be fixed to match the
         # requested one.
         frequency = (3 * frequency) // 2
     if self._frequency != frequency:
         self._ftdi.set_frequency(frequency)
         # store the requested value, not the actual one (best effort),
         # to avoid setting unavailable values on each call.
         self._frequency = frequency
     direction = self.direction & 0xFF  # low bits only
     cmd = bytearray()
     for ctrl in cs_prolog or []:
         ctrl &= self._spi_mask
         ctrl |= self._gpio_low
         cmd.extend((Ftdi.SET_BITS_LOW, ctrl, direction))
     epilog = bytearray()
     if cs_epilog:
         for ctrl in cs_epilog:
             ctrl &= self._spi_mask
             ctrl |= self._gpio_low
             epilog.extend((Ftdi.SET_BITS_LOW, ctrl, direction))
         # Restore idle state
         cs_high = [
             Ftdi.SET_BITS_LOW, self._cs_bits | self._gpio_low, direction
         ]
         if not self._turbo:
             cs_high.append(Ftdi.SEND_IMMEDIATE)
         epilog.extend(cs_high)
     exlen = len(out)
     if self._clock_phase != cpha:
         self._ftdi.enable_3phase_clock(cpha)
         self._clock_phase = cpha
     if not droptail:
         wcmd = (Ftdi.RW_BYTES_PVE_NVE_MSB
                 if not cpol else Ftdi.RW_BYTES_NVE_PVE_MSB)
         write_cmd = spack('<BH', wcmd, exlen - 1)
         cmd.extend(write_cmd)
         cmd.extend(out)
     else:
         bytelen = exlen - 1
         if bytelen:
             wcmd = (Ftdi.RW_BYTES_PVE_NVE_MSB
                     if not cpol else Ftdi.RW_BYTES_NVE_PVE_MSB)
             write_cmd = spack('<BH', wcmd, bytelen - 1)
             cmd.extend(write_cmd)
             cmd.extend(out[:-1])
         wcmd = (Ftdi.RW_BITS_PVE_NVE_MSB
                 if not cpol else Ftdi.RW_BITS_NVE_PVE_MSB)
         write_cmd = spack('<BBB', wcmd, 7 - droptail, out[-1])
         cmd.extend(write_cmd)
     cmd.extend(self._immediate)
     if self._turbo:
         if epilog:
             cmd.extend(epilog)
         self._ftdi.write_data(cmd)
     else:
         self._ftdi.write_data(cmd)
         if epilog:
             self._ftdi.write_data(epilog)
     # USB read cycle may occur before the FTDI device has actually
     # sent the data, so try to read more than once if no data is
     # actually received
     data = self._ftdi.read_data_bytes(exlen, 4)
     if droptail:
         data[-1] = 0xff & (data[-1] << droptail)
     return data
Beispiel #32
0
 def test_flashdevice_4_long_rw(self):
     """Long R/W test
     """
     # Max size to perform the test on
     size = 1 << 20
     # Whether to test with random value, or contiguous values to ease debug
     randomize = True
     # Fill in the whole flash with a monotonic increasing value, that is
     # the current flash 32-bit address, then verify the sequence has been
     # properly read back
     # limit the test to 1MiB to keep the test duration short, but performs
     # test at the end of the flash to verify that high addresses may be
     # reached
     self.flash = SerialFlashManager.get_flash_device(self.ftdi_url, 0,
                                                      self.frequency)
     length = min(len(self.flash), size)
     start = len(self.flash)-length
     print("Erase %s from flash @ 0x%06x (may take a while...)" %
           (pretty_size(length), start))
     delta = now()
     self.flash.unlock()
     self.flash.erase(start, length, True)
     delta = now()-delta
     self._report_bw('Erased', length, delta)
     if str(self.flash).startswith('SST'):
         # SST25 flash devices are tremendously slow at writing (one or two
         # bytes per SPI request MAX...). So keep the test sequence short
         # enough
         length = 16 << 10
     print("Build test sequence")
     if not randomize:
         buf = bytearray()
         for address in range(0, length, 4):
             buf.extend(spack('>I', address))
         # Expect to run on x86 or ARM (little endian), so swap the values
         # to ease debugging
     else:
         seed(0)
         buf = bytearray()
         buf.extend((randint(0, 255) for _ in range(0, length)))
     print("Writing %s to flash (may take a while...)" %
           pretty_size(len(buf)))
     delta = now()
     self.flash.write(start, buf)
     delta = now()-delta
     length = len(buf)
     self._report_bw('Wrote', length, delta)
     wmd = sha1()
     wmd.update(buf)
     refdigest = wmd.hexdigest()
     print("Reading %s from flash" % pretty_size(length))
     delta = now()
     back = self.flash.read(start, length)
     delta = now()-delta
     self._report_bw('Read', length, delta)
     # print "Dump flash"
     # print hexdump(back.tobytes())
     print("Verify flash")
     rmd = sha1()
     rmd.update(back)
     newdigest = rmd.hexdigest()
     print("Reference:", refdigest)
     print("Retrieved:", newdigest)
     if refdigest != newdigest:
         errcount = 0
         for pos in range(len(buf)):
             if buf[pos] != back[pos]:
                 print('Invalid byte @ offset 0x%06x: 0x%02x / 0x%02x' %
                       (pos, buf[pos], back[pos]))
                 errcount += 1
                 # Stop report after 16 errors
                 if errcount >= 32:
                     break
         raise self.fail('Data comparison mismatch')