blob: c94a1ed8f4610bd95e1e36079e14cf694ba4555d [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 Welteaf555052019-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 Welte9dbacf12019-04-18 17:59:19 +0200105
106 ccid_app_init();
Harald Weltec3f170d2019-02-24 09:06:59 +0100107}
108
Harald Welte9dbacf12019-04-18 17:59:19 +0200109/***********************************************************************
110 * CCID Driver integration
111 ***********************************************************************/
112
113#include <osmocom/core/linuxlist.h>
114#include <osmocom/core/msgb.h>
115#include "linuxlist_atomic.h"
116#include "ccid_df.h"
117
118struct usb_ep_q {
119 const char *name;
120 /* msgb queue of pending to-be-transmitted (IN/IRQ) or completed received (OUT)
121 * USB transfers */
122 struct llist_head list;
123 /* currently ongoing/processed msgb (USB transmit or receive */
124 struct msgb *in_progress;
125};
126
127struct ccid_state {
128 /* msgb queue of free msgs */
129 struct llist_head free_q;
130
131 /* msgb queue of pending to-be-transmitted (IN EP) */
132 struct usb_ep_q in_ep;
133 /* msgb queue of pending to-be-transmitted (IRQ EP) */
134 struct usb_ep_q irq_ep;
135 /* msgb queue of completed received (OUT EP) */
136 struct usb_ep_q out_ep;
137};
138static struct ccid_state g_ccid_s;
139
140static void ccid_out_read_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred);
141static void ccid_in_write_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred);
142static void ccid_irq_write_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred);
143
144static void usb_ep_q_init(struct usb_ep_q *ep_q, const char *name)
145{
146 ep_q->name = name;
147 INIT_LLIST_HEAD(&ep_q->list);
148 ep_q->in_progress = NULL;
149}
150
151void ccid_app_init(void)
152{
153 /* initialize data structures */
154 INIT_LLIST_HEAD(&g_ccid_s.free_q);
155 usb_ep_q_init(&g_ccid_s.in_ep, "IN");
156 usb_ep_q_init(&g_ccid_s.irq_ep, "IRQ");
157 usb_ep_q_init(&g_ccid_s.out_ep, "OUT");
158
159 /* OUT endpoint read complete callback (irq context) */
160 ccid_df_register_callback(CCID_DF_CB_READ_OUT, &ccid_out_read_compl);
161 /* IN endpoint write complete callback (irq context) */
162 ccid_df_register_callback(CCID_DF_CB_WRITE_IN, &ccid_in_write_compl);
163 /* IRQ endpoint write complete callback (irq context) */
164 ccid_df_register_callback(CCID_DF_CB_WRITE_IRQ, &ccid_irq_write_compl);
165}
166
167/* irqsafe version of msgb_enqueue */
168struct msgb *msgb_dequeue_irqsafe(struct llist_head *q)
169{
170 struct msgb *msg;
171 CRITICAL_SECTION_ENTER()
172 msg = msgb_dequeue(q);
173 CRITICAL_SECTION_LEAVE()
174 return msg;
175}
176
177/* submit the next pending (if any) message for the IN EP */
178static int submit_next_in(void)
179{
180 struct usb_ep_q *ep_q = &g_ccid_s.in_ep;
181 struct msgb *msg;
182 int rc;
183
184 OSMO_ASSERT(!ep_q->in_progress);
185 msg = msgb_dequeue_irqsafe(&ep_q->list);
186 if (!msg)
187 return 0;
188
189 ep_q->in_progress = msg;
190 rc = ccid_df_write_in(msgb_data(msg), msgb_length(msg));
191 if (rc != ERR_NONE) {
192 printf("EP %s failed: %d\r\n", ep_q->name, rc);
193 return -1;
194 }
195 return 1;
196
197}
198
199/* submit the next pending (if any) message for the IRQ EP */
200static int submit_next_irq(void)
201{
202 struct usb_ep_q *ep_q = &g_ccid_s.irq_ep;
203 struct msgb *msg;
204 int rc;
205
206 OSMO_ASSERT(!ep_q->in_progress);
207 msg = msgb_dequeue_irqsafe(&ep_q->list);
208 if (!msg)
209 return 0;
210
211 ep_q->in_progress = msg;
212 rc = ccid_df_write_irq(msgb_data(msg), msgb_length(msg));
213 /* may return HALTED/ERROR/DISABLED/BUSY/ERR_PARAM/ERR_FUNC/ERR_DENIED */
214 if (rc != ERR_NONE) {
215 printf("EP %s failed: %d\r\n", ep_q->name, rc);
216 return -1;
217 }
218 return 1;
219}
220
221static int submit_next_out(void)
222{
223 struct usb_ep_q *ep_q = &g_ccid_s.out_ep;
224 struct msgb *msg;
225 int rc;
226
227 OSMO_ASSERT(!ep_q->in_progress);
228 msg = msgb_dequeue_irqsafe(&g_ccid_s.free_q);
229 if (!msg)
230 return -1;
231 ep_q->in_progress = msg;
232
233 rc = ccid_df_read_out(msgb_data(msg), msgb_tailroom(msg));
234 if (rc != ERR_NONE) {
235 /* re-add to the list of free msgb's */
236 llist_add_tail_at(&g_ccid_s.free_q, &msg->list);
237 return 0;
238 }
239 return 1;
240}
241
242/* OUT endpoint read complete callback (irq context) */
243static void ccid_out_read_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred)
244{
245 struct msgb *msg = g_ccid_s.out_ep.in_progress;
246
247 /* add just-received msg to tail of endpoint queue */
248 OSMO_ASSERT(msg);
249 /* update msgb with the amount of data received */
250 msgb_put(msg, transferred);
251 /* append to list of pending-to-be-handed messages */
252 llist_add_tail_at(&msg->list, &g_ccid_s.out_ep.list);
253
254 /* submit another [free] msgb to receive the next transfer */
255 submit_next_out();
256}
257
258/* IN endpoint write complete callback (irq context) */
259static void ccid_in_write_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred)
260{
261 struct msgb *msg = g_ccid_s.in_ep.in_progress;
262
263 OSMO_ASSERT(msg);
264 /* return the message back to the queue of free message buffers */
265 llist_add_tail_at(&msg->list, &g_ccid_s.free_q);
266 g_ccid_s.in_ep.in_progress = NULL;
267
268 /* submit the next pending to-be-transmitted msgb (if any) */
269 submit_next_in();
270}
271
272/* IRQ endpoint write complete callback (irq context) */
273static void ccid_irq_write_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred)
274{
275 struct msgb *msg = g_ccid_s.irq_ep.in_progress;
276
277 OSMO_ASSERT(msg);
278 /* return the message back to the queue of free message buffers */
279 llist_add_tail_at(&msg->list, &g_ccid_s.free_q);
280 g_ccid_s.irq_ep.in_progress = NULL;
281
282 /* submit the next pending to-be-transmitted msgb (if any) */
283 submit_next_irq();
284}
285
286
287/***********************************************************************
288 * Command Line interface
289 ***********************************************************************/
290
Harald Welte1b9a5b82019-02-24 23:04:45 +0100291static int validate_slotnr(int argc, char **argv, int idx)
292{
293 int slotnr;
294 if (argc < idx+1) {
295 printf("You have to specify the slot number (0..7)\r\n");
296 return -1;
297 }
298 slotnr = atoi(argv[idx]);
299 if (slotnr < 0 || slotnr > 7) {
300 printf("You have to specify the slot number (0..7)\r\n");
301 return -1;
302 }
303 return slotnr;
304}
305
Kévin Redon072951b2019-05-02 15:17:46 +0200306/** change baud rate of card slot
307 * @param[in] slotnr slot number for which the baud rate should be set
308 * @param[in] baudrate baud rate in bps to set
309 * @return if the baud rate has been set, else a parameter is out of range
310 */
311static bool slot_set_baudrate(uint8_t slotnr, uint32_t baudrate)
312{
313 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
314
315 // calculate the error corresponding to the clock sources
316 uint16_t bauds[ARRAY_SIZE(sercom_glck_freqs)];
317 double errors[ARRAY_SIZE(sercom_glck_freqs)];
318 for (uint8_t i = 0; i < ARRAY_SIZE(sercom_glck_freqs); i++) {
319 double freq = sercom_glck_freqs[i]; // remember possible SERCOM frequency
320 uint32_t min = freq / (2 * (255 + 1)); // calculate the minimum baud rate for this frequency
321 uint32_t max = freq / (2 * (0 + 1)); // calculate the maximum baud rate for this frequency
322 if (baudrate < min || baudrate > max) { // baud rate it out of supported range
323 errors[i] = NAN;
324 } else {
325 uint16_t baud = round(freq / (2 * baudrate) - 1);
326 bauds[i] = baud;
327 double actual = freq / (2 * (baud + 1));
328 errors[i] = fabs(1.0 - (actual / baudrate));
329 }
330 }
331
332 // find the smallest error
333 uint8_t best = ARRAY_SIZE(sercom_glck_freqs);
334 for (uint8_t i = 0; i < ARRAY_SIZE(sercom_glck_freqs); i++) {
335 if (isnan(errors[i])) {
336 continue;
337 }
338 if (best >= ARRAY_SIZE(sercom_glck_freqs)) {
339 best = i;
340 } else if (errors[i] < errors[best]) {
341 best = i;
342 }
343 }
344 if (best >= ARRAY_SIZE(sercom_glck_freqs)) { // found no clock supporting this baud rate
345 return false;
346 }
347
348 // set clock and baud rate
349 struct usart_async_descriptor* slot = SIM_peripheral_descriptors[slotnr]; // get slot
350 if (NULL == slot) {
351 return false;
352 }
353 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]);
354 while (!usart_async_is_tx_empty(slot)); // wait for transmission to complete (WARNING no timeout)
355 usart_async_disable(slot); // disable SERCOM peripheral
356 hri_gclk_clear_PCHCTRL_reg(GCLK, SIM_peripheral_GCLK_ID[slotnr], (1 << GCLK_PCHCTRL_CHEN_Pos)); // disable clock for this peripheral
357 while (hri_gclk_get_PCHCTRL_reg(GCLK, SIM_peripheral_GCLK_ID[slotnr], (1 << GCLK_PCHCTRL_CHEN_Pos))); // wait until clock is really disabled
358 // it does not seem we need to completely disable the peripheral using hri_mclk_clear_APBDMASK_SERCOMn_bit
359 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
360 usart_async_set_baud_rate(slot, bauds[best]); // set the new baud rate
361 usart_async_enable(slot); // re-enable SERCOM peripheral
362
363 return true;
364}
365
Kévin Redon0f050722019-05-02 15:56:25 +0200366/** change ISO baud rate of card slot
367 * @param[in] slotnr slot number for which the baud rate should be set
368 * @param[in] clkdiv can clock divider
369 * @param[in] f clock rate conversion integer F
370 * @param[in] d baud rate adjustment factor D
371 * @return if the baud rate has been set, else a parameter is out of range
372 */
373static bool slot_set_isorate(uint8_t slotnr, enum ncn8025_sim_clkdiv clkdiv, uint16_t f, uint8_t d)
374{
375 // input checks
376 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
377 if (clkdiv != SIM_CLKDIV_1 && clkdiv != SIM_CLKDIV_2 && clkdiv != SIM_CLKDIV_4 && clkdiv != SIM_CLKDIV_8) {
378 return false;
379 }
380 if (!iso7816_3_valid_f(f)) {
381 return false;
382 }
383 if (!iso7816_3_valid_d(d)) {
384 return false;
385 }
386
387 // set clockdiv
388 struct ncn8025_settings settings;
389 ncn8025_get(slotnr, &settings);
390 if (settings.clkdiv != clkdiv) {
391 settings.clkdiv = clkdiv;
392 ncn8025_set(slotnr, &settings);
393 }
394
395 // calculate desired frequency
396 uint32_t freq = 20000000UL; // maximum frequency
397 switch (clkdiv) {
398 case SIM_CLKDIV_1:
399 freq /= 1;
400 break;
401 case SIM_CLKDIV_2:
402 freq /= 2;
403 break;
404 case SIM_CLKDIV_4:
405 freq /= 4;
406 break;
407 case SIM_CLKDIV_8:
408 freq /= 8;
409 break;
410 }
411
412 // set baud rate
413 uint32_t baudrate = (freq * d) / f; // calculate actual baud rate
Kévin Redon5188e9f2019-05-09 17:34:55 +0200414 return slot_set_baudrate(slotnr, baudrate); // set baud rate
415}
416
417/** write data to card
418 * @param[in] slotnr slot number on which to send data
419 * @param[in] data data to be transmitted
420 * @param[in] length length of data to be transmitted
421 * @return error code
422 */
423static int slot_card_write(uint8_t slotnr, const uint8_t* data, uint16_t length)
424{
425 // input checks
426 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
427 if (0 == length || NULL == data) {
428 return ERR_INVALID_ARG;
429 }
430
431 struct usart_async_descriptor* sim = SIM_peripheral_descriptors[slotnr];
432 ((Sercom *)sim->device.hw)->USART.CTRLB.bit.RXEN = 0; // disable receive (to avoid the echo back)
433 SIM_tx_count[slotnr] = false; // reset TX complete
434 for (uint16_t i = 0; i < length; i++) { // transmit data
435 while(!usart_async_is_tx_empty(sim)); // wait for previous byte to be transmitted (WARNING blocking)
436 if (1 != io_write(&sim->io, &data[i], 1)) { // put but in transmit buffer
437 return ERR_IO;
438 }
439 }
440 while (!SIM_tx_count[slotnr]); // wait until transmission is complete (WARNING blocking)
441 ((Sercom *)sim->device.hw)->USART.CTRLB.bit.RXEN = 1; // enable receive again
442
443 return ERR_NONE;
444}
445
446/** read data from card
447 * @param[in] slotnr slot number on which to send data
448 * @param[out] data buffer for read data to be stored
449 * @param[in] length length of data to be read
450 * @param[in] wt Waiting Time in ETU
451 * @return error code
452 * TODO fix WT/ETU duration
453 */
454static int slot_card_read(uint8_t slotnr, uint8_t* data, uint16_t length, uint32_t wt)
455{
456 // input checks
457 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
458 if (0 == length || NULL == data) {
459 return ERR_INVALID_ARG;
460 }
461
462 struct usart_async_descriptor* sim = SIM_peripheral_descriptors[slotnr];
463
464 ((Sercom *)sim->device.hw)->USART.CTRLB.bit.RXEN = 1; // ensure RX is enabled
465 uint32_t timeout = wt; // reset waiting time
466 for (uint16_t i = 0; i < length; i++) { // read all data
467 while (timeout && !usart_async_is_rx_not_empty(sim)) { // verify if data is present
468 delay_us(149); // wait for 1 ETU (372 / 1 / 2.5 MHz = 148.8 us)
469 timeout--;
470 }
471 if (0 == timeout) { // timeout reached
472 return ERR_TIMEOUT;
473 }
474 timeout = wt; // reset waiting time
475 if (1 != io_read(&sim->io, &data[i], 1)) { // read one byte
476 return ERR_IO;
477 }
478 }
479
480 return ERR_NONE;
481}
482
483/** transfer TPDU
484 * @param[in] slotnr slot number on which to transfer the TPDU
485 * @param[in] header TPDU header to send
486 * @param[io] data TPDU data to transfer
487 * @param[in] data_length length of TPDU data to transfer
488 * @param[in] write if the data should be written (true) or read (false)
489 * TODO fix WT
490 * TODO the data length can be deduce from the header
491 */
492static int slot_tpdu_xfer(uint8_t slotnr, const uint8_t* header, uint8_t* data, uint16_t data_length, bool write)
493{
494 // input checks
495 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
496 if (NULL == header || (data_length > 0 && NULL == data)) {
497 return ERR_INVALID_ARG;
498 }
499
500 int rc;
501 struct usart_async_descriptor* sim = SIM_peripheral_descriptors[slotnr]; // get USART peripheral
502 usart_async_flush_rx_buffer(sim); // flush RX buffer to start from scratch
503
504 // send command header
505 printf("(%d) TPDU: ", slotnr);
506 for (uint8_t i = 0; i < 5; i++) {
507 printf("%02x ", header[i]);
508 }
509 rc = slot_card_write(slotnr, header, 5); // transmit header
510 if (ERR_NONE != rc) {
511 printf("error in command header transmit (errno = %d)\r\n", rc);
512 return rc;
513 }
514
515 // read procedure byte, and handle data
516 uint8_t pb = 0x60; // wait more procedure byte
517 uint16_t data_i = 0; // progress in the data transfer
518 while (0x60 == pb) { // wait for SW
519 rc = slot_card_read(slotnr, &pb, 1, ISO7816_3_DEFAULT_WT);
520 if (ERR_NONE != rc) {
521 printf("error while receiving PB/SW1 (errno = %d)\r\n", rc);
522 return rc;
523 }
524 printf("%02x ", pb);
525 if (0x60 == pb) { // NULL byte
526 // just wait more time
527 } else if ((0x60 == (pb & 0xf0)) || (0x90 == (pb & 0xf0))) { // SW1 byte
528 // left the rest of the code handle it
529 } else if (header[1] == pb) { // ACK byte
530 // transfer rest of the data
531 if (data_i >= data_length) {
532 printf("error no more data to transfer\r\n");
533 return ERR_INVALID_DATA;
534 }
535 if (write) { // transmit remaining command data
536 rc = slot_card_write(slotnr, &data[data_i], data_length - data_i); // transmit command data
537 if (ERR_NONE != rc) {
538 printf("error in command data transmit (errno = %d)\r\n", rc);
539 return rc;
540 }
541 } else { // receive remaining command data
542 rc = slot_card_read(slotnr, &data[data_i], data_length - data_i, ISO7816_3_DEFAULT_WT);
543 if (ERR_NONE != rc) {
544 printf("error in command data receive (errno = %d)\r\n", rc);
545 return rc;
546 }
547 }
548 for (uint16_t i = data_i; i < data_length; i++) {
549 printf("%02x ", data[i]);
550 }
551 data_i = data_length; // remember we transferred the data
552 pb = 0x60; // wait for SW1
553 } else if (header[1] == (pb ^ 0xff)) { // ACK byte
554 // transfer only one byte
555 if (data_i >= data_length) {
556 printf("error no more data to transfer\r\n");
557 return ERR_INVALID_DATA;
558 }
559 if (write) { // transmit command data byte
560 rc = slot_card_write(slotnr, &data[data_i], 1); // transmit command data
561 if (ERR_NONE != rc) {
562 printf("error in command data transmit (errno = %d)\r\n", rc);
563 return rc;
564 }
565 } else { // receive command data byte
566 rc = slot_card_read(slotnr, &data[data_i], 1, ISO7816_3_DEFAULT_WT);
567 if (ERR_NONE != rc) {
568 printf("error in command data receive (errno = %d)\r\n", rc);
569 return rc;
570 }
571 }
572 printf("%02x ", data[data_i]);
573 data_i += 1; // remember we transferred one data byte
574 pb = 0x60; // wait for SW1
575 } else { // invalid byte
576 return ERR_INVALID_DATA;
577 }
578 }
579
580 // read SW2
581 uint8_t sw2;
582 rc = slot_card_read(slotnr, &sw2, 1, ISO7816_3_DEFAULT_WT);
583 if (ERR_NONE != rc) {
584 printf("error in receiving SW2 (errno = %d)\r\n", rc);
585 return rc;
586 }
587 printf("%02x", sw2);
588
589 printf("\r\n");
590 return ERR_NONE;
Kévin Redon0f050722019-05-02 15:56:25 +0200591}
592
Harald Welte1b9a5b82019-02-24 23:04:45 +0100593DEFUN(sim_status, cmd_sim_status, "sim-status", "Get state of specified NCN8025")
594{
595 struct ncn8025_settings settings;
596 int slotnr = validate_slotnr(argc, argv, 1);
597 if (slotnr < 0)
598 return;
599 ncn8025_get(slotnr, &settings);
600 printf("SIM%d: ", slotnr);
601 ncn8025_dump(&settings);
602 printf("\r\n");
603}
604
605DEFUN(sim_power, cmd_sim_power, "sim-power", "Enable/disable SIM card power")
606{
607 struct ncn8025_settings settings;
608 int slotnr = validate_slotnr(argc, argv, 1);
609 int enable;
610
611 if (slotnr < 0)
612 return;
613
614 if (argc < 3) {
615 printf("You have to specify 0=disable or 1=enable\r\n");
616 return;
617 }
618 enable = atoi(argv[2]);
619 ncn8025_get(slotnr, &settings);
620 if (enable)
621 settings.cmdvcc = true;
622 else
623 settings.cmdvcc = false;
624 ncn8025_set(slotnr, &settings);
625}
626
627DEFUN(sim_reset, cmd_sim_reset, "sim-reset", "Enable/disable SIM reset")
628{
629 struct ncn8025_settings settings;
630 int slotnr = validate_slotnr(argc, argv, 1);
631 int enable;
632
633 if (slotnr < 0)
634 return;
635
636 if (argc < 3) {
637 printf("You have to specify 0=disable or 1=enable\r\n");
638 return;
639 }
640 enable = atoi(argv[2]);
641 ncn8025_get(slotnr, &settings);
642 if (enable)
643 settings.rstin = true;
644 else
645 settings.rstin = false;
646 ncn8025_set(slotnr, &settings);
647}
648
649DEFUN(sim_clkdiv, cmd_sim_clkdiv, "sim-clkdiv", "Set SIM clock divider (1,2,4,8)")
650{
651 struct ncn8025_settings settings;
652 int slotnr = validate_slotnr(argc, argv, 1);
653 int clkdiv;
654
655 if (slotnr < 0)
656 return;
657
658 if (argc < 3) {
659 printf("You have to specify a valid divider (1,2,4,8)\r\n");
660 return;
661 }
662 clkdiv = atoi(argv[2]);
663 if (clkdiv != 1 && clkdiv != 2 && clkdiv != 4 && clkdiv != 8) {
664 printf("You have to specify a valid divider (1,2,4,8)\r\n");
665 return;
666 }
667 ncn8025_get(slotnr, &settings);
668 switch (clkdiv) {
669 case 1:
670 settings.clkdiv = SIM_CLKDIV_1;
671 break;
672 case 2:
673 settings.clkdiv = SIM_CLKDIV_2;
674 break;
675 case 4:
676 settings.clkdiv = SIM_CLKDIV_4;
677 break;
678 case 8:
679 settings.clkdiv = SIM_CLKDIV_8;
680 break;
681 }
682 ncn8025_set(slotnr, &settings);
683}
684
685DEFUN(sim_voltage, cmd_sim_voltage, "sim-voltage", "Set SIM voltage (5/3/1.8)")
686{
687 struct ncn8025_settings settings;
688 int slotnr = validate_slotnr(argc, argv, 1);
689
690 if (slotnr < 0)
691 return;
692
693 if (argc < 3) {
694 printf("You have to specify a valid voltage (5/3/1.8)\r\n");
695 return;
696 }
697 ncn8025_get(slotnr, &settings);
698 if (!strcmp(argv[2], "5"))
699 settings.vsel = SIM_VOLT_5V0;
700 else if (!strcmp(argv[2], "3"))
701 settings.vsel = SIM_VOLT_3V0;
702 else if (!strcmp(argv[2], "1.8"))
703 settings.vsel = SIM_VOLT_1V8;
704 else {
705 printf("You have to specify a valid voltage (5/3/1.8)\r\n");
706 return;
707 }
708 ncn8025_set(slotnr, &settings);
709}
710
711DEFUN(sim_led, cmd_sim_led, "sim-led", "Set SIM LED (1=on, 0=off)")
712{
713 struct ncn8025_settings settings;
714 int slotnr = validate_slotnr(argc, argv, 1);
715
716 if (slotnr < 0)
717 return;
718
719 if (argc < 3) {
720 printf("You have to specify 0=disable or 1=enable\r\n");
721 return;
722 }
723 ncn8025_get(slotnr, &settings);
724 if (atoi(argv[2]))
725 settings.led = true;
726 else
727 settings.led = false;
728 ncn8025_set(slotnr, &settings);
729}
730
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200731DEFUN(sim_atr, cmd_sim_atr, "sim-atr", "Read ATR from SIM card")
732{
733 struct ncn8025_settings settings;
734 int slotnr = validate_slotnr(argc, argv, 1);
Harald Welte1b9a5b82019-02-24 23:04:45 +0100735
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200736 if (slotnr < 0 || slotnr >= ARRAY_SIZE(SIM_peripheral_descriptors) || NULL == SIM_peripheral_descriptors[slotnr]) {
737 return;
738 }
739
740 // check if card is present (and read current settings)
741 ncn8025_get(slotnr, &settings);
742 if (!settings.simpres) {
Kévin Redon096c5052019-05-09 15:01:17 +0200743 printf("(%d) error: no card present\r\n", slotnr);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200744 return;
745 }
746
747 // switch card off (assert reset and disable power)
748 // note: ISO/IEC 7816-3:2006 section 6.4 provides the deactivation sequence, but not the minimum corresponding times
749 settings.rstin = true;
750 settings.cmdvcc = false;
Harald Weltedcf57832019-04-17 17:29:41 +0200751 settings.led = true;
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200752 ncn8025_set(slotnr, &settings);
753
754 // TODO wait some time for card to be completely deactivated
755 usart_async_flush_rx_buffer(SIM_peripheral_descriptors[slotnr]); // flush RX buffer to start from scratch
756
Kévin Redon0f050722019-05-02 15:56:25 +0200757
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200758 // set clock to lowest frequency (20 MHz / 8 = 2.5 MHz)
759 // 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
760 settings.clkdiv = SIM_CLKDIV_8;
Kévin Redon0f050722019-05-02 15:56:25 +0200761 // set USART baud rate to match the interface (f = 2.5 MHz) and card default settings (Fd = 372, Dd = 1)
762 slot_set_isorate(slotnr, settings.clkdiv, ISO7816_3_DEFAULT_FD, ISO7816_3_DEFAULT_DD);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200763 // set card voltage to 3.0 V (the most supported)
764 // note: according to ISO/IEC 7816-3:2006 no voltage should damage the card, and you should cycle from low to high
765 settings.vsel = SIM_VOLT_3V0;
766 // provide power (the NCN8025 should perform the activation according to spec)
767 // note: activation sequence is documented in ISO/IEC 7816-3:2006 section 6.2
768 settings.cmdvcc = true;
769 ncn8025_set(slotnr, &settings);
770
771 // wait for Tb=400 cycles before re-asserting reset
772 delay_us(400 * 10000 / 2500); // 400 cycles * 1000 for us, 2.5 MHz / 1000 for us
773
774 // de-assert reset to switch card back on
775 settings.rstin = false;
776 ncn8025_set(slotnr, &settings);
777
778 // wait for Tc=40000 cycles for transmission to start
779 uint32_t cycles = 40000;
780 while (cycles && !usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
781 delay_us(10);
782 cycles -= 25; // 10 us = 25 cycles at 2.5 MHz
783 }
784 if (!usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
785 delay_us(12 * 372 / 1 / 2); // wait more than one byte (approximate freq down to 2 MHz)
786 }
787 // verify if one byte has been received
788 if (!usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
Kévin Redon096c5052019-05-09 15:01:17 +0200789 printf("(%d) error: card not responsive\r\n", slotnr);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200790 return;
791 }
792
793 // read ATR (just do it until there is no traffic anymore)
Kévin Redon096c5052019-05-09 15:01:17 +0200794 // 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 +0200795 printf("(%d) ATR: ", slotnr);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200796 uint8_t atr_byte;
797 while (usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
798 if (1 == io_read(&SIM_peripheral_descriptors[slotnr]->io, &atr_byte, 1)) {
799 printf("%02x ", atr_byte);
800 }
Kévin Redon096c5052019-05-09 15:01:17 +0200801 uint16_t wt = ISO7816_3_DEFAULT_WT; // waiting time in ETU
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200802 while (wt && !usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
803 delay_us(149); // wait for 1 ETU (372 / 1 / 2.5 MHz = 148.8 us)
804 wt--;
805 }
806 }
807 printf("\r\n");
Harald Weltedcf57832019-04-17 17:29:41 +0200808
Kévin Redon096c5052019-05-09 15:01:17 +0200809 /* disable LED */
810 settings.led = false;
811 ncn8025_set(slotnr, &settings);
812}
813
814DEFUN(sim_iccid, cmd_sim_iccid, "sim-iccid", "Read ICCID from SIM card")
815{
816 struct ncn8025_settings settings;
817 int slotnr = validate_slotnr(argc, argv, 1);
818
819 if (slotnr < 0 || slotnr >= ARRAY_SIZE(SIM_peripheral_descriptors) || NULL == SIM_peripheral_descriptors[slotnr]) {
820 return;
821 }
822
823 // read current settings and check if card is present and powered
824 ncn8025_get(slotnr, &settings);
825 if (!settings.simpres) {
826 printf("(%d) error: no card present\r\n", slotnr);
827 return;
828 }
829 if (!settings.cmdvcc) {
830 printf("(%d) error: card not powered\r\n", slotnr);
831 return;
832 }
833 if (settings.rstin) {
834 printf("(%d) error: card under reset\r\n", slotnr);
835 return;
836 }
837
838 // enable LED
839 if (!settings.led) {
840 settings.led = true;
841 ncn8025_set(slotnr, &settings);
842 }
843
Kévin Redon5188e9f2019-05-09 17:34:55 +0200844 // select MF
845 printf("(%d) SELECT MF\r\n", slotnr);
Kévin Redon36efc6d2019-05-09 18:03:20 +0200846 const uint8_t select_header[] = {0xa0, 0xa4, 0x00, 0x00, 0x02}; // see TS 102.221 sec. 11.1.1
847 const uint8_t select_data_mf[] = {0x3f, 0x00}; // see TS 102.221 sec. 13.1
848 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 +0200849 if (ERR_NONE != rc) {
850 printf("error while SELECT MF (errno = %d)\r\n", rc);
Kévin Redon096c5052019-05-09 15:01:17 +0200851 }
Kévin Redon36efc6d2019-05-09 18:03:20 +0200852 // ignore response data
853
854 // select EF_ICCID
855 printf("(%d) SELECT EF_ICCID\r\n", slotnr);
856 const uint8_t select_data_ef_iccid[] = {0x2f, 0xe2}; // see TS 102.221 sec. 13.2
857 rc = slot_tpdu_xfer(slotnr, select_header, (uint8_t*)select_data_ef_iccid, ARRAY_SIZE(select_data_ef_iccid), true); // transfer TPDU
858 if (ERR_NONE != rc) {
859 printf("error while SELECT EF_ICCID (errno = %d)\r\n", rc);
860 }
861 // ignore response data
862
863 // read EF_ICCID
864 printf("(%d) READ EF_ICCID\r\n", slotnr);
865 uint8_t iccid[10];
866 uint8_t read_binary[] = {0xa0, 0xb0, 0x00, 0x00, ARRAY_SIZE(iccid)}; // see TS 102.221 sec. 11.1.3
867 rc = slot_tpdu_xfer(slotnr, read_binary, iccid, ARRAY_SIZE(iccid), false); // transfer TPDU
868 if (ERR_NONE != rc) {
869 printf("error while READ ICCID (errno = %d)\r\n", rc);
870 }
871 // ignore response data
872
873 printf("(%d) ICCID: ", slotnr);
874 for (uint8_t i = 0; i < ARRAY_SIZE(iccid); i++) {
875 uint8_t nibble = iccid[i] & 0xf;
876 if (0xf == nibble) {
877 break;
878 }
879 printf("%x", nibble);
880 nibble = iccid[i] >> 4;
881 if (0xf == nibble) {
882 break;
883 }
884 printf("%x", nibble);
885 }
886 printf("\r\n");
Kévin Redon096c5052019-05-09 15:01:17 +0200887
888 // disable LED
Harald Weltedcf57832019-04-17 17:29:41 +0200889 settings.led = false;
890 ncn8025_set(slotnr, &settings);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200891}
Harald Welte1b9a5b82019-02-24 23:04:45 +0100892
Harald Welte67b2aba2019-04-16 20:47:22 +0200893extern void testmode_init(void);
Harald Welte1b9a5b82019-02-24 23:04:45 +0100894
Harald Welte97bbd0f2019-04-17 21:19:18 +0200895#include "talloc.h"
Harald Welte6f7dde72019-04-17 22:08:57 +0200896#include <osmocom/core/msgb.h>
Harald Welte97bbd0f2019-04-17 21:19:18 +0200897void *g_tall_ctx;
Harald Welte9dbacf12019-04-18 17:59:19 +0200898void *g_msgb_ctx;
Harald Welte97bbd0f2019-04-17 21:19:18 +0200899
900DEFUN(_talloc_report, cmd_talloc_report, "talloc-report", "Generate a talloc report")
901{
902 talloc_report_full(g_tall_ctx, stdout);
903}
904
905DEFUN(talloc_test, cmd_talloc_test, "talloc-test", "Test the talloc allocator")
906{
907 for (int i = 0; i < 10; i++)
908 talloc_named_const(g_tall_ctx, 10, "sibling");
909}
910
911DEFUN(v_talloc_free, cmd_talloc_free, "talloc-free", "Release all memory")
912{
913 talloc_free(g_tall_ctx);
914 g_tall_ctx = NULL;
915}
916
Harald Welte6f7dde72019-04-17 22:08:57 +0200917/* dependency of libosmocore. FIXME: Implement it bsed on jiffies and/or RTC! */
918#include <sys/time.h>
919int _gettimeofday(struct timeval *tv, void *tz)
920{
921 tv->tv_sec = 0;
922 tv->tv_usec = 0;
923 return 0;
924}
925
Harald Welteaf555052019-04-18 18:30:49 +0200926/* Section 9.6 of SAMD5x/E5x Family Data Sheet */
927static int get_chip_unique_serial(uint8_t *out, size_t len)
928{
929 uint32_t *out32 = (uint32_t *)out;
930 if (len < 16)
931 return -EINVAL;
932
933 out32[0] = *(uint32_t *)0x008061fc;
934 out32[1] = *(uint32_t *)0x00806010;
935 out32[2] = *(uint32_t *)0x00806014;
936 out32[3] = *(uint32_t *)0x00806018;
937
938 return 0;
939}
940
941/* same as get_chip_unique_serial but in hex-string format */
942static int get_chip_unique_serial_str(char *out, size_t len)
943{
944 uint8_t buf[16];
945 int rc;
946
947 if (len < 16*2 + 1)
948 return -EINVAL;
949
950 rc = get_chip_unique_serial(buf, sizeof(buf));
951 if (rc < 0)
952 return rc;
953 osmo_hexdump_buf(out, len, buf, sizeof(buf), NULL, false);
954 return 0;
955}
956
Kévin Redon69b92d92019-01-24 16:39:20 +0100957int main(void)
958{
Harald Welteaf555052019-04-18 18:30:49 +0200959 char sernr_buf[16*2+1];
960
Kévin Redon69b92d92019-01-24 16:39:20 +0100961 atmel_start_init();
Harald Welteaf555052019-04-18 18:30:49 +0200962 get_chip_unique_serial_str(sernr_buf, sizeof(sernr_buf));
Kévin Redon78d2f442019-01-24 18:45:59 +0100963
Kévin Redon8e538002019-01-30 11:19:19 +0100964 usb_start();
965
Harald Weltec3f170d2019-02-24 09:06:59 +0100966 board_init();
Harald Welteff9f4ce2019-02-24 22:51:09 +0100967 command_init("sysmoOCTSIM> ");
Harald Welte1b9a5b82019-02-24 23:04:45 +0100968 command_register(&cmd_sim_status);
969 command_register(&cmd_sim_power);
970 command_register(&cmd_sim_reset);
971 command_register(&cmd_sim_clkdiv);
972 command_register(&cmd_sim_voltage);
973 command_register(&cmd_sim_led);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200974 command_register(&cmd_sim_atr);
Kévin Redon096c5052019-05-09 15:01:17 +0200975 command_register(&cmd_sim_iccid);
Harald Welte67b2aba2019-04-16 20:47:22 +0200976 testmode_init();
Harald Welte97bbd0f2019-04-17 21:19:18 +0200977 command_register(&cmd_talloc_test);
978 command_register(&cmd_talloc_report);
979 command_register(&cmd_talloc_free);
Harald Weltec3f170d2019-02-24 09:06:59 +0100980
Harald Welte361ed202019-02-24 21:15:39 +0100981 printf("\r\n\r\nsysmocom sysmoOCTSIM\r\n");
Harald Welteaf555052019-04-18 18:30:49 +0200982 printf("Chip-Id %s\r\n", sernr_buf);
Harald Weltee7aa5342019-04-16 21:11:14 +0200983
Harald Welte97bbd0f2019-04-17 21:19:18 +0200984 talloc_enable_null_tracking();
985 g_tall_ctx = talloc_named_const(NULL, 0, "global");
986 printf("g_tall_ctx=%p\r\n", g_tall_ctx);
Harald Welte9dbacf12019-04-18 17:59:19 +0200987 g_msgb_ctx = talloc_pool(g_tall_ctx, 20480);
988 talloc_set_memlimit(g_msgb_ctx, 20480);
Harald Welte97bbd0f2019-04-17 21:19:18 +0200989
Harald Weltee7aa5342019-04-16 21:11:14 +0200990 command_print_prompt();
Kévin Redon8e538002019-01-30 11:19:19 +0100991 while (true) { // main loop
Harald Welteff9f4ce2019-02-24 22:51:09 +0100992 command_try_recv();
Kévin Redon8e538002019-01-30 11:19:19 +0100993 }
Kévin Redon69b92d92019-01-24 16:39:20 +0100994}