2 # -*- coding: utf-8 -*-
4 # Copyright (c) 2020 Niklas Ekström
15 logging.basicConfig(format = '%(levelname)s, %(asctime)s, %(name)s, line %(lineno)d: %(message)s')
16 logger = logging.getLogger(__name__)
17 logger.setLevel(logging.INFO)
21 MSG_DEREGISTER_REQ = 3
22 MSG_DEREGISTER_RES = 4
28 MSG_CONNECT_RESPONSE = 10
35 while len(header) < 9:
36 data = drv.recv(9 - len(header))
38 logger.error('Connection to a314d was closed, terminating.')
41 (plen, stream_id, ptype) = struct.unpack('=IIB', header)
43 while len(payload) < plen:
44 data = drv.recv(plen - len(payload))
46 logger.error('Connection to a314d was closed, terminating.')
49 return (stream_id, ptype, payload)
51 def send_register_req(name):
52 m = struct.pack('=IIB', len(name), 0, MSG_REGISTER_REQ) + name
55 def send_read_mem_req(address, length):
56 m = struct.pack('=IIBII', 8, 0, MSG_READ_MEM_REQ, address, length)
59 def read_mem(address, length):
60 send_read_mem_req(address, length)
61 _, ptype, payload = wait_for_msg()
62 if ptype != MSG_READ_MEM_RES:
63 logger.error('Expected MSG_READ_MEM_RES but got %s. Shutting down.', ptype)
67 def send_write_mem_req(address, data):
68 m = struct.pack('=IIBI', 4 + len(data), 0, MSG_WRITE_MEM_REQ, address) + data
71 def write_mem(address, data):
72 send_write_mem_req(address, data)
73 _, ptype, _ = wait_for_msg()
74 if ptype != MSG_WRITE_MEM_RES:
75 logger.error('Expected MSG_WRITE_MEM_RES but got %s. Shutting down.', ptype)
78 def send_connect_response(stream_id, result):
79 m = struct.pack('=IIBB', 1, stream_id, MSG_CONNECT_RESPONSE, result)
82 def send_data(stream_id, data):
83 m = struct.pack('=IIB', len(data), stream_id, MSG_DATA) + data
86 def send_eos(stream_id):
87 m = struct.pack('=IIB', 0, stream_id, MSG_EOS)
90 def send_reset(stream_id):
91 m = struct.pack('=IIB', 0, stream_id, MSG_RESET)
94 ### A314 communication routines above. Actual driver below.
96 current_stream_id = None
101 SERVICE_NAME = b'ethernet'
111 # Can buffer as many frames as fit in memory.
112 # Maybe should have a limit on the number of buffers?
113 waiting_read_reqs = []
116 DROP_START_SECS = 15.0
118 def process_tap_frame(frame):
119 if current_stream_id is None:
124 if time.time() < start_time + DROP_START_SECS:
128 if waiting_read_reqs:
129 stream_id, address, length = waiting_read_reqs.pop(0)
131 if length < len(frame):
132 logger.error('Fatal error, read frame from TAP larger than buffer')
134 mem_write_queue.append((stream_id, address, len(frame)))
135 send_write_mem_req(address, frame)
137 buffered_frames.append(frame)
139 def process_stream_data(stream_id, data):
140 address, length, kind = struct.unpack('>IHH', data)
141 if kind == WRITE_FRAME_REQ:
142 mem_read_queue.append((stream_id, address, length))
143 send_read_mem_req(address, length)
144 elif kind == READ_FRAME_REQ:
146 frame = buffered_frames.pop(0)
148 if length < len(frame):
149 logger.error('Fatal error, read frame from TAP larger than buffer')
151 mem_write_queue.append((stream_id, address, len(frame)))
152 send_write_mem_req(address, frame)
154 waiting_read_reqs.append((stream_id, address, length))
156 def process_read_mem_res(frame):
159 stream_id, address, length = mem_read_queue.pop(0)
160 if stream_id == current_stream_id:
161 send_data(stream_id, struct.pack('>IHH', address, length, WRITE_FRAME_RES))
163 def process_write_mem_res():
164 stream_id, address, length = mem_write_queue.pop(0)
165 if stream_id == current_stream_id:
166 send_data(stream_id, struct.pack('>IHH', address, length, READ_FRAME_RES))
168 def process_drv_msg(stream_id, ptype, payload):
169 global current_stream_id
171 if ptype == MSG_CONNECT:
172 if payload == SERVICE_NAME and current_stream_id is None:
173 logger.info('Amiga connected')
174 current_stream_id = stream_id
175 send_connect_response(stream_id, 0)
177 send_connect_response(stream_id, 3)
178 elif ptype == MSG_READ_MEM_RES:
179 process_read_mem_res(payload)
180 elif ptype == MSG_WRITE_MEM_RES:
181 process_write_mem_res()
182 elif current_stream_id == stream_id:
183 if ptype == MSG_DATA:
184 process_stream_data(stream_id, payload)
185 elif ptype == MSG_EOS:
188 elif ptype == MSG_RESET:
189 current_stream_id = None
190 waiting_read_reqs.clear()
191 buffered_frames.clear()
192 logger.info('Amiga disconnected')
195 idx = sys.argv.index('-ondemand')
200 fd = int(sys.argv[idx + 1])
201 drv = socket.socket(fileno=fd)
203 drv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
204 drv.connect(('localhost', 7110))
205 drv.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
207 send_register_req(SERVICE_NAME)
208 _, _, payload = wait_for_msg()
210 logger.error('Unable to register ethernet with driver, shutting down')
216 tap = pytun.TunTapDevice(name=DEV_NAME, flags=pytun.IFF_TAP | pytun.IFF_NO_PI)
218 logger.error('Unable to open tap device at ' + DEV_NAME)
221 start_time = time.time()
225 logger.info('Ethernet service is running')
229 rl, _, _ = select.select([drv, tap], [], [], 10.0)
230 except KeyboardInterrupt:
232 if current_stream_id is not None:
233 send_reset(current_stream_id)
240 if current_stream_id is not None:
241 send_reset(current_stream_id)
250 (plen, stream_id, ptype) = struct.unpack('=IIB', rbuf[:9])
251 if len(rbuf) < 9 + plen:
254 payload = rbuf[9:9+plen]
257 process_drv_msg(stream_id, ptype, payload)
260 frame = tap.read(1600)
261 process_tap_frame(frame)
264 logger.info('Ethernet service stopped')