mac-can / RusokuCAN.dylib

macOS® Driver and SDK for TouCAN USB Interfaces from Rusoku
BSD 2-Clause "Simplified" License
5 stars 3 forks source link

Error -101 after wake up from hibernation #11

Closed mac-can closed 3 years ago

mac-can commented 3 years ago

If the Mac goes into hibernation mode and then wakes up again while a client application has loaded the library (by a call to can_init or InitializeChannel), any further call to a library function returns the error code -101 (vendor-specific error, hardware error ). This error can neither be cleared by re-initializing the CAN channel nor by restarting the client application; error -101 remains.

It seems that the hardware is stuck in a state where it does not accept the control commands or is incorrectly controlled by the library functions in this state.

Workaround:

Action points:

Additional information:

rusoku commented 3 years ago

I can confirm that after Mac hibernation mode adapter is stuck in a wrong state. After Windows 10 OS hibernating mode adapter works without any issues.

mickeyl commented 3 years ago

Might be worth looking at a low-level USB dump. It looks like the Mac sends a USB packet that makes the controller enter the "red" state.

rusoku commented 3 years ago

After hibernation USB host send several req frames and goes to error mode (101). I don't see any stall packets ... Need to go deeper to adapter code...

image

rusoku commented 3 years ago

In suspend mode firmware only close CAN interface, clear FIFO buffers and change LED mode to closed (RED). And after suspend mode off usb slave answers to all host requests... Need to check what means these class requests.I don't remeber already ;-)

void HAL_PCD_SuspendCallback(PCD_HandleTypeDef hpcd) { / Inform USB library that core enters in suspend Mode. / USBD_LL_Suspend((USBD_HandleTypeDef)hpcd->pData); __HAL_PCD_GATE_PHYCLOCK(hpcd); / Enter in STOP mode. / if (hpcd->Init.low_power_enable) { / Set SLEEPDEEP bit and SleepOnExit of Cortex System Control Register. / SCB->SCR |= (uint32_t)((uint32_t)(SCB_SCR_SLEEPDEEP_Msk | SCB_SCR_SLEEPONEXIT_Msk)); }

// Close CAN interface HAL_CAN_DeactivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING | CAN_IT_RX_FIFO0_OVERRUN);
HAL_CAN_DeactivateNotification(&hcan1, CAN_IT_RX_FIFO1_MSG_PENDING | CAN_IT_RX_FIFO1_OVERRUN);
HAL_CAN_DeactivateNotification(&hcan1, CAN_IT_TX_MAILBOX_EMPTY );

// Terminate TX messages if(HAL_CAN_IsTxMessagePending(&hcan1, CAN_TX_MAILBOX0))
HAL_CAN_AbortTxRequest(&hcan1, CAN_TX_MAILBOX0);

if(HAL_CAN_IsTxMessagePending(&hcan1, CAN_TX_MAILBOX1))
HAL_CAN_AbortTxRequest(&hcan1, CAN_TX_MAILBOX1);

if(HAL_CAN_IsTxMessagePending(&hcan1, CAN_TX_MAILBOX2)) HAL_CAN_AbortTxRequest(&hcan1, CAN_TX_MAILBOX2);

//Canal Interface DeInit
HAL_CAN_DeInit(&hcan1);

LED_change_status(LED_USB, LED_RED_ON); }

rusoku commented 3 years ago

Two last requests are incorrect. (25) USB2CAN_CLEAR_CAN_INTERFACE_ERROR_CODE (20) USB2CAN_GET_LAST_ERROR_CODE After this we get error (01) eq "HAL_ERROR" because CAN hardware is not initialized yet (HAL_CAN_STATE_RESET eq "0") In short, CAN interface must be initialized before these commands.

HAL_StatusTypeDef HAL_CAN_ResetError(CAN_HandleTypeDef *hcan) { HAL_StatusTypeDef status = HAL_OK;

if ((hcan->State == HAL_CAN_STATE_READY) || (hcan->State == HAL_CAN_STATE_LISTENING)) { / Reset CAN error code / hcan->ErrorCode = HAL_CAN_STATE_RESET; } else { / Update error code / hcan->ErrorCode |= HAL_CAN_ERROR_NOT_INITIALIZED; status = HAL_ERROR; / HAL_ERROR = 0x01U / } / Return the status / return status; }

image

rusoku commented 3 years ago

In "normal" (not after hibernating) mode there are no Class req OUT (0x25), so no error -101 ( HAL_ERROR )

typedef enum { HAL_OK = 0x00U, HAL_ERROR = 0x01U, HAL_BUSY = 0x02U, HAL_TIMEOUT = 0x03U } HAL_StatusTypeDef;

image

mac-can commented 3 years ago

As mentioned by rusoku in suspend mode the CAN interface will be closed (I guess this is de-intitialize). When the Mac awakes from hibernation no control command to the interface will succeed. Function TeardownChannel checks the state of the interface, and stops and de-intitializes the CAN interface when required by its current state. After this the error status is queried, and if there is an error pending (there is always an error set in suspend mode) the function commands a clear error request. This clear error request fails because the interface must be initialized to accept this control command. The same control sequence is also executed at the beginning of function InitializeChannel, therefore we don´t get out of this vicious cycle.

The solution is, when there is an error pending, first to initialize the interface, then to clear the error and to de-initialize it again. Afterward the function TeardownChannel as well as InitializeChannel can execute their normal control command sequences.

Fixed by commit 3feff400fc28956b46e4ac0037cabfd4f5a5181a.