Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Keygroups functions #669

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions doc/driver/Bootloader.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# kaleidoscope::driver::bootloader

We rarely have to work with or care about the bootloader from the user firmware,
but there's one scenario when we do: if we want to reset the device, and put it
into bootloader (programmable) mode, we need to do that in a bootloader-specific
manner.

This driver provides a number of helpers that implement the reset functionality
for various bootloaders.

## Using the driver

To use the driver, we need to include the appropriate header, from the hardware plugin of
our keyboard:

```c++
#include <kaleidoscope/driver/bootloader/avr/Caterina.h>
```

Next, we create a (private) member variable, based on the bootloader of our choice:

```c++
kaleidoscope::driver::bootloader::avr::Caterina bootloader_;
```

And then we can implement a thin wrapper for the bootloader's `resetDevice()` method:

```c++
void resetDevice() {
bootloader_.resetDevice();
}
```

## Methods provided by all bootloader drivers

### `.resetDevice()`

> Resets the device, and forces it into bootloader (programmable) mode.

## List of bootloaders

All of the drivers below live below the `kaleidoscope::driver::bootloader`
namespace.

## `avr::Caterina`:

Used by many (most?) arduino MCUs. Provided by
`kaleidoscope/driver/bootloader/avr/Caterina.h`.

### `avr::HalfKay`

Used by the Teensy2. Provided by `kaleidoscope/driver/bootloader/avr/HalfKay.h`.

### `avr::FLIP`

Used by the ATMega32U4 MCUs by default, unless another bootloader has been
flashed on them. Provided by `kaleidoscope/driver/bootloader/avr/FLIP.h`.

For this driver to work, one also needs to define the
`KALEIDOSCOPE_BOOTLOADER_FLIP` macro before including the driver header, for
technical reasons.

## Notes

When using the `ATMegaKeyboard` base class, there's no need to provide a
`resetDevice()` method, the base class does that for us. In this case, all that
is required is declaring a `bootloader_` protected member, of the appropriate
bootloader type.
57 changes: 57 additions & 0 deletions src/kaleidoscope/driver/bootloader/avr/Caterina.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* -*- mode: c++ -*-
* kaleidoscope::driver::bootloader::avr::Caterina -- Driver for the Caterina bootloader
* Copyright (C) 2019 Keyboard.io, Inc
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include <avr/wdt.h>

namespace kaleidoscope {
namespace driver {
namespace bootloader {
namespace avr {

class Caterina {
public:
Caterina() {}

static void resetDevice() {
// Set the magic bits to get a Caterina-based device
// to reboot into the bootloader and stay there, rather
// than run move onward
//
// These values are the same as those defined in
// Caterina.c:
// https://github.com/arduino/ArduinoCore-avr/blob/5755ddea49fa69d6c505c772ebee5af5078e2ebf/bootloaders/caterina/Caterina.c#L130-L133

uint16_t bootKey = 0x7777;
uint16_t *const bootKeyPtr = reinterpret_cast<uint16_t *>(0x0800);

// Stash the magic key
*bootKeyPtr = bootKey;

// Set a watchdog timer
wdt_enable(WDTO_120MS);

while (true) {} // This infinite loop ensures nothing else
// happens before the watchdog reboots us
}
};

}
}
}
}
76 changes: 76 additions & 0 deletions src/kaleidoscope/driver/bootloader/avr/FLIP.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/* -*- mode: c++ -*-
* kaleidoscope::driver::bootloader::avr::FLIP -- Driver for the Atmel FLIP bootloader
* Copyright (C) 2019 Keyboard.io, Inc
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <Kaleidoscope.h>

#ifdef KALEIDOSCOPE_BOOTLOADER_FLIP
#include "kaleidoscope/driver/bootloader/avr/FLIP.h"

namespace kaleidoscope {
namespace driver {
namespace bootloader {
namespace avr {

// The FLIP bootloader does not provide a way to boot into bootloader mode from
// within the firmware: it will always start the user firmware if available, and
// if the reset wasn't triggered by the physical reset button. To enter the
// bootloader mode, we need to jump there from the user firmware ourselves.
//
// Since we do not want to unconditionally jump into bootloader, we set a static
// memory address to a magic value, to signal our intention, then reboot via the
// watchdog timer. We need to reboot to clear registers and have a clean slate
// for the bootloader.
//
// For this to work, we need to run our signal checking code before `main()`, so
// we put the checker function into a special section that does that.
//
// Since this code cannot be optimized out on keyboards that use another
// bootloader, we need to guard this implementation with an ifdef (see above).
//
// For more information, see:
// http://www.fourwalledcubicle.com/files/LUFA/Doc/120730/html/_page__software_bootloader_start.html

#define BOOTLOADER_RESET_KEY 0xB007B007
static uint32_t reset_key __attribute__((section(".noinit")));

/*
* This function runs before main(), and jumps to the bootloader after a reset
* initiated by .resetDevice().
*/
static void _bootloader_jump_after_watchdog_reset()
__attribute__((used, naked, section(".init3")));
static void _bootloader_jump_after_watchdog_reset() {
if ((MCUSR & (1 << WDRF)) && reset_key == BOOTLOADER_RESET_KEY) {
reset_key = 0;

((void (*)(void))(((FLASHEND + 1L) - 4096) >> 1))();
}
}

void FLIP::resetDevice() {
reset_key = BOOTLOADER_RESET_KEY;
wdt_enable(WDTO_250MS);
while (true) {} // This infinite loop ensures nothing else
// happens before the watchdog reboots us
}

}
}
}
}

#endif
41 changes: 41 additions & 0 deletions src/kaleidoscope/driver/bootloader/avr/FLIP.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* -*- mode: c++ -*-
* kaleidoscope::driver::bootloader::avr::FLIP -- Driver for the Atmel FLIP bootloader
* Copyright (C) 2019 Keyboard.io, Inc
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include <avr/wdt.h>

#ifndef KALEIDOSCOPE_BOOTLOADER_FLIP
#error To use the FLIP bootloader driver, KALEIDOSCOPE_BOOTLOADER_FLIP *must* be defined prior to including this header!
#endif

namespace kaleidoscope {
namespace driver {
namespace bootloader {
namespace avr {

class FLIP {
public:
FLIP() {}

static void resetDevice();
};

}
}
}
}
73 changes: 73 additions & 0 deletions src/kaleidoscope/driver/bootloader/avr/HalfKay.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/* -*- mode: c++ -*-
* kaleidoscope::driver::bootloader::avr::HalfKay -- Driver for the HalfKay bootloader
* Copyright (C) 2019 Keyboard.io, Inc
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include <avr/wdt.h>

namespace kaleidoscope {
namespace driver {
namespace bootloader {
namespace avr {

class HalfKay {
public:
HalfKay() {}

// To reset a Teensy with the HalfKay bootloader, we need to disable all
// interrupts, all peripherals we have attached, USB, the watchdog timer, etc.
// Once done, we can jump to the bootloader address.
//
// Documentation: https://www.pjrc.com/teensy/jump_to_bootloader.html
static void resetDevice() {
cli();
UDCON = 1;
USBCON = (1 << FRZCLK);
UCSR1B = 0;
_delay_ms(5);

EIMSK = 0;
PCICR = 0;
SPCR = 0;
ACSR = 0;
EECR = 0;
ADCSRA = 0;
TIMSK0 = 0;
TIMSK1 = 0;
TIMSK3 = 0;
TIMSK4 = 0;
UCSR1B = 0;
TWCR = 0;
DDRB = 0;
DDRC = 0;
DDRD = 0;
DDRE = 0;
DDRF = 0;
TWCR = 0;
PORTB = 0;
PORTC = 0;
PORTD = 0;
PORTE = 0;
PORTF = 0;
asm volatile("jmp 0x7E00");
}
};

}
}
}
}
Loading