/*! \fn getTouchedPositionAnswer(uint8_t led_mask) * \brief Use the capacitive interface to get quarter position * \param led_mask Led mask for the touchdetection routine * \return Number between 0 and 5 for valid pos, -1 otherwise */ int8_t getTouchedPositionAnswer(uint8_t led_mask) { #ifdef HARDWARE_V1 _delay_ms(2000); #endif #if defined(ALWAYS_ACCEPT_REQUESTS) || defined(HARDWARE_V1) // First quarter is discarded, it means we want yes or no! if (led_mask & LED_MASK_WHEEL_TLEFT) { return TOUCHPOS_RIGHT; } else { return TOUCHPOS_WHEEL_TLEFT; } #endif RET_TYPE touch_detect_result; // Switch on lights activityDetectedRoutine(); // Clear possible remaining detection touchDetectionRoutine(led_mask); touchWaitForWheelReleased(); touchClearCurrentDetections(); // Wait for a touch press, delay stored in eeprom (1024 is quite close to 1000 ;-) ) activateTimer(TIMER_USERINT, ((uint16_t)controlEepromParameter(getMooltipassParameterInEeprom(USER_INTER_TIMEOUT_PARAM), MIN_USER_INTER_DEL/1000, MAX_USER_INTER_DEL/1000)) << 10); do { // User interaction timeout or smartcard removed if ((hasTimerExpired(TIMER_USERINT, TRUE) == TIMER_EXPIRED) || (isSmartCardAbsent() == RETURN_OK)) { return -1; } touch_detect_result = touchDetectionRoutine(led_mask) & TOUCH_PRESS_MASK; } while (!touch_detect_result); // Did the user press one of the two touch buttons? if (touch_detect_result & RETURN_LEFT_PRESSED) { return TOUCHPOS_LEFT; } else if (touch_detect_result & RETURN_RIGHT_PRESSED) { return TOUCHPOS_RIGHT; } else { return (int8_t)getWheelTouchDetectionQuarter(); } }
/*! \fn getYesNoAnswerInput(uint8_t blocking) * \brief Use the input interface to get user input * \param blocking Boolean to know if we should wait for input or timeout * \note In case of a non blocking call, caller must call activityDetectedRoutine() & miniWheelClearDetections() * \return see mini_input_yes_no_ret_t */ RET_TYPE getYesNoAnswerInput(uint8_t blocking) { #if defined(ALWAYS_ACCEPT_REQUESTS) return MINI_INPUT_RET_YES; #endif uint8_t incomingData[RAWHID_TX_SIZE]; RET_TYPE detect_result; if (blocking == TRUE) { // Switch on lights activityDetectedRoutine(); // Clear possible remaining detection miniWheelClearDetections(); } // Wait for a touch press do { // User interaction timeout or smartcard removed if ((hasTimerExpired(TIMER_USERINT, TRUE) == TIMER_EXPIRED) || (isSmartCardAbsent() == RETURN_OK)) { return MINI_INPUT_RET_TIMEOUT; } // Read usb comms as the plugin could ask to cancel the request if ((getMooltipassParameterInEeprom(USER_REQ_CANCEL_PARAM) != FALSE) && (usbRawHidRecv(incomingData) == RETURN_COM_TRANSF_OK)) { if (incomingData[HID_TYPE_FIELD] == CMD_CANCEL_REQUEST) { // Request canceled return MINI_INPUT_RET_TIMEOUT; } else { // Another packet (that shouldn't be sent!), ask to retry later... usbSendMessage(CMD_PLEASE_RETRY, 0, incomingData); } } // Check if something has been pressed detect_result = miniGetWheelAction(FALSE, TRUE); if (detect_result == WHEEL_ACTION_SHORT_CLICK) { return MINI_INPUT_RET_YES; } } while(blocking != FALSE); // Return MINI_INPUT_RET_NONE if nothing was pressed and no timeout occurred return MINI_INPUT_RET_NONE; }
/*! \fn guiGetPinFromUser(volatile uint16_t* pin_code, uint8_t stringID) * \brief Ask the user to enter a PIN * \param pin_code Pointer to where to store the pin code * \param stringID String ID * \return If the user approved the request */ RET_TYPE guiGetPinFromUser(volatile uint16_t* pin_code, uint8_t stringID) { // If we don't need a pin code, send default one #if defined(NO_PIN_CODE_REQUIRED) || defined(HARDWARE_V1) || defined(V2_DEVELOPERS_BOTPCB_BOOTLOADER_SETUP) *pin_code = SMARTCARD_DEFAULT_PIN; return RETURN_OK; #endif RET_TYPE ret_val = RETURN_NOK; uint8_t selected_digit = 0; uint8_t finished = FALSE; uint8_t current_pin[4]; RET_TYPE temp_rettype; int8_t temp_int8; // Set current pin to 0000 memset((void*)current_pin, 0, 4); // Draw pin entering bitmap oledClear(); oledBitmapDrawFlash(0, 0, BITMAP_YES_NO, 0); oledBitmapDrawFlash(83, 51, BITMAP_PIN_LINES, 0); oledBitmapDrawFlash(238, 23, BITMAP_RIGHT_ARROW, 0); oledPutstrXY(0, 0, OLED_CENTRE, readStoredStringToBuffer(stringID)); oledDisplayOtherBuffer(); oledSetFont(FONT_PROFONT_24); oledWriteActiveBuffer(); // Display current pin on screen guiDisplayPinOnPinEnteringScreen(current_pin, selected_digit); // While the user hasn't entered his pin while(!finished) { // Still process the USB commands usbProcessIncoming(USB_CALLER_PIN); // Detect key touches temp_rettype = touchDetectionRoutine(0); // Send it to the touch wheel interface logic temp_int8 = touchWheelIntefaceLogic(temp_rettype); // Position increment / decrement if (temp_int8 != 0) { if ((current_pin[selected_digit] == 0x0F) && (temp_int8 == 1)) { current_pin[selected_digit] = 0xFF; } else if ((current_pin[selected_digit] == 0) && (temp_int8 == -1)) { current_pin[selected_digit] = 0x10; } current_pin[selected_digit] += temp_int8; guiDisplayPinOnPinEnteringScreen(current_pin, selected_digit); } if ((isSmartCardAbsent() == RETURN_OK) || (hasTimerExpired(TIMER_USERINT, TRUE) == TIMER_EXPIRED)) { // Smartcard removed, no reason to continue ret_val = RETURN_NOK; finished = TRUE; } if (temp_rettype & RETURN_LEFT_PRESSED) { if (selected_digit == 1) { oledFillXY(0, 23, 18, 18, 0x00); oledBitmapDrawFlash(0, 24, BITMAP_CROSS, 0); } if (selected_digit > 0) { // When going back set pin digit to 0 current_pin[selected_digit] = 0; current_pin[--selected_digit] = 0; } else { ret_val = RETURN_NOK; finished = TRUE; } guiDisplayPinOnPinEnteringScreen(current_pin, selected_digit); oledBitmapDrawFlash(238, 23, BITMAP_RIGHT_ARROW, 0); } else if (temp_rettype & RETURN_RIGHT_PRESSED) { if (selected_digit == 2) { oledFillXY(238, 23, 18, 18, 0x00); oledBitmapDrawFlash(240, 24, BITMAP_TICK, 0); } if (selected_digit < 3) { selected_digit++; } else { ret_val = RETURN_OK; finished = TRUE; } guiDisplayPinOnPinEnteringScreen(current_pin, selected_digit); oledBitmapDrawFlash(0, 23, BITMAP_LEFT_ARROW, 0); } } // Reset default font oledSetFont(FONT_DEFAULT); oledWriteInactiveBuffer(); // Store the pin *pin_code = (uint16_t)(((uint16_t)(current_pin[0]) << 12) | (((uint16_t)current_pin[1]) << 8) | (current_pin[2] << 4) | current_pin[3]); // Set current pin to 0000 memset((void*)current_pin, 0, 4); // Prevent touches until the user lifts his finger touchInhibitUntilRelease(); // Return success status return ret_val; }
/*! \fn loginSelectionScreen(void) * \brief Screen displayed to let the user choose/find a login * \return Valid child node address or 0 otherwise */ uint16_t loginSelectionScreen(void) { char currentText[SEARCHTEXT_MAX_LENGTH+1]; uint8_t displayRefreshNeeded = TRUE; uint16_t ret_val = NODE_ADDR_NULL; uint8_t wasWheelReleased = TRUE; uint16_t tempParentAddresses[5]; uint8_t currentStringIndex = 0; uint8_t nbMatchedParents= 0; uint8_t finished = FALSE; RET_TYPE temp_rettype; uint8_t led_mask = 0; int8_t temp_int8; // Set current text to a last_matching_parent_addr = NODE_ADDR_NULL; last_matching_parent_number = 0; memcpy(currentText, "a\x00\x00\x00\x00", sizeof(currentText)); // Draw bitmap, display it and write active buffer oledBitmapDrawFlash(0, 0, BITMAP_LOGIN_FIND, 0); oledDisplayOtherBuffer(); oledWriteActiveBuffer(); // While the user hasn't chosen a credential while(!finished) { if (displayRefreshNeeded == TRUE) { nbMatchedParents = displayCurrentSearchLoginTexts(currentText, tempParentAddresses, currentStringIndex); displayRefreshNeeded = FALSE; // Light only the available choices and right arrow led_mask = 0; for (temp_int8 = nbMatchedParents; temp_int8 < 5; temp_int8++) { led_mask |= (1 << temp_int8); } } // Detect key touches temp_rettype = touchDetectionRoutine(led_mask); // Algo to differentiate a tap from a scroll if ((temp_rettype & RETURN_WHEEL_PRESSED) && (wasWheelReleased == TRUE)) { // We use the timer dedicated to touch inhibit for min activateTimer(TIMER_TOUCH_INHIBIT, TAP_MIN_DEL); // We use the timer dedicated to caps detect for max activateTimer(TIMER_CAPS, TAP_MAX_DEL); // This works because we use the same clearing flags wasWheelReleased = FALSE; } else if (temp_rettype & RETURN_WHEEL_RELEASED) { // Check if it's a tap and that the selected domain is valid if ((hasTimerExpired(TIMER_CAPS, FALSE) == TIMER_RUNNING) && (hasTimerExpired(TIMER_TOUCH_INHIBIT, FALSE) == TIMER_EXPIRED) && (getWheelTouchDetectionQuarter() < nbMatchedParents)) { ret_val = tempParentAddresses[getWheelTouchDetectionQuarter()]; finished = TRUE; } wasWheelReleased = TRUE; } // Send it to the touch wheel interface logic temp_int8 = touchWheelIntefaceLogic(temp_rettype); // Position increment / decrement if (temp_int8 != 0) { if ((currentText[currentStringIndex] == 0x7A) && (temp_int8 == 1)) { // z->0 wrap currentText[currentStringIndex] = 0x2F; } if ((currentText[currentStringIndex] == 0x39) && (temp_int8 == 1)) { // 9->a wrap currentText[currentStringIndex] = 0x60; } else if ((currentText[currentStringIndex] == 0x30) && (temp_int8 == -1)) { // 0->z wrap currentText[currentStringIndex] = 0x7B; } else if ((currentText[currentStringIndex] == 0x61) && (temp_int8 == -1)) { // a->9 wrap currentText[currentStringIndex] = 0x3A; } currentText[currentStringIndex] += temp_int8; displayRefreshNeeded = TRUE; } if ((isSmartCardAbsent() == RETURN_OK) || (hasTimerExpired(TIMER_USERINT, TRUE) == TIMER_EXPIRED)) { // Smartcard removed or no interaction for a while finished = TRUE; } else if (temp_rettype & RETURN_LEFT_PRESSED) { // Change search index if (currentStringIndex > 0) { currentText[currentStringIndex--] = 0; displayRefreshNeeded = TRUE; } else { finished = TRUE; } } else if ((temp_rettype & RETURN_RIGHT_PRESSED) && (nbMatchedParents > 4)) { // Change search index only if we need to... currentText[++currentStringIndex] = 'a'; displayRefreshNeeded = TRUE; } } // Set inactive buffer write by default oledWriteInactiveBuffer(); return ret_val; }
/*! \fn guiAskForConfirmation(const char* string) * \brief Ask for user confirmation for different things * \param nb_args Number of text lines (must be either 1 2 or 3/4 (depending on the MP version)) * \param text_object Pointer to the text object if more than 1 line, pointer to the string if not * \return User confirmation or not */ RET_TYPE guiAskForConfirmation(uint8_t nb_args, confirmationText_t* text_object) { uint8_t flash_flag_set = FALSE; uint8_t flash_flag = FALSE; uint8_t flash_sm = 0; // LED animation #ifdef LEDS_ENABLED_MINI miniLedsSetAnimation(ANIM_PULSE_UP_RAMP_DOWN); #endif // Check if we want to flash the screen if ((nb_args & 0xF0) != 0) { flash_flag_set = TRUE; nb_args = nb_args & 0x0F; // Check that the user didn't disable it if (getMooltipassParameterInEeprom(FLASH_SCREEN_PARAM) != FALSE) { flash_flag = TRUE; } } // Variables for scrolling uint8_t string_y_indexes[3]; uint8_t string_extra_chars[3]; uint8_t string_offset_cntrs[3] = {0,0,0}; // Display variables uint8_t approve_selected = TRUE; // Draw asking bitmap miniOledClearFrameBuffer(); miniOledSetMaxTextY(SSD1305_OLED_WIDTH-15); miniOledBitmapDrawFlash(SSD1305_OLED_WIDTH-15, 0, BITMAP_APPROVE, 0); // Display lines. // Note: line are truncated at the miniOled driver level when miniOledTextWritingYIncrement is set to FALSE (default) if (nb_args == 1) { miniOledPutCenteredString(THREE_LINE_TEXT_SECOND_POS, (char*)text_object); } else if (nb_args == 2) { string_y_indexes[0] = TWO_LINE_TEXT_FIRST_POS; string_y_indexes[1] = TWO_LINE_TEXT_SECOND_POS; } else { string_y_indexes[0] = THREE_LINE_TEXT_FIRST_POS; string_y_indexes[1] = THREE_LINE_TEXT_SECOND_POS; string_y_indexes[2] = THREE_LINE_TEXT_THIRD_POS; } // For loop to display lines when there is more than one arg if (nb_args > 1) { for (uint8_t i = 0; i < nb_args; i++) { string_extra_chars[i] = strlen(text_object->lines[i]) - miniOledPutCenteredString(string_y_indexes[i], text_object->lines[i]); } } miniOledFlushEntireBufferToDisplay(); miniOledResetMaxTextY(); // Wait for user input RET_TYPE input_answer = MINI_INPUT_RET_NONE; RET_TYPE detect_result; // Switch on lights activityDetectedRoutine(); // Clear possible remaining detection miniWheelClearDetections(); // Arm timer for scrolling (caps timer that isn't relevant here) activateTimer(TIMER_CAPS, SCROLLING_DEL); // Arm timer for flashing activateTimer(TIMER_FLASHING, 500); // Loop while no timeout occurs or no button is pressed while (input_answer == MINI_INPUT_RET_NONE) { // User interaction timeout or smartcard removed if ((hasTimerExpired(TIMER_USERINT, TRUE) == TIMER_EXPIRED) || (isSmartCardAbsent() == RETURN_OK)) { input_answer = MINI_INPUT_RET_TIMEOUT; } // Read usb comms as the plugin could ask to cancel the request if (usbCancelRequestReceived() == RETURN_OK) { input_answer = MINI_INPUT_RET_TIMEOUT; } // Screen flashing logic if ((hasTimerExpired(TIMER_FLASHING, TRUE) == TIMER_EXPIRED) && (flash_flag == TRUE) && (flash_sm < 4)) { // Look at the flash_sm LSb to know what is the display state if ((flash_sm++ & 0x01) != 0x00) { miniOledNormalDisplay(); } else { miniOledInvertedDisplay(); } // Re-arm timer activateTimer(TIMER_FLASHING, 500); } // Check if something has been pressed detect_result = miniGetWheelAction(FALSE, TRUE); if (detect_result == WHEEL_ACTION_SHORT_CLICK) { input_answer = MINI_INPUT_RET_YES; } else if (detect_result == WHEEL_ACTION_LONG_CLICK) { input_answer = MINI_INPUT_RET_BACK; } // Knock to approve #if defined(HARDWARE_MINI_CLICK_V2) if ((scanAndGetDoubleZTap(FALSE) == ACC_RET_KNOCK) && (flash_flag_set != FALSE)) { input_answer = MINI_INPUT_RET_YES; } #else (void)flash_flag_set; #endif // Text scrolling if ((hasTimerExpired(TIMER_CAPS, FALSE) == TIMER_EXPIRED) && (nb_args > 1)) { miniOledClearFrameBuffer(); activateTimer(TIMER_CAPS, SCROLLING_DEL); miniOledSetMaxTextY(SSD1305_OLED_WIDTH-15); if(approve_selected == FALSE) { miniOledBitmapDrawFlash(SSD1305_OLED_WIDTH-15, 0, BITMAP_DENY, 0); } else { miniOledBitmapDrawFlash(SSD1305_OLED_WIDTH-15, 0, BITMAP_APPROVE, 0); } for (uint8_t i = 0; i < nb_args; i++) { if (string_extra_chars[i] > 0) { miniOledPutCenteredString(string_y_indexes[i], (text_object->lines[i]) + string_offset_cntrs[i]); if (string_offset_cntrs[i]++ == string_extra_chars[i]) { string_offset_cntrs[i] = 0; } } else { miniOledPutCenteredString(string_y_indexes[i], text_object->lines[i]); } } miniOledFlushEntireBufferToDisplay(); miniOledResetMaxTextY(); } // Approve / deny display change if (getWheelCurrentIncrement() != 0) { if(approve_selected == FALSE) { miniOledBitmapDrawFlash(SSD1305_OLED_WIDTH-15, 0, BITMAP_APPROVE, 0); } else { miniOledBitmapDrawFlash(SSD1305_OLED_WIDTH-15, 0, BITMAP_DENY, 0); } approve_selected = !approve_selected; miniOledFlushEntireBufferToDisplay(); } } // In case display was inverted, set it normally miniOledNormalDisplay(); if ((input_answer == MINI_INPUT_RET_YES) && (approve_selected != FALSE)) { // LED animation #ifdef LEDS_ENABLED_MINI miniLedsSetAnimation(ANIM_NONE); #endif return RETURN_OK; } else if (input_answer == MINI_INPUT_RET_BACK) { // LED animation #ifdef LEDS_ENABLED_MINI miniLedsSetAnimation(ANIM_NONE); #endif return RETURN_BACK; } else { // LED animation #ifdef LEDS_ENABLED_MINI miniLedsSetAnimation(ANIM_NONE); #endif return RETURN_NOK; } }
/*! \fn getTouchedPositionAnswer(uint8_t led_mask) * \brief Use the capacitive interface to get quarter position * \param led_mask Led mask for the touchdetection routine * \return Number between 0 and 5 for valid pos, -1 otherwise */ int8_t getTouchedPositionAnswer(uint8_t led_mask) { #if defined(ALWAYS_ACCEPT_REQUESTS) // First quarter is discarded, it means we want yes or no! if (led_mask & LED_MASK_WHEEL_TLEFT) { return TOUCHPOS_RIGHT; } else { return TOUCHPOS_WHEEL_TLEFT; } #endif uint8_t incomingData[RAWHID_TX_SIZE]; RET_TYPE touch_detect_result; // Switch on lights activityDetectedRoutine(); // Clear possible remaining detection touchDetectionRoutine(led_mask); // Additional masking in case we only want left / right uint8_t additional_mask = 0xFF; if (led_mask == LED_MASK_WHEEL) { additional_mask = RETURN_LEFT_PRESSED | RETURN_RIGHT_PRESSED; } // Wait for a touch press do { // User interaction timeout or smartcard removed if ((hasTimerExpired(TIMER_USERINT, TRUE) == TIMER_EXPIRED) || (isSmartCardAbsent() == RETURN_OK)) { return -1; } // Read usb comms as the plugin could ask to cancel the request if ((getMooltipassParameterInEeprom(USER_REQ_CANCEL_PARAM) != FALSE) && (usbRawHidRecv(incomingData) == RETURN_COM_TRANSF_OK)) { if (incomingData[HID_TYPE_FIELD] == CMD_CANCEL_REQUEST) { // Request cancelled return -1; } else { // Another packet (that shouldn't be sent!), ask to retry later... usbSendMessage(CMD_PLEASE_RETRY, 0, incomingData); } } touch_detect_result = touchDetectionRoutine(led_mask) & TOUCH_PRESS_MASK & additional_mask; } while (!touch_detect_result); // Prevent touches until the user lifts his finger touchInhibitUntilRelease(); // Did the user press one of the two touch buttons? if (touch_detect_result & RETURN_LEFT_PRESSED) { return TOUCHPOS_LEFT; } else if (touch_detect_result & RETURN_RIGHT_PRESSED) { return TOUCHPOS_RIGHT; } else { return (int8_t)getWheelTouchDetectionQuarter(); } }