1 /*
2 * v3_0p_hw_nguif.c
3 *
4 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
5 *
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the
17 * distribution.
18 *
19 * Neither the name of Texas Instruments Incorporated nor the names of
20 * its contributors may be used to endorse or promote products derived
21 * from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 //! \ingroup MODULBIOS
37 //! \file v3_0p_hw_uif.c
38 //! \brief Bios included HAL (zero) and update function
39 //! \li forwarding (execute) messages to HAL
40 //! \li upload of HAL macros
41 //! \li loop management
42
43 #include "hw_compiler_specific.h"
44 #include <stdlib.h>
45
46 #include "bios.h"
47 #include "v3_0p.h"
48 #include "HAL_FLASH.h"
49 #include "MemorySegments.h"
50 #include "../../hil/msp_fet/archFpga.h"
51 #include "../fet/v3_0p_hw_fet.h"
52
53 #include "USB_API/USB_Common/types.h"
54 #include "USB_API/USB_Common/usb.h" //USB-specific functions
55 #include "F5xx_F6xx_Core_Lib/HAL_UCS.h"
56
57
calculateCrc(unsigned short sum,unsigned short * adress,unsigned long segmentLength)58 unsigned short calculateCrc(unsigned short sum, unsigned short *adress, unsigned long segmentLength)
59 {
60 //Initialize CRC register
61 CRCINIRES = sum;
62
63 //Compute CRC over the given segment
64 while (segmentLength--)
65 {
66 CRCDIRB = *adress++;
67 }
68 //Return CRC result
69 return CRCINIRES;
70 }
71
V3OP_GetHilCrc()72 unsigned short V3OP_GetHilCrc()
73 {
74 unsigned short hilCrc = 0x0000;
75 unsigned long segmentLength = 0;
76 unsigned short *address = 0;
77
78 //calculate CRC for HIL info segment 1 -------------------------------------
79 segmentLength = (CHECKSUM_HIL[0] - INFO_SEGMENTS_HIL[0])/2;
80 address = (unsigned short*)INFO_SEGMENTS_HIL[0];
81 hilCrc = calculateCrc(hilCrc, address, segmentLength);
82
83 //calculate CRC for HIL info segment 2
84 segmentLength = (INFO_SEGMENTS_HIL[1] - CHECKSUM_HIL[1])/2;
85 address = (unsigned short*)(CHECKSUM_HIL[1] + 1);
86 hilCrc = calculateCrc(hilCrc, address, segmentLength);
87
88 //calculate CRC for HIL main segment
89 segmentLength = (HIL_SEGMENTS[1] - HIL_SEGMENTS[0]+1)/2;
90 address = (unsigned short*)HIL_SEGMENTS[0];
91 hilCrc = calculateCrc(hilCrc, address, segmentLength);
92
93 #ifdef MSP_FET
94 //calculate CRC for Hil main segment 2
95 segmentLength = (HIL_SEGMENTS[3] - HIL_SEGMENTS[2] + 1)/2;
96 address = (unsigned short*)HIL_SEGMENTS[2];
97 hilCrc = calculateCrc(hilCrc, address, segmentLength);
98 #endif
99 //--------------------------------------------------------------------------
100
101 return hilCrc;
102 }
103
V3OP_GetHalFpgaCrc()104 unsigned short V3OP_GetHalFpgaCrc()
105 {
106 unsigned short halCrc = 0x0000;
107 #ifdef MSP_FET
108 unsigned long segmentLength = 0;
109 unsigned short *address = 0;
110
111 //calculate CRC for Hal info segment 1 --------------------------------------
112 segmentLength = (CHECKSUM_HAL[0] - INFO_SEGMENTS_HAL[0])/2;
113 address = (unsigned short*)INFO_SEGMENTS_HAL[0];
114 halCrc = calculateCrc(halCrc, address, segmentLength);
115 //calculate CRC for Hal info segment 2
116 segmentLength = (INFO_SEGMENTS_HAL[1] - CHECKSUM_HAL[1])/2;
117 address = (unsigned short*)(CHECKSUM_HAL[1] + 1);
118 halCrc = calculateCrc(halCrc, address, segmentLength);
119
120 //calculate CRC for Hal main segment 1
121 segmentLength = (HAL_FPGA_SEGMENTS[1] - HAL_FPGA_SEGMENTS[0] + 1)/2;
122 address = (unsigned short*)HAL_FPGA_SEGMENTS[0];
123 halCrc = calculateCrc(halCrc, address, segmentLength);
124 //--------------------------------------------------------------------------
125 #endif
126 return halCrc;
127 }
128
V3OP_GetHalCrc()129 unsigned short V3OP_GetHalCrc()
130 {
131 unsigned short halCrc = 0x0000;
132 unsigned long segmentLength = 0;
133 unsigned short *address = 0;
134
135 //calculate CRC for Hal info segment 1 --------------------------------------
136 segmentLength = (CHECKSUM_HAL[0] - INFO_SEGMENTS_HAL[0])/2;
137 address = (unsigned short*)INFO_SEGMENTS_HAL[0];
138 halCrc = calculateCrc(halCrc, address, segmentLength);
139 //calculate CRC for Hal info segment 2
140 segmentLength = (INFO_SEGMENTS_HAL[1] - CHECKSUM_HAL[1])/2;
141 address = (unsigned short*)(CHECKSUM_HAL[1] + 1);
142 halCrc = calculateCrc(halCrc, address, segmentLength);
143
144 //calculate CRC for Hal main segment 1
145 segmentLength = (HAL_SEGMENTS[1] - HAL_SEGMENTS[0] + 1)/2;
146 address = (unsigned short*)HAL_SEGMENTS[0];
147 halCrc = calculateCrc(halCrc, address, segmentLength);
148 #ifdef eZ_FET
149 //calculate CRC for Hal main segment 2
150 segmentLength = (HAL_SEGMENTS[3] - HAL_SEGMENTS[2] + 1)/2;
151 address = (unsigned short*)HAL_SEGMENTS[2];
152 halCrc = calculateCrc(halCrc, address, segmentLength);
153 #endif
154 //--------------------------------------------------------------------------
155
156 return halCrc;
157 }
158
V3OP_GetCoreCrc()159 unsigned short V3OP_GetCoreCrc()
160 {
161 unsigned short coreCrc = 0x0000;
162 unsigned long segmentLength = 0;
163 unsigned short *address = 0;
164
165 //calculate CRC for core info segment 1 ------------------------------------
166 segmentLength = (CHECKSUM_CORE[0] - CORE_SEGMENTS[0])/2;
167 address = (unsigned short*)CORE_SEGMENTS[0];
168 coreCrc = calculateCrc(coreCrc, address, segmentLength);
169
170 //calculate CRC for core main segment
171 segmentLength = (CORE_SEGMENTS[1] - CHECKSUM_CORE[1] + 1)/2;
172 address = (unsigned short*) (CHECKSUM_CORE[1] + 1);
173 coreCrc = calculateCrc(coreCrc, address, segmentLength);
174
175 //calculate CRC for reset vector segment
176 segmentLength = (CORE_SEGMENTS_RESET[1] - CORE_SEGMENTS_RESET[0] + 1)/2;
177 address = (unsigned short*)CORE_SEGMENTS_RESET[0];
178 coreCrc = calculateCrc(coreCrc, address, segmentLength);
179 //--------------------------------------------------------------------------
180
181 return coreCrc;
182 }
183
V3OP_GetDcdcCrc()184 unsigned short V3OP_GetDcdcCrc()
185 {
186 unsigned short dcdcCrc = 0x0000;
187 unsigned long segmentLength = 0;
188 unsigned short *address = 0;
189
190 //calculate CRC for dcdc info segment 1 -------------------------------------
191 segmentLength = (CHECKSUM_DCDC[0] - INFO_SEGMENTS_DCDC[0])/2;
192 address = (unsigned short*)INFO_SEGMENTS_DCDC[0];
193 dcdcCrc = calculateCrc(dcdcCrc, address, segmentLength);
194
195 //calculate CRC for dcdc info segment 1
196 segmentLength = (INFO_SEGMENTS_DCDC[1] - CHECKSUM_DCDC[1])/2;
197 address = (unsigned short*)(CHECKSUM_DCDC[1] + 1);
198 dcdcCrc = calculateCrc(dcdcCrc, address, segmentLength);
199
200 //calculate CRC for dcdc main segment
201 segmentLength = (DCDC_SEGMENTS[1] - DCDC_SEGMENTS[0] + 1)/2;
202 address = (unsigned short*)DCDC_SEGMENTS[0];
203 dcdcCrc = calculateCrc(dcdcCrc, address, segmentLength);
204 //--------------------------------------------------------------------------
205
206 return dcdcCrc;
207 }
208
V3OP_GetComChannelCrc()209 unsigned short V3OP_GetComChannelCrc()
210 {
211 unsigned short comChannelCrc = 0x0000;
212 unsigned long segmentLength = 0;
213 unsigned short *address = 0;
214
215 //calculate CRC for comchannel info segment 1 -------------------------------------
216 segmentLength = (CHECKSUM_COMCHANNEL[0] - INFO_SEGMENTS_COMCHANNEL[0])/2;
217 address = (unsigned short*)INFO_SEGMENTS_COMCHANNEL[0];
218 comChannelCrc = calculateCrc(comChannelCrc, address, segmentLength);
219
220 //calculate CRC for comchannel info segment 1
221 segmentLength = (INFO_SEGMENTS_COMCHANNEL[1] - CHECKSUM_COMCHANNEL[1])/2;
222 address = (unsigned short*)(CHECKSUM_COMCHANNEL[1] + 1);
223 comChannelCrc = calculateCrc(comChannelCrc, address, segmentLength);
224
225 //calculate CRC for comchannel main segment
226 segmentLength = (COMCHANNEL_SEGMENTS[1] - COMCHANNEL_SEGMENTS[0] + 1)/2;
227 address = (unsigned short*)COMCHANNEL_SEGMENTS[0];
228 comChannelCrc = calculateCrc(comChannelCrc, address, segmentLength);
229 //--------------------------------------------------------------------------
230
231 return comChannelCrc;
232 }
233
V3OP_HilCrcOk()234 unsigned char V3OP_HilCrcOk()
235 {
236 return(V3OP_GetHilCrc() == *((unsigned short*)CHECKSUM_HIL[0]));
237 }
238
V3OP_HalCrcOk()239 unsigned char V3OP_HalCrcOk()
240 {
241 return(V3OP_GetHalCrc() == *((unsigned short*)CHECKSUM_HAL[0]));
242 }
243
V3OP_HalFpgaCrcOk()244 unsigned char V3OP_HalFpgaCrcOk()
245 {
246 return(V3OP_GetHalFpgaCrc() == *((unsigned short*)CHECKSUM_HAL[0]));
247 }
248
V3OP_coreCrcOk()249 unsigned char V3OP_coreCrcOk()
250 {
251 return(V3OP_GetCoreCrc() == *((unsigned short*)CHECKSUM_CORE[0]));
252 }
253
V3OP_DcdcCrcOk()254 unsigned char V3OP_DcdcCrcOk()
255 {
256 return(V3OP_GetDcdcCrc() == *((unsigned short*)CHECKSUM_DCDC[0]));
257 }
258
V3OP_ComChannelCrcOk()259 unsigned char V3OP_ComChannelCrcOk()
260 {
261 return(V3OP_GetComChannelCrc() == *((unsigned short*)CHECKSUM_COMCHANNEL[0]));
262 }
263 //! \brief test address on memory type
264 //! \param[in] addr address to test
265 //! \return 0 -> flash memory
266 //! \return 1 -> info memory
267 //! \return 2 -> RAM
268 #pragma optimize = low
V3OP_GetSegmentType(unsigned long addr)269 unsigned long V3OP_GetSegmentType(unsigned long addr)
270 {
271 unsigned short i = 0;
272 for(i = 0; i < (sizeof(INFO_SEGMENTS_HIL)/sizeof(unsigned long)); i+=2)
273 {
274 if((addr >= INFO_SEGMENTS_HIL[i]) && (addr <= INFO_SEGMENTS_HIL[i+1]))
275 {
276 return (INFO_SEGMENT_HIL);
277 }
278 }
279 for(i = 0; i < (sizeof(INFO_SEGMENTS_HAL)/sizeof(unsigned long)); i+=2)
280 {
281 if((addr >= INFO_SEGMENTS_HAL[i]) && (addr <= INFO_SEGMENTS_HAL[i+1]))
282 {
283 return (INFO_SEGMENT_HAL);
284 }
285 }
286 for(i = 0; i < (sizeof(HAL_SEGMENTS)/sizeof(unsigned long)); i+=2)
287 {
288 if((addr >= HAL_SEGMENTS[i]) && (addr <= HAL_SEGMENTS[i+1]))
289 {
290 return (HAL_SEGMENT);
291 }
292 }
293 #ifdef MSP_FET
294 for(i = 0; i < (sizeof(HAL_FPGA_SEGMENTS)/sizeof(unsigned long)); i+=2)
295 {
296 if((addr >= HAL_FPGA_SEGMENTS[i]) && (addr <= HAL_FPGA_SEGMENTS[i+1]))
297 {
298 return (HAL_SEGMENT_FPGA);
299 }
300 }
301 #endif
302 for(i = 0; i < (sizeof(HIL_SEGMENTS)/sizeof(unsigned long)); i+=2)
303 {
304 if((addr >= HIL_SEGMENTS[i]) && (addr <= HIL_SEGMENTS[i+1]))
305 {
306 return (HIL_SEGMENT);
307 }
308 }
309 // dcdc segments
310 for(i = 0; i < (sizeof(DCDC_SEGMENTS)/sizeof(unsigned long)); i+=2)
311 {
312 if((addr >= DCDC_SEGMENTS[i]) && (addr <= DCDC_SEGMENTS[i+1]))
313 {
314 return (DCDC_SEGMENT);
315 }
316 }
317 for(i = 0; i < (sizeof(INFO_SEGMENTS_DCDC)/sizeof(unsigned long)); i+=2)
318 {
319 if((addr >= INFO_SEGMENTS_DCDC[i]) && (addr <= INFO_SEGMENTS_DCDC[i+1]))
320 {
321 return (INFO_SEGMENT_DCDC);
322 }
323 }
324 // comchannel segments
325 for(i = 0; i < (sizeof(COMCHANNEL_SEGMENTS)/sizeof(unsigned long)); i+=2)
326 {
327 if((addr >= COMCHANNEL_SEGMENTS[i]) && (addr <= COMCHANNEL_SEGMENTS[i+1]))
328 {
329 return (COMCHANNEL_SEGMENT);
330 }
331 }
332 // INFO comchannel segments
333 for(i = 0; i < (sizeof(INFO_SEGMENTS_COMCHANNEL)/sizeof(unsigned long)); i+=2)
334 {
335 if((addr >= INFO_SEGMENTS_COMCHANNEL[i]) && (addr <= INFO_SEGMENTS_COMCHANNEL[i+1]))
336 {
337 return (INFO_SEGMENT_COMCHANNEL);
338 }
339 }
340 return 0;
341 }
342
343 //! \brief test address on write access
344 //! \param[in] addr address to test
345 //! \return 0 -> no write access
346 //! \return 1 -> write allow
V3OP_WriteAllowed(unsigned short addr)347 unsigned char V3OP_WriteAllowed(unsigned short addr)
348 {
349 return(0);
350 }
351 //! \brief test address on erase access
352 //! \param[in] addr address to test
353 //! \return 0 -> no erase access
354 //! \return 1 -> erase allow
V3OP_EraseAllowed(unsigned short StartAddr)355 unsigned char V3OP_EraseAllowed(unsigned short StartAddr)
356 {
357 return(0);
358 }
359
360 //! \brief erase on UIF (HAL) all blocks which included in start address + size
361 //! \param[in] *payload pointer to receive buffer
362 //! \return 1 -> flash erase done
363 //! \return <0 -> error
364 #pragma optimize = low
V3OP_CoreFlashFunctionErase(unsigned char * payload)365 short V3OP_CoreFlashFunctionErase(unsigned char *payload)
366 {
367 unsigned long address_pointer;
368 unsigned long start_addr = (*(unsigned long*)&payload[4] & 0xFFFFF);
369 unsigned long end_addr = start_addr + (*(unsigned long*)&payload[8] & 0xFFFFF)-2;
370 unsigned char segment_type;
371 unsigned long segmentSize =0;
372
373 segment_type = V3OP_GetSegmentType(start_addr);
374
375 if(segment_type == NO_SEGMENT)
376 {
377 return -1;
378 }
379
380 // erase INFO mem
381 if(segment_type == HAL_SEGMENT || segment_type == INFO_SEGMENT_HAL)
382 {
383 segmentSize = SEGMENT_SIZE_HAL_HIL;
384 // First erase entry point into Hil Layer
385 // IF firmware update is interrupted HIL could not be started
386 Flash_SegmentErase((unsigned short*)INFO_SEGMENTS_HAL[1]);
387 }
388
389 if(segment_type == HIL_SEGMENT || segment_type == INFO_SEGMENT_HIL)
390 {
391 segmentSize = SEGMENT_SIZE_HAL_HIL;
392 // First erase entry point into HAL Layer
393 // IF firmware update is interrupted HAL could not be started
394 Flash_SegmentErase((unsigned short*)INFO_SEGMENTS_HIL[0]);
395 }
396
397 if(segment_type == INFO_SEGMENT_DCDC || segment_type == DCDC_SEGMENT)
398 {
399 segmentSize = SEGMENT_SIZE_HAL_HIL;
400 // First erase entry point into HAL Layer
401 // IF firmware update is interrupted HAL could not be started
402 Flash_SegmentErase((unsigned short*)INFO_SEGMENTS_DCDC[0]);
403 }
404
405 if(segment_type == COMCHANNEL_SEGMENT || segment_type == INFO_SEGMENT_COMCHANNEL)
406 {
407 segmentSize = SEGMENT_SIZE_HAL_HIL;
408 // First erase entry point into HAL Layer
409 // IF firmware update is interrupted HAL could not be started
410 UnlockInfoA();
411 Flash_SegmentErase((unsigned short*)INFO_SEGMENTS_COMCHANNEL[0]);
412 }
413
414 // Erase HIL/HAL/DCDC/COM
415 for(address_pointer = start_addr; address_pointer < end_addr; address_pointer += segmentSize)
416 {
417 if(V3OP_GetSegmentType(address_pointer) != NO_SEGMENT)
418 {
419 Flash_SegmentErase((unsigned short*)address_pointer);
420 }
421 }
422
423 // Do erase check of HIL/HAL/DCDC
424 for(address_pointer = start_addr; address_pointer < end_addr; address_pointer += segmentSize)
425 {
426 if(V3OP_GetSegmentType(address_pointer) != NO_SEGMENT)
427 {
428 Flash_EraseCheck((unsigned char*)address_pointer, (segmentSize+1)); // (unsigned long *Flash_ptr, unsigned long len)
429 }
430 }
431 LockInfoA();
432 return(1);
433 }
434 //! \brief write payload to UIF (HAL) flash memory, RAM is allowed, too
435 //! \param[in] *payload pointer to receive buffer
436 //! \details on first function call the:
437 //! \li bytes 4 -7: the flash address
438 //! \li bytes 8 -11: data length
439 //! \li bytes 12 ... payload
440 //! \details if data length is larger then message size the function returns after writing the message. Direct follow calls
441 //! of this function continue writing.
442 //! \return 1 -> flash write done
443 //! \return <0 -> error
444 #pragma optimize = low
V3OP_CoreFlashFunctionWrite(unsigned char * payload,unsigned short v30p_stream_flags_)445 short V3OP_CoreFlashFunctionWrite(unsigned char *payload, unsigned short v30p_stream_flags_)
446 {
447 static unsigned long address_pointer;
448 static unsigned long start_addr = 0; //(*(unsigned long*)&payload[4] & 0xFFFFF);
449 static unsigned long end_addr = 0; //start_addr + (*(unsigned long*)&payload[8] & 0xFFFFF);
450 unsigned char segment_type;
451 unsigned short *data_ptr = NULL;
452 unsigned short *data_ptr_end = NULL;
453 static unsigned long segmentSize =0;
454
455
456 if(v30p_stream_flags_ & MESSAGE_NEW_MSG)
457 {
458 start_addr = (*(unsigned long*)&payload[4] & 0xFFFFF);
459 end_addr = start_addr + (*(unsigned long*)&payload[8] & 0xFFFFF);
460 data_ptr = (unsigned short*)&payload[12];
461 data_ptr_end = data_ptr + (payload[0] - 11) / sizeof(unsigned short); // write words /2
462 segment_type = V3OP_GetSegmentType(start_addr);
463
464 if(segment_type == NO_SEGMENT)
465 {
466 return -1;
467 }
468 // 2 bytes are written in one line
469 segmentSize = 2;
470 address_pointer = start_addr;
471 UnlockInfoA();
472 }
473 else
474 {
475 data_ptr = (unsigned short*)&payload[4];
476 data_ptr_end = data_ptr + (payload[0] - 3) / sizeof(unsigned short);
477 LockInfoA();
478 }
479 for(;(address_pointer < end_addr) && (data_ptr < (data_ptr_end)); address_pointer += segmentSize)
480 {
481 // check if wirte is going into info or HAL/Hil segment
482 // don't programm any core segment
483 if(V3OP_GetSegmentType(address_pointer) != NO_SEGMENT)
484 {
485 FlashWrite_16(data_ptr, (unsigned short*)address_pointer, 1);
486 data_ptr++;
487 }
488 }
489 return 0;
490 }
491
492 //! \brief reads UIF memory, inclusive BIOS area
493 //! \param[in] *payload pointer to receive buffer
494 //! \li bytes 4 -7: the flash address
495 //! \li bytes 8 -11: data length
496 //! \return 0
V3OP_CoreFlashFunctionRead(unsigned char * payload)497 short V3OP_CoreFlashFunctionRead(unsigned char *payload)
498 {
499 return(0);
500 }
501
V3OP_UpCore(void)502 void V3OP_UpCore(void)
503 {
504 _DINT_FET(); // Ensure no application interrupts fire during stop
505 USB_disconnect();
506 FPGA_RESET_ASSERT
507 //~1.6 s delay
508 __delay_cycles(40000000);
509
510 USB_disable();
511 XT2_Stop();
512 //erase infoB
513 Flash_SegmentErase((unsigned short*)0x197F);
514 //erase infoC
515 Flash_SegmentErase((unsigned short*)0x18FF);
516 //erase infoD
517 Flash_SegmentErase((unsigned short*)0x187F);
518 // erase flash reset vector to invoke usb bsl by next startup
519 Flash_SegmentErase((unsigned short*)0xFFFE);
520
521 //~1.6 s delay
522 __delay_cycles(40000000);
523 PMMCTL0 = PMMPW | PMMSWBOR; // generate BOR for reseting device
524 }
525
V3OP_SystemOk(void)526 unsigned short V3OP_SystemOk(void)
527 {
528 if(!V3OP_coreCrcOk())
529 {
530 Flash_SegmentErase((unsigned short*)0xFFFE);
531 return 0;
532 }
533 return 1;
534 }
535