def get_header(raw_file: io.FileIO) -> Header: """Get the raw file header from an open ATOP file. Args: raw_file: An open ATOP file capable of reading as bytes. Returns: raw_header: The header at the beginning of an ATOP file. Raises: ValueError: If there are not enough bytes to read the header, or the bytes were invalid. """ # Read the header directly into the struct, there is no padding to consume or add. # Use default Header as the baseline in order to check the version. It can be transferred without re-reading. header = _HEADER_BY_VERSION[_DEFAULT_VERSION]() raw_file.readinto(header) if header.magic != MAGIC: msg = f'File does not contain raw atop output (wrong magic number): {hex(header.magic)}' raise ValueError(msg) header_version = header.get_version() if header_version != _DEFAULT_VERSION and header_version in _HEADER_BY_VERSION: # Header byte length is consistent across versions. Transfer the initial read into the versioned header. header = _HEADER_BY_VERSION[header_version].from_buffer(header) # Ensure all struct lengths match the lengths specific in the header. If not, we cannot read the file further. header.check_compatibility() return header
def build_blob(bin1: FileIO, bin2: FileIO, out: FileIO) -> None: """Combines two firmware binary blobs ``bin1`` and ``bin2`` into a single firmware blob and saves them in ``out``. """ bin1_size = os.fstat(bin1.fileno()).st_size bin2_size = os.fstat(bin2.fileno()).st_size bin2_offset = BIN2_BASE_OFFSET - BIN1_BASE_OFFSET size = bin2_offset + bin2_size max_size = FLASH_SIZE - BIN1_BASE_OFFSET if bin1_size >= bin2_offset: raise ValueError(f"{bin1.name} is too big!") if size >= max_size: raise ValueError(f"{bin2.name} is too big!") # Create a new combined firmware blob blob = memoryview(bytearray(size)) bin1.readinto(blob) bin2.readinto(blob[bin2_offset:]) # Read Reset_Handler pointers in vector tables. bin1_reset_handler = blob[4:8].tobytes() bin2_reset_handler = blob[bin2_offset + 4 : bin2_offset + 8].tobytes() # Swap Reset_Handler pointers. This will cause the second # firmware to boot first. blob[4:8] = bin2_reset_handler blob[bin2_offset + 4 : bin2_offset + 8] = bin1_reset_handler # The final checksum is for the entire new blob # This overrides the checksum of the second firmware. blob[-4:] = crc32_checksum(BytesIO(blob), max_size).to_bytes(4, "little") out.write(blob)
def get_record(raw_file: io.FileIO, record_cls: Type[Record]) -> Record: """Get the next raw record from an open ATOP file. Args: raw_file: An open ATOP file capable of reading as bytes. record_cls: Record struct class to read the raw bytes into. Returns: record: A single record representing the data before an SStat struct. Raises: ValueError: If there are not enough bytes to read a single record. """ record = record_cls() raw_file.readinto(record) return record
class HidrawDS4Device(DS4Device): def __init__(self, name, addr, type, hidraw_device, event_device): try: self.report_fd = os.open(hidraw_device, os.O_RDWR | os.O_NONBLOCK) self.fd = FileIO(self.report_fd, "rb+", closefd=False) self.input_device = InputDevice(event_device) self.input_device.grab() except (OSError, IOError) as err: raise DeviceError(err) self.buf = bytearray(self.report_size) super(HidrawDS4Device, self).__init__(name, addr, type) def read_report(self): try: ret = self.fd.readinto(self.buf) except IOError: return # Disconnection if ret == 0: return # Invalid report size or id, just ignore it if ret < self.report_size or self.buf[0] != self.valid_report_id: return False if self.type == "bluetooth": # Cut off bluetooth data buf = zero_copy_slice(self.buf, 2) else: buf = self.buf return self.parse_report(buf) def read_feature_report(self, report_id, size): op = HIDIOCGFEATURE(size + 1) buf = bytearray(size + 1) buf[0] = report_id return fcntl.ioctl(self.fd, op, bytes(buf)) def write_report(self, report_id, data): if self.type == "bluetooth": # TODO: Add a check for a kernel that supports writing # output reports when such a kernel has been released. return hid = bytearray((report_id,)) self.fd.write(hid + data) def close(self): try: self.fd.close() self.input_device.ungrab() except IOError: pass
class HidrawDS4Device(DS4Device): def __init__(self, name, addr, type, hidraw_device, event_device): try: self.report_fd = os.open(hidraw_device, os.O_RDWR | os.O_NONBLOCK) self.fd = FileIO(self.report_fd, "rb+", closefd=False) self.input_device = InputDevice(event_device) self.input_device.grab() except (OSError, IOError) as err: raise DeviceError(err) self.buf = bytearray(self.report_size) super(HidrawDS4Device, self).__init__(name, addr, type) def read_report(self): try: ret = self.fd.readinto(self.buf) except IOError: return 'ioerror' # Disconnection if ret == 0: return 'disconnection' # Invalid report size or id, just ignore it if ret < self.report_size or self.buf[0] != self.valid_report_id: return False if self.type == "bluetooth": # Cut off bluetooth data buf = zero_copy_slice(self.buf, 2) else: buf = self.buf return self.parse_report(buf) def read_feature_report(self, report_id, size): op = HIDIOCGFEATURE(size + 1) buf = bytearray(size + 1) buf[0] = report_id return fcntl.ioctl(self.fd, op, bytes(buf)) def write_report(self, report_id, data): hid = bytearray((report_id, )) self.fd.write(hid + data) def close(self): try: # Reset LED to original hidraw pairing colour. self.set_led(0, 0, 1) self.fd.close() self.input_device.ungrab() except IOError: pass
def _bench_file_read(hasher, expect): f = FileIO(tmpf.name, "r") b = bytearray(blksize) h = hasher() while 1: n = f.readinto(b) if n == 0: break h.update(xbuffer(b, 0, n)) # NOTE b[:n] does copy f.close() assert h.digest() == expect
def set_challenge(self, ep0: io.FileIO) -> None: buf = AuthReport() ep0.readinto(buf) #type: ignore[arg-type] crc = zlib.crc32( bytes(buf)[:ctypes.sizeof(AuthReport) - ctypes.sizeof(c_uint32)]) if crc != buf.crc32: # TODO do we need to do more here? logger.warning("Invalid CRC32.") if buf.type != int(ReportType.set_challenge): raise TypeError('Invalid request type for request set_challenge.') if buf.page != 0 and buf.seq != self._seq: logger.warning("Inconsistent sequence value.") elif buf.page != self._req_page: logger.warning("Out of order challenge write.") self._seq = buf.seq valid_data_size = min( max(0, AUTH_REQ_SIZE - buf.page * self._req_size), self._req_size) self._nonce.write( memoryview(cast(bytearray, buf.data))[:valid_data_size]) # bytearray-like if self._req_page == self._req_max_page: self._status = 0x01 self._rsa_task.submit(self.prepare_challenge) self._req_page += 1
class FileDataReader(AbstractDataReader): """ A reader that can read data from a file """ def __init__(self, filename): """ :param filename: The file to read :type filename: str :raise spinnman.exceptions.SpinnmanIOException: If the file\ cannot found or opened for reading """ try: self._fileio = FileIO(filename, "r") except IOError as e: raise SpinnmanIOException(str(e)) def read(self, n_bytes): """ See :py:meth:`spinnman.data.abstract_data_reader.AbstractDataReader.read` """ return bytearray(self._fileio.read(n_bytes)) def readinto(self, data): """ See :py:meth:`spinnman.data.abstract_data_reader.AbstractDataReader.readinto` """ return self._fileio.readinto(data) def readall(self): """ See :py:meth:`spinnman.data.abstract_data_reader.AbstractDataReader.readall` """ return self._fileio.readall() def close(self): """ Closes the file :return: Nothing is returned: :rtype: None :raise spinnman.exceptions.SpinnmanIOException: If the file\ cannot be closed """ try: self._fileio.close() except IOError as e: raise SpinnmanIOException(str(e))
def _bench_file_readbig(hasher, expect): f = FileIO(tmpf.name, "r") # b = mmap(-1, filesize, MAP_SHARED | MAP_ANONYMOUS, PROT_READ | PROT_WRITE) b = bytearray(filesize) bm = memoryview(b) h = hasher() pos = 0 while 1: n = f.readinto(bm[pos:]) if n == 0: break h.update(xbuffer(b, pos, n)) # NOTE b[pos:n] does copy pos += n del bm del b f.close() assert h.digest() == expect
class Joystick(Part): """Joystick to drive the car around manually without keyboard.""" def __init__(self, config): """Joystick to drive the car around manually without keyboard.""" super(Joystick, self).__init__(config, "joystick", []) # State/Controls self.speed = 0 self.steer = 0 self.speed_offset = 0 self.steer_offset = 0 self.is_calibrated = True self.is_autonomous = False self.state = DS4State() self.last_state = DS4State() self.__fd = None self.__input_device = None self.__report_fd = None self.__report_id = 0x11 self.__keep_running = True self.__connect() def __del__(self): self.publish("action", isManual=True, speed=0, steer=0) self.publish("controller", isAutonomous=False, speedOffset=0, steerOffset=0, exit=True) super(Joystick, self).__del__() try: self.send(red=1, rumble_high=1) time.sleep(0.5) self.send(blue=0.1, green=0.1, red=0.5) except: pass if self.__fd is not None: self.__fd.close() if self.__input_device is not None: self.__input_device.ungrab() def __find_device(self): context = Context() for hidraw_device in context.list_devices(subsystem="hidraw"): hid_device = hidraw_device.parent if hid_device.subsystem != "hid" or hid_device.get( "HID_NAME") != "Wireless Controller": continue for child in hid_device.parent.children: event_device = child.get("DEVNAME", "") if event_device.startswith("/dev/input/event"): break else: continue device_addr = hid_device.get("HID_UNIQ", "").upper() return device_addr, hidraw_device.device_node, event_device return None, None, None def __connect(self): device_addr, hidraw_device, event_device = self.__find_device() if device_addr is None: return False self.__report_fd = os.open(hidraw_device, os.O_RDWR | os.O_NONBLOCK) self.__fd = FileIO(self.__report_fd, "rb+", closefd=False) self.__input_device = InputDevice(event_device) self.__input_device.grab() buf = bytearray(38) buf[0] = 0x02 try: return bool(fcntl.ioctl(self.__fd, 3223734279, bytes(buf))) except: pass if self.recv(): self.update_controller() def __in_deadzone(self, value): """ Deadzone checker for analog sticks """ return 128 - self._config["deadzone"] < value <= 128 + self._config[ "deadzone"] def __normalize_stick(self, value, deadzone): """ Normalize stick value from [0, 255] to [0, 1] Ignore a 128-centered deadzone """ value -= 128 value = value - deadzone if value > 0 else value + deadzone value /= 127 - deadzone return value def recv(self, limit=1000, duration=0.001, report_size=78): """ Attempt to get a message from the device. Args: limit (int): number of device polls to do duration (int): how long to wait between polls Returns: Whether we have successfully updated the status of the program """ for i in range(limit): time.sleep(duration) recv_buffer = bytearray(report_size) try: ret = self.__fd.readinto(recv_buffer) except IOError: # print("joystick: IO Error") continue except AttributeError: # print("joystick: Attribute Error") continue if ret is None: # print("joystick: ret is none") continue if ret < report_size: # print("joystick: ret too small (%i) expected (%i)" % (ret, report_size)) continue if recv_buffer[0] != self.__report_id: # print("joystick: Wrong report id (%i) expected (%i):" # % (recv_buffer[0], self.__report_id)) continue self._timestamp = derp.util.get_timestamp() self.last_state = self.state self.state = DS4State(recv_buffer) self.process_state() return True return False def update_controller(self): """Send the state of the system to the controller""" green = 1.0 if self.is_autonomous else 0 red = 1.0 if self.is_calibrated else 0 blue = 1.0 light_on = 1.0 light_off = 0.0 self.send(red=red, green=green, blue=blue, light_on=light_on, light_off=light_off) return True def send(self, rumble_high=0, rumble_low=0, red=0, green=0, blue=0, light_on=0, light_off=0): """Actuate the controller by setting its rumble or light color/blink""" packet = bytearray(79) packet[:5] = [0xA2, 0x11, 0x80, 0x00, 0xFF] packet[7] = int(rumble_high * 255 + 0.5) packet[8] = int(rumble_low * 255 + 0.5) packet[9] = int(red * 255 + 0.5) packet[10] = int(green * 255 + 0.5) packet[11] = int(blue * 255 + 0.5) packet[12] = int(light_on * 255 + 0.5) packet[13] = int(light_off * 255 + 0.5) crc = crc32(packet[:-4]) packet[-4] = crc & 0x000000FF packet[-3] = (crc & 0x0000FF00) >> 8 packet[-2] = (crc & 0x00FF0000) >> 16 packet[-1] = (crc & 0xFF000000) >> 24 hid = bytearray((self.__report_id, )) if self.__fd is not None: self.__fd.write(hid + packet[2:]) return True return False def process_state(self): """ For the given input, figure out how we should affect the state and put that into out. """ self.controller_changed = False self.action_changed = False self.__keep_running = not self.state.button_trackpad if not self.__in_deadzone(self.state.left_analog_x): steer = self.__normalize_stick( self.state.left_analog_x, self._config["deadzone"]) * self._config['steer_normalizer'] if steer != self.steer: self.steer = steer self.action_changed = True elif not self.__in_deadzone(self.last_state.left_analog_x): self.steer = 0 self.action_changed = True if self.state.left_trigger: speed = -self.state.left_trigger / 255 * self._config[ 'speed_normalizer'] if speed != self.speed: self.speed = speed self.action_changed = True elif self.last_state.left_trigger: self.speed = 0 self.action_changed = True if self.state.right_trigger: speed = self.state.right_trigger / 255 * self._config[ 'speed_normalizer'] if speed != self.speed: self.speed = speed self.action_changed = True elif self.last_state.right_trigger: self.speed = 0 self.action_changed = True if self.state.left and not self.last_state.left: self.steer_offset -= 5 / 255 self.controller_changed = True if self.state.right and not self.last_state.right: self.steer_offset += 5 / 255 self.controller_changed = True if self.state.up and not self.last_state.up: self.speed_offset += 5 / 255 self.controller_changed = True if self.state.down and not self.last_state.down: self.speed_offset -= 5 / 255 self.controller_changed = True if self.state.button_square and not self.last_state.button_square: pass if self.state.button_cross and not self.last_state.button_cross: self.speed = 0 self.steer = 0 self.speed_offset = 0 self.is_autonomous = False self.action_changed = True self.controller_changed = True if self.state.button_triangle and not self.last_state.button_triangle: self.is_autonomous = True self.controller_changed = True if self.state.button_circle and not self.last_state.button_circle: self.controller_changed = True def run(self): """Query one set of inputs from the joystick and send it out.""" start_time = derp.util.get_timestamp() if not self.recv(): print("joystick: timed out", start_time) self.__connect() return True if self.controller_changed: self.update_controller() self.publish( "controller", isAutonomous=self.is_autonomous, speedOffset=self.speed_offset, steerOffset=self.steer_offset, ) if self.action_changed: self.publish("action", isManual=True, speed=self.speed, steer=self.steer) return self.__keep_running
"/dev/input/js1", os.O_RDWR | os.O_NONBLOCK ) # Ouvre le fichier contenant les données HID en mode R/W de manière asynchrone fd = FileIO( report_fd, "rb", closefd=False ) # Ouvre un FLUX de lecture du fichier concerné. Mode lecture binaire defBuf = bytearray(230) # Crée un bytearray de 230 octets vides voiture = Voiture() # Initialise la classe voiture dernierEvt = False # Dernier Event reçu (False si c'est le permier) while True: sleep(0.1) buf = defBuf # Buf = Buffer = Bytearray r = fd.readinto(buf) # Remplit le bytearray vide evt = Event(buf) # Crée un événement depuis le bytearray if evt.spam: evt = dernierEvt # Si c'est un spam, alors garder evt.comparer(dernierEvt) # Compare Event et dernierEvt if (DEBUG): logEvData(evt.changement) # Affiche des données formatées voiture.interagir( evt ) # Envoie les données à l'objet Voiture pour interagir avec les GPIO dernierEvt = evt
class File(RawIOBase): 'Create a file object wrapping an e[x]ploded zip file' HEADER = 0 DATA = 1 DESCRIPTOR = 2 DIRECTORY = 3 def __init__(self, path, flags, info, fh=None, base='.', depth=0): super(File, self).__init__() self.path = path self.flags = flags self.fh = fh self.info = info self.depth = depth self.cursor = 0 self.offset = 0 self.state = File.HEADER # stream item info self.stream_offset = 0 self.zip_header = b'' self.descriptor = b'' # data file info self.data = None self.data_name = '' self.data_len = 0 # streams prefix = os.path.join(base, 'meta', os.path.basename(path)) self.stream = FileIO(prefix + '.stream', 'rb') self.dir = FileIO(prefix + '.dir', 'rb') self.data_dir = os.path.join(base, 'data') # init self._load_stream_item() self.lock = threading.Lock() def _load_stream_item(self): 'Sets the next stream item as current.' if self.data: self.data.close() self.data = None # open the header so we can know the data file to open, and the # length of the var fields raw_header = self.stream.read(STREAM_ITEM.size) header = StreamItem._make(STREAM_ITEM.unpack(raw_header)) var_fields = header.filename_len + header.extra_field_len # I would think that b2a_hex should decode the raw bytes... sha1 = b2a_hex(header.sha).decode('ascii') # only save the zip part of the header self.zip_header = (raw_header[:HEADER_DIFF] + self.stream.read(var_fields)) self.descriptor = self.stream.read(header.descriptor_len) self.data_name = path.join(*([self.data_dir] + list(sha1[:self.depth]) + [sha1])) def _open_data_file(self): self.data = FileIO(self.data_name, 'rb') self.data_len = self.data.seek(0, 2) self.data.seek(0) def close(self): self.stream.close() self.dir.close() if self.data: self.data.close() def fileno(self): return self.fh def isatty(self): return False def read(self, count=-1): if count < 0: return self.readall() elif count == 0: return b'' state = self.state if state == File.HEADER: previous_offset = self.offset self.offset += count result = self.zip_header[previous_offset:self.offset] self.cursor += len(result) if self.offset >= len(self.zip_header): self.state = File.DATA if not self.data: self._open_data_file() return result elif state == File.DATA: result = self.data.read(count) self.cursor += len(result) if self.data.tell() >= self.data_len: self.state = File.DESCRIPTOR self.offset = 0 # empty data file (state will now be DESCRIPTOR) if not result: return self.read(count) return result elif state == File.DESCRIPTOR: previous_offset = self.offset self.offset += count result = self.descriptor[previous_offset:self.offset] self.cursor += len(result) if self.offset >= len(self.descriptor): if self.cursor >= self.info.directory_offset: self.state = File.DIRECTORY self.dir.seek(0) self.stream_offset = None if self.data: self.data.close() self.data = None else: self.state = File.HEADER self.offset = 0 self.stream_offset = self.stream.tell() self._load_stream_item() # descriptor is optional (state will now be HEADER or DIRECTORY) if not result: return self.read(count) return result elif state == File.DIRECTORY: result = self.dir.read(count) self.cursor += len(result) return result else: raise RuntimeError('Invalid state: %r' % self.state) def readable(self): return True def readinto(self, b): count = len(b) if count == 0: return 0 state = self.state if state == File.HEADER: header_len = len(self.zip_header) previous_offset = self.offset current_offset = self.offset = \ min(previous_offset + count, header_len) read = current_offset - previous_offset b[:read] = self.zip_header[previous_offset:current_offset] self.cursor += read if current_offset == header_len: self.state = File.DATA if not self.data: self._open_data_file() return read elif state == File.DATA: read = self.data.readinto(b) self.cursor += read if self.data.tell() >= self.data_len: self.state = File.DESCRIPTOR self.offset = 0 # empty data file (state will now be DESCRIPTOR) if not read: return self.readinto(b) return read elif state == File.DESCRIPTOR: descriptor_len = len(self.descriptor) previous_offset = self.offset current_offset = self.offset = \ min(previous_offset + count, descriptor_len) read = current_offset - previous_offset b[:read] = self.descriptor[previous_offset:current_offset] self.cursor += read if current_offset == descriptor_len: if self.cursor >= self.info.directory_offset: self.state = File.DIRECTORY self.dir.seek(0) self.stream_offset = None if self.data: self.data.close() self.data = None else: self.state = File.HEADER self.offset = 0 self.stream_offset = self.stream.tell() self._load_stream_item() # descriptor is optional (state will now be HEADER or DIRECTORY) if not read: return self.readinto(b) return read elif state == File.DIRECTORY: read = self.dir.readinto(b) self.cursor += read return read else: raise RuntimeError('Invalid state: %r' % self.state) def seek(self, pos, offset=0): if offset == 1: pos += self.cursor elif offset == 2: pos += self.info.filesize if pos == self.cursor: return pos self.cursor = pos # skip directly to the central directory if pos >= self.info.directory_offset: if self.data: self.data.close() self.data = None self.state = File.DIRECTORY self.stream_offset = None self.dir.seek(pos - self.info.directory_offset) return pos # calculate the offset into the stream file z_offset, s_offset = self.info.jump_tree.find(pos).location additional = pos - z_offset # we're looking at a different data file # (load local header into memory) if s_offset != self.stream_offset: self.stream_offset = s_offset self.stream.seek(s_offset) self._load_stream_item() header_len = len(self.zip_header) if additional < header_len: self.state = File.HEADER self.offset = additional return pos # assume currently in the data file additional -= header_len self.state = File.DATA # if the file hasn't been opened yet, open it and find its size if not self.data: self._open_data_file() if additional < self.data_len: self.data.seek(additional) else: self.state = File.DESCRIPTOR self.offset = additional - self.data_len return pos def seekable(self): return True def tell(self): return self.cursor def writeable(self): return False
class HidrawDS4Device(DS4Device): def __init__(self, name, addr, type, hidraw_device, event_device): try: self.report_fd = os.open(hidraw_device, os.O_RDWR | os.O_NONBLOCK) self.fd = FileIO(self.report_fd, "rb+", closefd=False) self.input_device = InputDevice(event_device) self.input_device.grab() except (OSError, IOError) as err: raise DeviceError(err) self.buf = bytearray(self.report_size) super(HidrawDS4Device, self).__init__(name, addr, type) def read_report(self): try: ret = self.fd.readinto(self.buf) except IOError: return # Disconnection if ret == 0: return # Invalid report size or id, just ignore it if ret < self.report_size or self.buf[0] != self.valid_report_id: return False if self.type == "bluetooth": # Cut off bluetooth data buf = zero_copy_slice(self.buf, 2) else: buf = self.buf return self.parse_report(buf) def read_feature_report(self, report_id, size): op = HIDIOCGFEATURE(size + 1) buf = bytearray(size + 1) buf[0] = report_id return fcntl.ioctl(self.fd, op, bytes(buf)) def write_report(self, report_id, report): """ Send a feature report. """ tmp = [0] * 79 tmp[:7] = [0xa2, 0x11, 0xC0, 0x00, 0xff, 0x00, 0x00] tmp[7] = report[5] #rumble weak tmp[8] = report[6] #rumble strong tmp[9] = report[7] #red tmp[10] = report[8] #green tmp[11] = report[9] #blue tmp[12] = report[10] # Time to flash bright (255 = 2.5 seconds) tmp[13] = report[11] # Time to flash dim (255 = 2.5 seconds) buf = bytearray(tmp) crc = zlib.crc32(bytes(buf[:(len(buf) - 4)])) & 0xffffffff buf[len(buf) - 4] = (crc & 0xFF) buf[len(buf) - 3] = ((crc & 0xFF00) >> 8) buf[len(buf) - 2] = ((crc & 0xFF0000) >> 16) buf[len(buf) - 1] = ((crc & 0xFF000000) >> 24) self.fd.write(buf[1:]) def close(self): try: # Reset LED to original hidraw pairing colour. # self.set_led(0, 0, 1) # Set LED to blinking orange to notify the driver disconnection. (0, 0, 1) is almost off so not that useful. self.set_led(255, 69, 0) # Orange self.start_led_flash(90, 50) self.fd.close() self.input_device.ungrab() except IOError: pass
import os, sys, datetime, colored from time import sleep from io import FileIO report_fd = os.open("/dev/input/js1", os.O_RDWR | os.O_NONBLOCK) fd = FileIO(report_fd, "rb+", closefd=False) defBuf = bytearray(230) while True: buf = defBuf r = fd.readinto(buf) key = [] arr = [] sign = buf[2] i = 0 while i < len(buf): try: if (buf[i] == sign and buf[i + 1] == 0): key.append(i + 2) key.append(i + 3) except Exception as e: pass i += 1 i = 0 if (buf[5] != 0): arr.append(colored.bg("blue") + "!!" + colored.attr("reset"))
class FakeDS4Device(DS4Device): def __init__(self, name, addr, type, hidraw_device, event_device): try: self.report_fd = os.open(hidraw_device, os.O_RDWR | os.O_NONBLOCK) self.fd = FileIO(self.report_fd, "rb+", closefd=False) self.input_device = InputDevice(event_device) self.input_device.grab() except (OSError, IOError) as err: raise DeviceError(err) self.buf = bytearray(self.report_size) super(FakeDS4Device, self).__init__(name, addr, type) def parse_report(self, buf): """parse a buffer containing a hid report.""" dpad = buf[5] % 16 return FakeDS4Report( # left analog stick buf[1], buf[2], # right analog stick buf[3], buf[4], # dpad up, down, left, right (dpad in (0, 1, 7)), (dpad in (3, 4, 5)), (dpad in (5, 6, 7)), (dpad in (1, 2, 3)), # buttons cross, circle, square, triangle (buf[5] & 32) != 0, (buf[5] & 64) != 0, (buf[5] & 16) != 0, (buf[5] & 128) != 0, # l1, r1 buttons (buf[6] & 1) != 0, (buf[6] & 2) != 0, # r1, r2 buttons (buf[6] & 4) != 0, (buf[6] & 8) != 0, # share and option buttons (buf[6] & 16) != 0, (buf[6] & 32) != 0, # l3 and r3 buttons (buf[6] & 64) != 0, (buf[6] & 128) != 0, # ps and trackpack buttons (buf[7] & 1) != 0, (buf[7] & 2) != 0, # l2 analog, r2 analog buf[8], buf[9], # timestamp and battery buf[7] >> 2, buf[30] % 16, # external inputs (usb, audio, mic) (buf[30] & 16) != 0, (buf[30] & 32) != 0, (buf[30] & 64) != 0) def read_report(self): try: ret = self.fd.readinto(self.buf) except IOError: return # Disconnection if ret == 0: return # Invalid report size or id, just ignore it if ret < self.report_size or self.buf[0] != self.valid_report_id: return False if self.type == "bluetooth": # Cut off bluetooth data buf = zero_copy_slice(self.buf, 2) else: buf = self.buf return self.parse_report(buf) def read_feature_report(self, report_id, size): op = HIDIOCGFEATURE(size + 1) buf = bytearray(size + 1) buf[0] = report_id return fcntl.ioctl(self.fd, op, bytes(buf)) def write_report(self, report_id, data): hid = bytearray((report_id, )) self.fd.write(hid + data) def close(self): try: # Reset LED to original hidraw pairing colour. self.set_led(0, 0, 1) self.fd.close() self.input_device.ungrab() except IOError: pass