diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3008a965..3e7e422a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,9 +28,10 @@ jobs: - 'stm32f1-generic -c bluepill' - 'efm32gg12b-generic -c sltb009a' - 'sams70-generic -c big-sammy' + - 'atmega32u4-generic -c octomouse' steps: - name: Update the system and install dependencies - run: pacman -Syu --noconfirm --noprogressbar --needed python-pip ninja gcc arm-none-eabi-gcc arm-none-eabi-binutils arm-none-eabi-newlib pkgconf readline git + run: pacman -Syu --noconfirm --noprogressbar --needed python-pip ninja gcc arm-none-eabi-gcc arm-none-eabi-binutils arm-none-eabi-newlib avr-gcc avr-binutils avr-libc pkgconf readline git - name: Install ninja_syntax run: pip install ninja_syntax diff --git a/.gitmodules b/.gitmodules index d1f98869..ef1b11e3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "external/cmsis-dfp-sams70"] path = external/cmsis-dfp-sams70 url = https://github.com/cmsis-packs/cmsis-dfp-sams70 +[submodule "external/lufa"] + path = external/lufa + url = https://github.com/abcminiuser/lufa diff --git a/build_system/dependencies.py b/build_system/dependencies.py index e23cf248..bb8769c3 100644 --- a/build_system/dependencies.py +++ b/build_system/dependencies.py @@ -146,6 +146,28 @@ def __init__(self, location: BuildLocation, target: str) -> None: } +@dataclasses.dataclass(init=False) +class LufaDependency(Dependency, name='lufa'): + def __init__(self, location: BuildLocation) -> None: + super().__init__(location) + + src_path = self.base_path / 'LUFA/Drivers/USB' + common_path = self.base_path / 'LUFA/Common' + + for path in src_path.rglob('*.c'): + self.source.add(path) + + self.include = { + src_path, + common_path, + self.location.code, + } + self.external_include = { + src_path, + common_path, + } + + @dataclasses.dataclass(init=False) class CMSIS5Dependency(Dependency, name='cmsis-5'): components: List[str] diff --git a/build_system/ninja.py b/build_system/ninja.py index 0057570b..f9c64345 100644 --- a/build_system/ninja.py +++ b/build_system/ninja.py @@ -120,6 +120,8 @@ def write_variables( self.writer.variable('c_flags', details.c_flags) self.writer.variable('c_include_flags', [ f'-I{self.path(self._location.code)}' + ] + [ + f'-I{self.path(self._location.code / "targets" / self._target.name)}' ] + [ f'-include {self.path(path)}' for path in details.include_files ] + [ diff --git a/config/families/avr.toml b/config/families/avr.toml new file mode 100644 index 00000000..f59a8ba1 --- /dev/null +++ b/config/families/avr.toml @@ -0,0 +1,23 @@ +toolchain = 'avr' +has-linker = false +c_flags = [ + '-Os', + '-fpack-struct', + '-fshort-enums', + '-ffunction-sections', + '-fdata-sections', +] +ld_flags = [ + '-fdata-sections', + '-ffunction-sections', + '-Wl,--gc-sections', +] +source = [ + 'boot.c', + 'usb.c', + 'usb_descriptors.c', + 'hal/hid.c', +] + +[dependencies] +lufa = {} diff --git a/config/targets/atmega32u4-generic.toml b/config/targets/atmega32u4-generic.toml new file mode 100644 index 00000000..68d031dc --- /dev/null +++ b/config/targets/atmega32u4-generic.toml @@ -0,0 +1,11 @@ +family = 'avr' +has-config = true +c_flags = [ + '-mmcu=atmega32u4', + '-DUSE_LUFA_CONFIG_HEADER', +] +ld_flags = [ + '-mmcu=atmega32u4', +] + +[dependencies] diff --git a/external/lufa b/external/lufa new file mode 160000 index 00000000..fa2cc3e1 --- /dev/null +++ b/external/lufa @@ -0,0 +1 @@ +Subproject commit fa2cc3e1c3abab7c971506bd86ca4e28ecc7d288 diff --git a/src/platform/avr/boot.c b/src/platform/avr/boot.c new file mode 100644 index 00000000..0c1589e6 --- /dev/null +++ b/src/platform/avr/boot.c @@ -0,0 +1,24 @@ +/* + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2022 Rafael Silva + */ + +#include + +// Assuming the Caterina bootloader that comes with the 32u4 +uint16_t bootKey = 0x7777; +volatile uint16_t *const bootKeyPtr = (volatile uint16_t *) 0x0800; + +void reset() +{ + wdt_enable(WDTO_15MS); + for (;;) { + } +} + +void bootloader() +{ + *bootKeyPtr = bootKey; + + reset(); +} diff --git a/src/platform/avr/boot.h b/src/platform/avr/boot.h new file mode 100644 index 00000000..28b9442f --- /dev/null +++ b/src/platform/avr/boot.h @@ -0,0 +1,8 @@ +/* + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2022 Rafael Silva + */ + +void reset() __attribute__((noreturn)); +void bootloader() __attribute__((noreturn)); +; diff --git a/src/platform/avr/hal/hid.c b/src/platform/avr/hal/hid.c new file mode 100644 index 00000000..cc23df2d --- /dev/null +++ b/src/platform/avr/hal/hid.c @@ -0,0 +1,21 @@ +/* + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2021 Rafael Silva + */ + +#include "platform/avr/hal/hid.h" +#include "platform/avr/usb.h" + +int hid_hal_send(struct hid_hal_t interface, u8 *buffer, size_t buffer_size) +{ + usb_write_descriptor(0, buffer, buffer_size); +} + +struct hid_hal_t hid_hal_init(void) +{ + struct hid_hal_t hal = { + .send = hid_hal_send, + .drv_data = NULL, + }; + return hal; +} diff --git a/src/platform/avr/hal/hid.h b/src/platform/avr/hal/hid.h new file mode 100644 index 00000000..5c1d4cc5 --- /dev/null +++ b/src/platform/avr/hal/hid.h @@ -0,0 +1,11 @@ +/* + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2021 Rafael Silva + */ + +#pragma once + +#include "hal/hid.h" +#include "util/types.h" + +struct hid_hal_t hid_hal_init(void); diff --git a/src/platform/avr/usb.c b/src/platform/avr/usb.c new file mode 100644 index 00000000..79fb7d26 --- /dev/null +++ b/src/platform/avr/usb.c @@ -0,0 +1,209 @@ +/* + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2022 Rafael Silva + */ + +#include + +#include "protocol/reports.h" +#include "usb.h" +#include "util/hid_descriptors.h" +#include "util/types.h" + +#include + +/** Buffer to hold the previously generated HID report, for comparison purposes inside the HID class driver. */ +static u8 openinput_hid_report_buff[sizeof(struct oi_report_t)]; +static u8 mouse_hid_report_buff[sizeof(struct mouse_report)]; +static u8 keyboard_hid_report_buff[sizeof(struct keyboard_report)]; + +static u8 new_oi_report; +static struct oi_report_t oi_report; +static u8 oi_report_size; + +static u8 new_mouse_report; +static struct mouse_report mouse_report; +static u8 mouse_report_size; + +static u8 new_keyboard_report; +static struct keyboard_report keyboard_report; +static u8 keyboard_report_size; + +static struct protocol_config_t protocol_config; + +/** LUFA HID Class driver interface configuration and state information. This structure is + * passed to all HID Class driver functions, so that multiple instances of the same class + * within a device can be differentiated from one another. + */ +USB_ClassInfo_HID_Device_t openinput_hid_interface = { + .Config = + { + .InterfaceNumber = 0, + .ReportINEndpoint = + { + .Address = 0x81, + .Size = 64, + .Banks = 1, + }, + .PrevReportINBuffer = openinput_hid_report_buff, + .PrevReportINBufferSize = sizeof(openinput_hid_report_buff), + }, +}; + +USB_ClassInfo_HID_Device_t mouse_hid_interface = { + .Config = + { + .InterfaceNumber = 1, + .ReportINEndpoint = + { + .Address = 0x83, + .Size = 64, + .Banks = 1, + }, + .PrevReportINBuffer = mouse_hid_report_buff, + .PrevReportINBufferSize = sizeof(mouse_hid_report_buff), + }, +}; + +USB_ClassInfo_HID_Device_t keyboard_hid_interface = { + .Config = + { + .InterfaceNumber = 2, + .ReportINEndpoint = + { + .Address = 0x84, + .Size = 64, + .Banks = 1, + }, + .PrevReportINBuffer = keyboard_hid_report_buff, + .PrevReportINBufferSize = sizeof(keyboard_hid_report_buff), + }, +}; + +void usb_init() +{ + /* Init USB stack */ + USB_Init(); +} + +void usb_task() +{ + USB_USBTask(); + + HID_Device_USBTask(&openinput_hid_interface); + HID_Device_USBTask(&mouse_hid_interface); + HID_Device_USBTask(&keyboard_hid_interface); +} + +void usb_attach_protocol_config(struct protocol_config_t config) +{ + protocol_config = config; +} + +void usb_write_descriptor(u8 interface, u8 *report_data, u16 report_size) +{ + if (interface == openinput_hid_interface.Config.InterfaceNumber) { + memcpy(&oi_report, report_data, report_size); + oi_report_size = report_size; + new_oi_report = 1; + } else if (interface == mouse_hid_interface.Config.InterfaceNumber) { + memcpy(&mouse_report, report_data, report_size); + mouse_report_size = report_size; + new_mouse_report = 1; + } else if (interface == keyboard_hid_interface.Config.InterfaceNumber) { + memcpy(&keyboard_report, report_data, report_size); + keyboard_report_size = report_size; + new_keyboard_report = 1; + } +} + +/** Event handler for the USB_Connect event. This indicates that the device is enumerating via the status LEDs and + * starts the library USB task to begin the enumeration and USB management process. + */ +void EVENT_USB_Device_Connect(void) +{ +} + +/** Event handler for the USB_Disconnect event. This indicates that the device is no longer connected to a host via + * the status LEDs and stops the USB management task. + */ +void EVENT_USB_Device_Disconnect(void) +{ +} + +/** Event handler for the library USB Configuration Changed event. */ +void EVENT_USB_Device_ConfigurationChanged(void) +{ + HID_Device_ConfigureEndpoints(&openinput_hid_interface); + HID_Device_ConfigureEndpoints(&mouse_hid_interface); + HID_Device_ConfigureEndpoints(&keyboard_hid_interface); +} + +/** HID class driver callback function for the creation of HID reports to the host. + * + * \param[in] HIDInterfaceInfo Pointer to the HID class interface configuration structure being referenced + * \param[in,out] ReportID Report ID requested by the host if non-zero, otherwise callback should set to the generated report ID + * \param[in] ReportType Type of the report to create, either HID_REPORT_ITEM_In or HID_REPORT_ITEM_Feature + * \param[out] ReportData Pointer to a buffer where the created report should be stored + * \param[out] ReportSize Number of bytes written in the report (or zero if no report is to be sent) + * + * \return Boolean \c true to force the sending of the report, \c false to let the library determine if it needs to be sent + */ +bool CALLBACK_HID_Device_CreateHIDReport( + USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo, + uint8_t *const ReportID, + const uint8_t ReportType, + void *ReportData, + uint16_t *const ReportSize) +{ + /* Determine which interface must have its report generated */ + if (HIDInterfaceInfo == &openinput_hid_interface) { + if (new_oi_report == 1) { + memcpy(ReportData, &oi_report, oi_report_size); + *ReportSize = oi_report_size; + new_oi_report = 0; + return true; + } else { + return false; + } + } else if (HIDInterfaceInfo == &mouse_hid_interface) { + if (new_mouse_report == 1) { + memcpy(ReportData, &mouse_report, mouse_report_size); + *ReportSize = mouse_report_size; + new_mouse_report = 0; + return true; + } else { + return false; + } + } else if (HIDInterfaceInfo == &keyboard_hid_interface) { + if (new_keyboard_report == 1) { + memcpy(ReportData, &keyboard_report, keyboard_report_size); + *ReportSize = keyboard_report_size; + new_keyboard_report = 0; + return true; + } else { + return false; + } + } +} + +/** HID class driver callback function for the processing of HID reports from the host. + * + * \param[in] HIDInterfaceInfo Pointer to the HID class interface configuration structure being referenced + * \param[in] ReportID Report ID of the received report from the host + * \param[in] ReportType The type of report that the host has sent, either HID_REPORT_ITEM_Out or HID_REPORT_ITEM_Feature + * \param[in] ReportData Pointer to a buffer where the received report has been stored + * \param[in] ReportSize Size in bytes of the received HID report + */ +void CALLBACK_HID_Device_ProcessHIDReport( + USB_ClassInfo_HID_Device_t *const HIDInterfaceInfo, + const uint8_t ReportID, + const uint8_t ReportType, + const void *ReportData, + const uint16_t ReportSize) +{ + if (HIDInterfaceInfo == &openinput_hid_interface) { + } else if (HIDInterfaceInfo == &keyboard_hid_interface) { + protocol_dispatch(protocol_config, (u8 *) ReportData, ReportSize); + } +} diff --git a/src/platform/avr/usb.h b/src/platform/avr/usb.h new file mode 100644 index 00000000..7c598400 --- /dev/null +++ b/src/platform/avr/usb.h @@ -0,0 +1,13 @@ +/* + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2022 Rafael Silva + */ + +#pragma once + +#include "protocol/protocol.h" + +void usb_init(); +void usb_task(); +void usb_attach_protocol_config(struct protocol_config_t config); +void usb_write_descriptor(u8 interface, u8 *report_data, u16 report_size); diff --git a/src/platform/avr/usb_descriptors.c b/src/platform/avr/usb_descriptors.c new file mode 100644 index 00000000..c9c9c228 --- /dev/null +++ b/src/platform/avr/usb_descriptors.c @@ -0,0 +1,381 @@ +/* + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2022 Rafael Silva + */ + +#include + +#include + +#include "util/data.h" +#include "util/types.h" + +/* Device descriptor */ +const u8 __attribute__((__progmem__)) desc_device[] = { + /* clang-format off */ + 0x12, /* LENGTH (18) */ + 0x01, /* DESCRIPTOR TYPE (Device) */ + 0x10, 0x01, /* USB SPEC VERSION (0x0110) */ + 0x00, /* DEVICE CLASS */ + 0x00, /* DEVICE SUBCLASS */ + 0x00, /* DEVICE PROTOCOL */ + 0x40, /* MAX EP0 PACKET SIZE (32) */ + 0x50, 0x1D, /* VENDOR ID (0x1D50) */ + 0x6A, 0x61, /* PRODUCT ID (0x616A) */ + 0x00, 0x01, /* DEVICE RELEASE */ + 0x01, /* MANUFACTURER STRING INDEX */ + 0x02, /* PRODUCT STRING INDEX */ + 0x00, /* SERIAL STRING INDEX (None) */ + 0x01, /* NO CONFIGURATIONS */ + /* clang-format on */ +}; + +const u8 __attribute__((__progmem__)) oi_rdesc[] = { + /* clang-format off */ + /* short report */ + 0x06, 0x00, 0xff, /* USAGE_PAGE (Vendor Page) */ + 0x09, 0x00, /* USAGE (Vendor Usage 0) */ + 0xa1, 0x01, /* COLLECTION (Application) */ + 0x85, 0x20, /* REPORT_ID (0x20) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x95, 0x08, /* REPORT_COUNT (8) */ + 0x15, 0x00, /* LOGICAL MINIMUM (0) */ + 0x26, 0xff, 0x00, /* LOGICAL MAXIMUM (255) */ + 0x09, 0x00, /* USAGE (Vendor Usage 0) */ + 0x81, 0x00, /* INPUT (Data,Arr,Abs) */ + 0x09, 0x00, /* USAGE (Vendor Usage 0) */ + 0x91, 0x00, /* OUTPUT (Data,Arr,Abs) */ + 0xc0, /* END_COLLECTION */ + /* long report */ + 0x06, 0x00, 0xff, /* USAGE_PAGE (Vendor Page) */ + 0x09, 0x00, /* USAGE (Vendor Usage 0) */ + 0xa1, 0x01, /* COLLECTION (Application) */ + 0x85, 0x21, /* REPORT_ID (0x21) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x95, 0x20, /* REPORT_COUNT (32) */ + 0x15, 0x00, /* LOGICAL MINIMUM (0) */ + 0x26, 0xff, 0x00, /* LOGICAL MAXIMUM (255) */ + 0x09, 0x00, /* USAGE (Vendor Usage 0) */ + 0x81, 0x00, /* INPUT (Data,Arr,Abs) */ + 0x09, 0x00, /* USAGE (Vendor Usage 0) */ + 0x91, 0x00, /* OUTPUT (Data,Arr,Abs) */ + 0xc0, /* END_COLLECTION */ + /* clang-format on */ +}; + +/* HID Mouse report descriptor */ +const u8 __attribute__((__progmem__)) desc_hid_mouse_report[] = { + /* clang-format off */ + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x02, /* USAGE (Mouse) */ + 0xa1, 0x01, /* COLLECTION (Application) */ + 0x09, 0x01, /* USAGE (Pointer) */ + 0xa1, 0x00, /* COLLECTION (Physical) */ + 0x85, 0x01, /* REPORT_ID (0x01) */ + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x30, /* USAGE (X) */ + 0x09, 0x31, /* USAGE (Y) */ + 0x09, 0x38, /* USAGE (WHEEL) */ + 0x15, 0x81, /* LOGICAL_MINIMUM (-127) */ + 0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x95, 0x03, /* REPORT_COUNT (3) */ + 0x81, 0x06, /* INPUT (Data,Var,Rel) */ + 0x05, 0x09, /* USAGE_PAGE (Button) */ + 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */ + 0x29, 0x03, /* USAGE_MAXIMUM (Button 3) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x95, 0x03, /* REPORT_COUNT (3) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x81, 0x02, /* INPUT (Data,Var,Abs) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x05, /* REPORT_SIZE (5) */ + 0x81, 0x01, /* INPUT (Cnst,Var,Abs) */ + 0xc0, /* END_COLLECTION */ + 0xc0, /* END_COLLECTION */ + /* clang-format on */ +}; + +/* HID keyboard report descriptor */ +const u8 __attribute__((__progmem__)) desc_hid_keyboard_report[] = { + /* clang-format off */ + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x06, /* USAGE (keyboard) */ + 0xa1, 0x01, /* COLLECTION (Application) */ + /* Report ID if any */ + 0x85, 0x02, /* REPORT_ID (0x02) */ + /* 8 bits Modifier Keys (Shfit, Control, Alt) */ + 0x05, 0x07, /* USAGE_PAGE (keyboard) */ + 0x19, 0xE0, /* USAGE_MINIMUM (224) */ + 0x29, 0xE7, /* USAGE_MAXIMUM (231) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x95, 0x08, /* REPORT_COUNT (8) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x81, 0x02, /* INPUT (Data,Var,Abs) */ + /* 8 bit reserved */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x81, 0x01, /* INPUT (Const) */ + /* 6-byte Keycodes */ + 0x05, 0x07, /* USAGE_PAGE (keyboard) */ + 0x19, 0x00, /* USAGE_MINIMUM (0) */ + 0x29, 0xFF, /* USAGE_MAXIMUM (255) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0xFF, /* LOGICAL_MAXIMUM (255) */ + 0x95, 0x06, /* REPORT_COUNT (6) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x81, 0x00, /* INPUT (Data,Arr,Abs) */ + /* 5-bit LED Indicator Kana | Compose | ScrollLock | CapsLock | NumLock */ + 0x05, 0x08, /* USAGE_PAGE (led) */ + 0x19, 0x00, /* USAGE_MINIMUM (0) */ + 0x29, 0x01, /* USAGE_MAXIMUM (1) */ + 0x95, 0x05, /* REPORT_COUNT (5) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ + /* led padding */ + 0x95, 0x01, /* REPORT_COUNT (5) */ + 0x75, 0x03, /* REPORT_SIZE (1) */ + 0x91, 0x01, /* INPUT (Const) */ + 0xc0, /* END_COLLECTION */ + /* clang-format on */ +}; + +const u8 __attribute__((__progmem__)) oi_hid_desc[] = { + /* HID */ + 0x09, /* LENGTH */ + 0x21, /* DESCRIPTOR TYPE (hid) */ + 0x11, + 0x01, /* HID VERSION (0x0111) */ + 0x00, /* COUNTRY CODE (None) */ + 0x01, /* NO DESCRIPTORS (1) */ + 0x22, /* DESCRIPTOR TYPE (Report) */ + sizeof(oi_rdesc), + 0x00, /* DESCRIPTOR LENGTH () */ +}; + +const u8 __attribute__((__progmem__)) mouse_hid_desc[] = { + /* HID */ + 0x09, /* LENGTH */ + 0x21, /* DESCRIPTOR TYPE (hid) */ + 0x11, + 0x01, /* HID VERSION (0x0111) */ + 0x00, /* COUNTRY CODE (None) */ + 0x01, /* NO DESCRIPTORS (1) */ + 0x22, /* DESCRIPTOR TYPE (Report) */ + sizeof(desc_hid_mouse_report), + 0x00, /* DESCRIPTOR LENGTH () */ +}; + +const u8 __attribute__((__progmem__)) keyboard_hid_desc[] = { + /* HID */ + 0x09, /* LENGTH */ + 0x21, /* DESCRIPTOR TYPE (hid) */ + 0x11, + 0x01, /* HID VERSION (0x0111) */ + 0x00, /* COUNTRY CODE (None) */ + 0x01, /* NO DESCRIPTORS (1) */ + 0x22, /* DESCRIPTOR TYPE (Report) */ + sizeof(desc_hid_keyboard_report), + 0x00, /* DESCRIPTOR LENGTH () */ +}; + +/* Configuration Descriptor */ +const u8 __attribute__((__progmem__)) desc_configuration[] = { + /* clang-format off */ + /* configuration */ + 0x09, /* LENGTH */ + 0x02, /* DESCRIPTOR TYPE (Configuration) */ + 0x62, 0x00, /* TOTAL LENGTH (98) */ + 0x03, /* NUM INTERFACES (3) */ + 0x01, /* CONFIG NUMBER (1) */ + 0x00, /* STRING INDEX (null) */ + 0xA0, /* ATTRIBUTES (Bus powered, Remote wakeup) */ + 0x32, /* MAX POWER (100mA) */ + + /* Interface 0 - openinput protocol */ + 0x09, /* LENGTH */ + 0x04, /* DESCRIPTOR TYPE (Interface) */ + 0x00, /* INTERFACE NO (0) */ + 0x00, /* ALTERNATE SETTING (none) */ + 0x02, /* NUM ENDPOINTS (2) */ + 0x03, /* INTERFACE CLASS (HID) */ + 0x00, /* INTERFACE SUBCLASS (None) */ + 0x00, /* INTERFACE PROTOCOL (None) */ + 0x00, /* INTERFACE STRING (Null) */ + /* HID */ + 0x09, /* LENGTH */ + 0x21, /* DESCRIPTOR TYPE (hid) */ + 0x11, 0x01, /* HID VERSION (0x0111) */ + 0x00, /* COUNTRY CODE (None) */ + 0x01, /* NO DESCRIPTORS (1) */ + 0x22, /* DESCRIPTOR TYPE (Report) */ + sizeof(oi_rdesc), + 0x00, /* DESCRIPTOR LENGTH () */ + /* Endpoint in */ + 0x07, /* LENGTH */ + 0x05, /* DESCRIPTOR TYPE (Endpoint) */ + 0x81, /* ENDPOINT ADDRESS (Endpoint 1, IN) */ + 0x03, /* ATTRIBUTES (Interrupt) */ + 0x40, 0x00, /* MAX PACKET SIZE (64) */ + 0x0A, /* POLLING INTERVAL (100Hz) */ + /* Endpoint out */ + 0x07, /* LENGTH */ + 0x05, /* DESCRIPTOR TYPE (Endpoint) */ + 0x02, /* ENDPOINT ADDRESS (Endpoint 2, OUT) */ + 0x03, /* ATTRIBUTES (Interrupt) */ + 0x40, 0x00, /* MAX PACKET SIZE (64) */ + 0x0A, /* POLLING INTERVAL (100Hz) */ + + /* Interface 1 - HID Mouse */ + 0x09, /* LENGTH */ + 0x04, /* DESCRIPTOR TYPE (Interface) */ + 0x01, /* INTERFACE NO (1) */ + 0x00, /* ALTERNATE SETTING (none) */ + 0x01, /* NUM ENDPOINTS (1) */ + 0x03, /* INTERFACE CLASS (HID) */ + 0x00, /* INTERFACE SUBCLASS (None) */ + 0x00, /* INTERFACE PROTOCOL (None) */ + 0x00, /* INTERFACE STRING (Null) */ + /* HID */ + 0x09, /* LENGTH */ + 0x21, /* DESCRIPTOR TYPE (hid) */ + 0x11, 0x01, /* HID VERSION (0x0111) */ + 0x00, /* COUNTRY CODE (None) */ + 0x01, /* NO DESCRIPTORS (1) */ + 0x22, /* DESCRIPTOR TYPE (Report) */ + sizeof(desc_hid_mouse_report), + 0x00, /* DESCRIPTOR LENGTH () */ + /* Endpoint in */ + 0x07, /* LENGTH */ + 0x05, /* DESCRIPTOR TYPE (Endpoint) */ + 0x83, /* ENDPOINT ADDRESS (Endpoint 3, IN) */ + 0x03, /* ATTRIBUTES (Interrupt) */ + 0x40, 0x00, /* MAX PACKET SIZE (64) */ + 0x01, /* POLLING INTERVAL (1000Hz) */ + + + /* Interface 2 - HID Keyboard */ + 0x09, /* LENGTH */ + 0x04, /* DESCRIPTOR TYPE (Interface) */ + 0x02, /* INTERFACE NO (2) */ + 0x00, /* ALTERNATE SETTING (none) */ + 0x02, /* NUM ENDPOINTS (2) */ + 0x03, /* INTERFACE CLASS (HID) */ + 0x00, /* INTERFACE SUBCLASS (None) */ + 0x00, /* INTERFACE PROTOCOL (None) */ + 0x00, /* INTERFACE STRING (Null) */ + /* HID */ + 0x09, /* LENGTH */ + 0x21, /* DESCRIPTOR TYPE (hid) */ + 0x11, 0x01, /* HID VERSION (0x0111) */ + 0x00, /* COUNTRY CODE (None) */ + 0x01, /* NO DESCRIPTORS (1) */ + 0x22, /* DESCRIPTOR TYPE (Report) */ + sizeof(desc_hid_keyboard_report), + 0x00, /* DESCRIPTOR LENGTH () */ + /* Endpoint in */ + 0x07, /* LENGTH */ + 0x05, /* DESCRIPTOR TYPE (Endpoint) */ + 0x84, /* ENDPOINT ADDRESS (Endpoint 4, IN) */ + 0x03, /* ATTRIBUTES (Interrupt) */ + 0x40, 0x00, /* MAX PACKET SIZE (64) */ + 0x0A, /* POLLING INTERVAL (100Hz) */ + /* Endpoint out */ + 0x07, /* LENGTH */ + 0x05, /* DESCRIPTOR TYPE (Endpoint) */ + 0x05, /* ENDPOINT ADDRESS (Endpoint 5, OUT) */ + 0x03, /* ATTRIBUTES (Interrupt) */ + 0x40, 0x00, /* MAX PACKET SIZE (64) */ + 0x0A, /* POLLING INTERVAL (100Hz) */ + /* clang-format on */ +}; + +/* String Descriptors */ +const USB_Descriptor_String_t __attribute__((__progmem__)) PROGMEM lang_str = USB_STRING_DESCRIPTOR_ARRAY(LANGUAGE_ID_ENG); +const USB_Descriptor_String_t __attribute__((__progmem__)) PROGMEM manu_str = USB_STRING_DESCRIPTOR(L"Openinput"); +const USB_Descriptor_String_t __attribute__((__progmem__)) PROGMEM prod_str = USB_STRING_DESCRIPTOR(L"Openinput Device"); + +/** + * This function is called by the library when in device mode, and must be overridden (see LUFA library "USB Descriptors" + * documentation) by the application code so that the address and size of a requested descriptor can be given + * to the USB library. When the device receives a Get Descriptor request on the control endpoint, this function + * is called so that the descriptor details can be passed back and the appropriate descriptor sent back to the + * USB host. + */ +u16 CALLBACK_USB_GetDescriptor(const u16 w_value, const u16 w_index, const void **const descriptor_address) +{ + const u8 descriptor_type = (w_value >> 8); + const u8 descriptor_number = (w_value & 0xFF); + + const void *addr = NULL; + u16 size = 0; + + switch (descriptor_type) { + case DTYPE_Device: + addr = desc_device; + size = sizeof(desc_device); + break; + + case DTYPE_Configuration: + addr = desc_configuration; + size = sizeof(desc_configuration); + break; + + case DTYPE_String: + switch (descriptor_number) { + case 0: + addr = &lang_str; + size = pgm_read_byte(&lang_str.Header.Size); + break; + case 1: + addr = &manu_str; + size = pgm_read_byte(&manu_str.Header.Size); + break; + case 2: + addr = &prod_str; + size = pgm_read_byte(&prod_str.Header.Size); + break; + } + + break; + + case HID_DTYPE_HID: + switch (w_index) { + case 0: + addr = oi_hid_desc; + size = sizeof(oi_hid_desc); + break; + case 1: + addr = mouse_hid_desc; + size = sizeof(mouse_hid_desc); + break; + case 2: + addr = keyboard_hid_desc; + size = sizeof(keyboard_hid_desc); + break; + } + break; + + case HID_DTYPE_Report: + switch (w_index) { + case 0: + addr = oi_rdesc; + size = sizeof(oi_rdesc); + break; + case 1: + addr = desc_hid_mouse_report; + size = sizeof(desc_hid_mouse_report); + break; + case 2: + addr = desc_hid_keyboard_report; + size = sizeof(desc_hid_keyboard_report); + break; + } + break; + } + + *descriptor_address = addr; + return size; +} diff --git a/src/targets/atmega32u4-generic/LUFAConfig.h b/src/targets/atmega32u4-generic/LUFAConfig.h new file mode 100644 index 00000000..b7c4cf91 --- /dev/null +++ b/src/targets/atmega32u4-generic/LUFAConfig.h @@ -0,0 +1,68 @@ +/* + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2022 Rafael Silva + */ + +/** + * LUFA Library Configuration Header File + * + * This header file is used to configure LUFA's compile time options, + * as an alternative to the compile time constants supplied through + * a makefile. + * + * For information on what each token does, refer to the LUFA + * manual section "Summary of Compile Tokens". + */ + +#ifndef _LUFA_CONFIG_H_ +#define _LUFA_CONFIG_H_ + +#if (ARCH == ARCH_AVR8) + +/* Non-USB Related Configuration Tokens: */ +// #define DISABLE_TERMINAL_CODES + +/* USB Class Driver Related Tokens: */ +// #define HID_HOST_BOOT_PROTOCOL_ONLY +// #define HID_STATETABLE_STACK_DEPTH {Insert Value Here} +// #define HID_USAGE_STACK_DEPTH {Insert Value Here} +// #define HID_MAX_COLLECTIONS {Insert Value Here} +// #define HID_MAX_REPORTITEMS {Insert Value Here} +// #define HID_MAX_REPORT_IDS {Insert Value Here} +// #define NO_CLASS_DRIVER_AUTOFLUSH + +/* General USB Driver Related Tokens: */ +// #define ORDERED_EP_CONFIG +#define USE_STATIC_OPTIONS (USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL) +#define USB_DEVICE_ONLY +// #define USB_HOST_ONLY +// #define USB_STREAM_TIMEOUT_MS {Insert Value Here} +// #define NO_LIMITED_CONTROLLER_CONNECT +// #define NO_SOF_EVENTS + +/* USB Device Mode Driver Related Tokens: */ +// #define USE_RAM_DESCRIPTORS +#define USE_FLASH_DESCRIPTORS +// #define USE_EEPROM_DESCRIPTORS +// #define NO_INTERNAL_SERIAL +// #define FIXED_CONTROL_ENDPOINT_SIZE 32 +// #define DEVICE_STATE_AS_GPIOR 0 +#define FIXED_NUM_CONFIGURATIONS 1 +// #define CONTROL_ONLY_DEVICE +// #define INTERRUPT_CONTROL_ENDPOINT +// #define NO_DEVICE_REMOTE_WAKEUP +// #define NO_DEVICE_SELF_POWER + +/* USB Host Mode Driver Related Tokens: */ +// #define HOST_STATE_AS_GPIOR {Insert Value Here} +// #define USB_HOST_TIMEOUT_MS {Insert Value Here} +// #define HOST_DEVICE_SETTLE_DELAY_MS {Insert Value Here} +// #define NO_AUTO_VBUS_MANAGEMENT +// #define INVERTED_VBUS_ENABLE_LINE + +#else + +#error Unsupported architecture for this LUFA configuration file. + +#endif +#endif diff --git a/src/targets/atmega32u4-generic/config/octomouse.h b/src/targets/atmega32u4-generic/config/octomouse.h new file mode 100644 index 00000000..389e91a0 --- /dev/null +++ b/src/targets/atmega32u4-generic/config/octomouse.h @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2021 Rafael Silva + */ + +/* clang-format off */ + +/* Clock Config */ + +#define ARCH AVR8 +#define F_CPU 16000000UL +#define F_USB F_CPU + +/* Sensor Config */ + +#define SENSOR_ENABLED +#define SENSOR_DRIVER PIXART_PMW + +#define SENSOR_FIRMWARE_BLOB pmw3360_blob + +//#define SENSOR_MOTION_IO { .port = , .pin = } + +#define SENSOR_INTERFACE SPI_INTERFACE_1 + +#define SENSOR_INTERFACE_SPEED 10000000 + +/* o -> active low, 1 -> active high */ +#define SENSOR_INTERFACE_CS_POL 0 +#define SENSOR_INTERFACE_CS_IO { .port = GPIO_PORT_B, .pin = 6 } + +#define SENSOR_INTERFACE_SCK_IO { .port = GPIO_PORT_B, .pin = 1 } +#define SENSOR_INTERFACE_MISO_IO { .port = GPIO_PORT_B, .pin = 3 } +#define SENSOR_INTERFACE_MOSI_IO { .port = GPIO_PORT_B, .pin = 2 } + +/* IO config */ +#define MB1_IO { .port = GPIO_PORT_F, .pin = 5 } +#define MB1_N_IO { .port = GPIO_PORT_F, .pin = 1 } +#define MB2_IO { .port = GPIO_PORT_F, .pin = 6 } +#define MB2_N_IO { .port = GPIO_PORT_F, .pin = 7 } +#define MB3_IO { .port = GPIO_PORT_D, .pin = 4 } +#define MB3_N_IO { .port = GPIO_PORT_D, .pin = 5 } +#define MB4_IO { .port = GPIO_PORT_D, .pin = 6 } +#define MB4_N_IO { .port = GPIO_PORT_D, .pin = 7 } +#define MB5_IO { .port = GPIO_PORT_B, .pin = 4 } +#define MB5_N_IO { .port = GPIO_PORT_B, .pin = 5 } + +#define WHEEL_A_IO { .port = GPIO_PORT_D, .pin = 2 } +#define WHEEL_B_IO { .port = GPIO_PORT_D, .pin = 3 } diff --git a/src/targets/atmega32u4-generic/main.c b/src/targets/atmega32u4-generic/main.c new file mode 100644 index 00000000..d1fb397a --- /dev/null +++ b/src/targets/atmega32u4-generic/main.c @@ -0,0 +1,60 @@ +/* + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2022 Rafael Silva + */ + +#include +#include +#include +#include + +#include + +#include "util/data.h" +#include "util/types.h" + +#include "platform/avr/boot.h" +#include "platform/avr/hal/hid.h" +#include "platform/avr/usb.h" + +void main() +{ + /* Disable watch dog */ + MCUSR &= ~(1 << WDRF); + wdt_disable(); + + /* Disable clock division */ + clock_prescale_set(clock_div_1); + + struct hid_hal_t hid_hal; + u8 info_functions[] = { + OI_FUNCTION_VERSION, + OI_FUNCTION_FW_INFO, + OI_FUNCTION_SUPPORTED_FUNCTION_PAGES, + OI_FUNCTION_SUPPORTED_FUNCTIONS, + }; + + /* create protocol config */ + struct protocol_config_t protocol_config; + memset(&protocol_config, 0, sizeof(protocol_config)); + protocol_config.device_name = "openinput Device"; + protocol_config.hid_hal = hid_hal_init(); + protocol_config.functions[INFO] = info_functions; + protocol_config.functions_size[INFO] = sizeof(info_functions); + + usb_attach_protocol_config(protocol_config); + + /* Initialize USB Subsystem */ + usb_init(); + + /* Enable global interrupts */ + sei(); + + u8 led_state = 0; + + for (;;) { + usb_task(); + } + + bootloader(); +} diff --git a/tools/caterina-load.sh b/tools/caterina-load.sh new file mode 100755 index 00000000..5e3e35d0 --- /dev/null +++ b/tools/caterina-load.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +# wait for port to spawn +while :; do + sleep 0.5 + [ -c "${UPLOAD_PORT}" ] && break +done + +# reset to bootloader (magic baudrate) +stty -F "${UPLOAD_PORT}" 1200 + +# wait for port to respawn +while :; do + sleep 0.5 + [ -c "${UPLOAD_PORT}" ] && break +done + +# upload +avrdude -v -patmega32u4 -cavr109 -P${UPLOAD_PORT} -b57600 -D -Uflash:w:${HEX_FILE}