1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-22 23:48:12 +00:00

Pipe support & async pipe transport

This commit is contained in:
slush0 2016-04-04 23:40:05 +02:00 committed by Pavol Rusnak
parent cbc2be00ca
commit 456e1fba44
No known key found for this signature in database
GPG Key ID: 91F3B339B9A02A3D
12 changed files with 530 additions and 341 deletions

View File

@ -13,14 +13,6 @@ class EventLoop:
self.q = [] self.q = []
self.cnt = 0 self.cnt = 0
self.last_sleep = 0 # For performance stats self.last_sleep = 0 # For performance stats
#self.button_cb = None
'''
def create_task(self, coro):
# CPython 3.4.2
self.call_at(0, coro)
# CPython asyncio incompatibility: we don't return Task object
'''
def call_soon(self, callback, *args): def call_soon(self, callback, *args):
self.call_at(0, callback, *args) self.call_at(0, callback, *args)
@ -78,27 +70,21 @@ class EventLoop:
delay = arg delay = arg
elif isinstance(ret, StopLoop): elif isinstance(ret, StopLoop):
return arg return arg
''' # elif isinstance(ret, IORead):
elif isinstance(ret, IORead): # self.add_reader(arg.fileno(), lambda self, c, f: self.call_soon(c, f), self, cb, arg)
self.add_reader(ret.obj.fileno(), lambda self, c, f: self.call_soon(c, f), self, cb, ret.obj) # self.add_reader(arg.fileno(), lambda c, f: self.call_soon(c, f), cb, arg)
self.add_reader(ret.obj.fileno(), lambda c, f: self.call_soon(c, f), cb, ret.obj) # self.add_reader(arg.fileno(), lambda cb: self.call_soon(cb), cb)
self.add_reader(arg.fileno(), lambda cb: self.call_soon(cb), cb) # self.add_reader(arg.fileno(), cb)
self.add_reader(arg.fileno(), cb) # continue
continue # elif isinstance(ret, IOWrite):
elif isinstance(ret, IOWrite): # self.add_writer(arg.fileno(), lambda cb: self.call_soon(cb), cb)
self.add_writer(arg.fileno(), lambda cb: self.call_soon(cb), cb) # self.add_writer(arg.fileno(), cb)
self.add_writer(arg.fileno(), cb) # continue
continue # elif isinstance(ret, IOReadDone):
elif isinstance(ret, IOReadDone): # self.remove_reader(arg.fileno())
self.remove_reader(arg.fileno()) # elif isinstance(ret, IOWriteDone):
elif isinstance(ret, IOWriteDone): # self.remove_writer(arg.fileno())
self.remove_writer(arg.fileno())
'''
#elif isinstance(ret, IOButton):
# print("TADY")
# self.button_cb = cb
# continue
elif isinstance(ret, type_gen): elif isinstance(ret, type_gen):
self.call_soon(ret) self.call_soon(ret)
elif ret is None: elif ret is None:

View File

@ -1,73 +0,0 @@
import uselect
import errno
from .core import EventLoop
if __debug__:
import logging
log = logging.getLogger("asyncio")
class EpollEventLoop(EventLoop):
def __init__(self):
EventLoop.__init__(self)
self.poller = uselect.poll()
self.objmap = {}
def add_reader(self, fd, cb, *args):
if __debug__:
log.debug("add_reader%s", (fd, cb, args))
if args:
self.poller.register(fd, uselect.POLLIN)
self.objmap[fd] = (cb, args)
else:
self.poller.register(fd, uselect.POLLIN)
self.objmap[fd] = cb
def remove_reader(self, fd):
if __debug__:
log.debug("remove_reader(%s)", fd)
self.poller.unregister(fd)
del self.objmap[fd]
def add_writer(self, fd, cb, *args):
if __debug__:
log.debug("add_writer%s", (fd, cb, args))
if args:
self.poller.register(fd, uselect.POLLOUT)
self.objmap[fd] = (cb, args)
else:
self.poller.register(fd, uselect.POLLOUT)
self.objmap[fd] = cb
def remove_writer(self, fd):
if __debug__:
log.debug("remove_writer(%s)", fd)
try:
self.poller.unregister(fd)
self.objmap.pop(fd, None)
except OSError as e:
# StreamWriter.awrite() first tries to write to an fd,
# and if that succeeds, yield IOWrite may never be called
# for that fd, and it will never be added to poller. So,
# ignore such error.
if e.args[0] != errno.ENOENT:
raise
def wait(self, delay):
if __debug__:
log.debug("epoll.wait(%d)", delay)
# We need one-shot behavior (second arg of 1 to .poll())
if delay == -1:
res = self.poller.poll(-1, 1)
else:
res = self.poller.poll(int(delay * 1000), 1)
# log.debug("epoll result: %s", res)
for fd, ev in res:
cb = self.objmap[fd]
if __debug__:
log.debug("Calling IO callback: %r", cb)
if isinstance(cb, tuple):
cb[0](*cb[1])
else:
self.call_soon(cb)

View File

@ -1,94 +0,0 @@
from collections.deque import deque
from uasyncio.core import sleep
class QueueEmpty(Exception):
"""Exception raised by get_nowait()."""
class QueueFull(Exception):
"""Exception raised by put_nowait()."""
class Queue:
"""A queue, useful for coordinating producer and consumer coroutines.
If maxsize is less than or equal to zero, the queue size is infinite. If it
is an integer greater than 0, then "yield from put()" will block when the
queue reaches maxsize, until an item is removed by get().
Unlike the standard library Queue, you can reliably know this Queue's size
with qsize(), since your single-threaded uasyncio application won't be
interrupted between calling qsize() and doing an operation on the Queue.
"""
_attempt_delay = 0.1
def __init__(self, maxsize=0):
self.maxsize = maxsize
self._queue = deque()
def _get(self):
return self._queue.popleft()
def get(self):
"""Returns generator, which can be used for getting (and removing)
an item from a queue.
Usage::
item = yield from queue.get()
"""
while not self._queue:
yield from sleep(self._attempt_delay)
return self._get()
def get_nowait(self):
"""Remove and return an item from the queue.
Return an item if one is immediately available, else raise QueueEmpty.
"""
if not self._queue:
raise QueueEmpty()
return self._get()
def _put(self, val):
self._queue.append(val)
def put(self, val):
"""Returns generator which can be used for putting item in a queue.
Usage::
yield from queue.put(item)
"""
while self.qsize() > self.maxsize and self.maxsize:
yield from sleep(self._attempt_delay)
self._put(val)
def put_nowait(self, val):
"""Put an item into the queue without blocking.
If no free slot is immediately available, raise QueueFull.
"""
if self.qsize() >= self.maxsize and self.maxsize:
raise QueueFull()
self._put(val)
def qsize(self):
"""Number of items in the queue."""
return len(self._queue)
def empty(self):
"""Return True if the queue is empty, False otherwise."""
return not self._queue
def full(self):
"""Return True if there are maxsize items in the queue.
Note: if the Queue was initialized with maxsize=0 (the default),
then full() is never True.
"""
if self.maxsize <= 0:
return False
else:
return self.qsize() >= self.maxsize

View File

@ -1,139 +0,0 @@
import usocket
import errno
from .core import IOReadDone, IOWriteDone, IORead, IOWrite
if __debug__:
import logging
log = logging.getLogger("asyncio")
class StreamReader:
def __init__(self, s):
self.s = s
def read(self, n=-1):
yield IORead(self.s)
while True:
res = self.s.read(n)
if res is not None:
break
log.warn("Empty read")
if not res:
yield IOReadDone(self.s)
return res
def readline(self):
if __debug__:
log.debug("StreamReader.readline()")
yield IORead(self.s)
# if __debug__:
# log.debug("StreamReader.readline(): after IORead: %s", s)
while True:
res = self.s.readline()
if res is not None:
break
log.warn("Empty read")
if not res:
yield IOReadDone(self.s)
if __debug__:
log.debug("StreamReader.readline(): res: %s", res)
return res
def aclose(self):
yield IOReadDone(self.s)
self.s.close()
def __repr__(self):
return "<StreamReader %r>" % self.s
class StreamWriter:
def __init__(self, s, extra):
self.s = s
self.extra = extra
def awrite(self, buf):
# This method is called awrite (async write) to not proliferate
# incompatibility with original asyncio. Unlike original asyncio
# whose .write() method is both not a coroutine and guaranteed
# to return immediately (which means it has to buffer all the
# data), this method is a coroutine.
sz = len(buf)
if __debug__:
log.debug("StreamWriter.awrite(): spooling %d bytes", sz)
while True:
res = self.s.write(buf)
# If we spooled everything, return immediately
if res == sz:
if __debug__:
log.debug("StreamWriter.awrite(): completed spooling %d bytes", res)
return
if res is None:
res = 0
if __debug__:
log.debug("StreamWriter.awrite(): spooled partial %d bytes", res)
assert res < sz
buf = buf[res:]
sz -= res
yield IOWrite(self.s)
# assert s2.fileno() == self.s.fileno()
if __debug__:
log.debug("StreamWriter.awrite(): can write more")
def aclose(self):
yield IOWriteDone(self.s)
self.s.close()
def get_extra_info(self, name, default=None):
return self.extra.get(name, default)
def __repr__(self):
return "<StreamWriter %r>" % self.s
def open_connection(host, port):
if __debug__:
log.debug("open_connection(%s, %s)", host, port)
s = usocket.socket()
s.setblocking(False)
ai = usocket.getaddrinfo(host, port)
addr = ai[0][4]
try:
s.connect(addr)
except OSError as e:
if e.args[0] != errno.EINPROGRESS:
raise
if __debug__:
log.debug("open_connection: After connect")
yield IOWrite(s)
# if __debug__:
# assert s2.fileno() == s.fileno()
if __debug__:
log.debug("open_connection: After iowait: %s", s)
return StreamReader(s), StreamWriter(s, {})
def start_server(client_coro, host, port, backlog=10):
if __debug__:
log.debug("start_server(%s, %s)", host, port)
s = usocket.socket()
s.setblocking(False)
ai = usocket.getaddrinfo(host, port)
addr = ai[0][4]
s.setsockopt(usocket.SOL_SOCKET, usocket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(backlog)
while True:
if __debug__:
log.debug("start_server: Before accept")
yield IORead(s)
if __debug__:
log.debug("start_server: After iowait")
s2, client_addr = s.accept()
s2.setblocking(False)
if __debug__:
log.debug("start_server: After accept: %s", s2)
extra = {"peername": client_addr}
yield client_coro(StreamReader(s2), StreamWriter(s2, extra))

38
src/lib_linux/errno.py Normal file
View File

@ -0,0 +1,38 @@
EPERM = 1 # Operation not permitted
ENOENT = 2 # No such file or directory
ESRCH = 3 # No such process
EINTR = 4 # Interrupted system call
EIO = 5 # I/O error
ENXIO = 6 # No such device or address
E2BIG = 7 # Argument list too long
ENOEXEC = 8 # Exec format error
EBADF = 9 # Bad file number
ECHILD = 10 # No child processes
EAGAIN = 11 # Try again
ENOMEM = 12 # Out of memory
EACCES = 13 # Permission denied
EFAULT = 14 # Bad address
ENOTBLK = 15 # Block device required
EBUSY = 16 # Device or resource busy
EEXIST = 17 # File exists
EXDEV = 18 # Cross-device link
ENODEV = 19 # No such device
ENOTDIR = 20 # Not a directory
EISDIR = 21 # Is a directory
EINVAL = 22 # Invalid argument
ENFILE = 23 # File table overflow
EMFILE = 24 # Too many open files
ENOTTY = 25 # Not a typewriter
ETXTBSY = 26 # Text file busy
EFBIG = 27 # File too large
ENOSPC = 28 # No space left on device
ESPIPE = 29 # Illegal seek
EROFS = 30 # Read-only file system
EMLINK = 31 # Too many links
EPIPE = 32 # Broken pipe
EDOM = 33 # Math argument out of domain of func
ERANGE = 34 # Math result not representable
EAFNOSUPPORT = 97 # Address family not supported by protocol
ECONNRESET = 104 # Connection timed out
ETIMEDOUT = 110 # Connection timed out
EINPROGRESS = 115 # Operation now in progress

46
src/lib_linux/ffilib.py Normal file
View File

@ -0,0 +1,46 @@
import sys
try:
import ffi
except ImportError:
ffi = None
_cache = {}
def open(name, maxver=10, extra=()):
if not ffi:
return None
try:
return _cache[name]
except KeyError:
pass
def libs():
if sys.platform == "linux":
yield '%s.so' % name
for i in range(maxver, -1, -1):
yield '%s.so.%u' % (name, i)
else:
for ext in ('dylib', 'dll'):
yield '%s.%s' % (name, ext)
for n in extra:
yield n
err = None
for n in libs():
try:
l = ffi.open(n)
_cache[name] = l
return l
except OSError as e:
err = e
raise err
def libc():
return open("libc", 6)
# Find out bitness of the platform, even if long ints are not supported
# TODO: All bitness differences should be removed from micropython-lib, and
# this snippet too.
bitness = 1
v = sys.maxsize
while v:
bitness += 1
v >>= 1

View File

@ -41,6 +41,9 @@ if libc:
opendir_ = libc.func("P", "opendir", "s") opendir_ = libc.func("P", "opendir", "s")
readdir_ = libc.func("P", "readdir", "P") readdir_ = libc.func("P", "readdir", "P")
open_ = libc.func("i", "open", "sii") open_ = libc.func("i", "open", "sii")
fdopen_ = libc.func("i", "fdopen", "sii")
fsync_ = libc.func("i", "fsync", "i")
#lseek = libc.func("i", "lseek", "iii")
read_ = libc.func("i", "read", "ipi") read_ = libc.func("i", "read", "ipi")
write_ = libc.func("i", "write", "iPi") write_ = libc.func("i", "write", "iPi")
close_ = libc.func("i", "close", "i") close_ = libc.func("i", "close", "i")
@ -48,6 +51,7 @@ if libc:
access_ = libc.func("i", "access", "si") access_ = libc.func("i", "access", "si")
fork_ = libc.func("i", "fork", "") fork_ = libc.func("i", "fork", "")
pipe_ = libc.func("i", "pipe", "p") pipe_ = libc.func("i", "pipe", "p")
mkfifo_ = libc.func("i", "mkfifo", "si")
_exit_ = libc.func("v", "_exit", "i") _exit_ = libc.func("v", "_exit", "i")
getpid_ = libc.func("i", "getpid", "") getpid_ = libc.func("i", "getpid", "")
waitpid_ = libc.func("i", "waitpid", "ipi") waitpid_ = libc.func("i", "waitpid", "ipi")
@ -56,9 +60,6 @@ if libc:
kill_ = libc.func("i", "kill", "ii") kill_ = libc.func("i", "kill", "ii")
getenv_ = libc.func("s", "getenv", "P") getenv_ = libc.func("s", "getenv", "P")
def check_error(ret): def check_error(ret):
# Return True is error was EINTR (which usually means that OS call # Return True is error was EINTR (which usually means that OS call
# should be restarted). # should be restarted).
@ -163,6 +164,11 @@ def open(n, flags, mode=0o777):
check_error(r) check_error(r)
return r return r
def fdopen(n, flags, mode=0o777):
r = fdopen_(n, flags, mode)
check_error(r)
return r
def read(fd, n): def read(fd, n):
buf = bytearray(n) buf = bytearray(n)
r = read_(fd, buf, n) r = read_(fd, buf, n)
@ -174,6 +180,11 @@ def write(fd, buf):
check_error(r) check_error(r)
return r return r
def fsync(fd):
r = fsync_(fd)
check_error(r)
return r
def close(fd): def close(fd):
r = close_(fd) r = close_(fd)
check_error(r) check_error(r)
@ -202,6 +213,11 @@ def pipe():
check_error(r) check_error(r)
return a[0], a[1] return a[0], a[1]
def mkfifo(n, mode=0o777):
r = mkfifo_(n, mode)
check_error(r)
return r
def _exit(n): def _exit(n):
_exit_(n) _exit_(n)

90
src/lib_linux/select.py Normal file
View File

@ -0,0 +1,90 @@
import ffi
import ustruct as struct
import os
import errno
import ffilib
libc = ffilib.libc()
#int epoll_create(int size);
epoll_create = libc.func("i", "epoll_create", "i")
#int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll_ctl = libc.func("i", "epoll_ctl", "iiiP")
#int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
epoll_wait = libc.func("i", "epoll_wait", "ipii")
EPOLLIN = 0x001
EPOLLPRI = 0x002
EPOLLOUT = 0x004
EPOLLERR = 0x008
EPOLLHUP = 0x010
EPOLLRDHUP = 0x2000
EPOLLONESHOT = 1 << 30
EPOLLET = 1 << 31
EPOLL_CTL_ADD = 1
EPOLL_CTL_DEL = 2
EPOLL_CTL_MOD = 3
# TODO: struct epoll_event's 2nd member is union of uint64_t, etc.
# On x86, uint64_t is 4-byte aligned, on many other platforms - 8-byte.
# Until uctypes module can assign native struct offset, use dirty hack
# below.
# TODO: Get rid of all this dirtiness, move it on C side
if ffilib.bitness > 32:
# On x86_64, epoll_event is packed struct
epoll_event = "<IO"
elif struct.calcsize("IQ") == 12:
epoll_event = "IO"
else:
epoll_event = "QO"
class Epoll:
def __init__(self, epfd):
self.epfd = epfd
self.evbuf = struct.pack(epoll_event, 0, None)
self.registry = {}
def register(self, fd, eventmask=EPOLLIN|EPOLLPRI|EPOLLOUT, retval=None):
"retval is extension to stdlib, value to use in results from .poll()."
if retval is None:
retval = fd
s = struct.pack(epoll_event, eventmask, retval)
r = epoll_ctl(self.epfd, EPOLL_CTL_ADD, fd, s)
if r == -1 and os.errno_() == errno.EEXIST:
r = epoll_ctl(self.epfd, EPOLL_CTL_MOD, fd, s)
os.check_error(r)
# We must keep reference to retval, or it may be GCed. And we must
# keep mapping from fd to retval to be able to get rid of this retval
# reference later.
self.registry[fd] = retval
def unregister(self, fd):
# Pass dummy event structure, to workaround kernel bug
r = epoll_ctl(self.epfd, EPOLL_CTL_DEL, fd, self.evbuf)
os.check_error(r)
del self.registry[fd]
def poll(self, timeout=-1):
s = bytearray(self.evbuf)
while True:
n = epoll_wait(self.epfd, s, 1, timeout)
if not os.check_error(n):
break
# TODO: what about timeout value?
res = []
if n > 0:
vals = struct.unpack(epoll_event, s)
res.append((vals[1], vals[0]))
return res
def close(self):
os.close(self.epfd)
def epoll(sizehint=4):
fd = epoll_create(sizehint)
os.check_error(fd)
return Epoll(fd)

149
src/lib_linux/stat.py Normal file
View File

@ -0,0 +1,149 @@
"""Constants/functions for interpreting results of os.stat() and os.lstat().
Suggested usage: from stat import *
"""
# Indices for stat struct members in the tuple returned by os.stat()
ST_MODE = 0
ST_INO = 1
ST_DEV = 2
ST_NLINK = 3
ST_UID = 4
ST_GID = 5
ST_SIZE = 6
ST_ATIME = 7
ST_MTIME = 8
ST_CTIME = 9
# Extract bits from the mode
def S_IMODE(mode):
"""Return the portion of the file's mode that can be set by
os.chmod().
"""
return mode & 0o7777
def S_IFMT(mode):
"""Return the portion of the file's mode that describes the
file type.
"""
return mode & 0o170000
# Constants used as S_IFMT() for various file types
# (not all are implemented on all systems)
S_IFDIR = 0o040000 # directory
S_IFCHR = 0o020000 # character device
S_IFBLK = 0o060000 # block device
S_IFREG = 0o100000 # regular file
S_IFIFO = 0o010000 # fifo (named pipe)
S_IFLNK = 0o120000 # symbolic link
S_IFSOCK = 0o140000 # socket file
# Functions to test for each file type
def S_ISDIR(mode):
"""Return True if mode is from a directory."""
return S_IFMT(mode) == S_IFDIR
def S_ISCHR(mode):
"""Return True if mode is from a character special device file."""
return S_IFMT(mode) == S_IFCHR
def S_ISBLK(mode):
"""Return True if mode is from a block special device file."""
return S_IFMT(mode) == S_IFBLK
def S_ISREG(mode):
"""Return True if mode is from a regular file."""
return S_IFMT(mode) == S_IFREG
def S_ISFIFO(mode):
"""Return True if mode is from a FIFO (named pipe)."""
return S_IFMT(mode) == S_IFIFO
def S_ISLNK(mode):
"""Return True if mode is from a symbolic link."""
return S_IFMT(mode) == S_IFLNK
def S_ISSOCK(mode):
"""Return True if mode is from a socket."""
return S_IFMT(mode) == S_IFSOCK
# Names for permission bits
S_ISUID = 0o4000 # set UID bit
S_ISGID = 0o2000 # set GID bit
S_ENFMT = S_ISGID # file locking enforcement
S_ISVTX = 0o1000 # sticky bit
S_IREAD = 0o0400 # Unix V7 synonym for S_IRUSR
S_IWRITE = 0o0200 # Unix V7 synonym for S_IWUSR
S_IEXEC = 0o0100 # Unix V7 synonym for S_IXUSR
S_IRWXU = 0o0700 # mask for owner permissions
S_IRUSR = 0o0400 # read by owner
S_IWUSR = 0o0200 # write by owner
S_IXUSR = 0o0100 # execute by owner
S_IRWXG = 0o0070 # mask for group permissions
S_IRGRP = 0o0040 # read by group
S_IWGRP = 0o0020 # write by group
S_IXGRP = 0o0010 # execute by group
S_IRWXO = 0o0007 # mask for others (not in group) permissions
S_IROTH = 0o0004 # read by others
S_IWOTH = 0o0002 # write by others
S_IXOTH = 0o0001 # execute by others
# Names for file flags
UF_NODUMP = 0x00000001 # do not dump file
UF_IMMUTABLE = 0x00000002 # file may not be changed
UF_APPEND = 0x00000004 # file may only be appended to
UF_OPAQUE = 0x00000008 # directory is opaque when viewed through a union stack
UF_NOUNLINK = 0x00000010 # file may not be renamed or deleted
UF_COMPRESSED = 0x00000020 # OS X: file is hfs-compressed
UF_HIDDEN = 0x00008000 # OS X: file should not be displayed
SF_ARCHIVED = 0x00010000 # file may be archived
SF_IMMUTABLE = 0x00020000 # file may not be changed
SF_APPEND = 0x00040000 # file may only be appended to
SF_NOUNLINK = 0x00100000 # file may not be renamed or deleted
SF_SNAPSHOT = 0x00200000 # file is a snapshot file
_filemode_table = (
((S_IFLNK, "l"),
(S_IFREG, "-"),
(S_IFBLK, "b"),
(S_IFDIR, "d"),
(S_IFCHR, "c"),
(S_IFIFO, "p")),
((S_IRUSR, "r"),),
((S_IWUSR, "w"),),
((S_IXUSR|S_ISUID, "s"),
(S_ISUID, "S"),
(S_IXUSR, "x")),
((S_IRGRP, "r"),),
((S_IWGRP, "w"),),
((S_IXGRP|S_ISGID, "s"),
(S_ISGID, "S"),
(S_IXGRP, "x")),
((S_IROTH, "r"),),
((S_IWOTH, "w"),),
((S_IXOTH|S_ISVTX, "t"),
(S_ISVTX, "T"),
(S_IXOTH, "x"))
)
def filemode(mode):
"""Convert a file's mode to a string of the form '-rwxrwxrwx'."""
perm = []
for table in _filemode_table:
for bit, char in table:
if mode & bit == bit:
perm.append(char)
break
else:
perm.append("-")
return "".join(perm)

View File

@ -0,0 +1,141 @@
'''PipeTransport implements fake wire transport over local named pipe.
Use this transport for talking with trezor simulator.'''
import os
import ustruct
import uselect
from uasyncio import core
read_fd = None
write_fd = None
poll = None
on_read = None
def init(filename):
global read_fd, write_fd, poll
filename_read = filename + '.to'
filename_write = filename + '.from'
os.mkfifo(filename_read, 0o600)
os.mkfifo(filename_write, 0o600)
write_fd = os.open(filename_write, os.O_RDWR, 0o600)
read_fd = os.open(filename_read, os.O_RDWR, 0o600)
poll = uselect.poll()
poll.register(read_fd, uselect.POLLIN)
# Setup polling
loop = core.get_event_loop()
loop.call_soon(watch_read())
def set_notify(_on_read):
global on_read
on_read = _on_read
def close():
global read_fd, write_fd
os.close(read_fd)
os.close(write_fd)
def watch_read():
global on_read
sleep = core.Sleep(0.01)
while True:
if ready_to_read() and on_read:
on_read()
yield sleep
def ready_to_read():
global poll
return len(poll.poll(0)) > 0
def read():
"""
If there is data available to be read from the transport, reads the data and tries to parse it as a protobuf message. If the parsing succeeds, return a protobuf object.
Otherwise, returns None.
"""
if not ready_to_read():
return None
data = _read()
if data == None:
return None
return _parse_message(data)
def write(msg):
"""
Write mesage to tansport. msg should be a member of a valid `protobuf class <https://developers.google.com/protocol-buffers/docs/pythontutorial>`_ with a SerializeToString() method.
"""
ser = msg.SerializeToString()
header = ustruct.pack(">HL", mapping.get_type(msg), len(ser))
_write(b"##%s%s" % (header, ser))
def _parse_message(data):
(msg_type, _data) = data
if msg_type == 'protobuf':
return _data
else:
# inst = mapping.get_class(msg_type)()
# inst.ParseFromString(_data)
inst = _data
return inst
def _read_headers():
global read_fd
# Try to read headers until some sane value are detected
is_ok = False
while not is_ok:
# Align cursor to the beginning of the header ("##")
c = os.read(read_fd, 1)
i = 0
while c != b'#':
i += 1
if i >= 64:
# timeout
raise Exception("Timed out while waiting for the magic character")
# print "Aligning to magic characters"
c = os.read(read_fd, 1)
print(c)
if os.read(read_fd, 1) != b'#':
# Second character must be # to be valid header
raise Exception("Second magic character is broken")
# Now we're most likely on the beginning of the header
try:
headerlen = ustruct.calcsize(">HL")
(msg_type, datalen) = ustruct.unpack(">HL", os.read(read_fd, headerlen))
break
except:
raise Exception("Cannot parse header length")
return (msg_type, datalen)
def _write(msg):
global write_fd
try:
os.write(write_fd, msg)
# os.fsync(write_fd)
except OSError:
print("Error while writing to socket")
raise
def _read():
global read_fd
try:
(msg_type, datalen) = _read_headers()
return (msg_type, os.read(read_fd, datalen))
except:
print("Failed to read from device")
raise

View File

@ -2,19 +2,26 @@
import sys import sys
sys.path.append('lib') sys.path.append('lib')
if sys.platform == 'linux':
# Packages used only on linux platform (named pipes, ...)
sys.path.append('lib_linux')
import utime import utime
import math import math
import gc import gc
from uasyncio import core from uasyncio import core
from trezor import ui # import transport_pipe as pipe
from trezor import ui, io
from trezor import msg2 as msg
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
loop = core.get_event_loop() loop = core.get_event_loop()
def perf_info(): def perf_info():
mem_free = gc.mem_free() mem_free = gc.mem_free()
# gc.collect() gc.collect()
print("free_mem: %s/%s, last_sleep: %.06f" % \ print("free_mem: %s/%s, last_sleep: %.06f" % \
(mem_free, gc.mem_free(), loop.last_sleep)) (mem_free, gc.mem_free(), loop.last_sleep))
loop.call_later(1, perf_info) loop.call_later(1, perf_info)
@ -87,9 +94,13 @@ def tap_to_confirm():
yield core.Sleep(DELAY) yield core.Sleep(DELAY)
def on_read():
print("READY TO READ")
print(msg.read())
def run(): def run():
# sekunda(3) # pipe.init('../pipe', on_read)
# loop.call_soon(wait_for()) msg.set_notify(on_read)
loop.call_soon(perf_info) loop.call_soon(perf_info)
loop.call_soon(tap_to_confirm()) loop.call_soon(tap_to_confirm())

18
src/trezor/msg2.py Normal file
View File

@ -0,0 +1,18 @@
import sys
if sys.platform == 'linux':
import transport_pipe as pipe
def write(msg):
return pipe.write(msg)
def read():
return pipe.read()
def set_notify(_on_read):
return pipe.set_notify(_on_read)
pipe.init('../pipe')
else:
NotImplemented("HID transport")