blob: 059c99dfe191cb729a698839060bf4b388df0906 [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
Eric Wildf5aa5892019-11-27 18:16:17 +010043#include "ccid_device.h"
44#include "usb_descriptors.h"
Harald Welte03d6ebb2019-09-28 23:19:31 +020045extern struct ccid_slot_ops iso_fsm_slot_ops;
46static struct ccid_instance g_ci;
Kévin Redonc89bb8c2019-04-17 01:20:23 +020047
Kévin Redon072951b2019-05-02 15:17:46 +020048
Harald Weltec7a58ba2019-04-18 17:59:19 +020049static void ccid_app_init(void);
50
Harald Weltec3f170d2019-02-24 09:06:59 +010051static void board_init()
52{
53 int i;
54
55 for (i = 0; i < 4; i++)
56 i2c_init(&i2c[i]);
57
Harald Welte255da5e2019-04-16 18:19:53 +020058 for (i = 0; i < 8; i++)
Harald Weltec3f170d2019-02-24 09:06:59 +010059 ncn8025_init(i);
Harald Weltef53f2262019-02-24 11:01:08 +010060
61 cache_init();
62 cache_enable(CMCC);
Harald Welted1bd5c42019-05-17 16:38:30 +020063 calendar_enable(&CALENDAR_0);
Harald Welte93f628a2019-02-24 14:32:30 +010064
65 /* increase drive strength of 20Mhz SIM clock output to 8mA
66 * (there are 8 inputs + traces to drive!) */
67 hri_port_set_PINCFG_DRVSTR_bit(PORT, 0, 11);
Kévin Redonc89bb8c2019-04-17 01:20:23 +020068
Harald Weltec7a58ba2019-04-18 17:59:19 +020069 ccid_app_init();
Harald Weltec3f170d2019-02-24 09:06:59 +010070}
71
Harald Weltec7a58ba2019-04-18 17:59:19 +020072/***********************************************************************
73 * CCID Driver integration
74 ***********************************************************************/
75
76#include <osmocom/core/linuxlist.h>
77#include <osmocom/core/msgb.h>
78#include "linuxlist_atomic.h"
79#include "ccid_df.h"
80
81struct usb_ep_q {
82 const char *name;
83 /* msgb queue of pending to-be-transmitted (IN/IRQ) or completed received (OUT)
84 * USB transfers */
85 struct llist_head list;
86 /* currently ongoing/processed msgb (USB transmit or receive */
87 struct msgb *in_progress;
88};
89
90struct ccid_state {
91 /* msgb queue of free msgs */
92 struct llist_head free_q;
93
94 /* msgb queue of pending to-be-transmitted (IN EP) */
95 struct usb_ep_q in_ep;
96 /* msgb queue of pending to-be-transmitted (IRQ EP) */
97 struct usb_ep_q irq_ep;
98 /* msgb queue of completed received (OUT EP) */
99 struct usb_ep_q out_ep;
Harald Welte5a8af4d2019-05-12 15:57:20 +0200100
101 /* bit-mask of card-insert status, as determined from NCN8025 IRQ output */
102 uint8_t card_insert_mask;
Harald Weltec7a58ba2019-04-18 17:59:19 +0200103};
104static struct ccid_state g_ccid_s;
105
106static void ccid_out_read_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred);
107static void ccid_in_write_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred);
108static void ccid_irq_write_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred);
109
110static void usb_ep_q_init(struct usb_ep_q *ep_q, const char *name)
111{
112 ep_q->name = name;
113 INIT_LLIST_HEAD(&ep_q->list);
114 ep_q->in_progress = NULL;
115}
116
117static void ccid_app_init(void)
118{
119 /* initialize data structures */
120 INIT_LLIST_HEAD(&g_ccid_s.free_q);
121 usb_ep_q_init(&g_ccid_s.in_ep, "IN");
122 usb_ep_q_init(&g_ccid_s.irq_ep, "IRQ");
123 usb_ep_q_init(&g_ccid_s.out_ep, "OUT");
124
125 /* OUT endpoint read complete callback (irq context) */
126 ccid_df_register_callback(CCID_DF_CB_READ_OUT, (FUNC_PTR)&ccid_out_read_compl);
127 /* IN endpoint write complete callback (irq context) */
128 ccid_df_register_callback(CCID_DF_CB_WRITE_IN, (FUNC_PTR)&ccid_in_write_compl);
129 /* IRQ endpoint write complete callback (irq context) */
130 ccid_df_register_callback(CCID_DF_CB_WRITE_IRQ, (FUNC_PTR)&ccid_irq_write_compl);
131}
132
133/* irqsafe version of msgb_enqueue */
134struct msgb *msgb_dequeue_irqsafe(struct llist_head *q)
135{
136 struct msgb *msg;
137 CRITICAL_SECTION_ENTER()
138 msg = msgb_dequeue(q);
139 CRITICAL_SECTION_LEAVE()
140 return msg;
141}
142
Harald Welte5a8af4d2019-05-12 15:57:20 +0200143void msgb_enqueue_irqsafe(struct llist_head *q, struct msgb *msg)
144{
145 CRITICAL_SECTION_ENTER()
146 msgb_enqueue(q, msg);
147 CRITICAL_SECTION_LEAVE()
148}
149
Harald Weltec7a58ba2019-04-18 17:59:19 +0200150/* submit the next pending (if any) message for the IN EP */
151static int submit_next_in(void)
152{
153 struct usb_ep_q *ep_q = &g_ccid_s.in_ep;
154 struct msgb *msg;
155 int rc;
156
Harald Welte03d6ebb2019-09-28 23:19:31 +0200157 if (ep_q->in_progress)
158 return 0;
159
Harald Weltec7a58ba2019-04-18 17:59:19 +0200160 msg = msgb_dequeue_irqsafe(&ep_q->list);
161 if (!msg)
162 return 0;
163
164 ep_q->in_progress = msg;
165 rc = ccid_df_write_in(msgb_data(msg), msgb_length(msg));
166 if (rc != ERR_NONE) {
167 printf("EP %s failed: %d\r\n", ep_q->name, rc);
168 return -1;
169 }
170 return 1;
171
172}
173
174/* submit the next pending (if any) message for the IRQ EP */
175static int submit_next_irq(void)
176{
177 struct usb_ep_q *ep_q = &g_ccid_s.irq_ep;
178 struct msgb *msg;
179 int rc;
180
Eric Wildeaafa9f2019-10-01 15:22:14 +0200181 if (ep_q->in_progress)
182 return 0;
183
Harald Weltec7a58ba2019-04-18 17:59:19 +0200184 msg = msgb_dequeue_irqsafe(&ep_q->list);
185 if (!msg)
186 return 0;
187
188 ep_q->in_progress = msg;
189 rc = ccid_df_write_irq(msgb_data(msg), msgb_length(msg));
190 /* may return HALTED/ERROR/DISABLED/BUSY/ERR_PARAM/ERR_FUNC/ERR_DENIED */
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
198static int submit_next_out(void)
199{
200 struct usb_ep_q *ep_q = &g_ccid_s.out_ep;
201 struct msgb *msg;
202 int rc;
203
204 OSMO_ASSERT(!ep_q->in_progress);
205 msg = msgb_dequeue_irqsafe(&g_ccid_s.free_q);
206 if (!msg)
207 return -1;
Harald Welte03d6ebb2019-09-28 23:19:31 +0200208 msgb_reset(msg);
Harald Weltec7a58ba2019-04-18 17:59:19 +0200209 ep_q->in_progress = msg;
210
211 rc = ccid_df_read_out(msgb_data(msg), msgb_tailroom(msg));
212 if (rc != ERR_NONE) {
213 /* re-add to the list of free msgb's */
214 llist_add_tail_at(&g_ccid_s.free_q, &msg->list);
215 return 0;
216 }
217 return 1;
218}
219
220/* OUT endpoint read complete callback (irq context) */
221static void ccid_out_read_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred)
222{
223 struct msgb *msg = g_ccid_s.out_ep.in_progress;
224
225 /* add just-received msg to tail of endpoint queue */
226 OSMO_ASSERT(msg);
227 /* update msgb with the amount of data received */
228 msgb_put(msg, transferred);
229 /* append to list of pending-to-be-handed messages */
230 llist_add_tail_at(&msg->list, &g_ccid_s.out_ep.list);
Harald Welte03d6ebb2019-09-28 23:19:31 +0200231 g_ccid_s.out_ep.in_progress = NULL;
Harald Weltec7a58ba2019-04-18 17:59:19 +0200232
233 /* submit another [free] msgb to receive the next transfer */
234 submit_next_out();
235}
236
237/* IN endpoint write complete callback (irq context) */
238static void ccid_in_write_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred)
239{
240 struct msgb *msg = g_ccid_s.in_ep.in_progress;
241
242 OSMO_ASSERT(msg);
243 /* return the message back to the queue of free message buffers */
244 llist_add_tail_at(&msg->list, &g_ccid_s.free_q);
245 g_ccid_s.in_ep.in_progress = NULL;
246
247 /* submit the next pending to-be-transmitted msgb (if any) */
248 submit_next_in();
249}
250
251/* IRQ endpoint write complete callback (irq context) */
252static void ccid_irq_write_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred)
253{
254 struct msgb *msg = g_ccid_s.irq_ep.in_progress;
255
256 OSMO_ASSERT(msg);
257 /* return the message back to the queue of free message buffers */
258 llist_add_tail_at(&msg->list, &g_ccid_s.free_q);
259 g_ccid_s.irq_ep.in_progress = NULL;
260
261 /* submit the next pending to-be-transmitted msgb (if any) */
262 submit_next_irq();
263}
264
Harald Welte5a8af4d2019-05-12 15:57:20 +0200265#include "ccid_proto.h"
Eric Wild2de998a2019-10-01 15:20:32 +0200266static struct msgb *ccid_gen_notify_slot_status(uint8_t old_bm, uint8_t new_bm)
Harald Welte5a8af4d2019-05-12 15:57:20 +0200267{
Eric Wild2de998a2019-10-01 15:20:32 +0200268 uint8_t statusbytes[2] = {0};
Harald Welte5a8af4d2019-05-12 15:57:20 +0200269 //struct msgb *msg = ccid_msgb_alloc();
270 struct msgb *msg = msgb_alloc(64, "IRQ");
Eric Wild2de998a2019-10-01 15:20:32 +0200271 struct ccid_rdr_to_pc_notify_slot_change *nsc = msgb_put(msg, sizeof(*nsc) + sizeof(statusbytes));
Harald Welte5a8af4d2019-05-12 15:57:20 +0200272 nsc->bMessageType = RDR_to_PC_NotifySlotChange;
Eric Wild2de998a2019-10-01 15:20:32 +0200273
274 for(int i = 0; i <8; i++) {
275 uint8_t byteidx = i >> 2;
276 uint8_t old_bit = old_bm & (1 << i);
277 uint8_t new_bit = new_bm & (1 << i);
278 uint8_t bv;
279 if (old_bit == new_bit && new_bit == 0)
280 bv = 0x00;
281 else if (old_bit == new_bit && new_bit == 1)
282 bv = 0x01;
283 else if (old_bit != new_bit && new_bit == 0)
284 bv = 0x02;
285 else
286 bv = 0x03;
287
288 statusbytes[byteidx] |= bv << ((i % 4) << 1);
289 }
290
291 memcpy(&nsc->bmSlotCCState, statusbytes, sizeof(statusbytes));
Harald Welte5a8af4d2019-05-12 15:57:20 +0200292
293 return msg;
294}
295
296/* check if any card detect state has changed */
297static void poll_card_detect(void)
298{
299 uint8_t new_mask = 0;
300 struct msgb *msg;
301 unsigned int i;
302
Harald Welte03d6ebb2019-09-28 23:19:31 +0200303 for (i = 0; i < 8; i++){
304 bool level = ncn8025_interrupt_level(i);
305 new_mask |= level << i;
306 g_ci.slot[i].icc_present = level;
307 }
Harald Welte5a8af4d2019-05-12 15:57:20 +0200308
309 /* notify the user/host about any changes */
310 if (g_ccid_s.card_insert_mask != new_mask) {
311 printf("CARD_DET 0x%02x -> 0x%02x\r\n",
312 g_ccid_s.card_insert_mask, new_mask);
Eric Wild2de998a2019-10-01 15:20:32 +0200313 msg = ccid_gen_notify_slot_status(g_ccid_s.card_insert_mask, new_mask);
Harald Welte5a8af4d2019-05-12 15:57:20 +0200314 msgb_enqueue_irqsafe(&g_ccid_s.irq_ep.list, msg);
315
316 g_ccid_s.card_insert_mask = new_mask;
317 }
318}
319
320
Harald Weltec7a58ba2019-04-18 17:59:19 +0200321
322/***********************************************************************
323 * Command Line interface
324 ***********************************************************************/
325
Harald Welte2dc67e92019-05-17 18:01:46 +0200326
Harald Welte67b2aba2019-04-16 20:47:22 +0200327extern void testmode_init(void);
Harald Weltebdf1b352019-05-17 10:21:45 +0200328extern void libosmo_emb_init(void);
Harald Welte1017a752019-05-17 20:39:49 +0200329extern void libosmo_emb_mainloop(void);
Harald Welte1b9a5b82019-02-24 23:04:45 +0100330
Harald Welte8049d662019-04-17 21:19:18 +0200331#include "talloc.h"
Harald Weltebdf1b352019-05-17 10:21:45 +0200332#include "logging.h"
Harald Welte03d6ebb2019-09-28 23:19:31 +0200333
Harald Welte8049d662019-04-17 21:19:18 +0200334void *g_tall_ctx;
335
Harald Welte8049d662019-04-17 21:19:18 +0200336
Harald Welte65101be2019-04-18 18:30:49 +0200337/* Section 9.6 of SAMD5x/E5x Family Data Sheet */
338static int get_chip_unique_serial(uint8_t *out, size_t len)
339{
340 uint32_t *out32 = (uint32_t *)out;
341 if (len < 16)
342 return -EINVAL;
343
344 out32[0] = *(uint32_t *)0x008061fc;
345 out32[1] = *(uint32_t *)0x00806010;
346 out32[2] = *(uint32_t *)0x00806014;
347 out32[3] = *(uint32_t *)0x00806018;
348
349 return 0;
350}
351
352/* same as get_chip_unique_serial but in hex-string format */
353static int get_chip_unique_serial_str(char *out, size_t len)
354{
355 uint8_t buf[16];
356 int rc;
357
358 if (len < 16*2 + 1)
359 return -EINVAL;
360
361 rc = get_chip_unique_serial(buf, sizeof(buf));
362 if (rc < 0)
363 return rc;
364 osmo_hexdump_buf(out, len, buf, sizeof(buf), NULL, false);
365 return 0;
366}
367
Harald Welte9ab4bc82019-05-17 18:36:01 +0200368#define RSTCAUSE_STR_SIZE 64
369static void get_rstcause_str(char *out)
370{
371 uint8_t val = hri_rstc_read_RCAUSE_reg(RSTC);
372 *out = '\0';
373 if (val & RSTC_RCAUSE_POR)
374 strcat(out, "POR ");
375 if (val & RSTC_RCAUSE_BODCORE)
376 strcat(out, "BODCORE ");
377 if (val & RSTC_RCAUSE_BODVDD)
378 strcat(out, "BODVDD ");
379 if (val & RSTC_RCAUSE_NVM)
380 strcat(out, "NVM ");
381 if (val & RSTC_RCAUSE_EXT)
382 strcat(out, "EXT ");
383 if (val & RSTC_RCAUSE_WDT)
384 strcat(out, "WDT ");
385 if (val & RSTC_RCAUSE_SYST)
386 strcat(out, "SYST ");
387 if (val & RSTC_RCAUSE_BACKUP)
388 strcat(out, "BACKUP ");
389}
390
Harald Welte03d6ebb2019-09-28 23:19:31 +0200391//#######################
392
393
394
395static uint32_t clock_freqs[] = {
396 2500000
397};
398
399static uint32_t data_rates[] = {
400 6720
401};
402extern struct usb_desc_collection usb_fs_descs;
403
404
405
406static int feed_ccid(void)
407{
408 struct usb_ep_q *ep_q = &g_ccid_s.out_ep;
409 struct msgb *msg;
410 int rc;
411
412 msg = msgb_dequeue_irqsafe(&g_ccid_s.out_ep.list);
413 if (!msg)
414 return -1;
415
416 ccid_handle_out(&g_ci, msg);
417 return 1;
418}
419
420static int ccid_ops_send_in(struct ccid_instance *ci, struct msgb *msg)
421{
422 /* add just-received msg to tail of endpoint queue */
423 OSMO_ASSERT(msg);
424
425 /* append to list of pending-to-be-handed messages */
426 llist_add_tail_at(&msg->list, &g_ccid_s.in_ep.list);
427 submit_next_in();
428 return 0;
429}
430
431static const struct ccid_ops c_ops = {
432 .send_in = ccid_ops_send_in,
433 .send_int = 0,
434};
435
436//#######################
437
438#define NUM_OUT_BUF 7
439
Kévin Redon69b92d92019-01-24 16:39:20 +0100440int main(void)
441{
Harald Welte65101be2019-04-18 18:30:49 +0200442 char sernr_buf[16*2+1];
Harald Welte9ab4bc82019-05-17 18:36:01 +0200443 char rstcause_buf[RSTCAUSE_STR_SIZE];
Harald Welte65101be2019-04-18 18:30:49 +0200444
Kévin Redon69b92d92019-01-24 16:39:20 +0100445 atmel_start_init();
Harald Welte65101be2019-04-18 18:30:49 +0200446 get_chip_unique_serial_str(sernr_buf, sizeof(sernr_buf));
Harald Welte9ab4bc82019-05-17 18:36:01 +0200447 get_rstcause_str(rstcause_buf);
Kévin Redon78d2f442019-01-24 18:45:59 +0100448
Kévin Redon8e538002019-01-30 11:19:19 +0100449 usb_start();
450
Harald Weltec3f170d2019-02-24 09:06:59 +0100451 board_init();
Harald Welteff9f4ce2019-02-24 22:51:09 +0100452 command_init("sysmoOCTSIM> ");
Harald Weltec3f170d2019-02-24 09:06:59 +0100453
Harald Welte729a7622019-05-17 11:02:11 +0200454 printf("\r\n\r\n"
455 "=============================================================================\n\r"
456 "sysmoOCTSIM firmware " GIT_VERSION "\n\r"
457 "(C) 2018-2019 by sysmocom - s.f.m.c. GmbH and contributors\n\r"
458 "=============================================================================\n\r");
459 printf("Chip ID: %s\r\n", sernr_buf);
Harald Welte9ab4bc82019-05-17 18:36:01 +0200460 printf("Reset cause: %s\r\n", rstcause_buf);
Harald Weltee7aa5342019-04-16 21:11:14 +0200461
Harald Welte8049d662019-04-17 21:19:18 +0200462 talloc_enable_null_tracking();
463 g_tall_ctx = talloc_named_const(NULL, 0, "global");
464 printf("g_tall_ctx=%p\r\n", g_tall_ctx);
Harald Weltebdf1b352019-05-17 10:21:45 +0200465
466 libosmo_emb_init();
467
468 LOGP(DUSB, LOGL_ERROR, "foobar usb\n");
Harald Welte8049d662019-04-17 21:19:18 +0200469
Harald Welte03d6ebb2019-09-28 23:19:31 +0200470 ccid_instance_init(&g_ci, &c_ops, &iso_fsm_slot_ops, &usb_fs_descs.ccid.class,
471 data_rates, clock_freqs, "", 0);
472
473 for(int i =0; i < NUM_OUT_BUF; i++){
474 struct msgb *msg = msgb_alloc(300, "ccid");
475 OSMO_ASSERT(msg);
476 /* return the message back to the queue of free message buffers */
477 llist_add_tail_at(&msg->list, &g_ccid_s.free_q);
478 }
479 submit_next_out();
480
481// command_print_prompt();
Kévin Redon8e538002019-01-30 11:19:19 +0100482 while (true) { // main loop
Harald Welteff9f4ce2019-02-24 22:51:09 +0100483 command_try_recv();
Harald Welte5a8af4d2019-05-12 15:57:20 +0200484 poll_card_detect();
Eric Wildeaafa9f2019-10-01 15:22:14 +0200485 submit_next_irq();
Harald Welte03d6ebb2019-09-28 23:19:31 +0200486 feed_ccid();
Harald Welte1017a752019-05-17 20:39:49 +0200487 osmo_timers_update();
Harald Welte03d6ebb2019-09-28 23:19:31 +0200488 int qs = llist_count_at(&g_ccid_s.free_q);
489 if(qs > NUM_OUT_BUF)
490 for (int i= 0; i < qs-NUM_OUT_BUF; i++){
491 struct msgb *msg = msgb_dequeue_irqsafe(&g_ccid_s.free_q);
492 if (msg)
493 msgb_free(msg);
494 }
495 if(qs < NUM_OUT_BUF)
496 for (int i= 0; i < qs-NUM_OUT_BUF; i++){
497 struct msgb *msg = msgb_alloc(300,"ccid");
498 OSMO_ASSERT(msg);
499 /* return the message back to the queue of free message buffers */
500 llist_add_tail_at(&msg->list, &g_ccid_s.free_q);
501 }
502
503
Kévin Redon8e538002019-01-30 11:19:19 +0100504 }
Kévin Redon69b92d92019-01-24 16:39:20 +0100505}