blob: db42240501670caccff44eff89c2a13c045b9755 [file] [log] [blame]
Kévin Redon69b92d92019-01-24 16:39:20 +01001/*
Kévin Redon78d2f442019-01-24 18:45:59 +01002 * Copyright (C) 2019 sysmocom -s.f.m.c. GmbH, Author: Kevin Redon <kredon@sysmocom.de>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17*/
Kévin Redon69b92d92019-01-24 16:39:20 +010018
Harald Welte1b9a5b82019-02-24 23:04:45 +010019#include <stdlib.h>
20#include <stdio.h>
Kévin Redon072951b2019-05-02 15:17:46 +020021#include <math.h>
Harald Weltef53f2262019-02-24 11:01:08 +010022#include <parts.h>
23#include <hal_cache.h>
Harald Welte93f628a2019-02-24 14:32:30 +010024#include <hri_port_e54.h>
Harald Weltef53f2262019-02-24 11:01:08 +010025
Kévin Redon69b92d92019-01-24 16:39:20 +010026#include "atmel_start.h"
27#include "atmel_start_pins.h"
Kévin Redon072951b2019-05-02 15:17:46 +020028#include "config/hpl_gclk_config.h"
Kévin Redon69b92d92019-01-24 16:39:20 +010029
Harald Weltec3f170d2019-02-24 09:06:59 +010030#include "i2c_bitbang.h"
31#include "octsim_i2c.h"
32#include "ncn8025.h"
Kévin Redon0f050722019-05-02 15:56:25 +020033#include "iso7816_3.h"
Harald Weltec3f170d2019-02-24 09:06:59 +010034
Harald Welteff9f4ce2019-02-24 22:51:09 +010035#include "command.h"
36
Kévin Redonc89bb8c2019-04-17 01:20:23 +020037// TODO put declaration in more global file
38// TODO for now SIM7 is not present because used for debug
39static struct usart_async_descriptor* SIM_peripheral_descriptors[] = {&SIM0, &SIM1, &SIM2, &SIM3, &SIM4, &SIM5, &SIM6, NULL};
40
Kévin Redon096c5052019-05-09 15:01:17 +020041/** number of bytes transmitted on the SIM peripheral */
42static volatile bool SIM_tx_count[8];
43
Kévin Redonc89bb8c2019-04-17 01:20:23 +020044static void SIM_rx_cb(const struct usart_async_descriptor *const io_descr)
45{
46}
Kévin Redon78d2f442019-01-24 18:45:59 +010047
Kévin Redon096c5052019-05-09 15:01:17 +020048/** called when the transmission is complete
49 * e.g. this is when the byte has been sent and there is no data to transmit anymore
50 */
51static void SIM_tx_cb(const struct usart_async_descriptor *const io_descr)
52{
53 // find slotnr for corresponding USART
54 uint8_t slotnr;
55 for (slotnr = 0; slotnr < ARRAY_SIZE(SIM_peripheral_descriptors) && SIM_peripheral_descriptors[slotnr] != io_descr; slotnr++);
56
57 // set flag
58 if (slotnr < ARRAY_SIZE(SIM_peripheral_descriptors)) {
59 SIM_tx_count[slotnr] = true;
60 }
61}
62
Kévin Redon072951b2019-05-02 15:17:46 +020063/** possible clock sources for the SERCOM peripheral
64 * warning: the definition must match the GCLK configuration
65 */
66static const uint8_t sercom_glck_sources[] = {GCLK_PCHCTRL_GEN_GCLK2_Val, GCLK_PCHCTRL_GEN_GCLK4_Val, GCLK_PCHCTRL_GEN_GCLK6_Val};
67
68/** possible clock frequencies in MHz for the SERCOM peripheral
69 * warning: the definition must match the GCLK configuration
70 */
71static const double sercom_glck_freqs[] = {100E6 / CONF_GCLK_GEN_2_DIV, 100E6 / CONF_GCLK_GEN_4_DIV, 120E6 / CONF_GCLK_GEN_6_DIV};
72
73/** the GCLK ID for the SERCOM SIM peripherals
74 * @note: used as index for PCHCTRL
75 */
76static const uint8_t SIM_peripheral_GCLK_ID[] = {SERCOM0_GCLK_ID_CORE, SERCOM1_GCLK_ID_CORE, SERCOM2_GCLK_ID_CORE, SERCOM3_GCLK_ID_CORE, SERCOM4_GCLK_ID_CORE, SERCOM5_GCLK_ID_CORE, SERCOM6_GCLK_ID_CORE, SERCOM7_GCLK_ID_CORE};
77
Eric Wildbd0d4b42019-10-17 19:52:25 +020078static void _SIM_error_cb(const struct usart_async_descriptor *const descr){
79 while(1){ asm("nop");}
80}
81
Harald Weltec3f170d2019-02-24 09:06:59 +010082static void board_init()
83{
84 int i;
85
86 for (i = 0; i < 4; i++)
87 i2c_init(&i2c[i]);
88
Harald Welte255da5e2019-04-16 18:19:53 +020089 for (i = 0; i < 8; i++)
Harald Weltec3f170d2019-02-24 09:06:59 +010090 ncn8025_init(i);
Harald Weltef53f2262019-02-24 11:01:08 +010091
92 cache_init();
93 cache_enable(CMCC);
Harald Welte93f628a2019-02-24 14:32:30 +010094
95 /* increase drive strength of 20Mhz SIM clock output to 8mA
96 * (there are 8 inputs + traces to drive!) */
97 hri_port_set_PINCFG_DRVSTR_bit(PORT, 0, 11);
Kévin Redonc89bb8c2019-04-17 01:20:23 +020098
99 // enable SIM interfaces
100 for (uint8_t i = 0; i < ARRAY_SIZE(SIM_peripheral_descriptors); i++) {
101 if (NULL == SIM_peripheral_descriptors[i]) {
102 continue;
103 }
104 usart_async_register_callback(SIM_peripheral_descriptors[i], USART_ASYNC_RXC_CB, SIM_rx_cb); // required for RX to work, even if the callback does nothing
Kévin Redon096c5052019-05-09 15:01:17 +0200105 usart_async_register_callback(SIM_peripheral_descriptors[i], USART_ASYNC_TXC_CB, SIM_tx_cb); // to count the number of bytes transmitted since we are using it asynchronously
Eric Wildbd0d4b42019-10-17 19:52:25 +0200106 usart_async_register_callback(SIM_peripheral_descriptors[i], USART_ASYNC_ERROR_CB, _SIM_error_cb);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200107 usart_async_enable(SIM_peripheral_descriptors[i]);
108 }
Harald Weltec3f170d2019-02-24 09:06:59 +0100109}
110
Harald Welte1b9a5b82019-02-24 23:04:45 +0100111static int validate_slotnr(int argc, char **argv, int idx)
112{
113 int slotnr;
114 if (argc < idx+1) {
115 printf("You have to specify the slot number (0..7)\r\n");
116 return -1;
117 }
118 slotnr = atoi(argv[idx]);
119 if (slotnr < 0 || slotnr > 7) {
120 printf("You have to specify the slot number (0..7)\r\n");
121 return -1;
122 }
123 return slotnr;
124}
125
Kévin Redon072951b2019-05-02 15:17:46 +0200126/** change baud rate of card slot
127 * @param[in] slotnr slot number for which the baud rate should be set
128 * @param[in] baudrate baud rate in bps to set
129 * @return if the baud rate has been set, else a parameter is out of range
130 */
131static bool slot_set_baudrate(uint8_t slotnr, uint32_t baudrate)
132{
133 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
134
135 // calculate the error corresponding to the clock sources
136 uint16_t bauds[ARRAY_SIZE(sercom_glck_freqs)];
137 double errors[ARRAY_SIZE(sercom_glck_freqs)];
138 for (uint8_t i = 0; i < ARRAY_SIZE(sercom_glck_freqs); i++) {
139 double freq = sercom_glck_freqs[i]; // remember possible SERCOM frequency
140 uint32_t min = freq / (2 * (255 + 1)); // calculate the minimum baud rate for this frequency
141 uint32_t max = freq / (2 * (0 + 1)); // calculate the maximum baud rate for this frequency
142 if (baudrate < min || baudrate > max) { // baud rate it out of supported range
143 errors[i] = NAN;
144 } else {
145 uint16_t baud = round(freq / (2 * baudrate) - 1);
146 bauds[i] = baud;
147 double actual = freq / (2 * (baud + 1));
148 errors[i] = fabs(1.0 - (actual / baudrate));
149 }
150 }
151
152 // find the smallest error
153 uint8_t best = ARRAY_SIZE(sercom_glck_freqs);
154 for (uint8_t i = 0; i < ARRAY_SIZE(sercom_glck_freqs); i++) {
155 if (isnan(errors[i])) {
156 continue;
157 }
158 if (best >= ARRAY_SIZE(sercom_glck_freqs)) {
159 best = i;
160 } else if (errors[i] < errors[best]) {
161 best = i;
162 }
163 }
164 if (best >= ARRAY_SIZE(sercom_glck_freqs)) { // found no clock supporting this baud rate
165 return false;
166 }
167
168 // set clock and baud rate
169 struct usart_async_descriptor* slot = SIM_peripheral_descriptors[slotnr]; // get slot
170 if (NULL == slot) {
171 return false;
172 }
173 printf("(%u) switching SERCOM clock to GCLK%u (freq = %lu kHz) and baud rate to %lu bps (baud = %u)\r\n", slotnr, (best + 1) * 2, (uint32_t)(round(sercom_glck_freqs[best] / 1000)), baudrate, bauds[best]);
174 while (!usart_async_is_tx_empty(slot)); // wait for transmission to complete (WARNING no timeout)
175 usart_async_disable(slot); // disable SERCOM peripheral
176 hri_gclk_clear_PCHCTRL_reg(GCLK, SIM_peripheral_GCLK_ID[slotnr], (1 << GCLK_PCHCTRL_CHEN_Pos)); // disable clock for this peripheral
177 while (hri_gclk_get_PCHCTRL_reg(GCLK, SIM_peripheral_GCLK_ID[slotnr], (1 << GCLK_PCHCTRL_CHEN_Pos))); // wait until clock is really disabled
178 // it does not seem we need to completely disable the peripheral using hri_mclk_clear_APBDMASK_SERCOMn_bit
179 hri_gclk_write_PCHCTRL_reg(GCLK, SIM_peripheral_GCLK_ID[slotnr], sercom_glck_sources[best] | (1 << GCLK_PCHCTRL_CHEN_Pos)); // set peripheral core clock and re-enable it
180 usart_async_set_baud_rate(slot, bauds[best]); // set the new baud rate
181 usart_async_enable(slot); // re-enable SERCOM peripheral
182
183 return true;
184}
185
Kévin Redon0f050722019-05-02 15:56:25 +0200186/** change ISO baud rate of card slot
187 * @param[in] slotnr slot number for which the baud rate should be set
188 * @param[in] clkdiv can clock divider
189 * @param[in] f clock rate conversion integer F
190 * @param[in] d baud rate adjustment factor D
191 * @return if the baud rate has been set, else a parameter is out of range
192 */
193static bool slot_set_isorate(uint8_t slotnr, enum ncn8025_sim_clkdiv clkdiv, uint16_t f, uint8_t d)
194{
195 // input checks
196 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
197 if (clkdiv != SIM_CLKDIV_1 && clkdiv != SIM_CLKDIV_2 && clkdiv != SIM_CLKDIV_4 && clkdiv != SIM_CLKDIV_8) {
198 return false;
199 }
200 if (!iso7816_3_valid_f(f)) {
201 return false;
202 }
203 if (!iso7816_3_valid_d(d)) {
204 return false;
205 }
206
207 // set clockdiv
208 struct ncn8025_settings settings;
209 ncn8025_get(slotnr, &settings);
210 if (settings.clkdiv != clkdiv) {
211 settings.clkdiv = clkdiv;
212 ncn8025_set(slotnr, &settings);
213 }
214
215 // calculate desired frequency
216 uint32_t freq = 20000000UL; // maximum frequency
217 switch (clkdiv) {
218 case SIM_CLKDIV_1:
219 freq /= 1;
220 break;
221 case SIM_CLKDIV_2:
222 freq /= 2;
223 break;
224 case SIM_CLKDIV_4:
225 freq /= 4;
226 break;
227 case SIM_CLKDIV_8:
228 freq /= 8;
229 break;
230 }
231
232 // set baud rate
233 uint32_t baudrate = (freq * d) / f; // calculate actual baud rate
Kévin Redon5188e9f2019-05-09 17:34:55 +0200234 return slot_set_baudrate(slotnr, baudrate); // set baud rate
235}
236
237/** write data to card
238 * @param[in] slotnr slot number on which to send data
239 * @param[in] data data to be transmitted
240 * @param[in] length length of data to be transmitted
241 * @return error code
242 */
243static int slot_card_write(uint8_t slotnr, const uint8_t* data, uint16_t length)
244{
245 // input checks
246 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
247 if (0 == length || NULL == data) {
248 return ERR_INVALID_ARG;
249 }
250
251 struct usart_async_descriptor* sim = SIM_peripheral_descriptors[slotnr];
252 ((Sercom *)sim->device.hw)->USART.CTRLB.bit.RXEN = 0; // disable receive (to avoid the echo back)
253 SIM_tx_count[slotnr] = false; // reset TX complete
254 for (uint16_t i = 0; i < length; i++) { // transmit data
255 while(!usart_async_is_tx_empty(sim)); // wait for previous byte to be transmitted (WARNING blocking)
256 if (1 != io_write(&sim->io, &data[i], 1)) { // put but in transmit buffer
257 return ERR_IO;
258 }
259 }
260 while (!SIM_tx_count[slotnr]); // wait until transmission is complete (WARNING blocking)
261 ((Sercom *)sim->device.hw)->USART.CTRLB.bit.RXEN = 1; // enable receive again
262
263 return ERR_NONE;
264}
265
266/** read data from card
267 * @param[in] slotnr slot number on which to send data
268 * @param[out] data buffer for read data to be stored
269 * @param[in] length length of data to be read
270 * @param[in] wt Waiting Time in ETU
271 * @return error code
272 * TODO fix WT/ETU duration
273 */
274static int slot_card_read(uint8_t slotnr, uint8_t* data, uint16_t length, uint32_t wt)
275{
276 // input checks
277 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
278 if (0 == length || NULL == data) {
279 return ERR_INVALID_ARG;
280 }
281
282 struct usart_async_descriptor* sim = SIM_peripheral_descriptors[slotnr];
283
284 ((Sercom *)sim->device.hw)->USART.CTRLB.bit.RXEN = 1; // ensure RX is enabled
285 uint32_t timeout = wt; // reset waiting time
286 for (uint16_t i = 0; i < length; i++) { // read all data
287 while (timeout && !usart_async_is_rx_not_empty(sim)) { // verify if data is present
288 delay_us(149); // wait for 1 ETU (372 / 1 / 2.5 MHz = 148.8 us)
289 timeout--;
290 }
291 if (0 == timeout) { // timeout reached
292 return ERR_TIMEOUT;
293 }
294 timeout = wt; // reset waiting time
295 if (1 != io_read(&sim->io, &data[i], 1)) { // read one byte
296 return ERR_IO;
297 }
298 }
299
300 return ERR_NONE;
301}
302
303/** transfer TPDU
304 * @param[in] slotnr slot number on which to transfer the TPDU
305 * @param[in] header TPDU header to send
306 * @param[io] data TPDU data to transfer
307 * @param[in] data_length length of TPDU data to transfer
308 * @param[in] write if the data should be written (true) or read (false)
309 * TODO fix WT
310 * TODO the data length can be deduce from the header
311 */
312static int slot_tpdu_xfer(uint8_t slotnr, const uint8_t* header, uint8_t* data, uint16_t data_length, bool write)
313{
314 // input checks
315 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
316 if (NULL == header || (data_length > 0 && NULL == data)) {
317 return ERR_INVALID_ARG;
318 }
319
320 int rc;
321 struct usart_async_descriptor* sim = SIM_peripheral_descriptors[slotnr]; // get USART peripheral
322 usart_async_flush_rx_buffer(sim); // flush RX buffer to start from scratch
323
324 // send command header
325 printf("(%d) TPDU: ", slotnr);
326 for (uint8_t i = 0; i < 5; i++) {
327 printf("%02x ", header[i]);
328 }
329 rc = slot_card_write(slotnr, header, 5); // transmit header
330 if (ERR_NONE != rc) {
331 printf("error in command header transmit (errno = %d)\r\n", rc);
332 return rc;
333 }
334
335 // read procedure byte, and handle data
336 uint8_t pb = 0x60; // wait more procedure byte
337 uint16_t data_i = 0; // progress in the data transfer
338 while (0x60 == pb) { // wait for SW
339 rc = slot_card_read(slotnr, &pb, 1, ISO7816_3_DEFAULT_WT);
340 if (ERR_NONE != rc) {
341 printf("error while receiving PB/SW1 (errno = %d)\r\n", rc);
342 return rc;
343 }
344 printf("%02x ", pb);
345 if (0x60 == pb) { // NULL byte
346 // just wait more time
347 } else if ((0x60 == (pb & 0xf0)) || (0x90 == (pb & 0xf0))) { // SW1 byte
348 // left the rest of the code handle it
349 } else if (header[1] == pb) { // ACK byte
350 // transfer rest of the data
351 if (data_i >= data_length) {
352 printf("error no more data to transfer\r\n");
353 return ERR_INVALID_DATA;
354 }
355 if (write) { // transmit remaining command data
356 rc = slot_card_write(slotnr, &data[data_i], data_length - data_i); // transmit command data
357 if (ERR_NONE != rc) {
358 printf("error in command data transmit (errno = %d)\r\n", rc);
359 return rc;
360 }
361 } else { // receive remaining command data
362 rc = slot_card_read(slotnr, &data[data_i], data_length - data_i, ISO7816_3_DEFAULT_WT);
363 if (ERR_NONE != rc) {
364 printf("error in command data receive (errno = %d)\r\n", rc);
365 return rc;
366 }
367 }
368 for (uint16_t i = data_i; i < data_length; i++) {
369 printf("%02x ", data[i]);
370 }
371 data_i = data_length; // remember we transferred the data
372 pb = 0x60; // wait for SW1
373 } else if (header[1] == (pb ^ 0xff)) { // ACK byte
374 // transfer only one byte
375 if (data_i >= data_length) {
376 printf("error no more data to transfer\r\n");
377 return ERR_INVALID_DATA;
378 }
379 if (write) { // transmit command data byte
380 rc = slot_card_write(slotnr, &data[data_i], 1); // transmit command data
381 if (ERR_NONE != rc) {
382 printf("error in command data transmit (errno = %d)\r\n", rc);
383 return rc;
384 }
385 } else { // receive command data byte
386 rc = slot_card_read(slotnr, &data[data_i], 1, ISO7816_3_DEFAULT_WT);
387 if (ERR_NONE != rc) {
388 printf("error in command data receive (errno = %d)\r\n", rc);
389 return rc;
390 }
391 }
392 printf("%02x ", data[data_i]);
393 data_i += 1; // remember we transferred one data byte
394 pb = 0x60; // wait for SW1
395 } else { // invalid byte
396 return ERR_INVALID_DATA;
397 }
398 }
399
400 // read SW2
401 uint8_t sw2;
402 rc = slot_card_read(slotnr, &sw2, 1, ISO7816_3_DEFAULT_WT);
403 if (ERR_NONE != rc) {
404 printf("error in receiving SW2 (errno = %d)\r\n", rc);
405 return rc;
406 }
407 printf("%02x", sw2);
408
409 printf("\r\n");
410 return ERR_NONE;
Kévin Redon0f050722019-05-02 15:56:25 +0200411}
412
Harald Welte1b9a5b82019-02-24 23:04:45 +0100413DEFUN(sim_status, cmd_sim_status, "sim-status", "Get state of specified NCN8025")
414{
415 struct ncn8025_settings settings;
416 int slotnr = validate_slotnr(argc, argv, 1);
417 if (slotnr < 0)
418 return;
419 ncn8025_get(slotnr, &settings);
420 printf("SIM%d: ", slotnr);
421 ncn8025_dump(&settings);
422 printf("\r\n");
423}
424
425DEFUN(sim_power, cmd_sim_power, "sim-power", "Enable/disable SIM card power")
426{
427 struct ncn8025_settings settings;
428 int slotnr = validate_slotnr(argc, argv, 1);
429 int enable;
430
431 if (slotnr < 0)
432 return;
433
434 if (argc < 3) {
435 printf("You have to specify 0=disable or 1=enable\r\n");
436 return;
437 }
438 enable = atoi(argv[2]);
439 ncn8025_get(slotnr, &settings);
440 if (enable)
441 settings.cmdvcc = true;
442 else
443 settings.cmdvcc = false;
444 ncn8025_set(slotnr, &settings);
445}
446
447DEFUN(sim_reset, cmd_sim_reset, "sim-reset", "Enable/disable SIM reset")
448{
449 struct ncn8025_settings settings;
450 int slotnr = validate_slotnr(argc, argv, 1);
451 int enable;
452
453 if (slotnr < 0)
454 return;
455
456 if (argc < 3) {
457 printf("You have to specify 0=disable or 1=enable\r\n");
458 return;
459 }
460 enable = atoi(argv[2]);
461 ncn8025_get(slotnr, &settings);
462 if (enable)
463 settings.rstin = true;
464 else
465 settings.rstin = false;
466 ncn8025_set(slotnr, &settings);
467}
468
469DEFUN(sim_clkdiv, cmd_sim_clkdiv, "sim-clkdiv", "Set SIM clock divider (1,2,4,8)")
470{
471 struct ncn8025_settings settings;
472 int slotnr = validate_slotnr(argc, argv, 1);
473 int clkdiv;
474
475 if (slotnr < 0)
476 return;
477
478 if (argc < 3) {
479 printf("You have to specify a valid divider (1,2,4,8)\r\n");
480 return;
481 }
482 clkdiv = atoi(argv[2]);
483 if (clkdiv != 1 && clkdiv != 2 && clkdiv != 4 && clkdiv != 8) {
484 printf("You have to specify a valid divider (1,2,4,8)\r\n");
485 return;
486 }
487 ncn8025_get(slotnr, &settings);
488 switch (clkdiv) {
489 case 1:
490 settings.clkdiv = SIM_CLKDIV_1;
491 break;
492 case 2:
493 settings.clkdiv = SIM_CLKDIV_2;
494 break;
495 case 4:
496 settings.clkdiv = SIM_CLKDIV_4;
497 break;
498 case 8:
499 settings.clkdiv = SIM_CLKDIV_8;
500 break;
501 }
502 ncn8025_set(slotnr, &settings);
503}
504
505DEFUN(sim_voltage, cmd_sim_voltage, "sim-voltage", "Set SIM voltage (5/3/1.8)")
506{
507 struct ncn8025_settings settings;
508 int slotnr = validate_slotnr(argc, argv, 1);
509
510 if (slotnr < 0)
511 return;
512
513 if (argc < 3) {
514 printf("You have to specify a valid voltage (5/3/1.8)\r\n");
515 return;
516 }
517 ncn8025_get(slotnr, &settings);
518 if (!strcmp(argv[2], "5"))
519 settings.vsel = SIM_VOLT_5V0;
520 else if (!strcmp(argv[2], "3"))
521 settings.vsel = SIM_VOLT_3V0;
522 else if (!strcmp(argv[2], "1.8"))
523 settings.vsel = SIM_VOLT_1V8;
524 else {
525 printf("You have to specify a valid voltage (5/3/1.8)\r\n");
526 return;
527 }
528 ncn8025_set(slotnr, &settings);
529}
530
531DEFUN(sim_led, cmd_sim_led, "sim-led", "Set SIM LED (1=on, 0=off)")
532{
533 struct ncn8025_settings settings;
534 int slotnr = validate_slotnr(argc, argv, 1);
535
536 if (slotnr < 0)
537 return;
538
539 if (argc < 3) {
540 printf("You have to specify 0=disable or 1=enable\r\n");
541 return;
542 }
543 ncn8025_get(slotnr, &settings);
544 if (atoi(argv[2]))
545 settings.led = true;
546 else
547 settings.led = false;
548 ncn8025_set(slotnr, &settings);
549}
550
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200551DEFUN(sim_atr, cmd_sim_atr, "sim-atr", "Read ATR from SIM card")
552{
553 struct ncn8025_settings settings;
554 int slotnr = validate_slotnr(argc, argv, 1);
Harald Welte1b9a5b82019-02-24 23:04:45 +0100555
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200556 if (slotnr < 0 || slotnr >= ARRAY_SIZE(SIM_peripheral_descriptors) || NULL == SIM_peripheral_descriptors[slotnr]) {
557 return;
558 }
559
560 // check if card is present (and read current settings)
561 ncn8025_get(slotnr, &settings);
562 if (!settings.simpres) {
Kévin Redon096c5052019-05-09 15:01:17 +0200563 printf("(%d) error: no card present\r\n", slotnr);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200564 return;
565 }
566
567 // switch card off (assert reset and disable power)
568 // note: ISO/IEC 7816-3:2006 section 6.4 provides the deactivation sequence, but not the minimum corresponding times
569 settings.rstin = true;
570 settings.cmdvcc = false;
Harald Weltedcf57832019-04-17 17:29:41 +0200571 settings.led = true;
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200572 ncn8025_set(slotnr, &settings);
573
574 // TODO wait some time for card to be completely deactivated
575 usart_async_flush_rx_buffer(SIM_peripheral_descriptors[slotnr]); // flush RX buffer to start from scratch
576
Kévin Redon0f050722019-05-02 15:56:25 +0200577
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200578 // set clock to lowest frequency (20 MHz / 8 = 2.5 MHz)
579 // note: according to ISO/IEC 7816-3:2006 section 5.2.3 the minimum value is 1 MHz, and maximum is 5 MHz during activation
580 settings.clkdiv = SIM_CLKDIV_8;
Kévin Redon0f050722019-05-02 15:56:25 +0200581 // set USART baud rate to match the interface (f = 2.5 MHz) and card default settings (Fd = 372, Dd = 1)
582 slot_set_isorate(slotnr, settings.clkdiv, ISO7816_3_DEFAULT_FD, ISO7816_3_DEFAULT_DD);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200583 // set card voltage to 3.0 V (the most supported)
584 // note: according to ISO/IEC 7816-3:2006 no voltage should damage the card, and you should cycle from low to high
585 settings.vsel = SIM_VOLT_3V0;
586 // provide power (the NCN8025 should perform the activation according to spec)
587 // note: activation sequence is documented in ISO/IEC 7816-3:2006 section 6.2
588 settings.cmdvcc = true;
589 ncn8025_set(slotnr, &settings);
590
591 // wait for Tb=400 cycles before re-asserting reset
592 delay_us(400 * 10000 / 2500); // 400 cycles * 1000 for us, 2.5 MHz / 1000 for us
593
594 // de-assert reset to switch card back on
595 settings.rstin = false;
596 ncn8025_set(slotnr, &settings);
597
598 // wait for Tc=40000 cycles for transmission to start
599 uint32_t cycles = 40000;
600 while (cycles && !usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
601 delay_us(10);
602 cycles -= 25; // 10 us = 25 cycles at 2.5 MHz
603 }
604 if (!usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
605 delay_us(12 * 372 / 1 / 2); // wait more than one byte (approximate freq down to 2 MHz)
606 }
607 // verify if one byte has been received
608 if (!usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
Kévin Redon096c5052019-05-09 15:01:17 +0200609 printf("(%d) error: card not responsive\r\n", slotnr);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200610 return;
611 }
612
613 // read ATR (just do it until there is no traffic anymore)
Kévin Redon096c5052019-05-09 15:01:17 +0200614 // TODO the ATR should be parsed to read the right number of bytes, instead we just wait until to end of WT
Harald Welte07725812019-04-17 17:30:28 +0200615 printf("(%d) ATR: ", slotnr);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200616 uint8_t atr_byte;
617 while (usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
618 if (1 == io_read(&SIM_peripheral_descriptors[slotnr]->io, &atr_byte, 1)) {
619 printf("%02x ", atr_byte);
620 }
Kévin Redon096c5052019-05-09 15:01:17 +0200621 uint16_t wt = ISO7816_3_DEFAULT_WT; // waiting time in ETU
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200622 while (wt && !usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
623 delay_us(149); // wait for 1 ETU (372 / 1 / 2.5 MHz = 148.8 us)
624 wt--;
625 }
626 }
627 printf("\r\n");
Harald Weltedcf57832019-04-17 17:29:41 +0200628
Kévin Redon096c5052019-05-09 15:01:17 +0200629 /* disable LED */
630 settings.led = false;
631 ncn8025_set(slotnr, &settings);
632}
633
634DEFUN(sim_iccid, cmd_sim_iccid, "sim-iccid", "Read ICCID from SIM card")
635{
636 struct ncn8025_settings settings;
637 int slotnr = validate_slotnr(argc, argv, 1);
638
639 if (slotnr < 0 || slotnr >= ARRAY_SIZE(SIM_peripheral_descriptors) || NULL == SIM_peripheral_descriptors[slotnr]) {
640 return;
641 }
642
643 // read current settings and check if card is present and powered
644 ncn8025_get(slotnr, &settings);
645 if (!settings.simpres) {
646 printf("(%d) error: no card present\r\n", slotnr);
647 return;
648 }
649 if (!settings.cmdvcc) {
650 printf("(%d) error: card not powered\r\n", slotnr);
651 return;
652 }
653 if (settings.rstin) {
654 printf("(%d) error: card under reset\r\n", slotnr);
655 return;
656 }
657
658 // enable LED
659 if (!settings.led) {
660 settings.led = true;
661 ncn8025_set(slotnr, &settings);
662 }
663
Kévin Redon5188e9f2019-05-09 17:34:55 +0200664 // select MF
665 printf("(%d) SELECT MF\r\n", slotnr);
Kévin Redon36efc6d2019-05-09 18:03:20 +0200666 const uint8_t select_header[] = {0xa0, 0xa4, 0x00, 0x00, 0x02}; // see TS 102.221 sec. 11.1.1
667 const uint8_t select_data_mf[] = {0x3f, 0x00}; // see TS 102.221 sec. 13.1
668 int rc = slot_tpdu_xfer(slotnr, select_header, (uint8_t*)select_data_mf, ARRAY_SIZE(select_data_mf), true); // transfer TPDU
Kévin Redon5188e9f2019-05-09 17:34:55 +0200669 if (ERR_NONE != rc) {
670 printf("error while SELECT MF (errno = %d)\r\n", rc);
Kévin Redon096c5052019-05-09 15:01:17 +0200671 }
Kévin Redon36efc6d2019-05-09 18:03:20 +0200672 // ignore response data
673
674 // select EF_ICCID
675 printf("(%d) SELECT EF_ICCID\r\n", slotnr);
676 const uint8_t select_data_ef_iccid[] = {0x2f, 0xe2}; // see TS 102.221 sec. 13.2
677 rc = slot_tpdu_xfer(slotnr, select_header, (uint8_t*)select_data_ef_iccid, ARRAY_SIZE(select_data_ef_iccid), true); // transfer TPDU
678 if (ERR_NONE != rc) {
679 printf("error while SELECT EF_ICCID (errno = %d)\r\n", rc);
680 }
681 // ignore response data
682
683 // read EF_ICCID
684 printf("(%d) READ EF_ICCID\r\n", slotnr);
685 uint8_t iccid[10];
686 uint8_t read_binary[] = {0xa0, 0xb0, 0x00, 0x00, ARRAY_SIZE(iccid)}; // see TS 102.221 sec. 11.1.3
687 rc = slot_tpdu_xfer(slotnr, read_binary, iccid, ARRAY_SIZE(iccid), false); // transfer TPDU
688 if (ERR_NONE != rc) {
689 printf("error while READ ICCID (errno = %d)\r\n", rc);
690 }
691 // ignore response data
692
693 printf("(%d) ICCID: ", slotnr);
694 for (uint8_t i = 0; i < ARRAY_SIZE(iccid); i++) {
695 uint8_t nibble = iccid[i] & 0xf;
696 if (0xf == nibble) {
697 break;
698 }
699 printf("%x", nibble);
700 nibble = iccid[i] >> 4;
701 if (0xf == nibble) {
702 break;
703 }
704 printf("%x", nibble);
705 }
706 printf("\r\n");
Kévin Redon096c5052019-05-09 15:01:17 +0200707
708 // disable LED
Harald Weltedcf57832019-04-17 17:29:41 +0200709 settings.led = false;
710 ncn8025_set(slotnr, &settings);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200711}
Harald Welte1b9a5b82019-02-24 23:04:45 +0100712
Harald Welte67b2aba2019-04-16 20:47:22 +0200713extern void testmode_init(void);
Harald Welte1b9a5b82019-02-24 23:04:45 +0100714
Harald Welte8049d662019-04-17 21:19:18 +0200715#include "talloc.h"
716void *g_tall_ctx;
717
718DEFUN(_talloc_report, cmd_talloc_report, "talloc-report", "Generate a talloc report")
719{
720 talloc_report_full(g_tall_ctx, stdout);
721}
722
723DEFUN(talloc_test, cmd_talloc_test, "talloc-test", "Test the talloc allocator")
724{
725 for (int i = 0; i < 10; i++)
726 talloc_named_const(g_tall_ctx, 10, "sibling");
727}
728
729DEFUN(v_talloc_free, cmd_talloc_free, "talloc-free", "Release all memory")
730{
731 talloc_free(g_tall_ctx);
732 g_tall_ctx = NULL;
733}
734
Kévin Redon69b92d92019-01-24 16:39:20 +0100735int main(void)
736{
737 atmel_start_init();
Kévin Redon78d2f442019-01-24 18:45:59 +0100738
Kévin Redon8e538002019-01-30 11:19:19 +0100739 usb_start();
740
Harald Weltec3f170d2019-02-24 09:06:59 +0100741 board_init();
Harald Welteff9f4ce2019-02-24 22:51:09 +0100742 command_init("sysmoOCTSIM> ");
Harald Welte1b9a5b82019-02-24 23:04:45 +0100743 command_register(&cmd_sim_status);
744 command_register(&cmd_sim_power);
745 command_register(&cmd_sim_reset);
746 command_register(&cmd_sim_clkdiv);
747 command_register(&cmd_sim_voltage);
748 command_register(&cmd_sim_led);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200749 command_register(&cmd_sim_atr);
Kévin Redon096c5052019-05-09 15:01:17 +0200750 command_register(&cmd_sim_iccid);
Harald Welte67b2aba2019-04-16 20:47:22 +0200751 testmode_init();
Harald Welte8049d662019-04-17 21:19:18 +0200752 command_register(&cmd_talloc_test);
753 command_register(&cmd_talloc_report);
754 command_register(&cmd_talloc_free);
Harald Weltec3f170d2019-02-24 09:06:59 +0100755
Harald Welte361ed202019-02-24 21:15:39 +0100756 printf("\r\n\r\nsysmocom sysmoOCTSIM\r\n");
Harald Weltee7aa5342019-04-16 21:11:14 +0200757
Harald Welte8049d662019-04-17 21:19:18 +0200758 talloc_enable_null_tracking();
759 g_tall_ctx = talloc_named_const(NULL, 0, "global");
760 printf("g_tall_ctx=%p\r\n", g_tall_ctx);
761
Harald Weltee7aa5342019-04-16 21:11:14 +0200762 command_print_prompt();
Kévin Redon8e538002019-01-30 11:19:19 +0100763 while (true) { // main loop
Harald Welteff9f4ce2019-02-24 22:51:09 +0100764 command_try_recv();
Kévin Redon8e538002019-01-30 11:19:19 +0100765 }
Kévin Redon69b92d92019-01-24 16:39:20 +0100766}