blob: 3c189eb7e3ba8ae40e48593bf97eaa89e3684d94 [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>
Harald Welte65101be2019-04-18 18:30:49 +020023#include <errno.h>
Harald Weltef53f2262019-02-24 11:01:08 +010024#include <hal_cache.h>
Harald Welte93f628a2019-02-24 14:32:30 +010025#include <hri_port_e54.h>
Harald Weltef53f2262019-02-24 11:01:08 +010026
Kévin Redon69b92d92019-01-24 16:39:20 +010027#include "atmel_start.h"
28#include "atmel_start_pins.h"
Kévin Redon072951b2019-05-02 15:17:46 +020029#include "config/hpl_gclk_config.h"
Kévin Redon69b92d92019-01-24 16:39:20 +010030
Harald Weltec3f170d2019-02-24 09:06:59 +010031#include "i2c_bitbang.h"
32#include "octsim_i2c.h"
33#include "ncn8025.h"
Kévin Redon0f050722019-05-02 15:56:25 +020034#include "iso7816_3.h"
Harald Weltec3f170d2019-02-24 09:06:59 +010035
Harald Welteff9f4ce2019-02-24 22:51:09 +010036#include "command.h"
37
Kévin Redonc89bb8c2019-04-17 01:20:23 +020038// TODO put declaration in more global file
39// TODO for now SIM7 is not present because used for debug
40static struct usart_async_descriptor* SIM_peripheral_descriptors[] = {&SIM0, &SIM1, &SIM2, &SIM3, &SIM4, &SIM5, &SIM6, NULL};
41
Kévin Redon096c5052019-05-09 15:01:17 +020042/** number of bytes transmitted on the SIM peripheral */
43static volatile bool SIM_tx_count[8];
44
Kévin Redonc89bb8c2019-04-17 01:20:23 +020045static void SIM_rx_cb(const struct usart_async_descriptor *const io_descr)
46{
47}
Kévin Redon78d2f442019-01-24 18:45:59 +010048
Kévin Redon096c5052019-05-09 15:01:17 +020049/** called when the transmission is complete
50 * e.g. this is when the byte has been sent and there is no data to transmit anymore
51 */
52static void SIM_tx_cb(const struct usart_async_descriptor *const io_descr)
53{
54 // find slotnr for corresponding USART
55 uint8_t slotnr;
56 for (slotnr = 0; slotnr < ARRAY_SIZE(SIM_peripheral_descriptors) && SIM_peripheral_descriptors[slotnr] != io_descr; slotnr++);
57
58 // set flag
59 if (slotnr < ARRAY_SIZE(SIM_peripheral_descriptors)) {
60 SIM_tx_count[slotnr] = true;
61 }
62}
63
Kévin Redon072951b2019-05-02 15:17:46 +020064/** possible clock sources for the SERCOM peripheral
65 * warning: the definition must match the GCLK configuration
66 */
67static const uint8_t sercom_glck_sources[] = {GCLK_PCHCTRL_GEN_GCLK2_Val, GCLK_PCHCTRL_GEN_GCLK4_Val, GCLK_PCHCTRL_GEN_GCLK6_Val};
68
69/** possible clock frequencies in MHz for the SERCOM peripheral
70 * warning: the definition must match the GCLK configuration
71 */
72static const double sercom_glck_freqs[] = {100E6 / CONF_GCLK_GEN_2_DIV, 100E6 / CONF_GCLK_GEN_4_DIV, 120E6 / CONF_GCLK_GEN_6_DIV};
73
74/** the GCLK ID for the SERCOM SIM peripherals
75 * @note: used as index for PCHCTRL
76 */
77static 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};
78
Harald Weltec3f170d2019-02-24 09:06:59 +010079static void board_init()
80{
81 int i;
82
83 for (i = 0; i < 4; i++)
84 i2c_init(&i2c[i]);
85
Harald Welte255da5e2019-04-16 18:19:53 +020086 for (i = 0; i < 8; i++)
Harald Weltec3f170d2019-02-24 09:06:59 +010087 ncn8025_init(i);
Harald Weltef53f2262019-02-24 11:01:08 +010088
89 cache_init();
90 cache_enable(CMCC);
Harald Welte93f628a2019-02-24 14:32:30 +010091
92 /* increase drive strength of 20Mhz SIM clock output to 8mA
93 * (there are 8 inputs + traces to drive!) */
94 hri_port_set_PINCFG_DRVSTR_bit(PORT, 0, 11);
Kévin Redonc89bb8c2019-04-17 01:20:23 +020095
96 // enable SIM interfaces
97 for (uint8_t i = 0; i < ARRAY_SIZE(SIM_peripheral_descriptors); i++) {
98 if (NULL == SIM_peripheral_descriptors[i]) {
99 continue;
100 }
101 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 +0200102 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
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200103 usart_async_enable(SIM_peripheral_descriptors[i]);
104 }
Harald Weltec3f170d2019-02-24 09:06:59 +0100105}
106
Harald Welte1b9a5b82019-02-24 23:04:45 +0100107static int validate_slotnr(int argc, char **argv, int idx)
108{
109 int slotnr;
110 if (argc < idx+1) {
111 printf("You have to specify the slot number (0..7)\r\n");
112 return -1;
113 }
114 slotnr = atoi(argv[idx]);
115 if (slotnr < 0 || slotnr > 7) {
116 printf("You have to specify the slot number (0..7)\r\n");
117 return -1;
118 }
119 return slotnr;
120}
121
Kévin Redon072951b2019-05-02 15:17:46 +0200122/** change baud rate of card slot
123 * @param[in] slotnr slot number for which the baud rate should be set
124 * @param[in] baudrate baud rate in bps to set
125 * @return if the baud rate has been set, else a parameter is out of range
126 */
127static bool slot_set_baudrate(uint8_t slotnr, uint32_t baudrate)
128{
129 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
130
131 // calculate the error corresponding to the clock sources
132 uint16_t bauds[ARRAY_SIZE(sercom_glck_freqs)];
133 double errors[ARRAY_SIZE(sercom_glck_freqs)];
134 for (uint8_t i = 0; i < ARRAY_SIZE(sercom_glck_freqs); i++) {
135 double freq = sercom_glck_freqs[i]; // remember possible SERCOM frequency
136 uint32_t min = freq / (2 * (255 + 1)); // calculate the minimum baud rate for this frequency
137 uint32_t max = freq / (2 * (0 + 1)); // calculate the maximum baud rate for this frequency
138 if (baudrate < min || baudrate > max) { // baud rate it out of supported range
139 errors[i] = NAN;
140 } else {
141 uint16_t baud = round(freq / (2 * baudrate) - 1);
142 bauds[i] = baud;
143 double actual = freq / (2 * (baud + 1));
144 errors[i] = fabs(1.0 - (actual / baudrate));
145 }
146 }
147
148 // find the smallest error
149 uint8_t best = ARRAY_SIZE(sercom_glck_freqs);
150 for (uint8_t i = 0; i < ARRAY_SIZE(sercom_glck_freqs); i++) {
151 if (isnan(errors[i])) {
152 continue;
153 }
154 if (best >= ARRAY_SIZE(sercom_glck_freqs)) {
155 best = i;
156 } else if (errors[i] < errors[best]) {
157 best = i;
158 }
159 }
160 if (best >= ARRAY_SIZE(sercom_glck_freqs)) { // found no clock supporting this baud rate
161 return false;
162 }
163
164 // set clock and baud rate
165 struct usart_async_descriptor* slot = SIM_peripheral_descriptors[slotnr]; // get slot
166 if (NULL == slot) {
167 return false;
168 }
169 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]);
170 while (!usart_async_is_tx_empty(slot)); // wait for transmission to complete (WARNING no timeout)
171 usart_async_disable(slot); // disable SERCOM peripheral
172 hri_gclk_clear_PCHCTRL_reg(GCLK, SIM_peripheral_GCLK_ID[slotnr], (1 << GCLK_PCHCTRL_CHEN_Pos)); // disable clock for this peripheral
173 while (hri_gclk_get_PCHCTRL_reg(GCLK, SIM_peripheral_GCLK_ID[slotnr], (1 << GCLK_PCHCTRL_CHEN_Pos))); // wait until clock is really disabled
174 // it does not seem we need to completely disable the peripheral using hri_mclk_clear_APBDMASK_SERCOMn_bit
175 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
176 usart_async_set_baud_rate(slot, bauds[best]); // set the new baud rate
177 usart_async_enable(slot); // re-enable SERCOM peripheral
178
179 return true;
180}
181
Kévin Redon0f050722019-05-02 15:56:25 +0200182/** change ISO baud rate of card slot
183 * @param[in] slotnr slot number for which the baud rate should be set
184 * @param[in] clkdiv can clock divider
185 * @param[in] f clock rate conversion integer F
186 * @param[in] d baud rate adjustment factor D
187 * @return if the baud rate has been set, else a parameter is out of range
188 */
189static bool slot_set_isorate(uint8_t slotnr, enum ncn8025_sim_clkdiv clkdiv, uint16_t f, uint8_t d)
190{
191 // input checks
192 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
193 if (clkdiv != SIM_CLKDIV_1 && clkdiv != SIM_CLKDIV_2 && clkdiv != SIM_CLKDIV_4 && clkdiv != SIM_CLKDIV_8) {
194 return false;
195 }
196 if (!iso7816_3_valid_f(f)) {
197 return false;
198 }
199 if (!iso7816_3_valid_d(d)) {
200 return false;
201 }
202
203 // set clockdiv
204 struct ncn8025_settings settings;
205 ncn8025_get(slotnr, &settings);
206 if (settings.clkdiv != clkdiv) {
207 settings.clkdiv = clkdiv;
208 ncn8025_set(slotnr, &settings);
209 }
210
211 // calculate desired frequency
212 uint32_t freq = 20000000UL; // maximum frequency
213 switch (clkdiv) {
214 case SIM_CLKDIV_1:
215 freq /= 1;
216 break;
217 case SIM_CLKDIV_2:
218 freq /= 2;
219 break;
220 case SIM_CLKDIV_4:
221 freq /= 4;
222 break;
223 case SIM_CLKDIV_8:
224 freq /= 8;
225 break;
226 }
227
228 // set baud rate
229 uint32_t baudrate = (freq * d) / f; // calculate actual baud rate
Kévin Redon5188e9f2019-05-09 17:34:55 +0200230 return slot_set_baudrate(slotnr, baudrate); // set baud rate
231}
232
233/** write data to card
234 * @param[in] slotnr slot number on which to send data
235 * @param[in] data data to be transmitted
236 * @param[in] length length of data to be transmitted
237 * @return error code
238 */
239static int slot_card_write(uint8_t slotnr, const uint8_t* data, uint16_t length)
240{
241 // input checks
242 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
243 if (0 == length || NULL == data) {
244 return ERR_INVALID_ARG;
245 }
246
247 struct usart_async_descriptor* sim = SIM_peripheral_descriptors[slotnr];
248 ((Sercom *)sim->device.hw)->USART.CTRLB.bit.RXEN = 0; // disable receive (to avoid the echo back)
249 SIM_tx_count[slotnr] = false; // reset TX complete
250 for (uint16_t i = 0; i < length; i++) { // transmit data
251 while(!usart_async_is_tx_empty(sim)); // wait for previous byte to be transmitted (WARNING blocking)
252 if (1 != io_write(&sim->io, &data[i], 1)) { // put but in transmit buffer
253 return ERR_IO;
254 }
255 }
256 while (!SIM_tx_count[slotnr]); // wait until transmission is complete (WARNING blocking)
257 ((Sercom *)sim->device.hw)->USART.CTRLB.bit.RXEN = 1; // enable receive again
258
259 return ERR_NONE;
260}
261
262/** read data from card
263 * @param[in] slotnr slot number on which to send data
264 * @param[out] data buffer for read data to be stored
265 * @param[in] length length of data to be read
266 * @param[in] wt Waiting Time in ETU
267 * @return error code
268 * TODO fix WT/ETU duration
269 */
270static int slot_card_read(uint8_t slotnr, uint8_t* data, uint16_t length, uint32_t wt)
271{
272 // input checks
273 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
274 if (0 == length || NULL == data) {
275 return ERR_INVALID_ARG;
276 }
277
278 struct usart_async_descriptor* sim = SIM_peripheral_descriptors[slotnr];
279
280 ((Sercom *)sim->device.hw)->USART.CTRLB.bit.RXEN = 1; // ensure RX is enabled
281 uint32_t timeout = wt; // reset waiting time
282 for (uint16_t i = 0; i < length; i++) { // read all data
283 while (timeout && !usart_async_is_rx_not_empty(sim)) { // verify if data is present
284 delay_us(149); // wait for 1 ETU (372 / 1 / 2.5 MHz = 148.8 us)
285 timeout--;
286 }
287 if (0 == timeout) { // timeout reached
288 return ERR_TIMEOUT;
289 }
290 timeout = wt; // reset waiting time
291 if (1 != io_read(&sim->io, &data[i], 1)) { // read one byte
292 return ERR_IO;
293 }
294 }
295
296 return ERR_NONE;
297}
298
299/** transfer TPDU
300 * @param[in] slotnr slot number on which to transfer the TPDU
301 * @param[in] header TPDU header to send
302 * @param[io] data TPDU data to transfer
303 * @param[in] data_length length of TPDU data to transfer
304 * @param[in] write if the data should be written (true) or read (false)
305 * TODO fix WT
306 * TODO the data length can be deduce from the header
307 */
308static int slot_tpdu_xfer(uint8_t slotnr, const uint8_t* header, uint8_t* data, uint16_t data_length, bool write)
309{
310 // input checks
311 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
312 if (NULL == header || (data_length > 0 && NULL == data)) {
313 return ERR_INVALID_ARG;
314 }
315
316 int rc;
317 struct usart_async_descriptor* sim = SIM_peripheral_descriptors[slotnr]; // get USART peripheral
318 usart_async_flush_rx_buffer(sim); // flush RX buffer to start from scratch
319
320 // send command header
321 printf("(%d) TPDU: ", slotnr);
322 for (uint8_t i = 0; i < 5; i++) {
323 printf("%02x ", header[i]);
324 }
325 rc = slot_card_write(slotnr, header, 5); // transmit header
326 if (ERR_NONE != rc) {
327 printf("error in command header transmit (errno = %d)\r\n", rc);
328 return rc;
329 }
330
331 // read procedure byte, and handle data
332 uint8_t pb = 0x60; // wait more procedure byte
333 uint16_t data_i = 0; // progress in the data transfer
334 while (0x60 == pb) { // wait for SW
335 rc = slot_card_read(slotnr, &pb, 1, ISO7816_3_DEFAULT_WT);
336 if (ERR_NONE != rc) {
337 printf("error while receiving PB/SW1 (errno = %d)\r\n", rc);
338 return rc;
339 }
340 printf("%02x ", pb);
341 if (0x60 == pb) { // NULL byte
342 // just wait more time
343 } else if ((0x60 == (pb & 0xf0)) || (0x90 == (pb & 0xf0))) { // SW1 byte
344 // left the rest of the code handle it
345 } else if (header[1] == pb) { // ACK byte
346 // transfer rest of the data
347 if (data_i >= data_length) {
348 printf("error no more data to transfer\r\n");
349 return ERR_INVALID_DATA;
350 }
351 if (write) { // transmit remaining command data
352 rc = slot_card_write(slotnr, &data[data_i], data_length - data_i); // transmit command data
353 if (ERR_NONE != rc) {
354 printf("error in command data transmit (errno = %d)\r\n", rc);
355 return rc;
356 }
357 } else { // receive remaining command data
358 rc = slot_card_read(slotnr, &data[data_i], data_length - data_i, ISO7816_3_DEFAULT_WT);
359 if (ERR_NONE != rc) {
360 printf("error in command data receive (errno = %d)\r\n", rc);
361 return rc;
362 }
363 }
364 for (uint16_t i = data_i; i < data_length; i++) {
365 printf("%02x ", data[i]);
366 }
367 data_i = data_length; // remember we transferred the data
368 pb = 0x60; // wait for SW1
369 } else if (header[1] == (pb ^ 0xff)) { // ACK byte
370 // transfer only one byte
371 if (data_i >= data_length) {
372 printf("error no more data to transfer\r\n");
373 return ERR_INVALID_DATA;
374 }
375 if (write) { // transmit command data byte
376 rc = slot_card_write(slotnr, &data[data_i], 1); // transmit command data
377 if (ERR_NONE != rc) {
378 printf("error in command data transmit (errno = %d)\r\n", rc);
379 return rc;
380 }
381 } else { // receive command data byte
382 rc = slot_card_read(slotnr, &data[data_i], 1, ISO7816_3_DEFAULT_WT);
383 if (ERR_NONE != rc) {
384 printf("error in command data receive (errno = %d)\r\n", rc);
385 return rc;
386 }
387 }
388 printf("%02x ", data[data_i]);
389 data_i += 1; // remember we transferred one data byte
390 pb = 0x60; // wait for SW1
391 } else { // invalid byte
392 return ERR_INVALID_DATA;
393 }
394 }
395
396 // read SW2
397 uint8_t sw2;
398 rc = slot_card_read(slotnr, &sw2, 1, ISO7816_3_DEFAULT_WT);
399 if (ERR_NONE != rc) {
400 printf("error in receiving SW2 (errno = %d)\r\n", rc);
401 return rc;
402 }
403 printf("%02x", sw2);
404
405 printf("\r\n");
406 return ERR_NONE;
Kévin Redon0f050722019-05-02 15:56:25 +0200407}
408
Harald Welte1b9a5b82019-02-24 23:04:45 +0100409DEFUN(sim_status, cmd_sim_status, "sim-status", "Get state of specified NCN8025")
410{
411 struct ncn8025_settings settings;
412 int slotnr = validate_slotnr(argc, argv, 1);
413 if (slotnr < 0)
414 return;
415 ncn8025_get(slotnr, &settings);
416 printf("SIM%d: ", slotnr);
417 ncn8025_dump(&settings);
418 printf("\r\n");
419}
420
421DEFUN(sim_power, cmd_sim_power, "sim-power", "Enable/disable SIM card power")
422{
423 struct ncn8025_settings settings;
424 int slotnr = validate_slotnr(argc, argv, 1);
425 int enable;
426
427 if (slotnr < 0)
428 return;
429
430 if (argc < 3) {
431 printf("You have to specify 0=disable or 1=enable\r\n");
432 return;
433 }
434 enable = atoi(argv[2]);
435 ncn8025_get(slotnr, &settings);
436 if (enable)
437 settings.cmdvcc = true;
438 else
439 settings.cmdvcc = false;
440 ncn8025_set(slotnr, &settings);
441}
442
443DEFUN(sim_reset, cmd_sim_reset, "sim-reset", "Enable/disable SIM reset")
444{
445 struct ncn8025_settings settings;
446 int slotnr = validate_slotnr(argc, argv, 1);
447 int enable;
448
449 if (slotnr < 0)
450 return;
451
452 if (argc < 3) {
453 printf("You have to specify 0=disable or 1=enable\r\n");
454 return;
455 }
456 enable = atoi(argv[2]);
457 ncn8025_get(slotnr, &settings);
458 if (enable)
459 settings.rstin = true;
460 else
461 settings.rstin = false;
462 ncn8025_set(slotnr, &settings);
463}
464
465DEFUN(sim_clkdiv, cmd_sim_clkdiv, "sim-clkdiv", "Set SIM clock divider (1,2,4,8)")
466{
467 struct ncn8025_settings settings;
468 int slotnr = validate_slotnr(argc, argv, 1);
469 int clkdiv;
470
471 if (slotnr < 0)
472 return;
473
474 if (argc < 3) {
475 printf("You have to specify a valid divider (1,2,4,8)\r\n");
476 return;
477 }
478 clkdiv = atoi(argv[2]);
479 if (clkdiv != 1 && clkdiv != 2 && clkdiv != 4 && clkdiv != 8) {
480 printf("You have to specify a valid divider (1,2,4,8)\r\n");
481 return;
482 }
483 ncn8025_get(slotnr, &settings);
484 switch (clkdiv) {
485 case 1:
486 settings.clkdiv = SIM_CLKDIV_1;
487 break;
488 case 2:
489 settings.clkdiv = SIM_CLKDIV_2;
490 break;
491 case 4:
492 settings.clkdiv = SIM_CLKDIV_4;
493 break;
494 case 8:
495 settings.clkdiv = SIM_CLKDIV_8;
496 break;
497 }
498 ncn8025_set(slotnr, &settings);
499}
500
501DEFUN(sim_voltage, cmd_sim_voltage, "sim-voltage", "Set SIM voltage (5/3/1.8)")
502{
503 struct ncn8025_settings settings;
504 int slotnr = validate_slotnr(argc, argv, 1);
505
506 if (slotnr < 0)
507 return;
508
509 if (argc < 3) {
510 printf("You have to specify a valid voltage (5/3/1.8)\r\n");
511 return;
512 }
513 ncn8025_get(slotnr, &settings);
514 if (!strcmp(argv[2], "5"))
515 settings.vsel = SIM_VOLT_5V0;
516 else if (!strcmp(argv[2], "3"))
517 settings.vsel = SIM_VOLT_3V0;
518 else if (!strcmp(argv[2], "1.8"))
519 settings.vsel = SIM_VOLT_1V8;
520 else {
521 printf("You have to specify a valid voltage (5/3/1.8)\r\n");
522 return;
523 }
524 ncn8025_set(slotnr, &settings);
525}
526
527DEFUN(sim_led, cmd_sim_led, "sim-led", "Set SIM LED (1=on, 0=off)")
528{
529 struct ncn8025_settings settings;
530 int slotnr = validate_slotnr(argc, argv, 1);
531
532 if (slotnr < 0)
533 return;
534
535 if (argc < 3) {
536 printf("You have to specify 0=disable or 1=enable\r\n");
537 return;
538 }
539 ncn8025_get(slotnr, &settings);
540 if (atoi(argv[2]))
541 settings.led = true;
542 else
543 settings.led = false;
544 ncn8025_set(slotnr, &settings);
545}
546
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200547DEFUN(sim_atr, cmd_sim_atr, "sim-atr", "Read ATR from SIM card")
548{
549 struct ncn8025_settings settings;
550 int slotnr = validate_slotnr(argc, argv, 1);
Harald Welte1b9a5b82019-02-24 23:04:45 +0100551
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200552 if (slotnr < 0 || slotnr >= ARRAY_SIZE(SIM_peripheral_descriptors) || NULL == SIM_peripheral_descriptors[slotnr]) {
553 return;
554 }
555
556 // check if card is present (and read current settings)
557 ncn8025_get(slotnr, &settings);
558 if (!settings.simpres) {
Kévin Redon096c5052019-05-09 15:01:17 +0200559 printf("(%d) error: no card present\r\n", slotnr);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200560 return;
561 }
562
563 // switch card off (assert reset and disable power)
564 // note: ISO/IEC 7816-3:2006 section 6.4 provides the deactivation sequence, but not the minimum corresponding times
565 settings.rstin = true;
566 settings.cmdvcc = false;
Harald Weltedcf57832019-04-17 17:29:41 +0200567 settings.led = true;
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200568 ncn8025_set(slotnr, &settings);
569
570 // TODO wait some time for card to be completely deactivated
571 usart_async_flush_rx_buffer(SIM_peripheral_descriptors[slotnr]); // flush RX buffer to start from scratch
572
Kévin Redon0f050722019-05-02 15:56:25 +0200573
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200574 // set clock to lowest frequency (20 MHz / 8 = 2.5 MHz)
575 // 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
576 settings.clkdiv = SIM_CLKDIV_8;
Kévin Redon0f050722019-05-02 15:56:25 +0200577 // set USART baud rate to match the interface (f = 2.5 MHz) and card default settings (Fd = 372, Dd = 1)
578 slot_set_isorate(slotnr, settings.clkdiv, ISO7816_3_DEFAULT_FD, ISO7816_3_DEFAULT_DD);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200579 // set card voltage to 3.0 V (the most supported)
580 // note: according to ISO/IEC 7816-3:2006 no voltage should damage the card, and you should cycle from low to high
581 settings.vsel = SIM_VOLT_3V0;
582 // provide power (the NCN8025 should perform the activation according to spec)
583 // note: activation sequence is documented in ISO/IEC 7816-3:2006 section 6.2
584 settings.cmdvcc = true;
585 ncn8025_set(slotnr, &settings);
586
587 // wait for Tb=400 cycles before re-asserting reset
588 delay_us(400 * 10000 / 2500); // 400 cycles * 1000 for us, 2.5 MHz / 1000 for us
589
590 // de-assert reset to switch card back on
591 settings.rstin = false;
592 ncn8025_set(slotnr, &settings);
593
594 // wait for Tc=40000 cycles for transmission to start
595 uint32_t cycles = 40000;
596 while (cycles && !usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
597 delay_us(10);
598 cycles -= 25; // 10 us = 25 cycles at 2.5 MHz
599 }
600 if (!usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
601 delay_us(12 * 372 / 1 / 2); // wait more than one byte (approximate freq down to 2 MHz)
602 }
603 // verify if one byte has been received
604 if (!usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
Kévin Redon096c5052019-05-09 15:01:17 +0200605 printf("(%d) error: card not responsive\r\n", slotnr);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200606 return;
607 }
608
609 // read ATR (just do it until there is no traffic anymore)
Kévin Redon096c5052019-05-09 15:01:17 +0200610 // 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 +0200611 printf("(%d) ATR: ", slotnr);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200612 uint8_t atr_byte;
613 while (usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
614 if (1 == io_read(&SIM_peripheral_descriptors[slotnr]->io, &atr_byte, 1)) {
615 printf("%02x ", atr_byte);
616 }
Kévin Redon096c5052019-05-09 15:01:17 +0200617 uint16_t wt = ISO7816_3_DEFAULT_WT; // waiting time in ETU
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200618 while (wt && !usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
619 delay_us(149); // wait for 1 ETU (372 / 1 / 2.5 MHz = 148.8 us)
620 wt--;
621 }
622 }
623 printf("\r\n");
Harald Weltedcf57832019-04-17 17:29:41 +0200624
Kévin Redon096c5052019-05-09 15:01:17 +0200625 /* disable LED */
626 settings.led = false;
627 ncn8025_set(slotnr, &settings);
628}
629
630DEFUN(sim_iccid, cmd_sim_iccid, "sim-iccid", "Read ICCID from SIM card")
631{
632 struct ncn8025_settings settings;
633 int slotnr = validate_slotnr(argc, argv, 1);
634
635 if (slotnr < 0 || slotnr >= ARRAY_SIZE(SIM_peripheral_descriptors) || NULL == SIM_peripheral_descriptors[slotnr]) {
636 return;
637 }
638
639 // read current settings and check if card is present and powered
640 ncn8025_get(slotnr, &settings);
641 if (!settings.simpres) {
642 printf("(%d) error: no card present\r\n", slotnr);
643 return;
644 }
645 if (!settings.cmdvcc) {
646 printf("(%d) error: card not powered\r\n", slotnr);
647 return;
648 }
649 if (settings.rstin) {
650 printf("(%d) error: card under reset\r\n", slotnr);
651 return;
652 }
653
654 // enable LED
655 if (!settings.led) {
656 settings.led = true;
657 ncn8025_set(slotnr, &settings);
658 }
659
Kévin Redon5188e9f2019-05-09 17:34:55 +0200660 // select MF
661 printf("(%d) SELECT MF\r\n", slotnr);
Kévin Redon36efc6d2019-05-09 18:03:20 +0200662 const uint8_t select_header[] = {0xa0, 0xa4, 0x00, 0x00, 0x02}; // see TS 102.221 sec. 11.1.1
663 const uint8_t select_data_mf[] = {0x3f, 0x00}; // see TS 102.221 sec. 13.1
664 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 +0200665 if (ERR_NONE != rc) {
666 printf("error while SELECT MF (errno = %d)\r\n", rc);
Kévin Redon096c5052019-05-09 15:01:17 +0200667 }
Kévin Redon36efc6d2019-05-09 18:03:20 +0200668 // ignore response data
669
670 // select EF_ICCID
671 printf("(%d) SELECT EF_ICCID\r\n", slotnr);
672 const uint8_t select_data_ef_iccid[] = {0x2f, 0xe2}; // see TS 102.221 sec. 13.2
673 rc = slot_tpdu_xfer(slotnr, select_header, (uint8_t*)select_data_ef_iccid, ARRAY_SIZE(select_data_ef_iccid), true); // transfer TPDU
674 if (ERR_NONE != rc) {
675 printf("error while SELECT EF_ICCID (errno = %d)\r\n", rc);
676 }
677 // ignore response data
678
679 // read EF_ICCID
680 printf("(%d) READ EF_ICCID\r\n", slotnr);
681 uint8_t iccid[10];
682 uint8_t read_binary[] = {0xa0, 0xb0, 0x00, 0x00, ARRAY_SIZE(iccid)}; // see TS 102.221 sec. 11.1.3
683 rc = slot_tpdu_xfer(slotnr, read_binary, iccid, ARRAY_SIZE(iccid), false); // transfer TPDU
684 if (ERR_NONE != rc) {
685 printf("error while READ ICCID (errno = %d)\r\n", rc);
686 }
687 // ignore response data
688
689 printf("(%d) ICCID: ", slotnr);
690 for (uint8_t i = 0; i < ARRAY_SIZE(iccid); i++) {
691 uint8_t nibble = iccid[i] & 0xf;
692 if (0xf == nibble) {
693 break;
694 }
695 printf("%x", nibble);
696 nibble = iccid[i] >> 4;
697 if (0xf == nibble) {
698 break;
699 }
700 printf("%x", nibble);
701 }
702 printf("\r\n");
Kévin Redon096c5052019-05-09 15:01:17 +0200703
704 // disable LED
Harald Weltedcf57832019-04-17 17:29:41 +0200705 settings.led = false;
706 ncn8025_set(slotnr, &settings);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200707}
Harald Welte1b9a5b82019-02-24 23:04:45 +0100708
Harald Welte67b2aba2019-04-16 20:47:22 +0200709extern void testmode_init(void);
Harald Welte1b9a5b82019-02-24 23:04:45 +0100710
Harald Welte8049d662019-04-17 21:19:18 +0200711#include "talloc.h"
Harald Welte3304ca22019-04-17 22:08:57 +0200712#include <osmocom/core/msgb.h>
Harald Welte8049d662019-04-17 21:19:18 +0200713void *g_tall_ctx;
714
715DEFUN(_talloc_report, cmd_talloc_report, "talloc-report", "Generate a talloc report")
716{
717 talloc_report_full(g_tall_ctx, stdout);
718}
719
720DEFUN(talloc_test, cmd_talloc_test, "talloc-test", "Test the talloc allocator")
721{
722 for (int i = 0; i < 10; i++)
723 talloc_named_const(g_tall_ctx, 10, "sibling");
Harald Welte3304ca22019-04-17 22:08:57 +0200724 msgb_alloc_c(g_tall_ctx, 1024, "foo");
Harald Welte8049d662019-04-17 21:19:18 +0200725}
726
727DEFUN(v_talloc_free, cmd_talloc_free, "talloc-free", "Release all memory")
728{
729 talloc_free(g_tall_ctx);
730 g_tall_ctx = NULL;
731}
732
Harald Welte3304ca22019-04-17 22:08:57 +0200733/* dependency of libosmocore. FIXME: Implement it bsed on jiffies and/or RTC! */
734#include <sys/time.h>
735int _gettimeofday(struct timeval *tv, void *tz)
736{
737 tv->tv_sec = 0;
738 tv->tv_usec = 0;
739 return 0;
740}
741
Harald Welte65101be2019-04-18 18:30:49 +0200742/* Section 9.6 of SAMD5x/E5x Family Data Sheet */
743static int get_chip_unique_serial(uint8_t *out, size_t len)
744{
745 uint32_t *out32 = (uint32_t *)out;
746 if (len < 16)
747 return -EINVAL;
748
749 out32[0] = *(uint32_t *)0x008061fc;
750 out32[1] = *(uint32_t *)0x00806010;
751 out32[2] = *(uint32_t *)0x00806014;
752 out32[3] = *(uint32_t *)0x00806018;
753
754 return 0;
755}
756
757/* same as get_chip_unique_serial but in hex-string format */
758static int get_chip_unique_serial_str(char *out, size_t len)
759{
760 uint8_t buf[16];
761 int rc;
762
763 if (len < 16*2 + 1)
764 return -EINVAL;
765
766 rc = get_chip_unique_serial(buf, sizeof(buf));
767 if (rc < 0)
768 return rc;
769 osmo_hexdump_buf(out, len, buf, sizeof(buf), NULL, false);
770 return 0;
771}
772
Kévin Redon69b92d92019-01-24 16:39:20 +0100773int main(void)
774{
Harald Welte65101be2019-04-18 18:30:49 +0200775 char sernr_buf[16*2+1];
776
Kévin Redon69b92d92019-01-24 16:39:20 +0100777 atmel_start_init();
Harald Welte65101be2019-04-18 18:30:49 +0200778 get_chip_unique_serial_str(sernr_buf, sizeof(sernr_buf));
Kévin Redon78d2f442019-01-24 18:45:59 +0100779
Kévin Redon8e538002019-01-30 11:19:19 +0100780 usb_start();
781
Harald Weltec3f170d2019-02-24 09:06:59 +0100782 board_init();
Harald Welteff9f4ce2019-02-24 22:51:09 +0100783 command_init("sysmoOCTSIM> ");
Harald Welte1b9a5b82019-02-24 23:04:45 +0100784 command_register(&cmd_sim_status);
785 command_register(&cmd_sim_power);
786 command_register(&cmd_sim_reset);
787 command_register(&cmd_sim_clkdiv);
788 command_register(&cmd_sim_voltage);
789 command_register(&cmd_sim_led);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200790 command_register(&cmd_sim_atr);
Kévin Redon096c5052019-05-09 15:01:17 +0200791 command_register(&cmd_sim_iccid);
Harald Welte67b2aba2019-04-16 20:47:22 +0200792 testmode_init();
Harald Welte8049d662019-04-17 21:19:18 +0200793 command_register(&cmd_talloc_test);
794 command_register(&cmd_talloc_report);
795 command_register(&cmd_talloc_free);
Harald Weltec3f170d2019-02-24 09:06:59 +0100796
Harald Welte361ed202019-02-24 21:15:39 +0100797 printf("\r\n\r\nsysmocom sysmoOCTSIM\r\n");
Harald Welte65101be2019-04-18 18:30:49 +0200798 printf("Chip-Id %s\r\n", sernr_buf);
Harald Weltee7aa5342019-04-16 21:11:14 +0200799
Harald Welte8049d662019-04-17 21:19:18 +0200800 talloc_enable_null_tracking();
801 g_tall_ctx = talloc_named_const(NULL, 0, "global");
802 printf("g_tall_ctx=%p\r\n", g_tall_ctx);
803
Harald Weltee7aa5342019-04-16 21:11:14 +0200804 command_print_prompt();
Kévin Redon8e538002019-01-30 11:19:19 +0100805 while (true) { // main loop
Harald Welteff9f4ce2019-02-24 22:51:09 +0100806 command_try_recv();
Kévin Redon8e538002019-01-30 11:19:19 +0100807 }
Kévin Redon69b92d92019-01-24 16:39:20 +0100808}