Host Communication Protocol  2.0
fpc_hcp.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Fingerprint Cards AB
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include "fpc_hcp.h"
26 
33 #define PACKET_ID_SIZE sizeof(((fpc_hcp_packet_t*)0)->id)
34 #define PACKET_NUM_ARGS_SIZE sizeof(((fpc_hcp_packet_t*)0)->num_args)
35 #define PACKET_HEADER_SIZE (PACKET_ID_SIZE + PACKET_NUM_ARGS_SIZE)
36 
44 #define ARGUMENT_ARG_SIZE sizeof(((fpc_hcp_arg_data_t*)0)->arg)
45 #define ARGUMENT_SIZE_SIZE sizeof(((fpc_hcp_arg_data_t*)0)->size)
46 #define ARGUMENT_HEADER_SIZE (ARGUMENT_ARG_SIZE + ARGUMENT_SIZE_SIZE)
47 
63 
64 uint16_t fpc_hcp_get_size(fpc_hcp_packet_t *packet, uint16_t *num_args)
65 {
66  uint16_t size = 0;
67  uint16_t args = 0;
68 
69  if (packet == NULL) {
70  goto exit;
71  }
72  size = PACKET_HEADER_SIZE;
73 
74  for (uint8_t i = 0; i < packet->num_args; i++) {
75  if (packet->arguments[i].arg != ARG_NONE) {
76  size += ARGUMENT_HEADER_SIZE;
77  size += packet->arguments[i].size;
78  args++;
79  }
80  }
81  if (num_args) {
82  *num_args = args;
83  }
84 
85 exit:
86  return size;
87 }
88 
90 {
91  fpc_com_result_t result;
92 
93  if (chain == NULL || packet == NULL || chain->initialized == false ||
94  chain->phy_mtu_buffer[FPC_COM_CHAIN_TX] == NULL) {
96  goto exit;
97  }
98  chain->private_vars.hcp_packet = packet;
99 
100  if (chain->app_mtu_size[FPC_COM_CHAIN_TX] == 0 ||
101  chain->app_mtu_buffer[FPC_COM_CHAIN_TX] == NULL) {
102  uint16_t tsp_offset;
103  uint16_t link_offset;
104  uint16_t overhead;
105 
106  overhead = chain->tsp_overhead_get(&tsp_offset) + chain->link_overhead_get(&link_offset);
108  + tsp_offset + link_offset;
109  chain->app_mtu_size[FPC_COM_CHAIN_TX] = chain->phy_mtu_size[FPC_COM_CHAIN_TX] - overhead;
110  }
111  result = transmit_chunks(chain);
112 
113 exit:
114  return result;
115 }
116 
118 {
119  fpc_com_result_t result;
120 
121  if (chain == NULL || packet == NULL || chain->initialized == false ||
122  chain->phy_mtu_buffer[FPC_COM_CHAIN_RX] == NULL) {
124  goto exit;
125  }
126  chain->private_vars.hcp_packet = packet;
127 
128  if (chain->app_mtu_size[FPC_COM_CHAIN_RX] == 0 ||
129  chain->app_mtu_buffer[FPC_COM_CHAIN_RX] == NULL) {
130  uint16_t tsp_offset;
131  uint16_t link_offset;
132  uint16_t overhead;
133 
134  overhead = chain->tsp_overhead_get(&tsp_offset) + chain->link_overhead_get(&link_offset);
136  + tsp_offset + link_offset;
137  chain->app_mtu_size[FPC_COM_CHAIN_RX] = chain->phy_mtu_size[FPC_COM_CHAIN_RX] - overhead;
138  }
139  result = recieve_chunks(chain);
140 
141 exit:
142  return result;
143 }
144 
145 bool fpc_hcp_arg_add(fpc_hcp_packet_t *packet, fpc_hcp_arg_t arg, uint16_t size, bool free_data,
146  void *data)
147 {
148  bool result = false;
149 
150  if (packet == NULL || (size > 0 && data == NULL)) {
151  goto exit;
152  }
153 
154  for (uint8_t i = 0; i < packet->num_args; i++) {
155  if (packet->arguments[i].arg == ARG_NONE) {
156  packet->arguments[i].arg = arg;
157  packet->arguments[i].size = size;
158  packet->arguments[i].free_data = free_data;
159  packet->arguments[i].data = data;
160  result = true;
161  break;
162  }
163  }
164 
165 exit:
166  return result;
167 }
168 
170  return fpc_hcp_arg_get(packet, arg) != NULL;
171 }
172 
174 {
175  for (uint8_t i = 0; i < packet->num_args; i++) {
176  if (packet->arguments[i].arg == arg) {
177  return &(packet->arguments[i]);
178  }
179  }
180  return NULL;
181 }
182 
183 bool fpc_hcp_arg_copy_data(fpc_hcp_packet_t *packet, fpc_hcp_arg_t arg, uint16_t data_size,
184  uint8_t *data)
185 {
186  fpc_hcp_arg_data_t *arg_data = fpc_hcp_arg_get(packet, arg);
187 
188  if (arg_data == NULL || data == NULL || data_size == 0 || arg_data->size == 0
189  || data_size > arg_data->size) {
190  return false;
191  }
192 
193  memcpy(data, arg_data->data, data_size);
194 
195  return true;
196 }
197 
199 {
200  if (chain && packet) {
201  for (uint8_t i = 0; i < packet->num_args; i++) {
202  if (packet->arguments[i].arg != ARG_NONE) {
203  chain->argument_free(packet->id, &packet->arguments[i], chain->context);
204  }
205  }
206  memset(packet->arguments, 0x0, sizeof(*packet->arguments) * packet->num_args);
207  packet->id = CMD_NONE;
208  }
209 }
210 
212 {
213  uint8_t *data;
214  uint16_t rem_size;
215  uint16_t id_rem_size;
216  uint16_t size_rem_size;
217  uint16_t arg_rem_size = 0;
218  uint16_t arg_size;
219  uint8_t *arg_data = NULL;
220  uint16_t overhead;
221  uint16_t arg_nr = 0;
222  uint16_t num_args = 0;
223  fpc_com_result_t result;
224  bool first_packet = true;
225  fpc_hcp_arg_t arg;
226  uint16_t app_offset;
227 
228  /* Calculate application packet data MTU size */
229  overhead = chain->app_overhead_get(&app_offset);
230  if (overhead > chain->app_mtu_size[FPC_COM_CHAIN_RX]) {
232  goto exit;
233  }
234 
235  /* Retrieve application packets */
236  do {
237  /* Get packet from application layer */
238  result = chain->app_rx(chain);
239  if (result != FPC_COM_RESULT_OK) {
240  break;
241  }
242 
243  /* Get packet size */
244  rem_size = chain->app_packet_size[FPC_COM_CHAIN_RX];
245  data = chain->app_mtu_buffer[FPC_COM_CHAIN_RX] + app_offset;
246 
247  if (first_packet) {
248  /* ID */
249  chain->private_vars.hcp_packet->id = *((uint16_t *) data);
250  data += PACKET_ID_SIZE;
251 
252  /* Number of arguments */
253  num_args = *((uint16_t *) data);
254  data += PACKET_NUM_ARGS_SIZE;
255 
256  first_packet = false;
257  rem_size -= PACKET_HEADER_SIZE;
258  id_rem_size = ARGUMENT_ARG_SIZE;
259  size_rem_size = ARGUMENT_SIZE_SIZE;
260  }
261 
262  while (rem_size && (arg_nr < num_args)) {
263  uint16_t copy_size;
264 
265  if (rem_size && id_rem_size) {
266  /* Set copy size */
267  copy_size = HCP_MIN(id_rem_size, rem_size);
268 
269  /* Copy arg id */
270  memcpy(((uint8_t *) &arg) + (ARGUMENT_ARG_SIZE - id_rem_size), data, copy_size);
271  data += copy_size;
272 
273  /* Update parameters */
274  id_rem_size -= copy_size;
275  rem_size -= copy_size;
276  }
277 
278  if (rem_size && size_rem_size) {
279  bool status;
280 
281  /* Set copy size */
282  copy_size = HCP_MIN(size_rem_size, rem_size);
283 
284  /* Copy arg size */
285  memcpy(((uint8_t *) &arg_size) + (ARGUMENT_SIZE_SIZE - size_rem_size),
286  data, copy_size);
287  data += copy_size;
288 
289  /* Update parameters */
290  size_rem_size -= copy_size;
291  rem_size -= copy_size;
292 
293  if (size_rem_size == 0) {
294  bool free_data = true;
295 
296  if (arg_size) {
297  arg_data = chain->argument_allocator(chain->private_vars.hcp_packet->id,
298  arg, arg_size, &free_data, chain->context);
299  if (arg_data == NULL) {
300  result = FPC_COM_RESULT_NO_MEMORY;
301  break;
302  }
303  }
304 
305  status = fpc_hcp_arg_add(chain->private_vars.hcp_packet, arg, arg_size,
306  free_data, arg_data);
307  if (status == false) {
308  fpc_hcp_arg_data_t arg_struct = { 0 };
309 
310  arg_struct.arg = arg;
311  arg_struct.free_data = free_data;
312  arg_struct.size = arg_size;
313  arg_struct.data = arg_data;
314  chain->argument_free(chain->private_vars.hcp_packet->id, &arg_struct,
315  chain->context);
316 
317  if (arg_nr < num_args) {
319  break;
320  } else {
321  result = FPC_COM_RESULT_NO_MEMORY;
322  break;
323  }
324  }
325  arg_rem_size = arg_size;
326  }
327  }
328 
329  if (rem_size && arg_rem_size) {
330  /* Set copy size */
331  copy_size = HCP_MIN(arg_rem_size, rem_size);
332 
333  /* Copy argument data */
334  memcpy(arg_data + (arg_size - arg_rem_size), data, copy_size);
335  data += copy_size;
336 
337  /* Update parameters */
338  rem_size -= copy_size;
339  arg_rem_size -= copy_size;
340  }
341 
342  /* If data has been copied set new arg to true */
343  if (id_rem_size == 0 && size_rem_size == 0 && arg_rem_size == 0) {
344  id_rem_size = ARGUMENT_ARG_SIZE;
345  size_rem_size = ARGUMENT_SIZE_SIZE;
346  arg_nr++;
347  }
348  }
349  } while (result == FPC_COM_RESULT_OK && arg_nr < num_args);
350 
351 exit:
352  return result;
353 }
354 
356 {
357  uint16_t serialized_size;
358  uint16_t packet_data_left;
359  uint16_t data_rem_size = 0;
360  uint16_t app_mtu;
361  uint16_t overhead;
362  fpc_com_result_t result;
363  uint16_t num_args = 0;
364  uint16_t arg_nr = 0;
365  uint16_t app_offset;
366  uint8_t *data;
367  fpc_hcp_arg_data_t *arg_data = NULL;
368 
369 
370  /* Calculate transport packet data MTU size */
371  overhead = chain->app_overhead_get(&app_offset);
372  if (overhead > chain->phy_mtu_size[FPC_COM_CHAIN_TX]) {
374  goto exit;
375  }
376  app_mtu = chain->app_mtu_size[FPC_COM_CHAIN_TX] - overhead;
377 
378  /* Get application packet total serialized size */
379  serialized_size = fpc_hcp_get_size(chain->private_vars.hcp_packet, &num_args);
380 
381  /* Set initial sequence length and size */
382  /* Calculate sequence length (number of application packages) */
383  chain->private_vars.hcp_seq_len = (serialized_size + app_mtu + 1) / app_mtu;
384  chain->private_vars.hcp_seq_nr = 1;
385 
386  /* Set first packet size */
387  chain->app_packet_size[FPC_COM_CHAIN_TX] = HCP_MIN(serialized_size, app_mtu);
388  packet_data_left = chain->app_packet_size[FPC_COM_CHAIN_TX];
389 
390  /* Point packet data to application buffer */
391  data = chain->app_mtu_buffer[FPC_COM_CHAIN_TX] + app_offset;
392 
393  /* Copy command ID to buffer */
394  *((fpc_hcp_cmd_t *) data) = chain->private_vars.hcp_packet->id;
395  data += PACKET_ID_SIZE;
396 
397  /* Copy number of arguments to buffer */
398  *((uint16_t *) data) = num_args;
399  data += PACKET_NUM_ARGS_SIZE;
400 
401  packet_data_left -= PACKET_HEADER_SIZE;
402 
403  do {
404  /* Copy arguments to data blob */
405  while (packet_data_left && arg_nr < num_args) {
406  /* Copy arg header to buffer */
407  if (!data_rem_size && packet_data_left >= ARGUMENT_HEADER_SIZE) {
408  /* Get current argument */
409  arg_data = &chain->private_vars.hcp_packet->arguments[arg_nr];
410 
411  /* Copy argument id */
412  memcpy(data, &arg_data->arg, ARGUMENT_ARG_SIZE);
413  data += ARGUMENT_ARG_SIZE;
414 
415  /* Copy argument size */
416  memcpy(data, &arg_data->size, ARGUMENT_SIZE_SIZE);
417  data += ARGUMENT_SIZE_SIZE;
418 
419  /* Update parameters */
420  packet_data_left -= ARGUMENT_HEADER_SIZE;
421  data_rem_size = arg_data->size;
422  } else if (!data_rem_size && packet_data_left < ARGUMENT_HEADER_SIZE) {
423  /*
424  * Special case to ensure that the argument header
425  * is not split over several packets as that will break the
426  * argument allocator.
427  */
428  chain->app_packet_size[FPC_COM_CHAIN_TX] -= packet_data_left;
429  break;
430  }
431 
432  /* Copy argument data to transport package data blob */
433  if (packet_data_left && data_rem_size) {
434  uint16_t copy_size;
435 
436  /* Set copy size */
437  copy_size = HCP_MIN(data_rem_size, packet_data_left);
438 
439  /* Copy argument data */
440  memcpy(data, arg_data->data + (arg_data->size - data_rem_size), copy_size);
441  data += copy_size;
442 
443  /* Update parameters */
444  data_rem_size -= copy_size;
445  packet_data_left -= copy_size;
446  }
447  if (!data_rem_size) {
448  arg_nr++;
449  }
450  }
451 
452  /* Transmit transport package */
453  result = chain->app_tx(chain);
454 
455  chain->private_vars.hcp_seq_nr++;
456 
457  /* Reduce data left counter */
458  serialized_size -= chain->app_packet_size[FPC_COM_CHAIN_TX];
459 
460  /* Set next packet size */
461  packet_data_left = HCP_MIN(serialized_size, app_mtu);
462  chain->app_packet_size[FPC_COM_CHAIN_TX] = packet_data_left;
463 
464  /* Set destination offset to zero for next packet */
465  data = chain->app_mtu_buffer[FPC_COM_CHAIN_TX] + app_offset;
466  } while (result == FPC_COM_RESULT_OK && serialized_size);
467 
468 exit:
469  return result;
470 }
uint16_t fpc_hcp_cmd_t
uint16_t(* tsp_overhead_get)(uint16_t *offset)
Definition: fpc_com_chain.h:95
fpc_com_result_t(* app_tx)(fpc_com_chain_t *chain)
Definition: fpc_com_chain.h:73
uint8_t * phy_mtu_buffer[2]
static fpc_com_result_t recieve_chunks(fpc_com_chain_t *chain)
Handle receive chunks.
Definition: fpc_hcp.c:211
uint16_t(* app_overhead_get)(uint16_t *offset)
Definition: fpc_com_chain.h:77
uint16_t phy_mtu_size[2]
#define ARGUMENT_HEADER_SIZE
Definition: fpc_hcp.c:46
bool fpc_hcp_arg_check(fpc_hcp_packet_t *packet, fpc_hcp_arg_t arg)
Check if command contains selected argument key.
Definition: fpc_hcp.c:169
Application Command Packet.
#define HCP_MIN(x, y)
uint16_t app_mtu_size[2]
Definition: fpc_com_chain.h:81
bool fpc_hcp_arg_copy_data(fpc_hcp_packet_t *packet, fpc_hcp_arg_t arg, uint16_t data_size, uint8_t *data)
Copy data from an argument with specified key.
Definition: fpc_hcp.c:183
fpc_hcp_cmd_t id
void *(* argument_allocator)(fpc_hcp_cmd_t cmd, fpc_hcp_arg_t arg, uint16_t size, bool *free_data, void *context)
Definition: fpc_com_chain.h:59
static fpc_com_result_t transmit_chunks(fpc_com_chain_t *chain)
Handle transmit chunks.
Definition: fpc_hcp.c:355
#define PACKET_HEADER_SIZE
Definition: fpc_hcp.c:35
fpc_hcp_arg_data_t * fpc_hcp_arg_get(fpc_hcp_packet_t *packet, fpc_hcp_arg_t arg)
Get Argument with specified key.
Definition: fpc_hcp.c:173
void * context
User context pointer. User private stuff, to be able to pass nessecary context to argument_allocator ...
void(* argument_free)(fpc_hcp_cmd_t cmd, fpc_hcp_arg_data_t *arg_data, void *context)
Definition: fpc_com_chain.h:62
#define PACKET_NUM_ARGS_SIZE
Definition: fpc_hcp.c:34
Command Argument.
#define ARGUMENT_ARG_SIZE
Definition: fpc_hcp.c:44
bool fpc_hcp_arg_add(fpc_hcp_packet_t *packet, fpc_hcp_arg_t arg, uint16_t size, bool free_data, void *data)
Add argument to packet.
Definition: fpc_hcp.c:145
fpc_com_result_t fpc_hcp_transmit(fpc_hcp_packet_t *packet, fpc_com_chain_t *chain)
Transmits an application packet through the supplied transmit chain.
Definition: fpc_hcp.c:89
uint16_t app_packet_size[2]
Definition: fpc_com_chain.h:79
uint8_t * app_mtu_buffer[2]
Definition: fpc_com_chain.h:83
uint16_t fpc_hcp_arg_t
uint16_t(* link_overhead_get)(uint16_t *offset)
#define PACKET_ID_SIZE
Definition: fpc_hcp.c:33
fpc_hcp_arg_data_t * arguments
uint8_t fpc_com_result_t
fpc_hcp_packet_t * hcp_packet
Definition: fpc_com_chain.h:38
fpc_com_chain_private_t private_vars
uint16_t fpc_hcp_get_size(fpc_hcp_packet_t *packet, uint16_t *num_args)
Calculate serialized packet size.
Definition: fpc_hcp.c:64
fpc_com_result_t fpc_hcp_receive(fpc_hcp_packet_t *packet, fpc_com_chain_t *chain)
Receives an application packet through the supplied transmit chain.
Definition: fpc_hcp.c:117
fpc_hcp_arg_t arg
Host Communication Protocol interface.
#define ARGUMENT_SIZE_SIZE
Definition: fpc_hcp.c:45
void fpc_hcp_free(fpc_com_chain_t *chain, fpc_hcp_packet_t *packet)
Frees the resources held by the packet i.e. the dynamic data held in the arguments.
Definition: fpc_hcp.c:198
fpc_com_result_t(* app_rx)(fpc_com_chain_t *chain)
Definition: fpc_com_chain.h:75