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
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)
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)
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))
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
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
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))
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())
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
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
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)
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
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)
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)
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)
def packf64(x): return spack('<d', x)
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()
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)
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)
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}")
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:
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
def inttoip(ipval): return inet_ntoa(spack('!I', ipval))
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))
def build_dhcp_options(self, clientname): if not clientname: return b'' return spack('!BB%ds' % len(clientname), 12, len(clientname), clientname)
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
def packu32(x): return spack('<I', x)
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''
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
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')