blob: 9ef7f2fda7cba50d87eb4148cc00d3ce3ecd22c5 [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>
Harald Welte5a8af4d2019-05-12 15:57:20 +020020#include <inttypes.h>
Harald Welte1b9a5b82019-02-24 23:04:45 +010021#include <stdio.h>
Kévin Redon072951b2019-05-02 15:17:46 +020022#include <math.h>
Harald Weltef53f2262019-02-24 11:01:08 +010023#include <parts.h>
Harald Welte65101be2019-04-18 18:30:49 +020024#include <errno.h>
Harald Weltec7a58ba2019-04-18 17:59:19 +020025
26#include <osmocom/core/utils.h>
27
Harald Weltef53f2262019-02-24 11:01:08 +010028#include <hal_cache.h>
Harald Welte93f628a2019-02-24 14:32:30 +010029#include <hri_port_e54.h>
Harald Weltef53f2262019-02-24 11:01:08 +010030
Kévin Redon69b92d92019-01-24 16:39:20 +010031#include "atmel_start.h"
32#include "atmel_start_pins.h"
Kévin Redon072951b2019-05-02 15:17:46 +020033#include "config/hpl_gclk_config.h"
Kévin Redon69b92d92019-01-24 16:39:20 +010034
Harald Weltec3f170d2019-02-24 09:06:59 +010035#include "i2c_bitbang.h"
36#include "octsim_i2c.h"
37#include "ncn8025.h"
Kévin Redon0f050722019-05-02 15:56:25 +020038#include "iso7816_3.h"
Harald Weltec3f170d2019-02-24 09:06:59 +010039
Harald Welteff9f4ce2019-02-24 22:51:09 +010040#include "command.h"
41
Kévin Redonc89bb8c2019-04-17 01:20:23 +020042// TODO put declaration in more global file
43// TODO for now SIM7 is not present because used for debug
44static struct usart_async_descriptor* SIM_peripheral_descriptors[] = {&SIM0, &SIM1, &SIM2, &SIM3, &SIM4, &SIM5, &SIM6, NULL};
45
Kévin Redon096c5052019-05-09 15:01:17 +020046/** number of bytes transmitted on the SIM peripheral */
47static volatile bool SIM_tx_count[8];
48
Kévin Redonc89bb8c2019-04-17 01:20:23 +020049static void SIM_rx_cb(const struct usart_async_descriptor *const io_descr)
50{
51}
Kévin Redon78d2f442019-01-24 18:45:59 +010052
Kévin Redon096c5052019-05-09 15:01:17 +020053/** called when the transmission is complete
54 * e.g. this is when the byte has been sent and there is no data to transmit anymore
55 */
56static void SIM_tx_cb(const struct usart_async_descriptor *const io_descr)
57{
58 // find slotnr for corresponding USART
59 uint8_t slotnr;
60 for (slotnr = 0; slotnr < ARRAY_SIZE(SIM_peripheral_descriptors) && SIM_peripheral_descriptors[slotnr] != io_descr; slotnr++);
61
62 // set flag
63 if (slotnr < ARRAY_SIZE(SIM_peripheral_descriptors)) {
64 SIM_tx_count[slotnr] = true;
65 }
66}
67
Kévin Redon072951b2019-05-02 15:17:46 +020068/** possible clock sources for the SERCOM peripheral
69 * warning: the definition must match the GCLK configuration
70 */
71static const uint8_t sercom_glck_sources[] = {GCLK_PCHCTRL_GEN_GCLK2_Val, GCLK_PCHCTRL_GEN_GCLK4_Val, GCLK_PCHCTRL_GEN_GCLK6_Val};
72
73/** possible clock frequencies in MHz for the SERCOM peripheral
74 * warning: the definition must match the GCLK configuration
75 */
76static const double sercom_glck_freqs[] = {100E6 / CONF_GCLK_GEN_2_DIV, 100E6 / CONF_GCLK_GEN_4_DIV, 120E6 / CONF_GCLK_GEN_6_DIV};
77
78/** the GCLK ID for the SERCOM SIM peripherals
79 * @note: used as index for PCHCTRL
80 */
81static 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};
82
Harald Weltec7a58ba2019-04-18 17:59:19 +020083static void ccid_app_init(void);
84
Harald Weltec3f170d2019-02-24 09:06:59 +010085static void board_init()
86{
87 int i;
88
89 for (i = 0; i < 4; i++)
90 i2c_init(&i2c[i]);
91
Harald Welte255da5e2019-04-16 18:19:53 +020092 for (i = 0; i < 8; i++)
Harald Weltec3f170d2019-02-24 09:06:59 +010093 ncn8025_init(i);
Harald Weltef53f2262019-02-24 11:01:08 +010094
95 cache_init();
96 cache_enable(CMCC);
Harald Welted1bd5c42019-05-17 16:38:30 +020097 calendar_enable(&CALENDAR_0);
Harald Welte93f628a2019-02-24 14:32:30 +010098
99 /* increase drive strength of 20Mhz SIM clock output to 8mA
100 * (there are 8 inputs + traces to drive!) */
101 hri_port_set_PINCFG_DRVSTR_bit(PORT, 0, 11);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200102
103 // enable SIM interfaces
104 for (uint8_t i = 0; i < ARRAY_SIZE(SIM_peripheral_descriptors); i++) {
105 if (NULL == SIM_peripheral_descriptors[i]) {
106 continue;
107 }
108 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 +0200109 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 +0200110 usart_async_enable(SIM_peripheral_descriptors[i]);
111 }
Harald Weltec7a58ba2019-04-18 17:59:19 +0200112
113 ccid_app_init();
Harald Weltec3f170d2019-02-24 09:06:59 +0100114}
115
Harald Weltec7a58ba2019-04-18 17:59:19 +0200116/***********************************************************************
117 * CCID Driver integration
118 ***********************************************************************/
119
120#include <osmocom/core/linuxlist.h>
121#include <osmocom/core/msgb.h>
122#include "linuxlist_atomic.h"
123#include "ccid_df.h"
124
125struct usb_ep_q {
126 const char *name;
127 /* msgb queue of pending to-be-transmitted (IN/IRQ) or completed received (OUT)
128 * USB transfers */
129 struct llist_head list;
130 /* currently ongoing/processed msgb (USB transmit or receive */
131 struct msgb *in_progress;
132};
133
134struct ccid_state {
135 /* msgb queue of free msgs */
136 struct llist_head free_q;
137
138 /* msgb queue of pending to-be-transmitted (IN EP) */
139 struct usb_ep_q in_ep;
140 /* msgb queue of pending to-be-transmitted (IRQ EP) */
141 struct usb_ep_q irq_ep;
142 /* msgb queue of completed received (OUT EP) */
143 struct usb_ep_q out_ep;
Harald Welte5a8af4d2019-05-12 15:57:20 +0200144
145 /* bit-mask of card-insert status, as determined from NCN8025 IRQ output */
146 uint8_t card_insert_mask;
Harald Weltec7a58ba2019-04-18 17:59:19 +0200147};
148static struct ccid_state g_ccid_s;
149
150static void ccid_out_read_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred);
151static void ccid_in_write_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred);
152static void ccid_irq_write_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred);
153
154static void usb_ep_q_init(struct usb_ep_q *ep_q, const char *name)
155{
156 ep_q->name = name;
157 INIT_LLIST_HEAD(&ep_q->list);
158 ep_q->in_progress = NULL;
159}
160
161static void ccid_app_init(void)
162{
163 /* initialize data structures */
164 INIT_LLIST_HEAD(&g_ccid_s.free_q);
165 usb_ep_q_init(&g_ccid_s.in_ep, "IN");
166 usb_ep_q_init(&g_ccid_s.irq_ep, "IRQ");
167 usb_ep_q_init(&g_ccid_s.out_ep, "OUT");
168
169 /* OUT endpoint read complete callback (irq context) */
170 ccid_df_register_callback(CCID_DF_CB_READ_OUT, (FUNC_PTR)&ccid_out_read_compl);
171 /* IN endpoint write complete callback (irq context) */
172 ccid_df_register_callback(CCID_DF_CB_WRITE_IN, (FUNC_PTR)&ccid_in_write_compl);
173 /* IRQ endpoint write complete callback (irq context) */
174 ccid_df_register_callback(CCID_DF_CB_WRITE_IRQ, (FUNC_PTR)&ccid_irq_write_compl);
175}
176
177/* irqsafe version of msgb_enqueue */
178struct msgb *msgb_dequeue_irqsafe(struct llist_head *q)
179{
180 struct msgb *msg;
181 CRITICAL_SECTION_ENTER()
182 msg = msgb_dequeue(q);
183 CRITICAL_SECTION_LEAVE()
184 return msg;
185}
186
Harald Welte5a8af4d2019-05-12 15:57:20 +0200187void msgb_enqueue_irqsafe(struct llist_head *q, struct msgb *msg)
188{
189 CRITICAL_SECTION_ENTER()
190 msgb_enqueue(q, msg);
191 CRITICAL_SECTION_LEAVE()
192}
193
Harald Weltec7a58ba2019-04-18 17:59:19 +0200194/* submit the next pending (if any) message for the IN EP */
195static int submit_next_in(void)
196{
197 struct usb_ep_q *ep_q = &g_ccid_s.in_ep;
198 struct msgb *msg;
199 int rc;
200
201 OSMO_ASSERT(!ep_q->in_progress);
202 msg = msgb_dequeue_irqsafe(&ep_q->list);
203 if (!msg)
204 return 0;
205
206 ep_q->in_progress = msg;
207 rc = ccid_df_write_in(msgb_data(msg), msgb_length(msg));
208 if (rc != ERR_NONE) {
209 printf("EP %s failed: %d\r\n", ep_q->name, rc);
210 return -1;
211 }
212 return 1;
213
214}
215
216/* submit the next pending (if any) message for the IRQ EP */
217static int submit_next_irq(void)
218{
219 struct usb_ep_q *ep_q = &g_ccid_s.irq_ep;
220 struct msgb *msg;
221 int rc;
222
223 OSMO_ASSERT(!ep_q->in_progress);
224 msg = msgb_dequeue_irqsafe(&ep_q->list);
225 if (!msg)
226 return 0;
227
228 ep_q->in_progress = msg;
229 rc = ccid_df_write_irq(msgb_data(msg), msgb_length(msg));
230 /* may return HALTED/ERROR/DISABLED/BUSY/ERR_PARAM/ERR_FUNC/ERR_DENIED */
231 if (rc != ERR_NONE) {
232 printf("EP %s failed: %d\r\n", ep_q->name, rc);
233 return -1;
234 }
235 return 1;
236}
237
238static int submit_next_out(void)
239{
240 struct usb_ep_q *ep_q = &g_ccid_s.out_ep;
241 struct msgb *msg;
242 int rc;
243
244 OSMO_ASSERT(!ep_q->in_progress);
245 msg = msgb_dequeue_irqsafe(&g_ccid_s.free_q);
246 if (!msg)
247 return -1;
248 ep_q->in_progress = msg;
249
250 rc = ccid_df_read_out(msgb_data(msg), msgb_tailroom(msg));
251 if (rc != ERR_NONE) {
252 /* re-add to the list of free msgb's */
253 llist_add_tail_at(&g_ccid_s.free_q, &msg->list);
254 return 0;
255 }
256 return 1;
257}
258
259/* OUT endpoint read complete callback (irq context) */
260static void ccid_out_read_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred)
261{
262 struct msgb *msg = g_ccid_s.out_ep.in_progress;
263
264 /* add just-received msg to tail of endpoint queue */
265 OSMO_ASSERT(msg);
266 /* update msgb with the amount of data received */
267 msgb_put(msg, transferred);
268 /* append to list of pending-to-be-handed messages */
269 llist_add_tail_at(&msg->list, &g_ccid_s.out_ep.list);
270
271 /* submit another [free] msgb to receive the next transfer */
272 submit_next_out();
273}
274
275/* IN endpoint write complete callback (irq context) */
276static void ccid_in_write_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred)
277{
278 struct msgb *msg = g_ccid_s.in_ep.in_progress;
279
280 OSMO_ASSERT(msg);
281 /* return the message back to the queue of free message buffers */
282 llist_add_tail_at(&msg->list, &g_ccid_s.free_q);
283 g_ccid_s.in_ep.in_progress = NULL;
284
285 /* submit the next pending to-be-transmitted msgb (if any) */
286 submit_next_in();
287}
288
289/* IRQ endpoint write complete callback (irq context) */
290static void ccid_irq_write_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred)
291{
292 struct msgb *msg = g_ccid_s.irq_ep.in_progress;
293
294 OSMO_ASSERT(msg);
295 /* return the message back to the queue of free message buffers */
296 llist_add_tail_at(&msg->list, &g_ccid_s.free_q);
297 g_ccid_s.irq_ep.in_progress = NULL;
298
299 /* submit the next pending to-be-transmitted msgb (if any) */
300 submit_next_irq();
301}
302
Harald Welte5a8af4d2019-05-12 15:57:20 +0200303#include "ccid_proto.h"
Eric Wild2de998a2019-10-01 15:20:32 +0200304static struct msgb *ccid_gen_notify_slot_status(uint8_t old_bm, uint8_t new_bm)
Harald Welte5a8af4d2019-05-12 15:57:20 +0200305{
Eric Wild2de998a2019-10-01 15:20:32 +0200306 uint8_t statusbytes[2] = {0};
Harald Welte5a8af4d2019-05-12 15:57:20 +0200307 //struct msgb *msg = ccid_msgb_alloc();
308 struct msgb *msg = msgb_alloc(64, "IRQ");
Eric Wild2de998a2019-10-01 15:20:32 +0200309 struct ccid_rdr_to_pc_notify_slot_change *nsc = msgb_put(msg, sizeof(*nsc) + sizeof(statusbytes));
Harald Welte5a8af4d2019-05-12 15:57:20 +0200310 nsc->bMessageType = RDR_to_PC_NotifySlotChange;
Eric Wild2de998a2019-10-01 15:20:32 +0200311
312 for(int i = 0; i <8; i++) {
313 uint8_t byteidx = i >> 2;
314 uint8_t old_bit = old_bm & (1 << i);
315 uint8_t new_bit = new_bm & (1 << i);
316 uint8_t bv;
317 if (old_bit == new_bit && new_bit == 0)
318 bv = 0x00;
319 else if (old_bit == new_bit && new_bit == 1)
320 bv = 0x01;
321 else if (old_bit != new_bit && new_bit == 0)
322 bv = 0x02;
323 else
324 bv = 0x03;
325
326 statusbytes[byteidx] |= bv << ((i % 4) << 1);
327 }
328
329 memcpy(&nsc->bmSlotCCState, statusbytes, sizeof(statusbytes));
Harald Welte5a8af4d2019-05-12 15:57:20 +0200330
331 return msg;
332}
333
334/* check if any card detect state has changed */
335static void poll_card_detect(void)
336{
337 uint8_t new_mask = 0;
338 struct msgb *msg;
339 unsigned int i;
340
Eric Wild2de998a2019-10-01 15:20:32 +0200341 for (i = 0; i < 8; i++)
342 new_mask |= ncn8025_interrupt_level(i) << i;
Harald Welte5a8af4d2019-05-12 15:57:20 +0200343
344 /* notify the user/host about any changes */
345 if (g_ccid_s.card_insert_mask != new_mask) {
346 printf("CARD_DET 0x%02x -> 0x%02x\r\n",
347 g_ccid_s.card_insert_mask, new_mask);
Eric Wild2de998a2019-10-01 15:20:32 +0200348 msg = ccid_gen_notify_slot_status(g_ccid_s.card_insert_mask, new_mask);
Harald Welte5a8af4d2019-05-12 15:57:20 +0200349 msgb_enqueue_irqsafe(&g_ccid_s.irq_ep.list, msg);
350
351 g_ccid_s.card_insert_mask = new_mask;
352 }
353}
354
355
Harald Weltec7a58ba2019-04-18 17:59:19 +0200356
357/***********************************************************************
358 * Command Line interface
359 ***********************************************************************/
360
Harald Welte1b9a5b82019-02-24 23:04:45 +0100361static int validate_slotnr(int argc, char **argv, int idx)
362{
363 int slotnr;
364 if (argc < idx+1) {
365 printf("You have to specify the slot number (0..7)\r\n");
366 return -1;
367 }
368 slotnr = atoi(argv[idx]);
369 if (slotnr < 0 || slotnr > 7) {
370 printf("You have to specify the slot number (0..7)\r\n");
371 return -1;
372 }
373 return slotnr;
374}
375
Kévin Redon072951b2019-05-02 15:17:46 +0200376/** change baud rate of card slot
377 * @param[in] slotnr slot number for which the baud rate should be set
378 * @param[in] baudrate baud rate in bps to set
379 * @return if the baud rate has been set, else a parameter is out of range
380 */
381static bool slot_set_baudrate(uint8_t slotnr, uint32_t baudrate)
382{
383 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
384
385 // calculate the error corresponding to the clock sources
386 uint16_t bauds[ARRAY_SIZE(sercom_glck_freqs)];
387 double errors[ARRAY_SIZE(sercom_glck_freqs)];
388 for (uint8_t i = 0; i < ARRAY_SIZE(sercom_glck_freqs); i++) {
389 double freq = sercom_glck_freqs[i]; // remember possible SERCOM frequency
390 uint32_t min = freq / (2 * (255 + 1)); // calculate the minimum baud rate for this frequency
391 uint32_t max = freq / (2 * (0 + 1)); // calculate the maximum baud rate for this frequency
392 if (baudrate < min || baudrate > max) { // baud rate it out of supported range
393 errors[i] = NAN;
394 } else {
395 uint16_t baud = round(freq / (2 * baudrate) - 1);
396 bauds[i] = baud;
397 double actual = freq / (2 * (baud + 1));
398 errors[i] = fabs(1.0 - (actual / baudrate));
399 }
400 }
401
402 // find the smallest error
403 uint8_t best = ARRAY_SIZE(sercom_glck_freqs);
404 for (uint8_t i = 0; i < ARRAY_SIZE(sercom_glck_freqs); i++) {
405 if (isnan(errors[i])) {
406 continue;
407 }
408 if (best >= ARRAY_SIZE(sercom_glck_freqs)) {
409 best = i;
410 } else if (errors[i] < errors[best]) {
411 best = i;
412 }
413 }
414 if (best >= ARRAY_SIZE(sercom_glck_freqs)) { // found no clock supporting this baud rate
415 return false;
416 }
417
418 // set clock and baud rate
419 struct usart_async_descriptor* slot = SIM_peripheral_descriptors[slotnr]; // get slot
420 if (NULL == slot) {
421 return false;
422 }
423 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]);
424 while (!usart_async_is_tx_empty(slot)); // wait for transmission to complete (WARNING no timeout)
425 usart_async_disable(slot); // disable SERCOM peripheral
426 hri_gclk_clear_PCHCTRL_reg(GCLK, SIM_peripheral_GCLK_ID[slotnr], (1 << GCLK_PCHCTRL_CHEN_Pos)); // disable clock for this peripheral
427 while (hri_gclk_get_PCHCTRL_reg(GCLK, SIM_peripheral_GCLK_ID[slotnr], (1 << GCLK_PCHCTRL_CHEN_Pos))); // wait until clock is really disabled
428 // it does not seem we need to completely disable the peripheral using hri_mclk_clear_APBDMASK_SERCOMn_bit
429 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
430 usart_async_set_baud_rate(slot, bauds[best]); // set the new baud rate
431 usart_async_enable(slot); // re-enable SERCOM peripheral
432
433 return true;
434}
435
Kévin Redon0f050722019-05-02 15:56:25 +0200436/** change ISO baud rate of card slot
437 * @param[in] slotnr slot number for which the baud rate should be set
438 * @param[in] clkdiv can clock divider
439 * @param[in] f clock rate conversion integer F
440 * @param[in] d baud rate adjustment factor D
441 * @return if the baud rate has been set, else a parameter is out of range
442 */
443static bool slot_set_isorate(uint8_t slotnr, enum ncn8025_sim_clkdiv clkdiv, uint16_t f, uint8_t d)
444{
445 // input checks
446 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
447 if (clkdiv != SIM_CLKDIV_1 && clkdiv != SIM_CLKDIV_2 && clkdiv != SIM_CLKDIV_4 && clkdiv != SIM_CLKDIV_8) {
448 return false;
449 }
450 if (!iso7816_3_valid_f(f)) {
451 return false;
452 }
453 if (!iso7816_3_valid_d(d)) {
454 return false;
455 }
456
457 // set clockdiv
458 struct ncn8025_settings settings;
459 ncn8025_get(slotnr, &settings);
460 if (settings.clkdiv != clkdiv) {
461 settings.clkdiv = clkdiv;
462 ncn8025_set(slotnr, &settings);
463 }
464
465 // calculate desired frequency
466 uint32_t freq = 20000000UL; // maximum frequency
467 switch (clkdiv) {
468 case SIM_CLKDIV_1:
469 freq /= 1;
470 break;
471 case SIM_CLKDIV_2:
472 freq /= 2;
473 break;
474 case SIM_CLKDIV_4:
475 freq /= 4;
476 break;
477 case SIM_CLKDIV_8:
478 freq /= 8;
479 break;
480 }
481
482 // set baud rate
483 uint32_t baudrate = (freq * d) / f; // calculate actual baud rate
Kévin Redon5188e9f2019-05-09 17:34:55 +0200484 return slot_set_baudrate(slotnr, baudrate); // set baud rate
485}
486
487/** write data to card
488 * @param[in] slotnr slot number on which to send data
489 * @param[in] data data to be transmitted
490 * @param[in] length length of data to be transmitted
491 * @return error code
492 */
493static int slot_card_write(uint8_t slotnr, const uint8_t* data, uint16_t length)
494{
495 // input checks
496 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
497 if (0 == length || NULL == data) {
498 return ERR_INVALID_ARG;
499 }
500
501 struct usart_async_descriptor* sim = SIM_peripheral_descriptors[slotnr];
502 ((Sercom *)sim->device.hw)->USART.CTRLB.bit.RXEN = 0; // disable receive (to avoid the echo back)
503 SIM_tx_count[slotnr] = false; // reset TX complete
504 for (uint16_t i = 0; i < length; i++) { // transmit data
505 while(!usart_async_is_tx_empty(sim)); // wait for previous byte to be transmitted (WARNING blocking)
506 if (1 != io_write(&sim->io, &data[i], 1)) { // put but in transmit buffer
507 return ERR_IO;
508 }
509 }
510 while (!SIM_tx_count[slotnr]); // wait until transmission is complete (WARNING blocking)
511 ((Sercom *)sim->device.hw)->USART.CTRLB.bit.RXEN = 1; // enable receive again
512
513 return ERR_NONE;
514}
515
516/** read data from card
517 * @param[in] slotnr slot number on which to send data
518 * @param[out] data buffer for read data to be stored
519 * @param[in] length length of data to be read
520 * @param[in] wt Waiting Time in ETU
521 * @return error code
522 * TODO fix WT/ETU duration
523 */
524static int slot_card_read(uint8_t slotnr, uint8_t* data, uint16_t length, uint32_t wt)
525{
526 // input checks
527 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
528 if (0 == length || NULL == data) {
529 return ERR_INVALID_ARG;
530 }
531
532 struct usart_async_descriptor* sim = SIM_peripheral_descriptors[slotnr];
533
534 ((Sercom *)sim->device.hw)->USART.CTRLB.bit.RXEN = 1; // ensure RX is enabled
535 uint32_t timeout = wt; // reset waiting time
536 for (uint16_t i = 0; i < length; i++) { // read all data
537 while (timeout && !usart_async_is_rx_not_empty(sim)) { // verify if data is present
538 delay_us(149); // wait for 1 ETU (372 / 1 / 2.5 MHz = 148.8 us)
539 timeout--;
540 }
541 if (0 == timeout) { // timeout reached
542 return ERR_TIMEOUT;
543 }
544 timeout = wt; // reset waiting time
545 if (1 != io_read(&sim->io, &data[i], 1)) { // read one byte
546 return ERR_IO;
547 }
548 }
549
550 return ERR_NONE;
551}
552
553/** transfer TPDU
554 * @param[in] slotnr slot number on which to transfer the TPDU
555 * @param[in] header TPDU header to send
556 * @param[io] data TPDU data to transfer
557 * @param[in] data_length length of TPDU data to transfer
558 * @param[in] write if the data should be written (true) or read (false)
559 * TODO fix WT
560 * TODO the data length can be deduce from the header
561 */
562static int slot_tpdu_xfer(uint8_t slotnr, const uint8_t* header, uint8_t* data, uint16_t data_length, bool write)
563{
564 // input checks
565 ASSERT(slotnr < ARRAY_SIZE(SIM_peripheral_descriptors));
566 if (NULL == header || (data_length > 0 && NULL == data)) {
567 return ERR_INVALID_ARG;
568 }
569
570 int rc;
571 struct usart_async_descriptor* sim = SIM_peripheral_descriptors[slotnr]; // get USART peripheral
572 usart_async_flush_rx_buffer(sim); // flush RX buffer to start from scratch
573
574 // send command header
575 printf("(%d) TPDU: ", slotnr);
576 for (uint8_t i = 0; i < 5; i++) {
577 printf("%02x ", header[i]);
578 }
579 rc = slot_card_write(slotnr, header, 5); // transmit header
580 if (ERR_NONE != rc) {
581 printf("error in command header transmit (errno = %d)\r\n", rc);
582 return rc;
583 }
584
585 // read procedure byte, and handle data
586 uint8_t pb = 0x60; // wait more procedure byte
587 uint16_t data_i = 0; // progress in the data transfer
588 while (0x60 == pb) { // wait for SW
589 rc = slot_card_read(slotnr, &pb, 1, ISO7816_3_DEFAULT_WT);
590 if (ERR_NONE != rc) {
591 printf("error while receiving PB/SW1 (errno = %d)\r\n", rc);
592 return rc;
593 }
594 printf("%02x ", pb);
595 if (0x60 == pb) { // NULL byte
596 // just wait more time
597 } else if ((0x60 == (pb & 0xf0)) || (0x90 == (pb & 0xf0))) { // SW1 byte
598 // left the rest of the code handle it
599 } else if (header[1] == pb) { // ACK byte
600 // transfer rest of the data
601 if (data_i >= data_length) {
602 printf("error no more data to transfer\r\n");
603 return ERR_INVALID_DATA;
604 }
605 if (write) { // transmit remaining command data
606 rc = slot_card_write(slotnr, &data[data_i], data_length - data_i); // transmit command data
607 if (ERR_NONE != rc) {
608 printf("error in command data transmit (errno = %d)\r\n", rc);
609 return rc;
610 }
611 } else { // receive remaining command data
612 rc = slot_card_read(slotnr, &data[data_i], data_length - data_i, ISO7816_3_DEFAULT_WT);
613 if (ERR_NONE != rc) {
614 printf("error in command data receive (errno = %d)\r\n", rc);
615 return rc;
616 }
617 }
618 for (uint16_t i = data_i; i < data_length; i++) {
619 printf("%02x ", data[i]);
620 }
621 data_i = data_length; // remember we transferred the data
622 pb = 0x60; // wait for SW1
623 } else if (header[1] == (pb ^ 0xff)) { // ACK byte
624 // transfer only one byte
625 if (data_i >= data_length) {
626 printf("error no more data to transfer\r\n");
627 return ERR_INVALID_DATA;
628 }
629 if (write) { // transmit command data byte
630 rc = slot_card_write(slotnr, &data[data_i], 1); // transmit command data
631 if (ERR_NONE != rc) {
632 printf("error in command data transmit (errno = %d)\r\n", rc);
633 return rc;
634 }
635 } else { // receive command data byte
636 rc = slot_card_read(slotnr, &data[data_i], 1, ISO7816_3_DEFAULT_WT);
637 if (ERR_NONE != rc) {
638 printf("error in command data receive (errno = %d)\r\n", rc);
639 return rc;
640 }
641 }
642 printf("%02x ", data[data_i]);
643 data_i += 1; // remember we transferred one data byte
644 pb = 0x60; // wait for SW1
645 } else { // invalid byte
646 return ERR_INVALID_DATA;
647 }
648 }
649
650 // read SW2
651 uint8_t sw2;
652 rc = slot_card_read(slotnr, &sw2, 1, ISO7816_3_DEFAULT_WT);
653 if (ERR_NONE != rc) {
654 printf("error in receiving SW2 (errno = %d)\r\n", rc);
655 return rc;
656 }
657 printf("%02x", sw2);
658
659 printf("\r\n");
660 return ERR_NONE;
Kévin Redon0f050722019-05-02 15:56:25 +0200661}
662
Harald Welte1b9a5b82019-02-24 23:04:45 +0100663DEFUN(sim_status, cmd_sim_status, "sim-status", "Get state of specified NCN8025")
664{
665 struct ncn8025_settings settings;
666 int slotnr = validate_slotnr(argc, argv, 1);
667 if (slotnr < 0)
668 return;
669 ncn8025_get(slotnr, &settings);
670 printf("SIM%d: ", slotnr);
671 ncn8025_dump(&settings);
672 printf("\r\n");
673}
674
675DEFUN(sim_power, cmd_sim_power, "sim-power", "Enable/disable SIM card power")
676{
677 struct ncn8025_settings settings;
678 int slotnr = validate_slotnr(argc, argv, 1);
679 int enable;
680
681 if (slotnr < 0)
682 return;
683
684 if (argc < 3) {
685 printf("You have to specify 0=disable or 1=enable\r\n");
686 return;
687 }
688 enable = atoi(argv[2]);
689 ncn8025_get(slotnr, &settings);
690 if (enable)
691 settings.cmdvcc = true;
692 else
693 settings.cmdvcc = false;
694 ncn8025_set(slotnr, &settings);
695}
696
697DEFUN(sim_reset, cmd_sim_reset, "sim-reset", "Enable/disable SIM reset")
698{
699 struct ncn8025_settings settings;
700 int slotnr = validate_slotnr(argc, argv, 1);
701 int enable;
702
703 if (slotnr < 0)
704 return;
705
706 if (argc < 3) {
707 printf("You have to specify 0=disable or 1=enable\r\n");
708 return;
709 }
710 enable = atoi(argv[2]);
711 ncn8025_get(slotnr, &settings);
712 if (enable)
713 settings.rstin = true;
714 else
715 settings.rstin = false;
716 ncn8025_set(slotnr, &settings);
717}
718
719DEFUN(sim_clkdiv, cmd_sim_clkdiv, "sim-clkdiv", "Set SIM clock divider (1,2,4,8)")
720{
721 struct ncn8025_settings settings;
722 int slotnr = validate_slotnr(argc, argv, 1);
723 int clkdiv;
724
725 if (slotnr < 0)
726 return;
727
728 if (argc < 3) {
729 printf("You have to specify a valid divider (1,2,4,8)\r\n");
730 return;
731 }
732 clkdiv = atoi(argv[2]);
733 if (clkdiv != 1 && clkdiv != 2 && clkdiv != 4 && clkdiv != 8) {
734 printf("You have to specify a valid divider (1,2,4,8)\r\n");
735 return;
736 }
737 ncn8025_get(slotnr, &settings);
738 switch (clkdiv) {
739 case 1:
740 settings.clkdiv = SIM_CLKDIV_1;
741 break;
742 case 2:
743 settings.clkdiv = SIM_CLKDIV_2;
744 break;
745 case 4:
746 settings.clkdiv = SIM_CLKDIV_4;
747 break;
748 case 8:
749 settings.clkdiv = SIM_CLKDIV_8;
750 break;
751 }
752 ncn8025_set(slotnr, &settings);
753}
754
755DEFUN(sim_voltage, cmd_sim_voltage, "sim-voltage", "Set SIM voltage (5/3/1.8)")
756{
757 struct ncn8025_settings settings;
758 int slotnr = validate_slotnr(argc, argv, 1);
759
760 if (slotnr < 0)
761 return;
762
763 if (argc < 3) {
764 printf("You have to specify a valid voltage (5/3/1.8)\r\n");
765 return;
766 }
767 ncn8025_get(slotnr, &settings);
768 if (!strcmp(argv[2], "5"))
769 settings.vsel = SIM_VOLT_5V0;
770 else if (!strcmp(argv[2], "3"))
771 settings.vsel = SIM_VOLT_3V0;
772 else if (!strcmp(argv[2], "1.8"))
773 settings.vsel = SIM_VOLT_1V8;
774 else {
775 printf("You have to specify a valid voltage (5/3/1.8)\r\n");
776 return;
777 }
778 ncn8025_set(slotnr, &settings);
779}
780
781DEFUN(sim_led, cmd_sim_led, "sim-led", "Set SIM LED (1=on, 0=off)")
782{
783 struct ncn8025_settings settings;
784 int slotnr = validate_slotnr(argc, argv, 1);
785
786 if (slotnr < 0)
787 return;
788
789 if (argc < 3) {
790 printf("You have to specify 0=disable or 1=enable\r\n");
791 return;
792 }
793 ncn8025_get(slotnr, &settings);
794 if (atoi(argv[2]))
795 settings.led = true;
796 else
797 settings.led = false;
798 ncn8025_set(slotnr, &settings);
799}
800
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200801DEFUN(sim_atr, cmd_sim_atr, "sim-atr", "Read ATR from SIM card")
802{
803 struct ncn8025_settings settings;
804 int slotnr = validate_slotnr(argc, argv, 1);
Harald Welte1b9a5b82019-02-24 23:04:45 +0100805
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200806 if (slotnr < 0 || slotnr >= ARRAY_SIZE(SIM_peripheral_descriptors) || NULL == SIM_peripheral_descriptors[slotnr]) {
807 return;
808 }
809
810 // check if card is present (and read current settings)
811 ncn8025_get(slotnr, &settings);
812 if (!settings.simpres) {
Kévin Redon096c5052019-05-09 15:01:17 +0200813 printf("(%d) error: no card present\r\n", slotnr);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200814 return;
815 }
816
817 // switch card off (assert reset and disable power)
818 // note: ISO/IEC 7816-3:2006 section 6.4 provides the deactivation sequence, but not the minimum corresponding times
819 settings.rstin = true;
820 settings.cmdvcc = false;
Harald Weltedcf57832019-04-17 17:29:41 +0200821 settings.led = true;
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200822 ncn8025_set(slotnr, &settings);
823
824 // TODO wait some time for card to be completely deactivated
825 usart_async_flush_rx_buffer(SIM_peripheral_descriptors[slotnr]); // flush RX buffer to start from scratch
826
Kévin Redon0f050722019-05-02 15:56:25 +0200827
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200828 // set clock to lowest frequency (20 MHz / 8 = 2.5 MHz)
829 // 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
830 settings.clkdiv = SIM_CLKDIV_8;
Kévin Redon0f050722019-05-02 15:56:25 +0200831 // set USART baud rate to match the interface (f = 2.5 MHz) and card default settings (Fd = 372, Dd = 1)
832 slot_set_isorate(slotnr, settings.clkdiv, ISO7816_3_DEFAULT_FD, ISO7816_3_DEFAULT_DD);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200833 // set card voltage to 3.0 V (the most supported)
834 // note: according to ISO/IEC 7816-3:2006 no voltage should damage the card, and you should cycle from low to high
835 settings.vsel = SIM_VOLT_3V0;
836 // provide power (the NCN8025 should perform the activation according to spec)
837 // note: activation sequence is documented in ISO/IEC 7816-3:2006 section 6.2
838 settings.cmdvcc = true;
839 ncn8025_set(slotnr, &settings);
840
841 // wait for Tb=400 cycles before re-asserting reset
842 delay_us(400 * 10000 / 2500); // 400 cycles * 1000 for us, 2.5 MHz / 1000 for us
843
844 // de-assert reset to switch card back on
845 settings.rstin = false;
846 ncn8025_set(slotnr, &settings);
847
848 // wait for Tc=40000 cycles for transmission to start
849 uint32_t cycles = 40000;
850 while (cycles && !usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
851 delay_us(10);
852 cycles -= 25; // 10 us = 25 cycles at 2.5 MHz
853 }
854 if (!usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
855 delay_us(12 * 372 / 1 / 2); // wait more than one byte (approximate freq down to 2 MHz)
856 }
857 // verify if one byte has been received
858 if (!usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
Kévin Redon096c5052019-05-09 15:01:17 +0200859 printf("(%d) error: card not responsive\r\n", slotnr);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200860 return;
861 }
862
863 // read ATR (just do it until there is no traffic anymore)
Kévin Redon096c5052019-05-09 15:01:17 +0200864 // 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 +0200865 printf("(%d) ATR: ", slotnr);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200866 uint8_t atr_byte;
867 while (usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
868 if (1 == io_read(&SIM_peripheral_descriptors[slotnr]->io, &atr_byte, 1)) {
869 printf("%02x ", atr_byte);
870 }
Kévin Redon096c5052019-05-09 15:01:17 +0200871 uint16_t wt = ISO7816_3_DEFAULT_WT; // waiting time in ETU
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200872 while (wt && !usart_async_is_rx_not_empty(SIM_peripheral_descriptors[slotnr])) {
873 delay_us(149); // wait for 1 ETU (372 / 1 / 2.5 MHz = 148.8 us)
874 wt--;
875 }
876 }
877 printf("\r\n");
Harald Weltedcf57832019-04-17 17:29:41 +0200878
Kévin Redon096c5052019-05-09 15:01:17 +0200879 /* disable LED */
880 settings.led = false;
881 ncn8025_set(slotnr, &settings);
882}
883
884DEFUN(sim_iccid, cmd_sim_iccid, "sim-iccid", "Read ICCID from SIM card")
885{
886 struct ncn8025_settings settings;
887 int slotnr = validate_slotnr(argc, argv, 1);
888
889 if (slotnr < 0 || slotnr >= ARRAY_SIZE(SIM_peripheral_descriptors) || NULL == SIM_peripheral_descriptors[slotnr]) {
890 return;
891 }
892
893 // read current settings and check if card is present and powered
894 ncn8025_get(slotnr, &settings);
895 if (!settings.simpres) {
896 printf("(%d) error: no card present\r\n", slotnr);
897 return;
898 }
899 if (!settings.cmdvcc) {
900 printf("(%d) error: card not powered\r\n", slotnr);
901 return;
902 }
903 if (settings.rstin) {
904 printf("(%d) error: card under reset\r\n", slotnr);
905 return;
906 }
907
908 // enable LED
909 if (!settings.led) {
910 settings.led = true;
911 ncn8025_set(slotnr, &settings);
912 }
913
Kévin Redon5188e9f2019-05-09 17:34:55 +0200914 // select MF
915 printf("(%d) SELECT MF\r\n", slotnr);
Kévin Redon36efc6d2019-05-09 18:03:20 +0200916 const uint8_t select_header[] = {0xa0, 0xa4, 0x00, 0x00, 0x02}; // see TS 102.221 sec. 11.1.1
917 const uint8_t select_data_mf[] = {0x3f, 0x00}; // see TS 102.221 sec. 13.1
918 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 +0200919 if (ERR_NONE != rc) {
920 printf("error while SELECT MF (errno = %d)\r\n", rc);
Kévin Redon096c5052019-05-09 15:01:17 +0200921 }
Kévin Redon36efc6d2019-05-09 18:03:20 +0200922 // ignore response data
923
924 // select EF_ICCID
925 printf("(%d) SELECT EF_ICCID\r\n", slotnr);
926 const uint8_t select_data_ef_iccid[] = {0x2f, 0xe2}; // see TS 102.221 sec. 13.2
927 rc = slot_tpdu_xfer(slotnr, select_header, (uint8_t*)select_data_ef_iccid, ARRAY_SIZE(select_data_ef_iccid), true); // transfer TPDU
928 if (ERR_NONE != rc) {
929 printf("error while SELECT EF_ICCID (errno = %d)\r\n", rc);
930 }
931 // ignore response data
932
933 // read EF_ICCID
934 printf("(%d) READ EF_ICCID\r\n", slotnr);
935 uint8_t iccid[10];
936 uint8_t read_binary[] = {0xa0, 0xb0, 0x00, 0x00, ARRAY_SIZE(iccid)}; // see TS 102.221 sec. 11.1.3
937 rc = slot_tpdu_xfer(slotnr, read_binary, iccid, ARRAY_SIZE(iccid), false); // transfer TPDU
938 if (ERR_NONE != rc) {
939 printf("error while READ ICCID (errno = %d)\r\n", rc);
940 }
941 // ignore response data
942
943 printf("(%d) ICCID: ", slotnr);
944 for (uint8_t i = 0; i < ARRAY_SIZE(iccid); i++) {
945 uint8_t nibble = iccid[i] & 0xf;
946 if (0xf == nibble) {
947 break;
948 }
949 printf("%x", nibble);
950 nibble = iccid[i] >> 4;
951 if (0xf == nibble) {
952 break;
953 }
954 printf("%x", nibble);
955 }
956 printf("\r\n");
Kévin Redon096c5052019-05-09 15:01:17 +0200957
958 // disable LED
Harald Weltedcf57832019-04-17 17:29:41 +0200959 settings.led = false;
960 ncn8025_set(slotnr, &settings);
Kévin Redonc89bb8c2019-04-17 01:20:23 +0200961}
Harald Welte1b9a5b82019-02-24 23:04:45 +0100962
Harald Welte2dc67e92019-05-17 18:01:46 +0200963DEFUN(get_time, cmd_get_time, "get-time", "Read Time from RTC")
964{
965 struct calendar_date_time dt;
966 calendar_get_date_time(&CALENDAR_0, &dt);
967 printf("%04u-%02u-%02u %02u:%02u:%02u\r\n", dt.date.year, dt.date.month, dt.date.day,
968 dt.time.hour, dt.time.min, dt.time.sec);
969}
970
Harald Welte1017a752019-05-17 20:39:49 +0200971#include <osmocom/core/timer.h>
972static struct osmo_timer_list t;
973static void tmr_cb(void *data)
974{
975 printf("timer fired!\r\n");
976}
977DEFUN(test_timer, cmd_test_timer, "test-timer", "Test osmo_timer")
978{
979 printf("Setting up timer for 3s...\n\r");
980 osmo_timer_setup(&t, &tmr_cb, NULL);
981 osmo_timer_schedule(&t, 3, 0);
982}
983
Harald Welte2dc67e92019-05-17 18:01:46 +0200984
Harald Welte67b2aba2019-04-16 20:47:22 +0200985extern void testmode_init(void);
Harald Weltebdf1b352019-05-17 10:21:45 +0200986extern void libosmo_emb_init(void);
Harald Welte1017a752019-05-17 20:39:49 +0200987extern void libosmo_emb_mainloop(void);
Harald Welte1b9a5b82019-02-24 23:04:45 +0100988
Harald Welte8049d662019-04-17 21:19:18 +0200989#include "talloc.h"
Harald Weltebdf1b352019-05-17 10:21:45 +0200990#include "logging.h"
Harald Welte3304ca22019-04-17 22:08:57 +0200991#include <osmocom/core/msgb.h>
Harald Welte8049d662019-04-17 21:19:18 +0200992void *g_tall_ctx;
993
994DEFUN(_talloc_report, cmd_talloc_report, "talloc-report", "Generate a talloc report")
995{
996 talloc_report_full(g_tall_ctx, stdout);
997}
998
999DEFUN(talloc_test, cmd_talloc_test, "talloc-test", "Test the talloc allocator")
1000{
1001 for (int i = 0; i < 10; i++)
1002 talloc_named_const(g_tall_ctx, 10, "sibling");
1003}
1004
1005DEFUN(v_talloc_free, cmd_talloc_free, "talloc-free", "Release all memory")
1006{
1007 talloc_free(g_tall_ctx);
1008 g_tall_ctx = NULL;
1009}
1010
Harald Welte65101be2019-04-18 18:30:49 +02001011/* Section 9.6 of SAMD5x/E5x Family Data Sheet */
1012static int get_chip_unique_serial(uint8_t *out, size_t len)
1013{
1014 uint32_t *out32 = (uint32_t *)out;
1015 if (len < 16)
1016 return -EINVAL;
1017
1018 out32[0] = *(uint32_t *)0x008061fc;
1019 out32[1] = *(uint32_t *)0x00806010;
1020 out32[2] = *(uint32_t *)0x00806014;
1021 out32[3] = *(uint32_t *)0x00806018;
1022
1023 return 0;
1024}
1025
1026/* same as get_chip_unique_serial but in hex-string format */
1027static int get_chip_unique_serial_str(char *out, size_t len)
1028{
1029 uint8_t buf[16];
1030 int rc;
1031
1032 if (len < 16*2 + 1)
1033 return -EINVAL;
1034
1035 rc = get_chip_unique_serial(buf, sizeof(buf));
1036 if (rc < 0)
1037 return rc;
1038 osmo_hexdump_buf(out, len, buf, sizeof(buf), NULL, false);
1039 return 0;
1040}
1041
Harald Welte9ab4bc82019-05-17 18:36:01 +02001042#define RSTCAUSE_STR_SIZE 64
1043static void get_rstcause_str(char *out)
1044{
1045 uint8_t val = hri_rstc_read_RCAUSE_reg(RSTC);
1046 *out = '\0';
1047 if (val & RSTC_RCAUSE_POR)
1048 strcat(out, "POR ");
1049 if (val & RSTC_RCAUSE_BODCORE)
1050 strcat(out, "BODCORE ");
1051 if (val & RSTC_RCAUSE_BODVDD)
1052 strcat(out, "BODVDD ");
1053 if (val & RSTC_RCAUSE_NVM)
1054 strcat(out, "NVM ");
1055 if (val & RSTC_RCAUSE_EXT)
1056 strcat(out, "EXT ");
1057 if (val & RSTC_RCAUSE_WDT)
1058 strcat(out, "WDT ");
1059 if (val & RSTC_RCAUSE_SYST)
1060 strcat(out, "SYST ");
1061 if (val & RSTC_RCAUSE_BACKUP)
1062 strcat(out, "BACKUP ");
1063}
1064
Kévin Redon69b92d92019-01-24 16:39:20 +01001065int main(void)
1066{
Harald Welte65101be2019-04-18 18:30:49 +02001067 char sernr_buf[16*2+1];
Harald Welte9ab4bc82019-05-17 18:36:01 +02001068 char rstcause_buf[RSTCAUSE_STR_SIZE];
Harald Welte65101be2019-04-18 18:30:49 +02001069
Kévin Redon69b92d92019-01-24 16:39:20 +01001070 atmel_start_init();
Harald Welte65101be2019-04-18 18:30:49 +02001071 get_chip_unique_serial_str(sernr_buf, sizeof(sernr_buf));
Harald Welte9ab4bc82019-05-17 18:36:01 +02001072 get_rstcause_str(rstcause_buf);
Kévin Redon78d2f442019-01-24 18:45:59 +01001073
Kévin Redon8e538002019-01-30 11:19:19 +01001074 usb_start();
1075
Harald Weltec3f170d2019-02-24 09:06:59 +01001076 board_init();
Harald Welteff9f4ce2019-02-24 22:51:09 +01001077 command_init("sysmoOCTSIM> ");
Harald Welte1b9a5b82019-02-24 23:04:45 +01001078 command_register(&cmd_sim_status);
1079 command_register(&cmd_sim_power);
1080 command_register(&cmd_sim_reset);
1081 command_register(&cmd_sim_clkdiv);
1082 command_register(&cmd_sim_voltage);
1083 command_register(&cmd_sim_led);
Kévin Redonc89bb8c2019-04-17 01:20:23 +02001084 command_register(&cmd_sim_atr);
Kévin Redon096c5052019-05-09 15:01:17 +02001085 command_register(&cmd_sim_iccid);
Harald Welte67b2aba2019-04-16 20:47:22 +02001086 testmode_init();
Harald Welte8049d662019-04-17 21:19:18 +02001087 command_register(&cmd_talloc_test);
1088 command_register(&cmd_talloc_report);
1089 command_register(&cmd_talloc_free);
Harald Welte2dc67e92019-05-17 18:01:46 +02001090 command_register(&cmd_get_time);
Harald Welte1017a752019-05-17 20:39:49 +02001091 command_register(&cmd_test_timer);
Harald Weltec3f170d2019-02-24 09:06:59 +01001092
Harald Welte729a7622019-05-17 11:02:11 +02001093 printf("\r\n\r\n"
1094 "=============================================================================\n\r"
1095 "sysmoOCTSIM firmware " GIT_VERSION "\n\r"
1096 "(C) 2018-2019 by sysmocom - s.f.m.c. GmbH and contributors\n\r"
1097 "=============================================================================\n\r");
1098 printf("Chip ID: %s\r\n", sernr_buf);
Harald Welte9ab4bc82019-05-17 18:36:01 +02001099 printf("Reset cause: %s\r\n", rstcause_buf);
Harald Weltee7aa5342019-04-16 21:11:14 +02001100
Harald Welte8049d662019-04-17 21:19:18 +02001101 talloc_enable_null_tracking();
1102 g_tall_ctx = talloc_named_const(NULL, 0, "global");
1103 printf("g_tall_ctx=%p\r\n", g_tall_ctx);
Harald Weltebdf1b352019-05-17 10:21:45 +02001104
1105 libosmo_emb_init();
1106
1107 LOGP(DUSB, LOGL_ERROR, "foobar usb\n");
Harald Welte8049d662019-04-17 21:19:18 +02001108
Harald Weltee7aa5342019-04-16 21:11:14 +02001109 command_print_prompt();
Kévin Redon8e538002019-01-30 11:19:19 +01001110 while (true) { // main loop
Harald Welteff9f4ce2019-02-24 22:51:09 +01001111 command_try_recv();
Harald Welte5a8af4d2019-05-12 15:57:20 +02001112 poll_card_detect();
Harald Welte1017a752019-05-17 20:39:49 +02001113 osmo_timers_update();
Kévin Redon8e538002019-01-30 11:19:19 +01001114 }
Kévin Redon69b92d92019-01-24 16:39:20 +01001115}