1 /******************************************************************************
2 *
3 * Copyright (C) 2010-2013 Broadcom Corporation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 ******************************************************************************/
18
19
20 /******************************************************************************
21 *
22 * This file contains source code for some utility functions to help parse
23 * and build NFC Data Exchange Format (NDEF) messages for Connection
24 * Handover
25 *
26 ******************************************************************************/
27
28 #include <string.h>
29 #include "ndef_utils.h"
30
31 /*******************************************************************************
32 **
33 ** Static Local Functions
34 */
35 static UINT8 *ndef_get_bt_oob_record (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
36 char *p_id_str);
37
38 /*******************************************************************************
39 **
40 ** Static data
41 */
42
43 /* Handover Request Record Type */
44 static UINT8 hr_rec_type[HR_REC_TYPE_LEN] = { 0x48, 0x72 }; /* "Hr" */
45
46 /* Handover Select Record Type */
47 static UINT8 hs_rec_type[HS_REC_TYPE_LEN] = { 0x48, 0x73 }; /* "Hs" */
48
49 /* Handover Carrier recrod Type */
50 static UINT8 hc_rec_type[HC_REC_TYPE_LEN] = { 0x48, 0x63 }; /* "Hc" */
51
52 /* Collision Resolution Record Type */
53 static UINT8 cr_rec_type[CR_REC_TYPE_LEN] = { 0x63, 0x72 }; /* "cr" */
54
55 /* Alternative Carrier Record Type */
56 static UINT8 ac_rec_type[AC_REC_TYPE_LEN] = { 0x61, 0x63 }; /* "ac" */
57
58 /* Error Record Type */
59 static UINT8 err_rec_type[ERR_REC_TYPE_LEN] = { 0x65, 0x72, 0x72 }; /* "err" */
60
61 /* Bluetooth OOB Data Type */
62 static UINT8 *p_bt_oob_rec_type = (UINT8 *)"application/vnd.bluetooth.ep.oob";
63
64 /* Wifi WSC Data Type */
65 static UINT8 *p_wifi_wsc_rec_type = (UINT8 *)"application/vnd.wfa.wsc";
66
67 /*******************************************************************************
68 **
69 ** Function NDEF_MsgCreateWktHr
70 **
71 ** Description This function creates Handover Request Record with version.
72 **
73 ** Returns NDEF_OK if all OK
74 **
75 *******************************************************************************/
NDEF_MsgCreateWktHr(UINT8 * p_msg,UINT32 max_size,UINT32 * p_cur_size,UINT8 version)76 tNDEF_STATUS NDEF_MsgCreateWktHr (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
77 UINT8 version )
78 {
79 tNDEF_STATUS status;
80
81 NDEF_MsgInit (p_msg, max_size, p_cur_size);
82
83 /* Add record with version */
84 status = NDEF_MsgAddRec (p_msg, max_size, p_cur_size,
85 NDEF_TNF_WKT, hr_rec_type, HR_REC_TYPE_LEN,
86 NULL, 0, &version, 1);
87
88 return (status);
89 }
90
91 /*******************************************************************************
92 **
93 ** Function NDEF_MsgCreateWktHs
94 **
95 ** Description This function creates Handover Select Record with version.
96 **
97 ** Returns NDEF_OK if all OK
98 **
99 *******************************************************************************/
NDEF_MsgCreateWktHs(UINT8 * p_msg,UINT32 max_size,UINT32 * p_cur_size,UINT8 version)100 tNDEF_STATUS NDEF_MsgCreateWktHs (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
101 UINT8 version )
102 {
103 tNDEF_STATUS status;
104
105 NDEF_MsgInit (p_msg, max_size, p_cur_size);
106
107 /* Add record with version */
108 status = NDEF_MsgAddRec (p_msg, max_size, p_cur_size,
109 NDEF_TNF_WKT, hs_rec_type, HS_REC_TYPE_LEN,
110 NULL, 0, &version, 1);
111
112 return (status);
113 }
114
115 /*******************************************************************************
116 **
117 ** Function NDEF_MsgAddWktHc
118 **
119 ** Description This function adds Handover Carrier Record.
120 **
121 ** Returns NDEF_OK if all OK
122 **
123 *******************************************************************************/
NDEF_MsgAddWktHc(UINT8 * p_msg,UINT32 max_size,UINT32 * p_cur_size,char * p_id_str,UINT8 ctf,UINT8 carrier_type_len,UINT8 * p_carrier_type,UINT8 carrier_data_len,UINT8 * p_carrier_data)124 tNDEF_STATUS NDEF_MsgAddWktHc (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
125 char *p_id_str, UINT8 ctf,
126 UINT8 carrier_type_len, UINT8 *p_carrier_type,
127 UINT8 carrier_data_len, UINT8 *p_carrier_data)
128 {
129 tNDEF_STATUS status;
130 UINT8 payload[256], *p, id_len;
131 UINT32 payload_len;
132
133 if (carrier_type_len + carrier_data_len + 2 > 256)
134 {
135 return (NDEF_MSG_INSUFFICIENT_MEM);
136 }
137
138 p = payload;
139
140 UINT8_TO_STREAM (p, (ctf & 0x07));
141 UINT8_TO_STREAM (p, carrier_type_len);
142 ARRAY_TO_STREAM (p, p_carrier_type, carrier_type_len);
143 ARRAY_TO_STREAM (p, p_carrier_data, carrier_data_len);
144
145 payload_len = (UINT32)carrier_type_len + carrier_data_len + 2;
146
147 id_len = (UINT8)strlen (p_id_str);
148
149 status = NDEF_MsgAddRec (p_msg, max_size, p_cur_size,
150 NDEF_TNF_WKT, hc_rec_type, HC_REC_TYPE_LEN,
151 (UINT8*)p_id_str, id_len, payload, payload_len);
152 return (status);
153 }
154
155 /*******************************************************************************
156 **
157 ** Function NDEF_MsgAddWktAc
158 **
159 ** Description This function adds Alternative Carrier Record.
160 **
161 ** Returns NDEF_OK if all OK
162 **
163 *******************************************************************************/
NDEF_MsgAddWktAc(UINT8 * p_msg,UINT32 max_size,UINT32 * p_cur_size,UINT8 cps,char * p_carrier_data_ref_str,UINT8 aux_data_ref_count,char * p_aux_data_ref_str[])164 tNDEF_STATUS NDEF_MsgAddWktAc (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
165 UINT8 cps, char *p_carrier_data_ref_str,
166 UINT8 aux_data_ref_count, char *p_aux_data_ref_str[])
167 {
168 tNDEF_STATUS status;
169 UINT32 payload_len;
170 UINT8 ref_str_len, xx;
171 UINT8 *p_rec, *p;
172
173 /* get payload length first */
174
175 /* CPS, length of carrier data ref, carrier data ref, Aux data reference count */
176 payload_len = 3 + (UINT8)strlen (p_carrier_data_ref_str);
177 for (xx = 0; xx < aux_data_ref_count; xx++)
178 {
179 /* Aux Data Reference length (1 byte) */
180 payload_len += 1 + (UINT8)strlen (p_aux_data_ref_str[xx]);
181 }
182
183 /* reserve memory for payload */
184 status = NDEF_MsgAddRec (p_msg, max_size, p_cur_size,
185 NDEF_TNF_WKT, ac_rec_type, AC_REC_TYPE_LEN,
186 NULL, 0, NULL, payload_len);
187
188 if (status == NDEF_OK)
189 {
190 /* get AC record added at the end */
191 p_rec = NDEF_MsgGetLastRecInMsg (p_msg);
192
193 /* get start pointer of reserved payload */
194 p = NDEF_RecGetPayload (p_rec, &payload_len);
195
196 /* Add Carrier Power State */
197 UINT8_TO_BE_STREAM (p, cps);
198
199 /* Carrier Data Reference length */
200 ref_str_len = (UINT8)strlen (p_carrier_data_ref_str);
201
202 UINT8_TO_BE_STREAM (p, ref_str_len);
203
204 /* Carrier Data Reference */
205 ARRAY_TO_BE_STREAM (p, p_carrier_data_ref_str, ref_str_len);
206
207 /* Aux Data Reference Count */
208 UINT8_TO_BE_STREAM (p, aux_data_ref_count);
209
210 for (xx = 0; xx < aux_data_ref_count; xx++)
211 {
212 /* Aux Data Reference length (1 byte) */
213 ref_str_len = (UINT8)strlen (p_aux_data_ref_str[xx]);
214
215 UINT8_TO_BE_STREAM (p, ref_str_len);
216
217 /* Aux Data Reference */
218 ARRAY_TO_BE_STREAM (p, p_aux_data_ref_str[xx], ref_str_len);
219 }
220 }
221
222 return (status);
223 }
224
225 /*******************************************************************************
226 **
227 ** Function NDEF_MsgAddWktCr
228 **
229 ** Description This function adds Collision Resolution Record.
230 **
231 ** Returns NDEF_OK if all OK
232 **
233 *******************************************************************************/
NDEF_MsgAddWktCr(UINT8 * p_msg,UINT32 max_size,UINT32 * p_cur_size,UINT16 random_number)234 tNDEF_STATUS NDEF_MsgAddWktCr (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
235 UINT16 random_number )
236 {
237 tNDEF_STATUS status;
238 UINT8 data[2], *p;
239
240 p = data;
241 UINT16_TO_BE_STREAM (p, random_number);
242
243 status = NDEF_MsgAddRec (p_msg, max_size, p_cur_size,
244 NDEF_TNF_WKT, cr_rec_type, CR_REC_TYPE_LEN,
245 NULL, 0, data, 2);
246 return (status);
247 }
248
249 /*******************************************************************************
250 **
251 ** Function NDEF_MsgAddWktErr
252 **
253 ** Description This function adds Error Record.
254 **
255 ** Returns NDEF_OK if all OK
256 **
257 *******************************************************************************/
NDEF_MsgAddWktErr(UINT8 * p_msg,UINT32 max_size,UINT32 * p_cur_size,UINT8 error_reason,UINT32 error_data)258 tNDEF_STATUS NDEF_MsgAddWktErr (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
259 UINT8 error_reason, UINT32 error_data )
260 {
261 tNDEF_STATUS status;
262 UINT8 payload[5], *p;
263 UINT32 payload_len;
264
265 p = payload;
266
267 UINT8_TO_BE_STREAM (p, error_reason);
268
269 if (error_reason == 0x02)
270 {
271 UINT32_TO_BE_STREAM (p, error_data);
272 payload_len = 5;
273 }
274 else
275 {
276 UINT8_TO_BE_STREAM (p, error_data);
277 payload_len = 2;
278 }
279
280 status = NDEF_MsgAddRec (p_msg, max_size, p_cur_size,
281 NDEF_TNF_WKT, err_rec_type, ERR_REC_TYPE_LEN,
282 NULL, 0, payload, payload_len);
283 return (status);
284 }
285
286 /*******************************************************************************
287 **
288 ** Function NDEF_MsgAddMediaBtOob
289 **
290 ** Description This function adds BT OOB Record.
291 **
292 ** Returns NDEF_OK if all OK
293 **
294 *******************************************************************************/
NDEF_MsgAddMediaBtOob(UINT8 * p_msg,UINT32 max_size,UINT32 * p_cur_size,char * p_id_str,BD_ADDR bd_addr)295 tNDEF_STATUS NDEF_MsgAddMediaBtOob (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
296 char *p_id_str, BD_ADDR bd_addr)
297 {
298 tNDEF_STATUS status;
299 UINT8 payload[BD_ADDR_LEN + 2];
300 UINT8 *p;
301 UINT8 payload_len, id_len;
302
303 p = payload;
304
305 /* length including itself */
306 UINT16_TO_STREAM (p, BD_ADDR_LEN + 2);
307
308 /* BD Addr */
309 BDADDR_TO_STREAM (p, bd_addr);
310
311 payload_len = BD_ADDR_LEN + 2;
312 id_len = (UINT8)strlen (p_id_str);
313
314 status = NDEF_MsgAddRec (p_msg, max_size, p_cur_size,
315 NDEF_TNF_MEDIA, p_bt_oob_rec_type, BT_OOB_REC_TYPE_LEN,
316 (UINT8*)p_id_str, id_len, payload, payload_len);
317 return (status);
318 }
319
320 /*******************************************************************************
321 **
322 ** Function NDEF_MsgAppendMediaBtOobCod
323 **
324 ** Description This function appends COD EIR data at the end of BT OOB Record.
325 **
326 ** Returns NDEF_OK if all OK
327 **
328 *******************************************************************************/
NDEF_MsgAppendMediaBtOobCod(UINT8 * p_msg,UINT32 max_size,UINT32 * p_cur_size,char * p_id_str,DEV_CLASS cod)329 tNDEF_STATUS NDEF_MsgAppendMediaBtOobCod (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
330 char *p_id_str, DEV_CLASS cod)
331 {
332 tNDEF_STATUS status;
333 UINT8 *p_rec;
334 UINT8 eir_data[BT_OOB_COD_SIZE + 2];
335 UINT8 *p;
336 UINT8 eir_data_len;
337 UINT32 oob_data_len;
338
339 /* find record by Payload ID */
340 p_rec = ndef_get_bt_oob_record (p_msg, max_size, p_cur_size, p_id_str);
341
342 if (!p_rec)
343 return (NDEF_REC_NOT_FOUND);
344
345 /* create EIR data format for COD */
346 p = eir_data;
347 UINT8_TO_STREAM (p, BT_OOB_COD_SIZE + 1);
348 UINT8_TO_STREAM (p, BT_EIR_OOB_COD_TYPE);
349 DEVCLASS_TO_STREAM (p, cod);
350 eir_data_len = BT_OOB_COD_SIZE + 2;
351
352 /* append EIR data at the end of record */
353 status = NDEF_MsgAppendPayload(p_msg, max_size, p_cur_size,
354 p_rec, eir_data, eir_data_len);
355
356 /* update BT OOB data length, if success */
357 if (status == NDEF_OK)
358 {
359 /* payload length is the same as BT OOB data length */
360 p = NDEF_RecGetPayload (p_rec, &oob_data_len);
361 UINT16_TO_STREAM (p, oob_data_len);
362 }
363
364 return (status);
365 }
366
367 /*******************************************************************************
368 **
369 ** Function NDEF_MsgAppendMediaBtOobName
370 **
371 ** Description This function appends Bluetooth Local Name EIR data
372 ** at the end of BT OOB Record.
373 **
374 ** Returns NDEF_OK if all OK
375 **
376 *******************************************************************************/
NDEF_MsgAppendMediaBtOobName(UINT8 * p_msg,UINT32 max_size,UINT32 * p_cur_size,char * p_id_str,BOOLEAN is_complete,UINT8 name_len,UINT8 * p_name)377 tNDEF_STATUS NDEF_MsgAppendMediaBtOobName (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
378 char *p_id_str, BOOLEAN is_complete,
379 UINT8 name_len, UINT8 *p_name)
380 {
381 tNDEF_STATUS status;
382 UINT8 *p_rec;
383 UINT8 eir_data[256];
384 UINT8 *p;
385 UINT8 eir_data_len;
386 UINT32 oob_data_len;
387
388 /* find record by Payload ID */
389 p_rec = ndef_get_bt_oob_record (p_msg, max_size, p_cur_size, p_id_str);
390
391 if (!p_rec)
392 return (NDEF_REC_NOT_FOUND);
393
394 /* create EIR data format for COD */
395 p = eir_data;
396 UINT8_TO_STREAM (p, name_len + 1);
397
398 if (is_complete)
399 {
400 UINT8_TO_STREAM (p, BT_EIR_COMPLETE_LOCAL_NAME_TYPE);
401 }
402 else
403 {
404 UINT8_TO_STREAM (p, BT_EIR_SHORTENED_LOCAL_NAME_TYPE);
405 }
406
407 ARRAY_TO_STREAM (p, p_name, name_len);
408 eir_data_len = name_len + 2;
409
410 /* append EIR data at the end of record */
411 status = NDEF_MsgAppendPayload(p_msg, max_size, p_cur_size,
412 p_rec, eir_data, eir_data_len);
413
414 /* update BT OOB data length, if success */
415 if (status == NDEF_OK)
416 {
417 /* payload length is the same as BT OOB data length */
418 p = NDEF_RecGetPayload (p_rec, &oob_data_len);
419 UINT16_TO_STREAM (p, oob_data_len);
420 }
421
422 return (status);
423 }
424
425 /*******************************************************************************
426 **
427 ** Function NDEF_MsgAppendMediaBtOobHashCRandR
428 **
429 ** Description This function appends Hash C and Rand R at the end of BT OOB Record.
430 **
431 ** Returns NDEF_OK if all OK
432 **
433 *******************************************************************************/
NDEF_MsgAppendMediaBtOobHashCRandR(UINT8 * p_msg,UINT32 max_size,UINT32 * p_cur_size,char * p_id_str,UINT8 * p_hash_c,UINT8 * p_rand_r)434 tNDEF_STATUS NDEF_MsgAppendMediaBtOobHashCRandR (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
435 char *p_id_str, UINT8 *p_hash_c, UINT8 *p_rand_r)
436 {
437 tNDEF_STATUS status;
438 UINT8 *p_rec;
439 UINT8 eir_data[BT_OOB_HASH_C_SIZE + BT_OOB_RAND_R_SIZE + 4];
440 UINT8 *p;
441 UINT8 eir_data_len;
442 UINT32 oob_data_len;
443
444 /* find record by Payload ID */
445 p_rec = ndef_get_bt_oob_record (p_msg, max_size, p_cur_size, p_id_str);
446
447 if (!p_rec)
448 return (NDEF_REC_NOT_FOUND);
449
450 /* create EIR data format */
451 p = eir_data;
452
453 UINT8_TO_STREAM (p, BT_OOB_HASH_C_SIZE + 1);
454 UINT8_TO_STREAM (p, BT_EIR_OOB_SSP_HASH_C_TYPE);
455 ARRAY16_TO_STREAM (p, p_hash_c);
456
457 UINT8_TO_STREAM (p, BT_OOB_RAND_R_SIZE + 1);
458 UINT8_TO_STREAM (p, BT_EIR_OOB_SSP_RAND_R_TYPE);
459 ARRAY16_TO_STREAM (p, p_rand_r);
460
461 eir_data_len = BT_OOB_HASH_C_SIZE + BT_OOB_RAND_R_SIZE + 4;
462
463 /* append EIR data at the end of record */
464 status = NDEF_MsgAppendPayload(p_msg, max_size, p_cur_size,
465 p_rec, eir_data, eir_data_len);
466
467 /* update BT OOB data length, if success */
468 if (status == NDEF_OK)
469 {
470 /* payload length is the same as BT OOB data length */
471 p = NDEF_RecGetPayload (p_rec, &oob_data_len);
472 UINT16_TO_STREAM (p, oob_data_len);
473 }
474
475 return (status);
476 }
477
478 /*******************************************************************************
479 **
480 ** Function NDEF_MsgAppendMediaBtOobEirData
481 **
482 ** Description This function appends EIR Data at the end of BT OOB Record.
483 **
484 ** Returns NDEF_OK if all OK
485 **
486 *******************************************************************************/
NDEF_MsgAppendMediaBtOobEirData(UINT8 * p_msg,UINT32 max_size,UINT32 * p_cur_size,char * p_id_str,UINT8 eir_type,UINT8 data_len,UINT8 * p_data)487 tNDEF_STATUS NDEF_MsgAppendMediaBtOobEirData (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
488 char *p_id_str,
489 UINT8 eir_type, UINT8 data_len, UINT8 *p_data)
490 {
491 tNDEF_STATUS status;
492 UINT8 *p_rec;
493 UINT8 eir_data[256];
494 UINT8 *p;
495 UINT8 eir_data_len;
496 UINT32 oob_data_len;
497
498 /* find record by Payload ID */
499 p_rec = ndef_get_bt_oob_record (p_msg, max_size, p_cur_size, p_id_str);
500
501 if (!p_rec)
502 return (NDEF_REC_NOT_FOUND);
503
504 /* create EIR data format */
505 p = eir_data;
506 UINT8_TO_STREAM (p, data_len + 1);
507 UINT8_TO_STREAM (p, eir_type);
508 ARRAY_TO_STREAM (p, p_data, data_len);
509 eir_data_len = data_len + 2;
510
511 /* append EIR data at the end of record */
512 status = NDEF_MsgAppendPayload(p_msg, max_size, p_cur_size,
513 p_rec, eir_data, eir_data_len);
514
515 /* update BT OOB data length, if success */
516 if (status == NDEF_OK)
517 {
518 /* payload length is the same as BT OOB data length */
519 p = NDEF_RecGetPayload (p_rec, &oob_data_len);
520 UINT16_TO_STREAM (p, oob_data_len);
521 }
522
523 return (status);
524 }
525
526 /*******************************************************************************
527 **
528 ** Function NDEF_MsgAddMediaWifiWsc
529 **
530 ** Description This function adds Wifi Wsc Record header.
531 **
532 ** Returns NDEF_OK if all OK
533 **
534 *******************************************************************************/
NDEF_MsgAddMediaWifiWsc(UINT8 * p_msg,UINT32 max_size,UINT32 * p_cur_size,char * p_id_str,UINT8 * p_payload,UINT32 payload_len)535 tNDEF_STATUS NDEF_MsgAddMediaWifiWsc (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
536 char *p_id_str, UINT8 *p_payload, UINT32 payload_len)
537 {
538 tNDEF_STATUS status;
539 UINT8 id_len = 0;
540
541 if (p_id_str)
542 id_len = (UINT8)strlen (p_id_str);
543
544 status = NDEF_MsgAddRec (p_msg, max_size, p_cur_size,
545 NDEF_TNF_MEDIA, p_wifi_wsc_rec_type, WIFI_WSC_REC_TYPE_LEN,
546 (UINT8*)p_id_str, id_len, p_payload, payload_len);
547 return (status);
548 }
549
550 /*******************************************************************************
551 **
552 ** Static Local Functions
553 **
554 *******************************************************************************/
555 /*******************************************************************************
556 **
557 ** Function ndef_get_bt_oob_record
558 **
559 ** Description This function returns BT OOB record which has matched payload ID
560 **
561 ** Returns pointer of record if found, otherwise NULL
562 **
563 *******************************************************************************/
ndef_get_bt_oob_record(UINT8 * p_msg,UINT32 max_size,UINT32 * p_cur_size,char * p_id_str)564 static UINT8 *ndef_get_bt_oob_record (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
565 char *p_id_str)
566 {
567 UINT8 *p_rec, *p_type;
568 UINT8 id_len, tnf, type_len;
569
570 /* find record by Payload ID */
571 id_len = (UINT8)strlen (p_id_str);
572 p_rec = NDEF_MsgGetFirstRecById (p_msg, (UINT8*)p_id_str, id_len);
573
574 if (!p_rec)
575 return (NULL);
576
577 p_type = NDEF_RecGetType (p_rec, &tnf, &type_len);
578
579 /* check type if this is BT OOB record */
580 if ((!p_rec)
581 ||(tnf != NDEF_TNF_MEDIA)
582 ||(type_len != BT_OOB_REC_TYPE_LEN)
583 ||(memcmp (p_type, p_bt_oob_rec_type, BT_OOB_REC_TYPE_LEN)))
584 {
585 return (NULL);
586 }
587
588 return (p_rec);
589 }
590
591