/*! \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;
}    
Beispiel #3
0
/*! \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();
    } 
}