blob: 825c4585ba3fb6773684dbe3198f443ad0a63ecb [file] [log] [blame]
Sylvain Munaut76504e02010-12-07 00:24:32 +01001#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4""" pySim: Transport Link for serial (RS232) based readers included with simcard
5"""
6
7#
8# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation, either version 2 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22#
23
24from __future__ import absolute_import
25
26import serial
27import time
28
29from pySim.exceptions import NoCardError, ProtocolError
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010030from pySim.transport import LinkBase
Sylvain Munaut76504e02010-12-07 00:24:32 +010031from pySim.utils import h2b, b2h
32
33
Sylvain Munaute7c15cd2010-12-07 10:01:55 +010034class SerialSimLink(LinkBase):
Sylvain Munaut76504e02010-12-07 00:24:32 +010035
36 def __init__(self, device='/dev/ttyUSB0', baudrate=9600, rst='-rts', debug=False):
37 self._sl = serial.Serial(
38 port = device,
39 parity = serial.PARITY_EVEN,
40 bytesize = serial.EIGHTBITS,
41 stopbits = serial.STOPBITS_TWO,
42 timeout = 1,
43 xonxoff = 0,
44 rtscts = 0,
45 baudrate = baudrate,
46 )
47 self._rst_pin = rst
48 self._debug = debug
49
Sylvain Munautbdca2522010-12-09 13:31:58 +010050 def __del__(self):
51 self._sl.close()
52
53 def wait_for_card(self, timeout=None, newcardonly=False):
54 # Direct try
55 existing = False
56
57 try:
58 self.reset_card()
59 if not newcardonly:
60 return
61 else:
62 existing = True
63 except NoCardError:
64 pass
65
66 # Poll ...
67 mt = time.time() + timeout if timeout is not None else None
68 pe = 0
69
70 while (mt is None) or (time.time() < mt):
71 try:
72 time.sleep(0.5)
73 self.reset_card()
74 if not existing:
75 return
76 except NoCardError:
77 existing = False
78 except ProtocolError:
79 if existing:
80 existing = False
81 else:
82 # Tolerate a couple of protocol error ... can happen if
83 # we try when the card is 'half' inserted
84 pe += 1
85 if (pe > 2):
86 raise
87
88 # Timed out ...
89 raise NoCardError()
90
91 def connect(self):
92 self.reset_card()
93
94 def disconnect(self):
95 pass # Nothing to do really ...
96
97 def reset_card(self):
98 rv = self._reset_card()
Sylvain Munaut76504e02010-12-07 00:24:32 +010099 if rv == 0:
100 raise NoCardError()
101 elif rv < 0:
102 raise ProtocolError()
103
Sylvain Munautbdca2522010-12-09 13:31:58 +0100104 def _reset_card(self):
Sylvain Munaut76504e02010-12-07 00:24:32 +0100105 rst_meth_map = {
106 'rts': self._sl.setRTS,
107 'dtr': self._sl.setDTR,
108 }
109 rst_val_map = { '+':0, '-':1 }
110
111 try:
112 rst_meth = rst_meth_map[self._rst_pin[1:]]
113 rst_val = rst_val_map[self._rst_pin[0]]
114 except:
115 raise ValueError('Invalid reset pin %s' % self._rst_pin);
116
117 rst_meth(rst_val)
118 time.sleep(0.1) # 100 ms
119 self._sl.flushInput()
120 rst_meth(rst_val ^ 1)
121
122 b = self._rx_byte()
123 if not b:
124 return 0
125 if ord(b) != 0x3b:
126 return -1;
127 self._dbg_print("TS: 0x%x Direct convention" % ord(b))
128
129 while ord(b) == 0x3b:
130 b = self._rx_byte()
131
132 if not b:
133 return -1
134 t0 = ord(b)
135 self._dbg_print("T0: 0x%x" % t0)
136
137 for i in range(4):
138 if t0 & (0x10 << i):
139 self._dbg_print("T%si = %x" % (chr(ord('A')+i), ord(self._rx_byte())))
140
141 for i in range(0, t0 & 0xf):
142 self._dbg_print("Historical = %x" % ord(self._rx_byte()))
143
144 while True:
145 x = self._rx_byte()
146 if not x:
147 break
148 self._dbg_print("Extra: %x" % ord(x))
149
150 return 1
151
152 def _dbg_print(self, s):
153 if self._debug:
154 print s
155
156 def _tx_byte(self, b):
157 self._sl.write(b)
158 r = self._sl.read()
159 if r != b: # TX and RX are tied, so we must clear the echo
160 raise ProtocolError("Bad echo value. Expected %02x, got %s)" % (ord(b), '%02x'%ord(r) if r else '(nil)'))
161
162 def _tx_string(self, s):
163 """This is only safe if it's guaranteed the card won't send any data
164 during the time of tx of the string !!!"""
165 self._sl.write(s)
166 r = self._sl.read(len(s))
167 if r != s: # TX and RX are tied, so we must clear the echo
168 raise ProtocolError("Bad echo value (Expected: %s, got %s)" % (b2h(s), b2h(r)))
169
170 def _rx_byte(self):
171 return self._sl.read()
172
173 def send_apdu_raw(self, pdu):
Sylvain Munaute7c15cd2010-12-07 10:01:55 +0100174 """see LinkBase.send_apdu_raw"""
Sylvain Munaut76504e02010-12-07 00:24:32 +0100175
176 pdu = h2b(pdu)
177 data_len = ord(pdu[4]) # P3
178
179 # Send first CLASS,INS,P1,P2,P3
180 self._tx_string(pdu[0:5])
181
182 # Wait ack which can be
183 # - INS: Command acked -> go ahead
184 # - 0x60: NULL, just wait some more
185 # - SW1: The card can apparently proceed ...
186 while True:
187 b = self._rx_byte()
188 if b == pdu[1]:
189 break
190 elif b != '\x60':
191 # Ok, it 'could' be SW1
192 sw1 = b
193 sw2 = self._rx_byte()
194 nil = self._rx_byte()
195 if (sw2 and not nil):
196 return '', b2h(sw1+sw2)
197
198 raise ProtocolError()
199
200 # Send data (if any)
201 if len(pdu) > 5:
202 self._tx_string(pdu[5:])
203
204 # Receive data (including SW !)
205 # length = [P3 - tx_data (=len(pdu)-len(hdr)) + 2 (SW1/2) ]
206 to_recv = data_len - len(pdu) + 5 + 2
207
208 data = ''
209 while (len(data) < to_recv):
210 b = self._rx_byte()
211 if (to_recv == 2) and (b == '\x60'): # Ignore NIL if we have no RX data (hack ?)
212 continue
213 if not b:
214 break;
215 data += b
216
217 # Split datafield from SW
218 if len(data) < 2:
219 return None, None
220 sw = data[-2:]
221 data = data[0:-2]
222
223 # Return value
224 return b2h(data), b2h(sw)