def _decode_eeprom_string(self, offset): str_offset, str_size = sunpack('<BB', self._eeprom[offset:offset + 2]) if str_size: str_size -= scalc('<H') str_offset += scalc('<H') manufacturer = self._eeprom[str_offset:str_offset + str_size] return manufacturer.decode('utf16', errors='ignore') return ''
def __init__(self, defs: dict, **kwargs): class DeviceDescriptor(EasyDict): pass self.desc = DeviceDescriptor( bLength=scalc(self.DESCRIPTOR_FORMAT), bDescriptorType=USBCONST.descriptors.DEVICE, bcdUSB=0x200, # USB 2.0 bDeviceClass=0, bDeviceSubClass=0, bDeviceProtocol=0, bMaxPacketSize0=8, idVendor=0, idProduct=0, bcdDevice=0, iManufacturer=0, iProduct=0, iSerialNumber=0, bNumConfigurations=0, # updated later port_number=None, # unsupported port_numbers=None, # unsupported bus=0, address=0, speed=3) self.desc.update(defs) for key in kwargs: if key not in defs: self.desc[key] = kwargs[key] self.configurations = [] self.strings = [''] # slot 0 is reserved
def _stream_sink(cls, port, size, results): pos = 0 first = None data = bytearray() sample_size = scalc('>I') rx_size = 0 port.timeout = 1.0 start = now() while rx_size < size: buf = port.read(1024) if not buf: print('T') break rx_size += len(buf) data.extend(buf) sample_count = len(data) // sample_size length = sample_count * sample_size samples = sunpack('>%dI' % sample_count, data[:length]) data = data[length:] for sample in samples: if first is None: first = sample pos = sample continue pos += 1 if sample != pos: results[1] = AssertionError('Lost byte @ %d', pos - first) return delta = now() - start results[1] = (rx_size, delta)
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: try: ustr = self._config[name].encode('utf-16le') except KeyError: ustr = '' length = len(ustr) + 2 stream.append(length) stream.append(0x03) # string descriptor 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: mtp = self._ftdi.device_version == 0x1000 crc_pos = 0x100 if mtp else len(self._eeprom) crc_pos -= crc_size rem = len(self._eeprom) - (dynpos + len(stream)) - crc_size self._eeprom[dynpos + len(stream):crc_pos] = bytes(rem)
def decode_tve_parameter(data): """Generic byte decoding function for TVE parameters. Given an array of bytes, tries to interpret a TVE parameter from the beginning of the array. Returns the decoded data and the number of bytes it read.""" # decode the TVE field's header (1 bit "reserved" + 7-bit type) (msgtype, ) = sunpack(tve_header, data[:tve_header_len]) if not msgtype & 0b10000000: # not a TV-encoded param return None, 0 msgtype = msgtype & 0x7f try: param_name, param_fmt = tve_param_formats[msgtype] logger.debug('found %s (type=%s)', param_name, msgtype) except KeyError as err: return None, 0 # decode the body nbytes = scalc(param_fmt) end = tve_header_len + nbytes try: unpacked = sunpack(param_fmt, data[tve_header_len:end]) return {param_name: unpacked}, end except serror: return None, 0
def poll_cond(self, address: int, fmt: str, mask: int, value: int, count: int, relax: bool = True) -> Optional[bytes]: """Poll a remove slave, watching for condition to satisfy. On each poll cycle, a repeated start condition is emitted, without releasing the I2C bus, and an ACK is returned to the slave. If relax is set, this method releases the I2C bus however it leaves. :param address: the address on the I2C bus, or None to discard start :param fmt: struct format for poll register :param mask: binary mask to apply on the condition register before testing for the value :param value: value to test the masked condition register against. Condition is satisfied when register & mask == value :param count: maximum poll count before raising a timeout :param relax: whether to relax the bus (emit STOP) or not :return: the polled register value, or None if poll failed """ if not self.configured: raise I2cIOError("FTDI controller not initialized") self.validate_address(address) if address is None: i2caddress = None else: i2caddress = (address << 1) & self.HIGH i2caddress |= self.BIT0 do_epilog = True with self._lock: try: retry = 0 while retry < count: retry += 1 size = scalc(fmt) self._do_prolog(i2caddress) data = self._do_read(size) self.log.debug("Poll data: %s", hexlify(data).decode()) cond, = sunpack(fmt, data) if (cond & mask) == value: self.log.debug('Poll condition matched') break else: data = None self.log.debug('Poll condition not fulfilled: %x/%x', cond & mask, value) do_epilog = relax if not data: self.log.warning('Poll condition failed') return data except I2cNackError: self.log.info('Not ready') return None finally: if do_epilog: self._do_epilog()
def _handle_advertisement(self, adv): data = adv.service_data if not data: return type_fmt = f'<{self.SERVICE_FMT[0]}' size = scalc(type_fmt) if len(data) < size: return service, = sunpack(type_fmt, data[:size]) data = data[size:] if service != self.ESS: return data_fmt = f'>{self.SERVICE_FMT[1:]}' size = scalc(data_fmt) if len(data) < size: self._log.warning('to short') return macbytes, temp, humi, bat, batv, packet = sunpack(data_fmt, data) if packet == self._last_packet and not self._all_msgs: return self._last_packet = packet mac = sum([b << (o << 3) for o, b in enumerate(reversed(macbytes))]) oui = mac >> 24 if oui != self.XIAOMI_OUI: return temp = float(temp) / 10.0 batv = float(batv) / 1000.0 payload = { 'mac': adv.address.address, 'temperature_C': temp, 'humidity': humi, 'battery': bat, 'battery_v': batv, 'packet': packet, 'rssi': adv.rssi, } if 'event' in self._publish: jstr = f'{jdumps(payload)}\n' jbytes = jstr.encode() self._mqtt.publish(f'{self._source}/events', jbytes) if 'device' in self._publish: mac = adv.address.address for name, val in payload.items(): if name == 'mac': continue self._mqtt.publish(f'{self._source}/devices/{mac}/{name}', val)
def _compute_crc(self, check=False): mtp = self._ftdi.device_version == 0x1000 crc_pos = 0x100 if mtp else len(self._eeprom) crc_size = scalc('<H') if not check: # check mode: add CRC itself, so that result should be zero crc_pos -= crc_size crc = self._ftdi.calc_eeprom_checksum(self._eeprom[:crc_pos]) return crc, crc_pos, crc_size
def poll_cond(self, address, fmt, mask, value, count, relax=True): """Poll a remove slave, watching for condition to satisfy. On each poll cycle, a repeated start condition is emitted, without releasing the I2C bus, and an ACK is returned to the slave. If relax is set, this method releases the I2C bus however it leaves. :param address: the address on the I2C bus, or None to discard start :type address: int or None :param str fmt: struct format for poll register :param int mask: binary mask to apply on the condition register before testing for the value :param int value: value to test the masked condition register against. Condition is satisfied when register & mask == value :param int count: maximum poll count before raising a timeout :param bool relax: whether to relax the bus (emit STOP) or not :return: the polled register value, or None if poll failed :rtype: array or None """ if not self.configured: raise I2cIOError("FTDI controller not initialized") self.validate_address(address) if address is None: i2caddress = None else: i2caddress = (address << 1) & self.HIGH i2caddress |= self.BIT0 do_epilog = True try: retry = 0 while retry < count: retry += 1 size = scalc(fmt) self._do_prolog(i2caddress) data = self._do_read(size) self.log.debug("Poll data: %s", hexlify(data).decode()) cond, = sunpack(fmt, data) if (cond & mask) == value: self.log.debug('Poll condition matched') break else: data = None self.log.debug('Poll condition not fulfilled: %x/%x', cond & mask, value) do_epilog = relax if not data: self.log.warning('Poll condition failed') return data except I2cNackError: self.log.info('Not ready') return None finally: if do_epilog: self._do_epilog()
def _handle_advertisement(self, adv): data = adv.service_data if not data: return type_fmt = f'<{self.SERVICE_FMT[0]}' size = scalc(type_fmt) if len(data) < size: return service, = sunpack(type_fmt, data[:size]) data = data[size:] if service != self.ESS: return data_fmt = f'>{self.SERVICE_FMT[1:]}' size = scalc(data_fmt) if len(data) < size: self._log.warning('to short') return macbytes, temp, humi, bat, batv, packet = sunpack(data_fmt, data) mac = sum([b << (o << 3) for o, b in enumerate(reversed(macbytes))]) oui = mac >> 24 if oui != self.XIAOMI_OUI: return temp = float(temp) / 10.0 batv = float(batv) / 1000.0 json = { 'mac': adv.address.address, 'temperature': temp, 'humidity': humi, 'battery': bat, 'battery_v': batv, 'packet': packet, 'rssi': adv.rssi, } jstr = f'{jdumps(json)}\n' jbytes = jstr.encode() with self._qlock: if self._channels: self._log.debug('Notify %d clients', len(self._channels)) for channel in self._channels: channel.queue.append(jbytes) channel.event.set()
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 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 _compute_crc(self, eeprom: Union[bytes, bytearray], check=False): mtp = self._ftdi.device_version == 0x1000 crc_pos = 0x100 if mtp else len(eeprom) crc_size = scalc('<H') if not check: # check mode: add CRC itself, so that result should be zero crc_pos -= crc_size crc = self._ftdi.calc_eeprom_checksum(eeprom[:crc_pos]) if check: self._valid = not bool(crc) if not self._valid: self.log.debug('CRC is now 0x%04x', crc) else: self.log.debug('CRC OK') return crc, crc_pos, crc_size
def _read_raw(self, read_high): if read_high: cmd = bytearray( [Ftdi.GET_BITS_LOW, Ftdi.GET_BITS_HIGH, Ftdi.SEND_IMMEDIATE]) fmt = '<H' else: cmd = bytearray([Ftdi.GET_BITS_LOW, Ftdi.SEND_IMMEDIATE]) fmt = 'B' self._ftdi.write_data(cmd) size = scalc(fmt) data = self._ftdi.read_data_bytes(size, 4) if len(data) != size: raise SpiIOError('Cannot read GPIO') value, = sunpack(fmt, data) return value
def __init__(self, defs: dict, extra: Optional[bytes] = None): class EndpointDescriptor(EasyDict): pass if extra and not isinstance(extra, (bytes, bytearray)): raise ValueError('Invalid extra payload') self.desc = EndpointDescriptor( bLength=scalc(self.DESCRIPTOR_FORMAT), bDescriptorType=USBCONST.descriptors.ENDPOINT, bEndpointAddress=0, bmAttributes=0, wMaxPacketSize=64, bInterval=0, bRefresh=0, bSynchAddress=0, extra_descriptors=extra or b'') self.desc.update(defs)
def _read_raw(self, read_high: bool) -> int: if not self._ftdi.is_connected: raise SpiIOError("FTDI controller not initialized") if read_high: cmd = bytes( [Ftdi.GET_BITS_LOW, Ftdi.GET_BITS_HIGH, Ftdi.SEND_IMMEDIATE]) fmt = '<H' else: cmd = bytes([Ftdi.GET_BITS_LOW, Ftdi.SEND_IMMEDIATE]) fmt = 'B' self._ftdi.write_data(cmd) size = scalc(fmt) data = self._ftdi.read_data_bytes(size, 4) if len(data) != size: raise SpiIOError('Cannot read GPIO') value, = sunpack(fmt, data) return value
def __init__(self, defs: dict, extra: Optional[bytes] = None): class ConfigDescriptor(EasyDict): pass if extra and not isinstance(extra, (bytes, bytearray)): raise ValueError('Invalid extra payload') self.desc = ConfigDescriptor( bLength=scalc(self.DESCRIPTOR_FORMAT), bDescriptorType=USBCONST.descriptors.CONFIG, wTotalLength=0, bNumInterfaces=0, bConfigurationValue=0, iConfiguration=0, # string index bmAttributes=0x80, # bus-powered bMaxPower=150//2, # 150 mA extra_descriptors=extra or b'') self.desc.update(defs) self.interfaces = []
def _read_raw(self, read_high): if read_high: cmd = array('B', [Ftdi.GET_BITS_LOW, Ftdi.GET_BITS_HIGH, Ftdi.SEND_IMMEDIATE]) fmt = '<H' else: cmd = array('B', [Ftdi.GET_BITS_LOW, Ftdi.SEND_IMMEDIATE]) fmt = 'B' self._ftdi.write_data(cmd) size = scalc(fmt) data = self._ftdi.read_data_bytes(size, 4) if len(data) != size: raise SpiIOError('Cannot read GPIO') value, = sunpack(fmt, data) return value
def _read_mpsse(self, count: int) -> Tuple[int]: if self._width > 8: cmd = bytearray([Ftdi.GET_BITS_LOW, Ftdi.GET_BITS_HIGH] * count) fmt = '<%dH' % count else: cmd = bytearray([Ftdi.GET_BITS_LOW] * count) fmt = None cmd.append(Ftdi.SEND_IMMEDIATE) if len(cmd) > self.MPSSE_PAYLOAD_MAX_LENGTH: raise ValueError('Too many samples') self._ftdi.write_data(cmd) size = scalc(fmt) if fmt else count data = self._ftdi.read_data_bytes(size, 4) if len(data) != size: raise FtdiError('Cannot read GPIO') if fmt: return sunpack(fmt, data) return data
def _read_raw(self, read_high): if read_high: cmd = array( 'B', [Ftdi.GET_BITS_LOW, Ftdi.GET_BITS_HIGH, Ftdi.SEND_IMMEDIATE]) fmt = '<H' self._ftdi.write_data(cmd) size = scalc(fmt) data = self._ftdi.read_data_bytes(size, 4) if len(data) != size: raise GpioException('Cannot read GPIO') value, = sunpack(fmt, data) else: # If not using read_high method, then also means this is # BIT BANG and not MPSSE, so just use read_pins() value = self._ftdi.read_pins() return value
def __init__(self, defs: dict, extra: Optional[bytes] = None): class InterfaceDescriptor(EasyDict): pass if extra and not isinstance(extra, (bytes, bytearray)): raise ValueError('Invalid extra payload') desc = InterfaceDescriptor( bLength=scalc(self.DESCRIPTOR_FORMAT), bDescriptorType=USBCONST.descriptors.INTERFACE, bInterfaceNumber=0, bAlternateSetting=0, bNumEndpoints=0, bInterfaceClass=0xFF, bInterfaceSubClass=0xFF, bInterfaceProtocol=0xFF, iInterface=0, # String desc index extra_descriptors=extra or b'') desc.update(defs) self.alt = 0 self.altsettings = [(desc, [])]
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 _compute_crc(self, eeprom: Union[bytes, bytearray], check=False): mtp = self._ftdi.device_version == 0x1000 crc_pos = 0x100 if mtp else len(eeprom) crc_size = scalc('<H') if not check: # check mode: add CRC itself, so that result should be zero crc_pos -= crc_size if self.is_mirroring_enabled: mirror_s1_crc_pos = self.mirror_sector if not check: mirror_s1_crc_pos -= crc_size # if mirroring, only calculate the crc for the first sector/half # of the eeprom. Data (including this crc) are duplicated in # the second sector/half crc = self._ftdi.calc_eeprom_checksum(eeprom[:mirror_s1_crc_pos]) else: crc = self._ftdi.calc_eeprom_checksum(eeprom[:crc_pos]) if check: self._valid = not bool(crc) if not self._valid: self.log.debug('CRC is now 0x%04x', crc) else: self.log.debug('CRC OK') return crc, crc_pos, crc_size
def __init__(self, defs: dict, **kwargs): class DeviceDescriptor(EasyDict): pass self.desc = DeviceDescriptor( bLength=scalc(self.DESCRIPTOR_FORMAT), bDescriptorType=USBCONST.descriptors.DEVICE, bcdUSB=0x200, # USB 2.0 bDeviceClass=0, bDeviceSubClass=0, bDeviceProtocol=0, bMaxPacketSize0=8, idVendor=0, idProduct=0, bcdDevice=0, iManufacturer=0, iProduct=0, iSerialNumber=0, bNumConfigurations=0, # updated later port_number=None, # unsupported port_numbers=None, # unsupported bus=0, address=0, speed=3, eeprom=None) self.desc.update(defs) self._props = set() for key in kwargs: # be sure not to allow descriptor override by arbitrary properties if key not in defs: self.desc[key] = kwargs[key] self._props.add(key) self.configurations = [] self.strings = [''] # slot 0 is reserved self._ftdi = VirtFtdi(self.desc.bcdDevice, self.desc.bus, self.desc.address, kwargs.get('eeprom', {}))
class I2cPort: """I2C port. An I2C port is never instanciated directly: use :py:meth:`I2cController.get_port()` method to obtain an I2C port. ``relax`` parameter in I2cPort methods may be used to prevent the master from releasing the I2C bus, if some further data should be exchanged with the slave device. Note that in case of any error, the I2C bus is released and the ``relax`` parameter is ignored in such an event. Example: >>> ctrl = I2cController() >>> ctrl.configure('ftdi://ftdi:232h/1') >>> i2c = ctrl.get_port(0x21) >>> # send 2 bytes >>> i2c.write([0x12, 0x34]) >>> # send 2 bytes, then receive 2 bytes >>> out = i2c.exchange([0x12, 0x34], 2) """ FORMATS = {scalc(fmt): fmt for fmt in 'BHI'} def __init__(self, controller: 'I2cController', address: int): self._controller = controller self._address = address self._shift = 0 self._endian = '<' self._format = 'B' def configure_register(self, bigendian: bool = False, width: int = 1) -> None: """Reconfigure the format of the slave address register (if any) :param bigendian: True for a big endian encoding, False otherwise :param width: width, in bytes, of the register """ try: self._format = self.FORMATS[width] except KeyError: raise I2cIOError('Unsupported integer width') self._endian = '>' if bigendian else '<' def shift_address(self, offset: int): """Tweak the I2C slave address, as required with some devices """ I2cController.validate_address(self._address+offset) self._shift = offset def read(self, readlen: int = 0, relax: bool = True, start: bool = True) -> bytes: """Read one or more bytes from a remote slave :param readlen: count of bytes to read out. :param relax: whether to relax the bus (emit STOP) or not :param start: whether to emit a start sequence (w/ address) :return: byte sequence of read out bytes :raise I2cIOError: if device is not configured or input parameters are invalid """ return self._controller.read( self._address+self._shift if start else None, readlen=readlen, relax=relax) def write(self, out: Union[bytes, bytearray, Iterable[int]], relax: bool = True, start: bool = True) -> None: """Write one or more bytes to a remote slave :param out: the byte buffer to send :param relax: whether to relax the bus (emit STOP) or not :param start: whether to emit a start sequence (w/ address) :raise I2cIOError: if device is not configured or input parameters are invalid """ return self._controller.write( self._address+self._shift if start else None, out, relax=relax) def read_from(self, regaddr: int, readlen: int = 0, relax: bool = True, start: bool = True) -> bytes: """Read one or more bytes from a remote slave :param regaddr: slave register address to read from :param readlen: count of bytes to read out. :param relax: whether to relax the bus (emit STOP) or not :param start: whether to emit a start sequence (w/ address) :return: data read out from the slave :raise I2cIOError: if device is not configured or input parameters are invalid """ return self._controller.exchange( self._address+self._shift if start else None, out=self._make_buffer(regaddr), readlen=readlen, relax=relax) def write_to(self, regaddr: int, out: Union[bytes, bytearray, Iterable[int]], relax: bool = True, start: bool = True): """Read one or more bytes from a remote slave :param regaddr: slave register address to write to :param out: the byte buffer to send :param relax: whether to relax the bus (emit STOP) or not :param start: whether to emit a start sequence (w/ address) :raise I2cIOError: if device is not configured or input parameters are invalid """ return self._controller.write( self._address+self._shift if start else None, out=self._make_buffer(regaddr, out), relax=relax) def exchange(self, out: Union[bytes, bytearray, Iterable[int]] = b'', readlen: int = 0, relax: bool = True, start: bool = True) -> bytes: """Perform an exchange or a transaction with the I2c slave :param out: an array of bytes to send to the I2c slave, may be empty to only read out data from the slave :param readlen: count of bytes to read out from the slave, may be zero to only write to the slave :param relax: whether to relax the bus (emit STOP) or not :param start: whether to emit a start sequence (w/ address) :return: data read out from the slave """ return self._controller.exchange( self._address+self._shift if start else None, out, readlen, relax=relax) def poll(self, write: bool = False, relax: bool = True, start: bool = True) -> bool: """Poll a remote slave, expect ACK or NACK. :param write: poll in write mode (vs. read) :param relax: whether to relax the bus (emit STOP) or not :param start: whether to emit a start sequence (w/ address) :return: True if the slave acknowledged, False otherwise """ return self._controller.poll( self._address+self._shift if start else None, write, relax=relax) def poll_cond(self, width: int, mask: int, value: int, count: int, relax: bool = True, start: bool = True) -> Optional[bytes]: """Poll a remove slave, watching for condition to satisfy. On each poll cycle, a repeated start condition is emitted, without releasing the I2C bus, and an ACK is returned to the slave. If relax is set, this method releases the I2C bus however it leaves. :param width: count of bytes to poll for the condition check, that is the size of the condition register :param mask: binary mask to apply on the condition register before testing for the value :param value: value to test the masked condition register against. Condition is satisfied when register & mask == value :param count: maximum poll count before raising a timeout :param relax: whether to relax the bus (emit STOP) or not :param start: whether to emit a start sequence (w/ address) :return: the polled register value :raise I2cTimeoutError: if poll condition is not satisified """ try: fmt = ''.join((self._endian, self.FORMATS[width])) except KeyError: raise I2cIOError('Unsupported integer width') return self._controller.poll_cond( self._address+self._shift if start else None, fmt, mask, value, count, relax=relax) def flush(self) -> None: """Force the flush of the HW FIFOs. """ self._controller.flush() @property def frequency(self) -> float: """Provide the current I2c bus frequency. """ return self._controller.frequency @property def address(self) -> int: """Return the slave address.""" return self._address 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 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''
#!/usr/bin/env python2.7 # Decode custom format from a log file import sys from struct import unpack as sunpack, calcsize as scalc with open(sys.argv[1], "rb") as log: fmt = '<II' fmt_size = scalc(fmt) total_irq = 0 total_read = 0 ticks = 0 period_ms = 3000 tick_ms = 2 start = 3000 end = (period_ms / tick_ms) + start inperiod_irq = 0 inperiod_read = 0 with open('/Users/eblot/Desktop/output1.txt', 'wt') as out1, \ open('/Users/eblot/Desktop/output2.txt', 'wt') as out2: while True: words = log.read(fmt_size) if not words: break irq_count, read_count = sunpack(fmt, words) total_irq += irq_count total_read += read_count ticks += 1 if start <= ticks < end: inperiod_irq += irq_count
class I2cPort(object): """I2C port An I2C port is never instanciated directly. Use I2cController.get_port() method to obtain an I2C port :Example: ctrl = I2cController() ctrl.configure('ftdi://ftdi:232h/1') i2c = ctrl.get_port(0x21) # send 2 bytes i2c.write([0x12, 0x34]) # send 2 bytes, then receive 2 bytes out = i2c.exchange([0x12, 0x34], 2) """ FORMATS = {scalc(fmt): fmt for fmt in 'BHI'} def __init__(self, controller, address): self._controller = controller self._address = address self._shift = 0 self._endian = '<' self._format = 'B' def configure_register(self, bigendian=False, width=1): """Reconfigure the format of the slave address register (if any) :param bigendian: True for a big endian encoding, False otherwise :param width: width, in bytes, of the register """ try: self._format = self.FORMATS[width] except KeyError: raise I2cIOError('Unsupported integer width') self._endian = bigendian and '>' or '<' def shift_address(self, offset): """Tweak the I2C slave address, as required with some devices """ I2cController.validate_address(address + offset) self._shift = offset def read(self, readlen=0): """Read one or more bytes from a remote slave :param readlen: count of bytes to read out. :return: byte sequence of read out bytes """ return self._controller.read(self._address + self._shift, readlen=readlen) def write(self, out): """Write one or more bytes to a remote slave :param out: the byte buffer to send """ return self._controller.write(self._address + self._shift, out) def read_from(self, regaddr, readlen=0): """Read one or more bytes from a remote slave :param regaddr: slave register address to read from :param readlen: count of bytes to read out. :return: byte sequence of read out bytes """ return self._controller.exchange(self._address + self._shift, out=self._make_buffer(regaddr), readlen=readlen) def write_to(self, regaddr, out): """Read one or more bytes from a remote slave :param regaddr: slave register address to write to :param out: the byte buffer to send """ return self._controller.write(self._address + self._shift, out=self._make_buffer(regaddr, out)) def exchange(self, out='', readlen=0): """Perform an exchange or a transaction with the I2c slave :param out: an array of bytes to send to the I2c slave, may be empty to only read out data from the slave :param readlen: count of bytes to read out from the slave, may be zero to only write to the slave :return: byte sequence containing the data read out from the slave """ return self._controller.exchange(self._address + self._shift, out, readlen) def poll(self): """Poll a remote slave, expect ACK or NACK. :return: True if the slave acknowledged, False otherwise """ return self._controller.poll(self._address + self._shift) def flush(self): """Force the flush of the HW FIFOs. """ self._controller.flush() @property def frequency(self): """Return the current I2c bus. """ return self._controller.frequency 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()
class LLRPMessage(object): hdr_fmt = '!HI' hdr_len = scalc(hdr_fmt) # == 6 bytes full_hdr_fmt = hdr_fmt + 'I' full_hdr_len = scalc(full_hdr_fmt) # == 10 bytes msgdict = None msgbytes = None def __init__(self, msgdict=None, msgbytes=None): if not (msgdict or msgbytes): raise LLRPError('Provide either a message dict or a sequence' ' of bytes.') if msgdict: self.msgdict = LLRPMessageDict(msgdict) if not msgbytes: self.serialize() if msgbytes: self.msgbytes = msgbytes if not msgdict: self.deserialize() self.peername = None 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 deserialize(self): """Turns a sequence of bytes into a message dictionary.""" if self.msgbytes is None: raise LLRPError('No message bytes to deserialize.') data = self.msgbytes msgtype, length, msgid = sunpack(self.full_hdr_fmt, data[:self.full_hdr_len]) ver = (msgtype >> 10) & BITMASK(3) msgtype = msgtype & BITMASK(10) try: name = Message_Type2Name[msgtype] logger.debug('deserializing %s command', name) decoder = Message_struct[name]['decode'] except KeyError: raise LLRPError('Cannot find decoder for message type ' '{}'.format(msgtype)) body = data[self.full_hdr_len:length] try: self.msgdict = {name: dict(decoder(body))} self.msgdict[name]['Ver'] = ver self.msgdict[name]['Type'] = msgtype self.msgdict[name]['ID'] = msgid logger.debug('done deserializing %s command', name) except LLRPError: logger.exception('Problem with %s message format', name) return '' return '' def isSuccess(self): if not self.msgdict: return False msgName = self.getName() md = self.msgdict[msgName] try: if msgName == 'READER_EVENT_NOTIFICATION': ev = md['ReaderEventNotificationData'] if 'ConnectionAttemptEvent' in ev: return ev['ConnectionAttemptEvent']['Status'] == 'Success' elif 'AntennaEvent' in ev: return ev['AntennaEvent']['EventType'] == 'Connected' elif 'LLRPStatus' in md: return md['LLRPStatus']['StatusCode'] == 'Success' except KeyError: logger.exception('failed to parse status from %s', msgName) return False def getName(self): if not self.msgdict: return None return list(self.msgdict.keys())[0] def __repr__(self): try: ret = llrp_data2xml(self.msgdict) except TypeError as te: logger.exception(te) ret = '' return ret
#pylint: disable-msg=missing-docstring #pylint: disable-msg=too-many-return-statements #pylint: disable-msg=too-many-branches #pylint: disable-msg=too-many-locals #pylint: disable-msg=too-many-statements #pylint: disable-msg=too-many-nested-blocks BOOTP_PORT_REQUEST = 67 BOOTP_PORT_REPLY = 68 BOOTREQUEST = 1 BOOTREPLY = 2 BOOTPFORMAT = '!4bIHH4s4s4s4s16s64s128s64s' BOOTPFORMATSIZE = scalc(BOOTPFORMAT) DHCPFORMAT = '!4bIHH4s4s4s4s16s64s128s4s' DHCPFORMATSIZE = scalc(DHCPFORMAT) (BOOTP_OP, BOOTP_HTYPE, BOOTP_HLEN, BOOTP_HOPS, BOOTP_XID, BOOTP_SECS, BOOTP_FLAGS, BOOTP_CIADDR, BOOTP_YIADDR, BOOTP_SIADDR, BOOTP_GIADDR, BOOTP_CHADDR, BOOTP_SNAME, BOOTP_FILE, BOOTP_VEND) = range(15) BOOTP_FLAGS_NONE = 0 BOOTP_FLAGS_BROADCAST = 1<<15 COOKIE = r'\0x63\0x82\0x53\0x63' DHCP_OPTIONS = {0: 'Byte padding', 1: 'Subnet mask', 2: 'Time offset',
from logging import getLogger from struct import calcsize as scalc, error as serror, unpack as sunpack logger = getLogger(__name__) tve_header = '!B' tve_header_len = scalc(tve_header) tve_param_formats = { # param type: (param name, struct format) 1: ('AntennaID', '!H'), 2: ('FirstSeenTimestampUTC', '!Q'), 3: ('FirstSeenTimestampUptime', '!Q'), 4: ('LastSeenTimestampUTC', '!Q'), 5: ('LastSeenTimestampUptime', '!Q'), 6: ('PeakRSSI', '!b'), 7: ('ChannelIndex', '!H'), 8: ('TagSeenCount', '!H'), 9: ('ROSpecID', '!I'), 10: ('InventoryParameterSpecID', '!H'), 14: ('SpecIndex', '!H'), 15: ('ClientRequestOpSpecResult', '!H'), 16: ('AccessSpecID', '!I') } def decode_tve_parameter(data): """Generic byte decoding function for TVE parameters. Given an array of bytes, tries to interpret a TVE parameter from the beginning of the array. Returns the decoded data and the number of bytes