Tanure

I2C Cypress

Jun 30th, 2020
145
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 17.74 KB | None | 0 0
  1. /*
  2. ## Cypress USB 3.0 Platform source file (cyfxusbi2cregmode.c)
  3. ## ===========================
  4. ##
  5. ##  Copyright Cypress Semiconductor Corporation, 2010-2018,
  6. ##  All Rights Reserved
  7. ##  UNPUBLISHED, LICENSED SOFTWARE.
  8. ##
  9. ##  CONFIDENTIAL AND PROPRIETARY INFORMATION
  10. ##  WHICH IS THE PROPERTY OF CYPRESS.
  11. ##
  12. ##  Use of this file is governed
  13. ##  by the license agreement included in the file
  14. ##
  15. ##     <install>/license/license.txt
  16. ##
  17. ##  where <install> is the Cypress software
  18. ##  installation root directory path.
  19. ##
  20. ## ===========================
  21. */
  22.  
  23. /* This application is used to program I2C EEPROM connected to FX3.
  24.    The firmware enumerates as a custom device communicating with
  25.    the CyUsb3.sys driver and provides a set of vendor commands that can
  26.    be used to read / write to EEPROM devices on I2C.
  27.  */
  28.  
  29. #include "cyu3system.h"
  30. #include "cyu3os.h"
  31. #include "cyu3dma.h"
  32. #include "cyu3error.h"
  33. #include "cyu3usb.h"
  34. #include "cyu3i2c.h"
  35. #include "cyu3spi.h"
  36. #include "cyu3uart.h"
  37. #include "cyfxusbi2cregmode.h"
  38.  
  39. CyU3PThread appThread;           /* Application thread object. */
  40. CyBool_t glIsApplnActive = CyFalse;
  41.  
  42. /* Firmware ID variable that may be used to verify I2C firmware. */
  43. const uint8_t glFirmwareID[32] __attribute__ ((aligned (32))) = { 'F', 'X', '3', ' ', 'I', '2', 'C', '\0' };
  44.  
  45. uint8_t glEp0Buffer[4096] __attribute__ ((aligned (32)));
  46.  
  47. uint16_t glI2cPageSize = 0x40;   /* I2C Page size to be used for transfers. */
  48.  
  49. /* Initialize the debug module with UART. */
  50. CyU3PReturnStatus_t CyFxDebugInit (void)
  51. {
  52.     CyU3PUartConfig_t uartConfig;
  53.     CyU3PReturnStatus_t status = CY_U3P_SUCCESS;
  54.  
  55.     /* Initialize and configure the UART for logging. */
  56.     status = CyU3PUartInit ();
  57.     if (status != CY_U3P_SUCCESS)
  58.     {
  59.         return status;
  60.     }
  61.  
  62.     CyU3PMemSet ((uint8_t *)&uartConfig, 0, sizeof (uartConfig));
  63.     uartConfig.baudRate = CY_U3P_UART_BAUDRATE_115200;
  64.     uartConfig.stopBit  = CY_U3P_UART_ONE_STOP_BIT;
  65.     uartConfig.parity   = CY_U3P_UART_NO_PARITY;
  66.     uartConfig.txEnable = CyTrue;
  67.     uartConfig.rxEnable = CyFalse;
  68.     uartConfig.flowCtrl = CyFalse;
  69.     uartConfig.isDma    = CyTrue;
  70.     status = CyU3PUartSetConfig (&uartConfig, NULL);
  71.     if (status != CY_U3P_SUCCESS)
  72.     {
  73.         return status;
  74.     }
  75.  
  76.     /* Set the dma for an inifinity transfer */
  77.     status = CyU3PUartTxSetBlockXfer (0xFFFFFFFF);
  78.     if (status != CY_U3P_SUCCESS)
  79.     {
  80.         return status;
  81.     }
  82.  
  83.     /* Start the debug module for printing log messages. */
  84.     status = CyU3PDebugInit (CY_U3P_LPP_SOCKET_UART_CONS, 8);
  85.  
  86.     return status;
  87. }
  88.  
  89. /* I2c initialization for EEPROM programming. */
  90. CyU3PReturnStatus_t CyFxI2cInit (uint16_t pageLen)
  91. {
  92.     CyU3PI2cConfig_t i2cConfig;
  93.     CyU3PReturnStatus_t status = CY_U3P_SUCCESS;
  94.  
  95.     /* Initialize and configure the I2C master module. */
  96.     status = CyU3PI2cInit ();
  97.     if (status != CY_U3P_SUCCESS)
  98.     {
  99.         return status;
  100.     }
  101.  
  102.     /* Start the I2C master block. The bit rate is set at 100KHz.
  103.      * The data transfer is done via DMA. */
  104.     CyU3PMemSet ((uint8_t *)&i2cConfig, 0, sizeof(i2cConfig));
  105.     i2cConfig.bitRate    = CY_FX_USBI2C_I2C_BITRATE;
  106.     i2cConfig.busTimeout = 0xFFFFFFFF;
  107.     i2cConfig.dmaTimeout = 0xFFFF;
  108.     i2cConfig.isDma      = CyFalse;
  109.  
  110.     status = CyU3PI2cSetConfig (&i2cConfig, NULL);
  111.     if (status == CY_U3P_SUCCESS)
  112.     {
  113.         glI2cPageSize = pageLen;
  114.     }
  115.  
  116.     return status;
  117. }
  118.  
  119. /* I2C read / write for programmer application. */
  120. CyU3PReturnStatus_t CyFxUsbI2cTransfer(uint16_t byteAddress, uint8_t devAddr, uint16_t byteCount, uint8_t *buffer, CyBool_t isRead)
  121. {
  122.     CyU3PI2cPreamble_t preamble;
  123.     uint16_t pageCount = (byteCount / glI2cPageSize);
  124.     CyU3PReturnStatus_t status = CY_U3P_SUCCESS;
  125.     uint16_t resCount = glI2cPageSize;
  126.  
  127.     if (byteCount == 0)
  128.     {
  129.         return CY_U3P_SUCCESS;
  130.     }
  131.  
  132.     if ((byteCount % glI2cPageSize) != 0)
  133.     {
  134.         pageCount ++;
  135.         resCount = byteCount % glI2cPageSize;
  136.     }
  137.  
  138.     CyU3PDebugPrint (2, "I2C access - dev: 0x%x, address: 0x%x, size: 0x%x, pages: 0x%x.\r\n", devAddr, byteAddress, byteCount, pageCount);
  139.  
  140.     while (pageCount != 0) {
  141.         if (isRead) {
  142.             /* Update the preamble information. */
  143.             preamble.length    = 4;
  144.             preamble.buffer[0] = devAddr;
  145.             preamble.buffer[1] = (uint8_t)(byteAddress >> 8);
  146.             preamble.buffer[2] = (uint8_t)(byteAddress & 0xFF);
  147.             preamble.buffer[3] = (devAddr | 0x01);
  148.             preamble.ctrlMask  = 0x0004;
  149.  
  150.             status = CyU3PI2cReceiveBytes (&preamble, buffer, (pageCount == 1) ? resCount : glI2cPageSize, 0);
  151.             if (status != CY_U3P_SUCCESS)
  152.             {
  153.                 CyU3PDebugPrint (2, "CyU3PI2cReceiveBytes status != CY_U3P_SUCCESS\r\n");
  154.                 return status;
  155.             }
  156.         } else {
  157.             /* Update the preamble information. */
  158.             preamble.length    = 3;
  159.             preamble.buffer[0] = devAddr;
  160.             preamble.buffer[1] = (uint8_t)(byteAddress >> 8);
  161.             preamble.buffer[2] = (uint8_t)(byteAddress & 0xFF);
  162.             preamble.ctrlMask  = 0x0000;
  163.  
  164.             status = CyU3PI2cTransmitBytes (&preamble, buffer, (pageCount == 1) ? resCount : glI2cPageSize, 0);
  165.             if (status != CY_U3P_SUCCESS)
  166.             {
  167.                 CyU3PDebugPrint (2, "CyU3PI2cTransmitBytes status != CY_U3P_SUCCESS\r\n");
  168.                 return status;
  169.             }
  170.  
  171.             /* Wait for the write to complete.
  172.             preamble.length = 1;
  173.             status = CyU3PI2cWaitForAck(&preamble, 200);
  174.             if (status != CY_U3P_SUCCESS)
  175.             {
  176.                 CyU3PDebugPrint (2, "CyU3PI2cWaitForAck status != CY_U3P_SUCCESS\r\n");
  177.                 return status;
  178.             }
  179.             */
  180.         }
  181.  
  182.         /* An additional delay seems to be required after receiving an ACK.
  183.         CyU3PThreadSleep (1);*/
  184.  
  185.         /* Update the parameters */
  186.         byteAddress  += glI2cPageSize;
  187.         buffer += glI2cPageSize;
  188.         pageCount --;
  189.     }
  190.  
  191.     return CY_U3P_SUCCESS;
  192. }
  193.  
  194. CyBool_t CyFxUSBSetupCB ( uint32_t setupdat0, uint32_t setupdat1)
  195. {
  196.     /* Fast enumeration is used. Only requests addressed to the interface, class,
  197.      * vendor and unknown control requests are received by this function. */
  198.  
  199.     uint8_t  bRequest, bReqType;
  200.     uint8_t  bType, bTarget;
  201.     uint16_t wValue, wIndex, wLength;
  202.     CyBool_t isHandled = CyFalse;
  203.     CyU3PReturnStatus_t status = CY_U3P_SUCCESS;
  204.  
  205.     /* Decode the fields from the setup request. */
  206.     bReqType = (setupdat0 & CY_U3P_USB_REQUEST_TYPE_MASK);
  207.     bType    = (bReqType & CY_U3P_USB_TYPE_MASK);
  208.     bTarget  = (bReqType & CY_U3P_USB_TARGET_MASK);
  209.     bRequest = ((setupdat0 & CY_U3P_USB_REQUEST_MASK) >> CY_U3P_USB_REQUEST_POS);
  210.     wValue   = ((setupdat0 & CY_U3P_USB_VALUE_MASK)   >> CY_U3P_USB_VALUE_POS);
  211.     wIndex   = ((setupdat1 & CY_U3P_USB_INDEX_MASK)   >> CY_U3P_USB_INDEX_POS);
  212.     wLength   = ((setupdat1 & CY_U3P_USB_LENGTH_MASK)   >> CY_U3P_USB_LENGTH_POS);
  213.  
  214.     if (bType == CY_U3P_USB_STANDARD_RQT)
  215.     {
  216.         /* Handle SET_FEATURE(FUNCTION_SUSPEND) and CLEAR_FEATURE(FUNCTION_SUSPEND)
  217.          * requests here. It should be allowed to pass if the device is in configured
  218.          * state and failed otherwise. */
  219.         if ((bTarget == CY_U3P_USB_TARGET_INTF) && ((bRequest == CY_U3P_USB_SC_SET_FEATURE)
  220.                     || (bRequest == CY_U3P_USB_SC_CLEAR_FEATURE)) && (wValue == 0))
  221.         {
  222.             if (glIsApplnActive)
  223.                 CyU3PUsbAckSetup ();
  224.             else
  225.                 CyU3PUsbStall (0, CyTrue, CyFalse);
  226.  
  227.             isHandled = CyTrue;
  228.         }
  229.     }
  230.  
  231.     /* Handle supported vendor requests. */
  232.     if (bType == CY_U3P_USB_VENDOR_RQT)
  233.     {
  234.         isHandled = CyTrue;
  235.  
  236.         switch (bRequest)
  237.         {
  238.             case CY_FX_RQT_I2C_EEPROM_WRITE:
  239.                 status  = CyU3PUsbGetEP0Data(wLength, glEp0Buffer, NULL);
  240.                 if (status == CY_U3P_SUCCESS)
  241.                 {
  242.                     CyFxUsbI2cTransfer (wIndex, wValue, wLength, glEp0Buffer, CyFalse);
  243.                 }
  244.                 break;
  245.  
  246.             case CY_FX_RQT_I2C_EEPROM_READ:
  247.                 CyU3PMemSet (glEp0Buffer, 0, sizeof (glEp0Buffer));
  248.                 status = CyFxUsbI2cTransfer (wIndex, wValue, wLength, glEp0Buffer, CyTrue);
  249.                 if (status == CY_U3P_SUCCESS)
  250.                 {
  251.                     status = CyU3PUsbSendEP0Data(wLength, glEp0Buffer);
  252.                 }
  253.                 break;
  254.  
  255.             default:
  256.                 /* This is unknown request. */
  257.                 isHandled = CyFalse;
  258.                 break;
  259.         }
  260.  
  261.         /* If there was any error, return not handled so that the library will
  262.          * stall the request. Alternatively EP0 can be stalled here and return
  263.          * CyTrue. */
  264.         if (status != CY_U3P_SUCCESS)
  265.         {
  266.             isHandled = CyFalse;
  267.         }
  268.     }
  269.  
  270.     return isHandled;
  271. }
  272.  
  273. /* This is the callback function to handle the USB events. */
  274. void
  275. CyFxUSBEventCB ( CyU3PUsbEventType_t evtype, /* Event type */ uint16_t evdata  /* Event data */)
  276. {
  277.     switch (evtype)
  278.     {
  279.         case CY_U3P_USB_EVENT_SETCONF:
  280.             glIsApplnActive = CyTrue;
  281.             /* Disable the low power entry to optimize USB throughput */
  282.             CyU3PUsbLPMDisable();
  283.             break;
  284.  
  285.         case CY_U3P_USB_EVENT_RESET:
  286.         case CY_U3P_USB_EVENT_DISCONNECT:
  287.             glIsApplnActive = CyFalse;
  288.             break;
  289.  
  290.         default:
  291.             break;
  292.     }
  293. }
  294.  
  295. /* Callback function to handle LPM requests from the USB 3.0 host. This function is invoked by the API
  296.    whenever a state change from U0 -> U1 or U0 -> U2 happens. If we return CyTrue from this function, the
  297.    FX3 device is retained in the low power state. If we return CyFalse, the FX3 device immediately tries
  298.    to trigger an exit back to U0.
  299.  
  300.    This application does not have any state in which we should not allow U1/U2 transitions; and therefore
  301.    the function always return CyTrue.
  302.  */
  303. CyBool_t CyFxApplnLPMRqtCB (CyU3PUsbLinkPowerMode link_mode)
  304. {
  305.     return CyTrue;
  306. }
  307.  
  308. /* Initialize all interfaces for the application. */
  309. CyU3PReturnStatus_t CyFxUsbI2cInit (void)
  310. {
  311.     CyU3PReturnStatus_t status = CY_U3P_SUCCESS;
  312.  
  313.     /* Initialize the I2C interface for the EEPROM of page size 64 bytes. */
  314.     status = CyFxI2cInit (CY_FX_USBI2C_I2C_PAGE_SIZE);
  315.     if (status != CY_U3P_SUCCESS)
  316.     {
  317.         return status;
  318.     }
  319.  
  320.     /* Start the USB functionality. */
  321.     status = CyU3PUsbStart();
  322.     if (status != CY_U3P_SUCCESS)
  323.     {
  324.         return status;
  325.     }
  326.  
  327.     /* The fast enumeration is the easiest way to setup a USB connection,
  328.      * where all enumeration phase is handled by the library. Only the
  329.      * class / vendor requests need to be handled by the application. */
  330.     CyU3PUsbRegisterSetupCallback(CyFxUSBSetupCB, CyTrue);
  331.  
  332.     /* Setup the callback to handle the USB events. */
  333.     CyU3PUsbRegisterEventCallback(CyFxUSBEventCB);
  334.  
  335.     /* Register a callback to handle LPM requests from the USB 3.0 host. */
  336.     CyU3PUsbRegisterLPMRequestCallback(CyFxApplnLPMRqtCB);    
  337.  
  338.     /* Set the USB Enumeration descriptors */
  339.  
  340.     /* Super speed device descriptor. */
  341.     status = CyU3PUsbSetDesc(CY_U3P_USB_SET_SS_DEVICE_DESCR, 0, (uint8_t *)CyFxUSB30DeviceDscr);
  342.     if (status != CY_U3P_SUCCESS)
  343.     {
  344.         return status;
  345.     }
  346.  
  347.     /* High speed device descriptor. */
  348.     status = CyU3PUsbSetDesc(CY_U3P_USB_SET_HS_DEVICE_DESCR, 0, (uint8_t *)CyFxUSB20DeviceDscr);
  349.     if (status != CY_U3P_SUCCESS)
  350.     {
  351.         return status;
  352.     }
  353.  
  354.     /* BOS descriptor */
  355.     status = CyU3PUsbSetDesc(CY_U3P_USB_SET_SS_BOS_DESCR, 0, (uint8_t *)CyFxUSBBOSDscr);
  356.     if (status != CY_U3P_SUCCESS)
  357.     {
  358.         return status;
  359.     }
  360.  
  361.     /* Device qualifier descriptor */
  362.     status = CyU3PUsbSetDesc(CY_U3P_USB_SET_DEVQUAL_DESCR, 0, (uint8_t *)CyFxUSBDeviceQualDscr);
  363.     if (status != CY_U3P_SUCCESS)
  364.     {
  365.         return status;
  366.     }
  367.  
  368.     /* Super speed configuration descriptor */
  369.     status = CyU3PUsbSetDesc(CY_U3P_USB_SET_SS_CONFIG_DESCR, 0, (uint8_t *)CyFxUSBSSConfigDscr);
  370.     if (status != CY_U3P_SUCCESS)
  371.     {
  372.         return status;
  373.     }
  374.  
  375.     /* High speed configuration descriptor */
  376.     status = CyU3PUsbSetDesc(CY_U3P_USB_SET_HS_CONFIG_DESCR, 0, (uint8_t *)CyFxUSBHSConfigDscr);
  377.     if (status != CY_U3P_SUCCESS)
  378.     {
  379.         return status;
  380.     }
  381.  
  382.     /* Full speed configuration descriptor */
  383.     status = CyU3PUsbSetDesc(CY_U3P_USB_SET_FS_CONFIG_DESCR, 0, (uint8_t *)CyFxUSBFSConfigDscr);
  384.     if (status != CY_U3P_SUCCESS)
  385.     {
  386.         return status;
  387.     }
  388.  
  389.     /* String descriptor 0 */
  390.     status = CyU3PUsbSetDesc(CY_U3P_USB_SET_STRING_DESCR, 0, (uint8_t *)CyFxUSBStringLangIDDscr);
  391.     if (status != CY_U3P_SUCCESS)
  392.     {
  393.         return status;
  394.     }
  395.  
  396.     /* String descriptor 1 */
  397.     status = CyU3PUsbSetDesc(CY_U3P_USB_SET_STRING_DESCR, 1, (uint8_t *)CyFxUSBManufactureDscr);
  398.     if (status != CY_U3P_SUCCESS)
  399.     {
  400.         return status;
  401.     }
  402.  
  403.     /* String descriptor 2 */
  404.     status = CyU3PUsbSetDesc(CY_U3P_USB_SET_STRING_DESCR, 2, (uint8_t *)CyFxUSBProductDscr);
  405.     if (status != CY_U3P_SUCCESS)
  406.     {
  407.         return status;
  408.     }
  409.  
  410.     /* Connect the USB Pins with super speed operation enabled. */
  411.     status = CyU3PConnectState(CyTrue, CyTrue);
  412.  
  413.     return status;
  414. }
  415.  
  416. /*
  417.  * Entry function for the application thread. This function performs
  418.  * the initialization of the Debug, I2C, SPI and USB modules and then
  419.  * executes in a loop printing out heartbeat messages through the UART.
  420.  */
  421. void AppThread_Entry (uint32_t input)
  422. {
  423.     uint8_t count = 0;
  424.     CyU3PReturnStatus_t status = CY_U3P_SUCCESS;
  425.  
  426.     /* Initialize the debug interface. */
  427.     status = CyFxDebugInit ();
  428.     if (status != CY_U3P_SUCCESS)
  429.     {
  430.         goto handle_error;
  431.     }
  432.  
  433.     /* Initialize the application. */
  434.     status = CyFxUsbI2cInit ();
  435.     if (status != CY_U3P_SUCCESS)
  436.     {
  437.         goto handle_error;
  438.     }
  439.  
  440.     for (;;)
  441.     {
  442.         //CyU3PDebugPrint (4, "%x: Device initialized. Firmware ID: %x %x %x %x %x %x %x %x\r\n", count++, glFirmwareID[3], glFirmwareID[2], glFirmwareID[1], glFirmwareID[0], glFirmwareID[7], glFirmwareID[6], glFirmwareID[5], glFirmwareID[4]);
  443.         CyU3PThreadSleep (1000);
  444.     }
  445.  
  446. handle_error:
  447.     CyU3PDebugPrint (4, "%x: Application failed to initialize. Error code: %d.\n", status);
  448.     while (1);
  449. }
  450.  
  451. /* Application define function which creates the application threads. */
  452. void CyFxApplicationDefine (void)
  453. {
  454.     void *ptr = NULL;
  455.     uint32_t retThrdCreate = CY_U3P_SUCCESS;
  456.  
  457.     /* Allocate the memory for the threads and create threads */
  458.     ptr = CyU3PMemAlloc (APPTHREAD_STACK);
  459.     retThrdCreate = CyU3PThreadCreate (&appThread, /* Thread structure. */
  460.             "21:AppThread",                        /* Thread ID and name. */        
  461.             AppThread_Entry,                       /* Thread entry function. */
  462.             0,                                     /* Thread input parameter. */
  463.             ptr,                                   /* Pointer to the allocated thread stack. */
  464.             APPTHREAD_STACK,                       /* Allocated thread stack size. */
  465.             APPTHREAD_PRIORITY,                    /* Thread priority. */
  466.             APPTHREAD_PRIORITY,                    /* Thread pre-emption threshold: No preemption. */
  467.             CYU3P_NO_TIME_SLICE,                   /* No time slice. Thread will run until task is
  468.                                                       completed or until the higher priority
  469.                                                       thread gets active. */
  470.             CYU3P_AUTO_START                       /* Start the thread immediately. */
  471.             );
  472.  
  473.     /* Check the return code */
  474.     if (retThrdCreate != 0)
  475.     {
  476.         /* Thread creation failed with the error code retThrdCreate */
  477.  
  478.         /* Add custom recovery or debug actions here */
  479.  
  480.         /* Application cannot continue. Loop indefinitely */
  481.         while(1);
  482.     }
  483. }
  484.  
  485. /*
  486.  * Main function
  487.  */
  488. int
  489. main (void)
  490. {
  491.     CyU3PIoMatrixConfig_t io_cfg;
  492.     CyU3PReturnStatus_t status = CY_U3P_SUCCESS;
  493.  
  494.     /* Initialize the device */
  495.     status = CyU3PDeviceInit (NULL);
  496.     if (status != CY_U3P_SUCCESS)
  497.     {
  498.         goto handle_fatal_error;
  499.     }
  500.  
  501.     /* Initialize the caches. Enable both Instruction and Data Caches. */
  502.     status = CyU3PDeviceCacheControl (CyTrue, CyTrue, CyTrue);
  503.     if (status != CY_U3P_SUCCESS)
  504.     {
  505.         goto handle_fatal_error;
  506.     }
  507.  
  508.     /* Configure the IO matrix for the device. On the FX3 DVK board, the COM port
  509.      * is connected to the IO(53:56). Either select 32-bit GPIF or use UART_ONLY mode. */
  510.     CyU3PMemSet ((uint8_t *)&io_cfg, 0, sizeof(io_cfg));
  511.     io_cfg.isDQ32Bit = CyFalse;
  512.     io_cfg.s0Mode = CY_U3P_SPORT_INACTIVE;
  513.     io_cfg.s1Mode = CY_U3P_SPORT_INACTIVE;
  514.     io_cfg.useUart   = CyTrue;
  515.     io_cfg.useI2C    = CyTrue;
  516.     io_cfg.useI2S    = CyFalse;
  517.     io_cfg.useSpi    = CyFalse;
  518.     /* This mode is for SPI, UART and I2S. I2C is still enabled. */
  519.     io_cfg.lppMode   = CY_U3P_IO_MATRIX_LPP_UART_ONLY;
  520.     /* No GPIOs are enabled. */
  521.     io_cfg.gpioSimpleEn[0]  = 0;
  522.     io_cfg.gpioSimpleEn[1]  = 0;
  523.     io_cfg.gpioComplexEn[0] = 0;
  524.     io_cfg.gpioComplexEn[1] = 0;
  525.     status = CyU3PDeviceConfigureIOMatrix (&io_cfg);
  526.     if (status != CY_U3P_SUCCESS)
  527.     {
  528.         goto handle_fatal_error;
  529.     }
  530.  
  531.     /* This is a non returnable call for initializing the RTOS kernel */
  532.     CyU3PKernelEntry ();
  533.  
  534.     /* Dummy return to make the compiler happy */
  535.     return 0;
  536.  
  537. handle_fatal_error:
  538.  
  539.     /* Cannot recover from this error. */
  540.     while (1);
  541. }
  542.  
  543. /* [ ] */
Add Comment
Please, Sign In to add comment