blob: 98330a97a2422344b363ba1027facda65254b9a2 [file] [log] [blame]
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +07001# -*- coding: utf-8 -*-
2
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +07003# Copyright (C) 2018 Vadim Yanitskiy <axilirator@gmail.com>
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070019import select
20import struct
21import socket
22import os
Philipp Maier8c823782023-10-23 10:44:44 +020023import argparse
Harald Weltef9f8d7a2023-07-09 17:06:16 +020024from typing import Optional
Harald Welteab6897c2023-07-09 16:21:23 +020025
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070026from pySim.transport import LinkBase
27from pySim.exceptions import *
Harald Weltef9f8d7a2023-07-09 17:06:16 +020028from pySim.utils import h2b, b2h, Hexstr, ResTuple
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070029
Harald Weltec91085e2022-02-10 18:05:45 +010030
Vadim Yanitskiy04b5d9d2022-07-07 03:05:30 +070031class L1CTLMessage:
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070032
Harald Weltec91085e2022-02-10 18:05:45 +010033 # Every (encoded) L1CTL message has the following structure:
34 # - msg_length (2 bytes, net order)
35 # - l1ctl_hdr (packed structure)
36 # - msg_type
37 # - flags
38 # - padding (2 spare bytes)
39 # - ... payload ...
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070040
Harald Weltec91085e2022-02-10 18:05:45 +010041 def __init__(self, msg_type, flags=0x00):
42 # Init L1CTL message header
43 self.data = struct.pack("BBxx", msg_type, flags)
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070044
Harald Weltec91085e2022-02-10 18:05:45 +010045 def gen_msg(self):
46 return struct.pack("!H", len(self.data)) + self.data
47
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070048
49class L1CTLMessageReset(L1CTLMessage):
50
Harald Weltec91085e2022-02-10 18:05:45 +010051 # L1CTL message types
52 L1CTL_RESET_REQ = 0x0d
53 L1CTL_RESET_IND = 0x07
54 L1CTL_RESET_CONF = 0x0e
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070055
Harald Weltec91085e2022-02-10 18:05:45 +010056 # Reset types
57 L1CTL_RES_T_BOOT = 0x00
58 L1CTL_RES_T_FULL = 0x01
59 L1CTL_RES_T_SCHED = 0x02
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070060
Harald Weltec91085e2022-02-10 18:05:45 +010061 def __init__(self, type=L1CTL_RES_T_FULL):
62 super(L1CTLMessageReset, self).__init__(self.L1CTL_RESET_REQ)
63 self.data += struct.pack("Bxxx", type)
64
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070065
66class L1CTLMessageSIM(L1CTLMessage):
67
Harald Weltec91085e2022-02-10 18:05:45 +010068 # SIM related message types
69 L1CTL_SIM_REQ = 0x16
70 L1CTL_SIM_CONF = 0x17
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070071
Harald Weltec91085e2022-02-10 18:05:45 +010072 def __init__(self, pdu):
73 super(L1CTLMessageSIM, self).__init__(self.L1CTL_SIM_REQ)
74 self.data += pdu
75
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070076
77class CalypsoSimLink(LinkBase):
Harald Weltec91085e2022-02-10 18:05:45 +010078 """Transport Link for Calypso based phones."""
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070079
Harald Weltec91085e2022-02-10 18:05:45 +010080 def __init__(self, sock_path: str = "/tmp/osmocom_l2", **kwargs):
81 super().__init__(**kwargs)
Philipp Maier4af63dc2023-10-26 12:17:32 +020082 if os.environ.get('PYSIM_INTEGRATION_TEST') == "1":
83 print("Using Calypso-based (OsmocomBB) reader interface")
84 else:
85 print("Using Calypso-based (OsmocomBB) reader at socket %s" % sock_path)
Harald Weltec91085e2022-02-10 18:05:45 +010086 # Make sure that a given socket path exists
87 if not os.path.exists(sock_path):
88 raise ReaderError(
89 "There is no such ('%s') UNIX socket" % sock_path)
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070090
Harald Weltec91085e2022-02-10 18:05:45 +010091 print("Connecting to osmocon at '%s'..." % sock_path)
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070092
Harald Weltec91085e2022-02-10 18:05:45 +010093 # Establish a client connection
94 self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
95 self.sock.connect(sock_path)
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +070096
Philipp Maier6bfa8a82023-10-09 13:32:49 +020097 # Remember socket path
98 self._sock_path = sock_path
99
Harald Weltec91085e2022-02-10 18:05:45 +0100100 def __del__(self):
101 self.sock.close()
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700102
Harald Welteab6897c2023-07-09 16:21:23 +0200103 def wait_for_rsp(self, exp_len: int = 128):
Harald Weltec91085e2022-02-10 18:05:45 +0100104 # Wait for incoming data (timeout is 3 seconds)
105 s, _, _ = select.select([self.sock], [], [], 3.0)
106 if not s:
107 raise ReaderError("Timeout waiting for card response")
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700108
Harald Weltec91085e2022-02-10 18:05:45 +0100109 # Receive expected amount of bytes from osmocon
110 rsp = self.sock.recv(exp_len)
111 return rsp
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700112
Harald Weltec91085e2022-02-10 18:05:45 +0100113 def reset_card(self):
114 # Request FULL reset
115 req_msg = L1CTLMessageReset()
116 self.sock.send(req_msg.gen_msg())
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700117
Harald Weltec91085e2022-02-10 18:05:45 +0100118 # Wait for confirmation
119 rsp = self.wait_for_rsp()
120 rsp_msg = struct.unpack_from("!HB", rsp)
121 if rsp_msg[1] != L1CTLMessageReset.L1CTL_RESET_CONF:
122 raise ReaderError("Failed to reset Calypso PHY")
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700123
Harald Weltec91085e2022-02-10 18:05:45 +0100124 def connect(self):
125 self.reset_card()
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700126
Harald Weltec91085e2022-02-10 18:05:45 +0100127 def disconnect(self):
128 pass # Nothing to do really ...
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700129
Harald Welteab6897c2023-07-09 16:21:23 +0200130 def wait_for_card(self, timeout: Optional[int] = None, newcardonly: bool = False):
Harald Weltec91085e2022-02-10 18:05:45 +0100131 pass # Nothing to do really ...
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700132
Harald Weltef9f8d7a2023-07-09 17:06:16 +0200133 def _send_apdu_raw(self, pdu: Hexstr) -> ResTuple:
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700134
Harald Weltec91085e2022-02-10 18:05:45 +0100135 # Request FULL reset
136 req_msg = L1CTLMessageSIM(h2b(pdu))
137 self.sock.send(req_msg.gen_msg())
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700138
Harald Weltec91085e2022-02-10 18:05:45 +0100139 # Read message length first
140 rsp = self.wait_for_rsp(struct.calcsize("!H"))
141 msg_len = struct.unpack_from("!H", rsp)[0]
142 if msg_len < struct.calcsize("BBxx"):
143 raise ReaderError("Missing L1CTL header for L1CTL_SIM_CONF")
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700144
Harald Weltec91085e2022-02-10 18:05:45 +0100145 # Read the whole message then
146 rsp = self.sock.recv(msg_len)
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700147
Harald Weltec91085e2022-02-10 18:05:45 +0100148 # Verify L1CTL header
149 hdr = struct.unpack_from("BBxx", rsp)
150 if hdr[0] != L1CTLMessageSIM.L1CTL_SIM_CONF:
151 raise ReaderError("Unexpected L1CTL message received")
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700152
Harald Weltec91085e2022-02-10 18:05:45 +0100153 # Verify the payload length
154 offset = struct.calcsize("BBxx")
155 if len(rsp) <= offset:
156 raise ProtocolError("Empty response from SIM?!?")
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700157
Harald Weltec91085e2022-02-10 18:05:45 +0100158 # Omit L1CTL header
159 rsp = rsp[offset:]
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700160
Harald Weltec91085e2022-02-10 18:05:45 +0100161 # Unpack data and SW
162 data = rsp[:-2]
163 sw = rsp[-2:]
Vadim Yanitskiy1381ff12018-10-27 01:48:15 +0700164
Harald Weltec91085e2022-02-10 18:05:45 +0100165 return b2h(data), b2h(sw)
Philipp Maier6bfa8a82023-10-09 13:32:49 +0200166
Philipp Maier58e89eb2023-10-10 11:59:03 +0200167 def __str__(self) -> str:
Philipp Maier6bfa8a82023-10-09 13:32:49 +0200168 return "osmocon:%s" % (self._sock_path)
Philipp Maier8c823782023-10-23 10:44:44 +0200169
170 @staticmethod
171 def argparse_add_reader_args(arg_parser: argparse.ArgumentParser):
172 osmobb_group = arg_parser.add_argument_group('OsmocomBB Reader')
173 osmobb_group.add_argument('--osmocon', dest='osmocon_sock', metavar='PATH', default=None,
174 help='Socket path for Calypso (e.g. Motorola C1XX) based reader (via OsmocomBB)')