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