00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00044 #include <cfg/memory.h>
00045 #include <sys/nutdebug.h>
00046
00047 #include <dev/spi_node_at45d.h>
00048
00049 #include <stdlib.h>
00050 #include <string.h>
00051
00056
00057 #ifndef FLASH_BUFFERS_AT45D
00058 #define FLASH_BUFFERS_AT45D 2
00059 #endif
00060
00061 #ifdef AT45D_CRC_PAGE
00062 #define AT45D_CRC_SIZE 2
00063 #else
00064 #define AT45D_CRC_SIZE 0
00065 #endif
00066
00068 #define FLASH_BUFFER_DIRTY 0x0001
00069
00075 typedef struct _AT45D_FLASH {
00080 sf_unit_t dxb_page[FLASH_BUFFERS_AT45D];
00081
00087 uint_fast8_t flags[FLASH_BUFFERS_AT45D];
00088
00094 int dxb_locks[FLASH_BUFFERS_AT45D];
00095
00100 HANDLE dxb_lque;
00101
00107 uint_fast8_t dxb_pshft;
00108
00110 uint8_t *dxb_pbuf[FLASH_BUFFERS_AT45D];
00111 } AT45D_FLASH;
00112
00113 #ifdef AT45D_CRC_PAGE
00114
00115
00116
00117
00118 static uint16_t crc_ccitt_update(uint16_t crc, uint8_t data)
00119 {
00120 data ^= (uint8_t) (crc);
00121 data ^= data << 4;
00122
00123 return ((((uint16_t) data << 8) | (crc >> 8)) ^ (uint8_t) (data >> 4) ^ ((uint16_t) data << 3));
00124 }
00125
00134 static int CalculateChecksum(AT45D_FLASH * at, int_fast8_t b, uint16_t * crc16, int xlen)
00135 {
00136 int rc;
00137 NUTSPIBUS *bus;
00138
00139
00140 NUTASSERT(at != NULL);
00141 NUTASSERT(crc16 != NULL);
00142
00143 *crc16 = 0xffff;
00144 if (xlen) {
00145 int i;
00146 uint8_t c;
00147 uint_fast8_t ne = 0;
00148
00149 for (i = 0; i < xlen; i++) {
00150 c = at->dxb_pbuf[i];
00151 if (ne || c != 0xff) {
00152 ne = 1;
00153 *crc16 = crc_ccitt_update(*crc16, c);
00154 }
00155 }
00156 if (*crc16 == 0) {
00157 *crc16 = 0xffff;
00158 }
00159 }
00160 }
00161
00162 #endif
00163
00172 static int At45dFlashSaveUnit(NUTSERIALFLASH * sfi, int_fast8_t b)
00173 {
00174 int rc;
00175 AT45D_FLASH *at;
00176
00177 at = (AT45D_FLASH *) sfi->sf_info;
00178
00179 #ifdef AT45D_CRC_PAGE
00180
00181 {
00182 uint16_t crc16;
00183
00184 CalculateChecksum(at, b, &crc16, sfi->sf_unit_size);
00185 memcpy(at->dxb_pbuf[b] + sfi->sf_unit_size, &crc16, sizeof(crc16));
00186 }
00187 #endif
00188
00189 rc = At45dNodeLock(sfi->sf_node);
00190 if (rc == 0) {
00191 rc = At45dNodeTransfer(sfi->sf_node, DFCMD_BUF2_WRITE, 0, 4, at->dxb_pbuf[b], NULL, sfi->sf_unit_size + AT45D_CRC_SIZE);
00192 if (rc == 0) {
00193 uint32_t pga;
00194
00195 pga = at->dxb_page[b];
00196 pga <<= at->dxb_pshft;
00197 rc = At45dNodeCommand(sfi->sf_node, DFCMD_BUF2_FLASH_NE, pga, 4);
00198 if (rc == 0) {
00199 rc = At45dNodeWaitReady(sfi->sf_node, AT45_WRITE_POLLS, 0);
00200 }
00201 At45dNodeUnlock(sfi->sf_node);
00202 if (rc == 0) {
00203 at->flags[b] &= ~FLASH_BUFFER_DIRTY;
00204 }
00205 }
00206 }
00207 return rc;
00208 }
00209
00216 static void At45dFlashRelease(NUTSERIALFLASH * sfi, int b)
00217 {
00218 AT45D_FLASH *at = (AT45D_FLASH *) sfi->sf_info;
00219
00220 at->dxb_locks[b]--;
00221 NutEventPost(&at->dxb_lque);
00222 }
00223
00241 static int_fast8_t At45dFlashLoadUnit(NUTSERIALFLASH * sfi, sf_unit_t pgn, int_fast8_t lock)
00242 {
00243 static int_fast8_t bnxt = 0;
00244 int_fast8_t b;
00245 AT45D_FLASH *at;
00246
00247 at = (AT45D_FLASH *) sfi->sf_info;
00248 for (;;) {
00249
00250
00251
00252 for (b = 0; b < FLASH_BUFFERS_AT45D; b++) {
00253 if (at->dxb_page[b] == pgn) {
00254 if (lock) {
00255 at->dxb_locks[b]++;
00256 at->flags[b] |= FLASH_BUFFER_DIRTY;
00257 }
00258 return b;
00259 }
00260 }
00261
00262
00263
00264
00265 b = bnxt;
00266 do {
00267 if (at->dxb_locks[b] == 0 && (at->flags[b] & FLASH_BUFFER_DIRTY) == 0) {
00268 break;
00269 }
00270 if (++b >= FLASH_BUFFERS_AT45D) {
00271 b = 0;
00272 }
00273 if (b == bnxt) {
00274 b = -1;
00275 }
00276 } while (b >= 0);
00277 if (b >= 0) {
00278 int rc;
00279 uint32_t pga = pgn;
00280
00281 if (At45dNodeLock(sfi->sf_node) == 0) {
00282 pga <<= at->dxb_pshft;
00283 if (lock) {
00284 at->dxb_locks[b]++;
00285 }
00286 rc = At45dNodeTransfer(sfi->sf_node, DFCMD_READ_PAGE, pga, 8, NULL, at->dxb_pbuf[b],
00287 sfi->sf_unit_size + AT45D_CRC_SIZE);
00288 At45dNodeUnlock(sfi->sf_node);
00289 if (rc == 0) {
00290 at->dxb_page[b] = pgn;
00291 if (lock) {
00292 at->flags[b] |= FLASH_BUFFER_DIRTY;
00293 }
00294 if (++bnxt >= FLASH_BUFFERS_AT45D) {
00295 bnxt = 0;
00296 }
00297 return b;
00298 }
00299 if (lock) {
00300 At45dFlashRelease(sfi, b);
00301 }
00302 }
00303 return -1;
00304 }
00305
00306
00307
00308
00309 for (b = 0; b < FLASH_BUFFERS_AT45D; b++) {
00310 if (at->dxb_locks[b] == 0 && (at->flags[b] & FLASH_BUFFER_DIRTY) == FLASH_BUFFER_DIRTY) {
00311 if (At45dFlashSaveUnit(sfi, b)) {
00312 return -1;
00313 }
00314 break;
00315 }
00316 }
00317
00318
00319
00320
00321 if (b >= FLASH_BUFFERS_AT45D) {
00322 NutEventWait(&at->dxb_lque, 0);
00323 }
00324 }
00325 }
00326
00334 static int SpiAt45dFlashInit(NUTSERIALFLASH * sfi)
00335 {
00336 int_fast8_t b;
00337 AT45D_INFO *df;
00338 AT45D_FLASH *at;
00339
00340
00341 NUTASSERT(sfi != NULL);
00342 NUTASSERT(sfi->sf_node != NULL);
00343
00344 df = At45dNodeProbe(sfi->sf_node);
00345 if (df == NULL) {
00346
00347 return -1;
00348 }
00349
00350 at = calloc(1, sizeof(AT45D_FLASH));
00351 if (at == NULL) {
00352 return -1;
00353 }
00354 at->dxb_pshft = df->at45d_pshft;
00355 for (b = 0; b < FLASH_BUFFERS_AT45D; b++) {
00356 at->dxb_page[b] = SERIALFLASH_MAX_UNITS;
00357 at->dxb_pbuf[b] = malloc(df->at45d_psize);
00358 }
00359 sfi->sf_info = (void *) at;
00360 sfi->sf_units = (sf_unit_t) df->at45d_pages;
00361 sfi->sf_unit_size = df->at45d_psize - AT45D_CRC_SIZE;
00362
00363 return 0;
00364 }
00365
00371 static void SpiAt45dFlashExit(NUTSERIALFLASH * sfi)
00372 {
00373 int_fast8_t b;
00374 AT45D_FLASH *at;
00375
00376 NUTASSERT(sfi != NULL);
00377 NUTASSERT(sfi->sf_info != NULL);
00378
00379 at = (AT45D_FLASH *) sfi->sf_info;
00380 for (b = 0; b < FLASH_BUFFERS_AT45D; b++) {
00381 free(at->dxb_pbuf[b]);
00382 }
00383 free(sfi->sf_info);
00384 }
00385
00396 static int SpiAt45dFlashCheck(NUTSERIALFLASH * sfi, sf_unit_t pgn, int cnt)
00397 {
00398 int rc = 0;
00399
00400 #ifdef AT45D_CRC_PAGE
00401 AT45D_FLASH *at;
00402 int_fast8_t b;
00403 uint16_t crc16 = 0;
00404
00405
00406 NUTASSERT(sfi != NULL);
00407 NUTASSERT(sfi->sf_info != NULL);
00408 NUTASSERT(pgn + cnt <= sfi->sf_units);
00409 NUTASSERT(cnt >= 0);
00410
00411 at = (AT45D_FLASH *) sfi->sf_info;
00412 while (cnt--) {
00413 b = At45dFlashLoadUnit(sfi, pgn + cnt, 0);
00414 if (b >= 0) {
00415 CalculateChecksum(at, b, &crc16, sfi->sf_unit_size + AT45D_CRC_SIZE);
00416 }
00417 if (crc16 != 0xFFFF) {
00418 rc = -1;
00419 break;
00420 }
00421 }
00422 #endif
00423 return rc;
00424 }
00425
00438 static int SpiAt45dFlashRead(NUTSERIALFLASH * sfi, sf_unit_t pgn, int off, void *data, int len)
00439 {
00440 int rc = 0;
00441
00442
00443 NUTASSERT(sfi != NULL);
00444 NUTASSERT(sfi->sf_info != NULL);
00445 NUTASSERT(pgn < sfi->sf_units);
00446 NUTASSERT(off >= -(int) sfi->sf_unit_size && off <= (int) sfi->sf_unit_size);
00447 NUTASSERT(data != NULL);
00448 NUTASSERT(len >= 0 && len <= sfi->sf_unit_size);
00449
00450 if (len) {
00451 int_fast8_t b;
00452 AT45D_FLASH *at = (AT45D_FLASH *) sfi->sf_info;
00453
00454
00455 if (off < 0) {
00456 off += sfi->sf_unit_size;
00457 }
00458 NUTASSERT(off + len <= (int) sfi->sf_unit_size);
00459
00460 b = At45dFlashLoadUnit(sfi, pgn, 0);
00461 if (b < 0) {
00462 rc = -1;
00463 } else {
00464 memcpy(data, at->dxb_pbuf[b] + off, len);
00465 }
00466 }
00467 return rc;
00468 }
00469
00482 static int SpiAt45dFlashCompare(NUTSERIALFLASH * sfi, sf_unit_t pgn, int off, CONST void *data, int len)
00483 {
00484 int rc = 0;
00485
00486
00487 NUTASSERT(sfi != NULL);
00488 NUTASSERT(sfi->sf_info != NULL);
00489 NUTASSERT(pgn < sfi->sf_units);
00490 NUTASSERT(off >= -(int) sfi->sf_unit_size && off <= (int) sfi->sf_unit_size);
00491 NUTASSERT(data != NULL);
00492 NUTASSERT(len >= 0 && len <= sfi->sf_unit_size);
00493
00494 if (len) {
00495 int_fast8_t b;
00496 AT45D_FLASH *at = (AT45D_FLASH *) sfi->sf_info;
00497
00498
00499 if (off < 0) {
00500 off += sfi->sf_unit_size;
00501 }
00502 NUTASSERT(off + len <= (int) sfi->sf_unit_size);
00503
00504 b = At45dFlashLoadUnit(sfi, pgn, 0);
00505 if (b < 0 || memcmp(at->dxb_pbuf[b] + off, data, len)) {
00506 rc = -1;
00507 }
00508 }
00509 return rc;
00510 }
00511
00525 static int SpiAt45dFlashUsed(NUTSERIALFLASH * sfi, sf_unit_t pgn, int skip)
00526 {
00527 int rc;
00528 int len;
00529 AT45D_FLASH *at;
00530 int_fast8_t b;
00531
00532
00533 NUTASSERT(sfi != NULL);
00534 NUTASSERT(pgn < sfi->sf_units);
00535 NUTASSERT(skip <= (int) sfi->sf_unit_size);
00536
00537 at = (AT45D_FLASH *) sfi->sf_info;
00538
00539
00540 len = (int) sfi->sf_unit_size;
00541 if (skip < 0) {
00542 len += skip;
00543 skip = 0;
00544 } else {
00545 len -= skip;
00546 }
00547
00548 b = At45dFlashLoadUnit(sfi, pgn, 0);
00549 if (b < 0) {
00550 rc = -1;
00551 } else {
00552 rc = 0;
00553 if (len) {
00554 uint8_t *cp = at->dxb_pbuf[b] + skip;
00555 int i;
00556
00557 for (i = 0; i < len; i++) {
00558 if (*cp++ != 0xff) {
00559 rc = i + 1;
00560 }
00561 }
00562 }
00563 }
00564 return rc;
00565 }
00566
00583 static int SpiAt45dFlashWrite(NUTSERIALFLASH * sfi, sf_unit_t pgn, int off, CONST void *data, int len)
00584 {
00585 int rc = 0;
00586
00587
00588 NUTASSERT(sfi != NULL);
00589 NUTASSERT(pgn < sfi->sf_units);
00590 NUTASSERT(off >= -(int) sfi->sf_unit_size && off <= (int) sfi->sf_unit_size);
00591 NUTASSERT(len == 0 || data != NULL);
00592 NUTASSERT(len >= 0 && len <= sfi->sf_unit_size);
00593
00594 if (len) {
00595 int_fast8_t b;
00596 AT45D_FLASH *at = (AT45D_FLASH *) sfi->sf_info;
00597
00598
00599 if (off < 0) {
00600 off += sfi->sf_unit_size;
00601 }
00602 NUTASSERT(off + len <= (int) sfi->sf_unit_size);
00603
00604
00605 b = At45dFlashLoadUnit(sfi, pgn, 1);
00606 if (b < 0) {
00607 return -1;
00608 }
00609
00610 memcpy(at->dxb_pbuf[b] + off, data, len);
00611 At45dFlashRelease(sfi, b);
00612 }
00613 return rc;
00614 }
00615
00629 static int SpiAt45dFlashCopy(NUTSERIALFLASH * sfi, sf_unit_t spg, sf_unit_t dpg)
00630 {
00631 int_fast8_t b;
00632
00633
00634 NUTASSERT(sfi != NULL);
00635 NUTASSERT(sfi->sf_info != NULL);
00636 NUTASSERT(spg < sfi->sf_units);
00637 NUTASSERT(dpg < sfi->sf_units);
00638
00639
00640 if (spg == dpg) {
00641 b = At45dFlashLoadUnit(sfi, spg, 1);
00642 } else {
00643 AT45D_FLASH *at = (AT45D_FLASH *) sfi->sf_info;
00644
00645
00646 for (b = 0; b < FLASH_BUFFERS_AT45D; b++) {
00647 if (at->dxb_page[b] == dpg) {
00648
00649 if (at->dxb_locks[b]) {
00650 return -1;
00651 }
00652 at->dxb_page[b] = SERIALFLASH_MAX_UNITS;
00653 }
00654 }
00655
00656 b = At45dFlashLoadUnit(sfi, spg, 1);
00657 if (b >= 0) {
00658 at->dxb_page[b] = dpg;
00659 }
00660 }
00661 if (b >= 0) {
00662 At45dFlashRelease(sfi, b);
00663 return 0;
00664 }
00665 return -1;
00666 }
00667
00680 static int SpiAt45dFlashCommit(NUTSERIALFLASH * sfi, sf_unit_t pgn)
00681 {
00682 int rc = 0;
00683 int_fast8_t b;
00684 AT45D_FLASH *at;
00685
00686
00687 NUTASSERT(sfi != NULL);
00688 NUTASSERT(pgn < sfi->sf_units);
00689
00690 at = (AT45D_FLASH *) sfi->sf_info;
00691 for (b = 0; b < FLASH_BUFFERS_AT45D && at->dxb_page[b] != pgn; b++);
00692 if (b < FLASH_BUFFERS_AT45D && (at->flags[b] & FLASH_BUFFER_DIRTY) == FLASH_BUFFER_DIRTY) {
00693 rc = At45dFlashSaveUnit(sfi, b);
00694 }
00695 return rc;
00696 }
00697
00707 static int SpiAt45dFlashErase(NUTSERIALFLASH * sfi, sf_unit_t pgn, int cnt)
00708 {
00709 int rc = 0;
00710 int_fast8_t b;
00711 AT45D_FLASH *at;
00712
00713
00714 NUTASSERT(sfi != NULL);
00715 NUTASSERT(pgn + cnt <= sfi->sf_units);
00716 NUTASSERT(cnt >= 0);
00717
00718 at = (AT45D_FLASH *) sfi->sf_info;
00719
00720 for (b = 0; b < FLASH_BUFFERS_AT45D; b++) {
00721 if (at->dxb_page[b] >= pgn && at->dxb_page[b] < pgn + cnt) {
00722 at->dxb_page[b] = SERIALFLASH_MAX_UNITS;
00723 at->flags[b] &= ~FLASH_BUFFER_DIRTY;
00724 }
00725 }
00726 while (rc == 0 && cnt--) {
00727 uint32_t pga = pgn + cnt;
00728 pga <<= at->dxb_pshft;
00729
00730 rc = At45dNodeLock(sfi->sf_node);
00731 if (rc == 0) {
00732 rc = At45dNodeCommand(sfi->sf_node, DFCMD_PAGE_ERASE, pga, 4);
00733 if (rc == 0) {
00734 At45dNodeWaitReady(sfi->sf_node, AT45_WRITE_POLLS, 0);
00735 }
00736 At45dNodeUnlock(sfi->sf_node);
00737 }
00738 }
00739 return rc;
00740 }
00741
00742 #ifndef FLASH_MOUNT_OFFSET_AT45D0
00743 #ifdef MOUNT_OFFSET_AT45D0
00744 #define FLASH_MOUNT_OFFSET_AT45D0 MOUNT_OFFSET_AT45D0
00745 #else
00746 #define FLASH_MOUNT_OFFSET_AT45D0 0
00747 #endif
00748 #endif
00749
00750 #ifndef FLASH_MOUNT_TOP_RESERVE_AT45D0
00751 #ifdef MOUNT_TOP_RESERVE_AT45D0
00752 #define FLASH_MOUNT_TOP_RESERVE_AT45D0 MOUNT_TOP_RESERVE_AT45D0
00753 #else
00754 #define FLASH_MOUNT_TOP_RESERVE_AT45D0 1
00755 #endif
00756 #endif
00757
00761 NUTSERIALFLASH flashAt45d0 = {
00762 &nodeAt45d0,
00763 NULL,
00764 0,
00765 0,
00766 FLASH_MOUNT_OFFSET_AT45D0,
00767 FLASH_MOUNT_TOP_RESERVE_AT45D0,
00768 SpiAt45dFlashInit,
00769 SpiAt45dFlashExit,
00770 SpiAt45dFlashCheck,
00771 SpiAt45dFlashRead,
00772 SpiAt45dFlashCompare,
00773 SpiAt45dFlashUsed,
00774 SpiAt45dFlashWrite,
00775 SpiAt45dFlashCopy,
00776 SpiAt45dFlashCommit,
00777 SpiAt45dFlashErase
00778 };
00779
00780 #ifndef FLASH_MOUNT_OFFSET_AT45D1
00781 #ifdef MOUNT_OFFSET_AT45D1
00782 #define FLASH_MOUNT_OFFSET_AT45D1 MOUNT_OFFSET_AT45D1
00783 #else
00784 #define FLASH_MOUNT_OFFSET_AT45D1 0
00785 #endif
00786 #endif
00787
00788 #ifndef FLASH_MOUNT_TOP_RESERVE_AT45D1
00789 #ifdef MOUNT_TOP_RESERVE_AT45D1
00790 #define FLASH_MOUNT_TOP_RESERVE_AT45D1 MOUNT_TOP_RESERVE_AT45D1
00791 #else
00792 #define FLASH_MOUNT_TOP_RESERVE_AT45D1 1
00793 #endif
00794 #endif
00795
00799 NUTSERIALFLASH flashAt45d1 = {
00800 &nodeAt45d1,
00801 NULL,
00802 0,
00803 0,
00804 FLASH_MOUNT_OFFSET_AT45D1,
00805 FLASH_MOUNT_TOP_RESERVE_AT45D1,
00806 SpiAt45dFlashInit,
00807 SpiAt45dFlashExit,
00808 SpiAt45dFlashCheck,
00809 SpiAt45dFlashRead,
00810 SpiAt45dFlashCompare,
00811 SpiAt45dFlashUsed,
00812 SpiAt45dFlashWrite,
00813 SpiAt45dFlashCopy,
00814 SpiAt45dFlashCommit,
00815 SpiAt45dFlashErase
00816 };
00817
00818 #ifndef FLASH_MOUNT_OFFSET_AT45D2
00819 #ifdef MOUNT_OFFSET_AT45D2
00820 #define FLASH_MOUNT_OFFSET_AT45D2 MOUNT_OFFSET_AT45D2
00821 #else
00822 #define FLASH_MOUNT_OFFSET_AT45D2 0
00823 #endif
00824 #endif
00825
00826 #ifndef FLASH_MOUNT_TOP_RESERVE_AT45D2
00827 #ifdef MOUNT_TOP_RESERVE_AT45D2
00828 #define FLASH_MOUNT_TOP_RESERVE_AT45D2 MOUNT_TOP_RESERVE_AT45D2
00829 #else
00830 #define FLASH_MOUNT_TOP_RESERVE_AT45D2 1
00831 #endif
00832 #endif
00833
00837 NUTSERIALFLASH flashAt45d2 = {
00838 &nodeAt45d2,
00839 NULL,
00840 0,
00841 0,
00842 FLASH_MOUNT_OFFSET_AT45D2,
00843 FLASH_MOUNT_TOP_RESERVE_AT45D2,
00844 SpiAt45dFlashInit,
00845 SpiAt45dFlashExit,
00846 SpiAt45dFlashCheck,
00847 SpiAt45dFlashRead,
00848 SpiAt45dFlashCompare,
00849 SpiAt45dFlashUsed,
00850 SpiAt45dFlashWrite,
00851 SpiAt45dFlashCopy,
00852 SpiAt45dFlashCommit,
00853 SpiAt45dFlashErase
00854 };
00855
00856 #ifndef FLASH_MOUNT_OFFSET_AT45D3
00857 #ifdef MOUNT_OFFSET_AT45D3
00858 #define FLASH_MOUNT_OFFSET_AT45D3 MOUNT_OFFSET_AT45D3
00859 #else
00860 #define FLASH_MOUNT_OFFSET_AT45D3 0
00861 #endif
00862 #endif
00863
00864 #ifndef FLASH_MOUNT_TOP_RESERVE_AT45D3
00865 #ifdef MOUNT_TOP_RESERVE_AT45D3
00866 #define FLASH_MOUNT_TOP_RESERVE_AT45D3 MOUNT_TOP_RESERVE_AT45D3
00867 #else
00868 #define FLASH_MOUNT_TOP_RESERVE_AT45D3 1
00869 #endif
00870 #endif
00871
00875 NUTSERIALFLASH flashAt45d3 = {
00876 &nodeAt45d3,
00877 NULL,
00878 0,
00879 0,
00880 FLASH_MOUNT_OFFSET_AT45D3,
00881 FLASH_MOUNT_TOP_RESERVE_AT45D3,
00882 SpiAt45dFlashInit,
00883 SpiAt45dFlashExit,
00884 SpiAt45dFlashCheck,
00885 SpiAt45dFlashRead,
00886 SpiAt45dFlashCompare,
00887 SpiAt45dFlashUsed,
00888 SpiAt45dFlashWrite,
00889 SpiAt45dFlashCopy,
00890 SpiAt45dFlashCommit,
00891 SpiAt45dFlashErase
00892 };
00893