SEDbgMuxApp: employ DbgMuxFrameDisp, implement basic handlers

This commit is contained in:
Vadim Yanitskiy 2022-06-26 00:02:02 +07:00
parent 91abeb7220
commit c81e0e2ad8
2 changed files with 85 additions and 54 deletions

View File

@ -112,12 +112,12 @@ class DbgMuxPeer:
# Init frame dispatcher # Init frame dispatcher
self.disp = DbgMuxFrameDisp(self._rx_queue, self.disp = DbgMuxFrameDisp(self._rx_queue,
self._tx_queue) self._tx_queue)
# TODO: self.disp.start()
def start(self) -> None: def start(self) -> None:
self._shutdown.clear() self._shutdown.clear()
self._rx_thread.start() self._rx_thread.start()
self._tx_thread.start() self._tx_thread.start()
self.disp.start()
def stop(self) -> None: def stop(self) -> None:
# Set the shutdown event # Set the shutdown event
@ -125,6 +125,7 @@ class DbgMuxPeer:
# Wait for both threads to terminate # Wait for both threads to terminate
self._tx_thread.join() self._tx_thread.join()
self._rx_thread.join() self._rx_thread.join()
self.disp.stop()
def _rx_worker(self) -> None: def _rx_worker(self) -> None:
while not self._shutdown.is_set(): while not self._shutdown.is_set():

View File

@ -23,11 +23,74 @@ import argparse
import cmd2 import cmd2
import enum import enum
import sys import sys
import time
from transport import TransportModem from transport import TransportModem
from proto import DbgMuxFrame from proto import DbgMuxFrame
from peer import DbgMuxPeer from peer import DbgMuxPeer
from construct import Container
from handlers import DbgMuxFrameHandler
from handlers import DbgMuxConnUdpBridge
from handlers import DbgMuxConnInteractiveTerminal
class CommonFrameHandler(DbgMuxFrameHandler):
''' Handles some common messages '''
def __init__(self, cmd2):
self.cmd2 = cmd2
def _handle_frame(self, frame: Container) -> None:
if frame['MsgType'] == DbgMuxFrame.MsgType.Ident:
log.info("Identified target: '%s', IMEI=%s",
frame['Msg']['Ident'][:-15],
frame['Msg']['Ident'][-15:])
elif frame['MsgType'] == DbgMuxFrame.MsgType.DPAnnounce:
log.info("Data Provider available (DPRef=0x%04x): '%s'",
frame['Msg']['DPRef'], frame['Msg']['Name'])
elif frame['MsgType'] == DbgMuxFrame.MsgType.FlowControl:
log.warning("Rx FlowControl message, which is not yet supported")
elif frame['MsgType'] == DbgMuxFrame.MsgType.ConnEstablished \
and frame['Msg']['ConnRef'] == 0xffff:
log.warning("Connection establishment failed, "
"no such DPRef=0x%04x?", frame['Msg']['DPRef'])
elif frame['MsgType'] == DbgMuxFrame.MsgType.ConnTerminated \
and frame['Msg']['ConnRef'] == 0xffff:
log.warning("Connection termination failed, "
"no such DPRef=0x%04x?", frame['Msg']['DPRef'])
elif frame['MsgType'] == DbgMuxFrame.MsgType.Ack:
raise StopIteration
else: # To be consumed by other handlers
return
self.send(DbgMuxFrame.MsgType.Ack)
raise StopIteration
class PingPongHandler(DbgMuxFrameHandler):
''' Handles DbgMuxFrame.MsgType.Pong '''
def __init__(self):
self.expect_pong: bool = False
def ping(self, payload: str):
log.info('Tx Ping with payload \'%s\'', payload)
self.send(DbgMuxFrame.MsgType.Ping, payload)
self.expect_pong = True
# TODO: start a timer?
def handle_pong(self, payload: str):
if not self.expect_pong:
log.warning('Rx unexpected Pong, sending ACK anyway')
return
log.info('Rx Pong with payload \'%s\'', payload)
self.expect_pong = False
def _handle_frame(self, frame: Container) -> None:
if frame['MsgType'] == DbgMuxFrame.MsgType.Pong:
self.handle_pong(frame['Msg'])
self.send(DbgMuxFrame.MsgType.Ack)
raise StopIteration
class SEDbgMuxApp(cmd2.Cmd): class SEDbgMuxApp(cmd2.Cmd):
DESC = 'DebugMux client for [Sony] Ericsson phones and modems' DESC = 'DebugMux client for [Sony] Ericsson phones and modems'
@ -48,6 +111,10 @@ class SEDbgMuxApp(cmd2.Cmd):
self.transport = TransportModem(self.argv) self.transport = TransportModem(self.argv)
self.peer = DbgMuxPeer(self.transport) self.peer = DbgMuxPeer(self.transport)
# Register DebugMux frame handlers
self.peer.disp.register(CommonFrameHandler(self), 'Common')
self.peer.disp.register(PingPongHandler(), 'PingPong')
# Modem connection state # Modem connection state
self.set_connected(False) self.set_connected(False)
@ -88,23 +155,8 @@ class SEDbgMuxApp(cmd2.Cmd):
def do_enquiry(self, opts) -> None: def do_enquiry(self, opts) -> None:
''' Enquiry target identifier and available Data Providers ''' ''' Enquiry target identifier and available Data Providers '''
self.peer.send(DbgMuxFrame.MsgType.Enquiry) self.peer.send(DbgMuxFrame.MsgType.Enquiry)
while True: # The responce to be handled by CommonFrameHandler
f = self.peer.recv() time.sleep(0.5)
if f['MsgType'] == DbgMuxFrame.MsgType.Ident:
log.info("Identified target: '%s', IMEI=%s",
f['Msg']['Ident'][:-15],
f['Msg']['Ident'][-15:])
elif f['MsgType'] == DbgMuxFrame.MsgType.DPAnnounce:
log.info("Data Provider available (DPRef=0x%04x): '%s'",
f['Msg']['DPRef'], f['Msg']['Name'])
# No more data in the buffer
# FIXME: layer violation!
if self.transport._sl.in_waiting == 0:
break
# ACKnowledge reception of the info
self.peer.send(DbgMuxFrame.MsgType.Ack)
ping_parser = cmd2.Cmd2ArgumentParser() ping_parser = cmd2.Cmd2ArgumentParser()
ping_parser.add_argument('-p', '--payload', ping_parser.add_argument('-p', '--payload',
@ -115,52 +167,30 @@ class SEDbgMuxApp(cmd2.Cmd):
@cmd2.with_category(CATEGORY_DBGMUX) @cmd2.with_category(CATEGORY_DBGMUX)
def do_ping(self, opts) -> None: def do_ping(self, opts) -> None:
''' Send a Ping to the target, expect Pong ''' ''' Send a Ping to the target, expect Pong '''
log.info('Tx Ping with payload \'%s\'', opts.payload) hinst = self.peer.disp.find_by_name('PingPong')
self.peer.send(DbgMuxFrame.MsgType.Ping, opts.payload) hinst.ping(opts.payload)
f = self.peer.recv()
assert f['MsgType'] == DbgMuxFrame.MsgType.Pong
log.info('Rx Pong with payload \'%s\'', f['Msg'])
self.peer.send(DbgMuxFrame.MsgType.Ack)
establish_parser = cmd2.Cmd2ArgumentParser() establish_parser = cmd2.Cmd2ArgumentParser()
establish_parser.add_argument('DPRef', establish_parser.add_argument('DPRef',
type=lambda v: int(v, 16), type=lambda v: int(v, 16),
help='DPRef of a Data Provider in hex') help='DPRef of a Data Provider in hex')
establish_parser.add_argument('mode',
choices=['interactive', 'udp-bridge'],
help='Connection mode')
@cmd2.with_argparser(establish_parser) @cmd2.with_argparser(establish_parser)
@cmd2.with_category(CATEGORY_DBGMUX) @cmd2.with_category(CATEGORY_DBGMUX)
def do_establish(self, opts) -> None: def do_establish(self, opts) -> None:
''' Establish connection with a Data Provider ''' ''' Establish connection with a Data Provider '''
log.info("Establishing connection with DPRef=0x%04x", opts.DPRef) if opts.mode == 'interactive':
self.peer.send(DbgMuxFrame.MsgType.ConnEstablish, ch = DbgMuxConnInteractiveTerminal()
dict(DPRef=opts.DPRef)) self.peer.disp.register(ch)
ch.establish(opts.DPRef)
f = self.peer.recv() ch.attach()
assert f['MsgType'] == DbgMuxFrame.MsgType.ConnEstablished elif opts.mode == 'udp-bridge':
if f['Msg']['ConnRef'] == 0xffff: ch = DbgMuxConnUdpBridge()
log.warning("Connection failed: unknown DPRef=0x%04x?", opts.DPRef) self.peer.disp.register(ch)
self.peer.send(DbgMuxFrame.MsgType.Ack) ch.establish(opts.DPRef)
return
log.info("Connection established (ConnRef=0x%04x)",
f['Msg']['ConnRef'])
# Read the messages
while True:
f = self.peer.recv()
if f['MsgType'] != DbgMuxFrame.MsgType.ConnData:
log.warning('Unexpected frame: %s', f)
self.peer.send(DbgMuxFrame.MsgType.Ack)
continue
try: # FIXME: there can be binary data
self.stdout.write(f['Msg']['Data'].decode())
except: # ... ignore it for now
continue
# ACKnowledge reception of a frame
self.peer.send(DbgMuxFrame.MsgType.Ack)
ap = argparse.ArgumentParser(prog='sedbgmux', description=SEDbgMuxApp.DESC, ap = argparse.ArgumentParser(prog='sedbgmux', description=SEDbgMuxApp.DESC,