Пример #1
0
 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 ''
Пример #2
0
 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
Пример #3
0
 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)
Пример #4
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:
         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)
Пример #5
0
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
Пример #6
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()
Пример #7
0
 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)
Пример #8
0
 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
Пример #9
0
    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()
Пример #10
0
 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()
Пример #11
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
Пример #12
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())
Пример #13
0
 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
Пример #14
0
 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
Пример #15
0
    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)
Пример #16
0
 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
Пример #17
0
 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 = []
Пример #18
0
 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
Пример #19
0
 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
Пример #20
0
    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
Пример #21
0
 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, [])]
Пример #22
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)
Пример #23
0
 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
Пример #24
0
    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', {}))
Пример #25
0
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)
Пример #26
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''
Пример #27
0
#!/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
Пример #28
0
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()
Пример #29
0
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
Пример #30
0
#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',
Пример #31
0
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