terminal.c 12.3 KB
Newer Older
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
1 2 3 4
/**
 *  @file
 *  @brief Terminal client for access control system (ACS).
 *
5 6
 *				 Contains main processing loop.
 *
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
7 8 9
 *  @author Petr Elexa
 *  @see LICENSE
 *
10 11 12
 */

#include "terminal.h"
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
13
#include "board.h"
14
#include "weigand.h"
15
#include "watchdog.h"
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
16
#include "static_cache.h"
17 18 19
#include "FreeRTOS.h"
#include "task.h"
#include "stream_buffer.h"
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
20
#include "can/can_term_driver.h"
21
#include "acs_can_protocol.h"
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
22
#include <stdio.h>
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
23
#include <string.h>
24

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
25 26 27 28 29
/*****************************************************************************
 * Private types/enumerations/variables
 ****************************************************************************/

// How long to wait for user request.
30 31 32
static const uint16_t USER_REQUEST_WAIT_MS = 500;
// This is minimal period between each request.
// Also controls intensity of door status messages (sent each 2*period).
33 34
static const uint16_t USER_REQUEST_MIN_PERIOD_MS = 1000;

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
35 36
// Cache entry type mapping to our type.
typedef cache_item_t term_cache_item_t;  // 4 bytes
37

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
38
// Available cache values.
39
enum term_cache_reader
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
40
{
41 42 43 44
  cache_reader_none = 0,
  cache_reader_A = 1,
  cache_reader_B = 2,
  cache_reader_all = 3
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
45 46
};

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
47
// Address for currently active master.
48
static uint16_t _act_master = ACS_RESERVED_ADDR;
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
49

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
50
// Flag for master alive broadcast timeout.
51
static bool _master_timeout = true;
52

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
53
// Timer handle for master timeout.
54
static TimerHandle_t _act_timer = NULL;
55

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
56 57
// Timer ID for master timeout.
static const uint32_t _act_timer_id = TERMINAL_TIMER_ID;
58

59
// Last door open(true) / close(false) status
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
60
static bool _last_door_state[ACS_READER_MAXCOUNT] = {false, false};
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
61

62 63 64
// Signal request to clear cache.
static bool _cache_clear_req = false;

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
65 66 67
/*****************************************************************************
 * Private functions
 ****************************************************************************/
68

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
69
// Map reader index to correct cache value.
70
static inline uint8_t map_reader_idx_to_cache(uint8_t reader_idx)
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
71
{
72
  return (reader_idx == ACS_READER_A_IDX ? cache_reader_A : cache_reader_B);
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
73 74
}

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
75
static inline void _terminal_user_authorized(uint8_t reader_idx)
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
76 77
{
  DEBUGSTR("auth OK\n");
78
  reader_unlock(reader_idx, BEEP_ON_SUCCESS, OK_LED_ON_SUCCESS);
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
79 80
}

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
81
static inline void __terminal_user_not_authorized(uint8_t reader_idx)
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
82
{
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
83
  (void)reader_idx;
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
84 85
  DEBUGSTR("auth FAIL\n");
}
86

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
87
// Callback for timer dedicated to master master activity.
88 89 90 91
static void _timer_callback(TimerHandle_t pxTimer)
{
  configASSERT(pxTimer);

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
92
  // Which timer expired.
93 94 95 96
  uint32_t id = (uint32_t) pvTimerGetTimerID(pxTimer);

  if (id == _act_timer_id)
  {
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
97
    // Active master timeout after T = 2 * MASTER_ALIVE_TIMEOUT.
98 99 100 101
    portENTER_CRITICAL();
    if (_master_timeout == true)
    {
      _act_master = ACS_RESERVED_ADDR;
102
      Board_LED_Set(BOARD_LED_STATUS, false);
103 104 105 106 107 108
    }
    _master_timeout = true;
    portEXIT_CRITICAL();
  }
}

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
109 110 111 112
/*****************************************************************************
 * Public functions
 ****************************************************************************/

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
113
void term_can_error(uint32_t error_info)
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
114
{
115 116 117 118 119
  if (error_info == CAN_ERROR_BOFF) // if bus off do reset by WDG timeout
  {
  	WDT_Feed();
    configASSERT(false);
  }
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
120 121 122 123 124
}

void term_can_send(uint8_t msg_obj_num)
{
  (void)msg_obj_num;
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
125 126
}

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
127
// This is called from interrupt. We must not block.
128
void term_can_recv(uint8_t msg_obj_num)
129
{
130
  CCAN_MSG_OBJ_T msg_obj;
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
131
  // Determine which CAN message has been received.
132
  msg_obj.msgobj = msg_obj_num;
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
133
  // Now load up the msg_obj structure with the CAN message.
134 135
  LPC_CCAN_API->can_receive(&msg_obj);

136
  acs_msg_head_t head;
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
137
  head.scalar = msg_obj.mode_id;
138

139
  uint8_t reader_idx;
140

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
141
  // Get target door if message is for us.
142
  if (msg_obj.msgobj == ACS_MSGOBJ_RECV_DOOR_A)
143
  {
144
    reader_idx = ACS_READER_A_IDX;
145 146 147 148
    DEBUGSTR("for door A\n");
  }
  else if (msg_obj.msgobj == ACS_MSGOBJ_RECV_DOOR_B)
  {
149
    reader_idx = ACS_READER_B_IDX;
150 151 152 153
    DEBUGSTR("for door B\n");
  }
  else if (msg_obj.msgobj == ACS_MSGOBJ_RECV_BCAST)
  {
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
154
    // Broadcast message.
155 156 157
    if (head.fc == FC_ALIVE &&
        head.src >= ACS_MSTR_FIRST_ADDR &&
        head.src <= ACS_MSTR_LAST_ADDR)
158
    {
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
159
      // Update master address if timeout occurred.
160
      portENTER_CRITICAL();
161 162 163 164
      if (_master_timeout == true)
      {
        _act_master = head.src;
        Board_LED_Set(BOARD_LED_STATUS, true);
165
        _cache_clear_req = true;
166
      }
167 168 169
      _master_timeout = false;
      portEXIT_CRITICAL();
      DEBUGSTR("master alive\n");
170
    }
171
    return;
172 173
  }
  else return;
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
174

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
175
  // Stop processing if card reader not configured.
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
176
  if (reader_idx >= ACS_READER_MAXCOUNT || !reader_conf[reader_idx].enabled) return;
177

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
178
  // Continue deducing action and execute it.
179
  if (head.fc == FC_USER_AUTH_RESP)
180
  {
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
181
    _terminal_user_authorized(reader_idx);
182

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
183
    #if CACHING_ENABLED
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
184 185 186 187 188
      uint32_t user_id;
      uint8_t len = msg_obj.dlc > sizeof(user_id) ? sizeof(user_id) : msg_obj.dlc;
      memcpy(&user_id, msg_obj.data, len);
      term_cache_item_t user;
      user.key = user_id;
189
      user.value = map_reader_idx_to_cache(reader_idx);
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
190 191
      static_cache_insert(user);
    #endif
192
  }
193
  else if (head.fc == FC_USER_NOT_AUTH_RESP)
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
194
  {
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
195
    __terminal_user_not_authorized(reader_idx);
196

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
197
    #if CACHING_ENABLED
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
198 199 200 201 202
      uint32_t user_id;
      uint8_t len = msg_obj.dlc >= sizeof(user_id) ? sizeof(user_id) : msg_obj.dlc;
      memcpy(&user_id, msg_obj.data, len);
      term_cache_item_t user;
      user.key = user_id;
203
      user.value = cache_reader_none;
204 205
      static_cache_insert(user);
    #endif
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
206
  }
207
  else if (head.fc == FC_DOOR_CTRL)
208 209 210
  {
    switch (msg_obj.data[0])
    {
211
      case DATA_DOOR_CTRL_REMOTE_UNLCK:
212
        DEBUGSTR("cmd UNLOCK\n");
213
        reader_unlock(reader_idx, BEEP_ON_SUCCESS, OK_LED_ON_SUCCESS);
214
        break;
215
      case DATA_DOOR_CTRL_CLR_CACHE:
216
        DEBUGSTR("cmd CLR CACHE\n");
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
217
#if CACHING_ENABLED
218
        _cache_clear_req = true;
219
#endif
220 221 222 223 224 225
        break;
      default:
        break;
    }
  }
  else return;
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
226 227
}

228
// Send door status update to server
229
static void terminal_send_door_status(uint8_t reader_idx, bool is_open)
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
230
{
231 232 233 234 235 236 237
  //check if master online
  if (_act_master == ACS_RESERVED_ADDR)
  {
    DEBUGSTR("master offline\n");
    return;
  }

238
  // Prepare msg head to send request on CAN
239
  acs_msg_head_t head;
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
240
  head.scalar = CAN_MSGOBJ_EXT;
241 242
  head.prio = PRIO_DOOR_STATUS;
  head.fc = FC_DOOR_STATUS;
243
  head.dst = _act_master;
244

245
  uint8_t status = (is_open ? DATA_DOOR_STATUS_OPEN : DATA_DOOR_STATUS_CLOSED);
246

247
  if (reader_idx == ACS_READER_A_IDX)
248
  {
249
    head.src = get_reader_a_addr();
250
    CAN_send_once(ACS_MSGOBJ_SEND_DOOR_A, head.scalar, (void *)&status, sizeof(status));
251
  }
252
  else if (reader_idx == ACS_READER_B_IDX)
253
  {
254
    head.src = get_reader_b_addr();
255
    CAN_send_once(ACS_MSGOBJ_SEND_DOOR_B, head.scalar, (void *)&status, sizeof(status));
256
  }
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
257 258
}

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
259
// Send command to server that request authorization of user for given reader.
260
static void terminal_request_auth(uint32_t user_id, uint8_t reader_idx)
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
261
{
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
262
  // Check if master online.
263 264
  if (_act_master == ACS_RESERVED_ADDR)
  {
265
    DEBUGSTR("master off-line\n");
266 267 268
    return;
  }

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
269
  // Prepare message head to send request on CAN.
270
  acs_msg_head_t head;
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
271
  head.scalar = CAN_MSGOBJ_EXT;
272 273
  head.prio = PRIO_USER_AUTH_REQ;
  head.fc = FC_USER_AUTH_REQ;
274
  head.dst = _act_master;
275

276
  if (reader_idx == ACS_READER_A_IDX)
277
  {
278
    head.src = get_reader_a_addr();
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
279
    CAN_send_once(ACS_MSGOBJ_SEND_DOOR_A, head.scalar, (void *)&user_id, sizeof(user_id));
280
  }
281
  else if (reader_idx == ACS_READER_B_IDX)
282
  {
283
    head.src = get_reader_b_addr();
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
284
    CAN_send_once(ACS_MSGOBJ_SEND_DOOR_B, head.scalar, (void *)&user_id, sizeof(user_id));
285
  }
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
286 287
}

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
288
// Process user identification on a reader.
289
static void terminal_user_identified(uint32_t user_id, uint8_t reader_idx)
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
290
{
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
291 292 293
#if CACHING_ENABLED
  term_cache_item_t user = {.key = user_id};
#endif
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
294

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
295
  if (reader_idx < ACS_READER_MAXCOUNT && reader_conf[reader_idx].enabled)
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
296
  {
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
297
#if CACHING_ENABLED
298 299
  	// Read from cache online if master is offline.
    if ((_act_master == ACS_RESERVED_ADDR) && static_cache_get(&user))
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
300
    {
301
      if (map_reader_idx_to_cache(reader_idx) & user.value)
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
302
      {
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
303
        _terminal_user_authorized(reader_idx);
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
304
      }
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
305
    }
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
306
    else
307
#else
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
308
    {
309
      terminal_request_auth(user_id, reader_idx);
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
310
    }
311
#endif
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
312
  }
313 314
}

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
315
// Main loop in terminal processing task.
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
316
//
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
317
// Waked only when request is in the reader buffer or after timeout.
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
318
static void terminal_task(void *pvParameters)
319
{
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
320 321
  (void)pvParameters;

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
322
  // start timer for detecting master timeout
323 324
  configASSERT(xTimerStart(_act_timer, 0));

325 326
  bool send_door_status = true;

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
327 328
  WDT_Feed(); // Feed HW watchdog

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
329 330
  while (true)
  {
331 332
    TickType_t begin_time = xTaskGetTickCount();

333 334
    // Get pending user request
    uint32_t user_id;
335

336
    uint8_t reader_idx = reader_get_request_from_buffer(&user_id, USER_REQUEST_WAIT_MS);
337

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
338
    if (reader_idx < ACS_READER_MAXCOUNT && reader_conf[reader_idx].enabled)
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
339
    {
340
      DEBUGSTR("user req\n");
341
      terminal_user_identified(user_id, reader_idx);
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
342
    }
343 344 345

#if CACHING_ENABLED
    if (_cache_clear_req)
346
    {
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
    	_cache_clear_req = false;
      static_cache_reset();
    }
#endif

    // Calculate actual processing time.
		TickType_t proces_time = xTaskGetTickCount() - begin_time;

    // Protect against brute-force attack by limiting processing frequency.
    // Also controls frequency of door status update.
    if (proces_time < pdMS_TO_TICKS(USER_REQUEST_MIN_PERIOD_MS))
		{
      vTaskDelay(pdMS_TO_TICKS(USER_REQUEST_MIN_PERIOD_MS) - proces_time);
		}

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
362 363
    WDT_Feed(); // Feed HW watchdog

364 365 366
    if (send_door_status)
    {
			// Check if door open/close state changed
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
367
			for (size_t idx = 0; idx < ACS_READER_MAXCOUNT; ++idx)
368
			{
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
369
				if (reader_conf[idx].enabled && (reader_is_door_open(idx) != _last_door_state[idx]))
370 371 372 373 374 375
				{
					DEBUGSTR("new door state\n");
					_last_door_state[idx] = !_last_door_state[idx];
					terminal_send_door_status(idx, _last_door_state[idx]);
				}
			}
376
    }
377
    send_door_status = !send_door_status;
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
378 379 380 381 382
  }
}

void terminal_init(void)
{
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
383
  // Initialize configuration for terminal.
384
  configASSERT(terminal_config_init());
385

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
386
  // Assign CAN callback functions of on-chip drivers.
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
387 388
  CCAN_CALLBACKS_T term_can_callbacks =
  {
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
389 390 391 392 393 394 395 396 397
    term_can_recv,  // Callback for any message received CAN frame which ID
                    // matches with any of the message objects' masks.
    term_can_send,  // Callback for every transmitted CAN frame.
    term_can_error, // Callback for CAN errors.
    NULL,           // Not used.
    NULL,           // Not used.
    NULL,           // Not used.
    NULL,           // Not used.
    NULL,           // Not used.
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
398 399
  };

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
400
  // Init CAN driver.
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
401
  CAN_init(&term_can_callbacks, CAN_BAUD_RATE);
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
402

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
403
  // CAN msg filter for door A.
404
  CAN_recv_filter(ACS_MSGOBJ_RECV_DOOR_A,
405
                  get_reader_a_addr() << ACS_DST_ADDR_OFFSET,
406
                  ACS_DST_ADDR_MASK, true);
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
407
  // CAN msg filter for door B.
408
  CAN_recv_filter(ACS_MSGOBJ_RECV_DOOR_B,
409
                  get_reader_b_addr() << ACS_DST_ADDR_OFFSET,
410
                  ACS_DST_ADDR_MASK, true);
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
411
  // CAN msg filter for broadcast.
412 413 414 415
  CAN_recv_filter(ACS_MSGOBJ_RECV_BCAST,
                  ACS_BROADCAST_ADDR << ACS_DST_ADDR_OFFSET,
                  ACS_DST_ADDR_MASK, true);

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
416
  // Initialize card readers.
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
417
  for (size_t id = 0; id < ACS_READER_MAXCOUNT; ++id)
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
418
  {
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
419
  	// Config only present CR
420
    if (reader_conf[id].enabled) terminal_reconfigure(NULL, id);
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
421
  }
422

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
423
  // Create timer for master alive status timeout.
424
  _act_timer = xTimerCreate("MAT", (ACS_MASTER_ALIVE_TIMEOUT_MS / portTICK_PERIOD_MS),
425 426
               pdTRUE, (void *)_act_timer_id, _timer_callback);
  configASSERT(_act_timer);
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
427

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
428
  // Create task for terminal loop.
429
  xTaskCreate(terminal_task, "term_tsk", configMINIMAL_STACK_SIZE + 128, NULL, (tskIDLE_PRIORITY + 1UL), NULL);
430 431
}

432
void terminal_reconfigure(reader_conf_t * reader_cfg, uint8_t reader_idx)
433
{
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
434
  if (reader_idx >= ACS_READER_MAXCOUNT) return;
435

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
436
  portENTER_CRITICAL(); // Effectively disables interrupts.
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
437

438
  if (reader_cfg != NULL)
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
439
  {
440
    memcpy(&reader_conf[reader_idx], reader_cfg, sizeof(reader_conf_t));
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
441

Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
442
    // Reconfigure the reader instance.
443
    if (reader_conf[reader_idx].enabled)
444
    {
445 446
      reader_init(reader_idx);
      DEBUGSTR("reader enabled\n");
447 448 449
    }
    else
    {
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
450
      // Disable interface.
451 452
      reader_deinit(reader_idx);
      DEBUGSTR("reader disabled\n");
453
    }
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
454
  }
455
  else
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
456
  {
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
457
    // No new configuration given -- just init.
458
    reader_init(reader_idx);
Bc. Petr Elexa's avatar
Bc. Petr Elexa committed
459
  }
460 461

  portEXIT_CRITICAL();
462
}