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

Avr port #82

Draft
wants to merge 16 commits into
base: main
Choose a base branch
from
Draft
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
3 changes: 2 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -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
22 changes: 22 additions & 0 deletions build_system/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
2 changes: 2 additions & 0 deletions build_system/ninja.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
] + [
Expand Down
23 changes: 23 additions & 0 deletions config/families/avr.toml
Original file line number Diff line number Diff line change
@@ -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 = {}
11 changes: 11 additions & 0 deletions config/targets/atmega32u4-generic.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
family = 'avr'
has-config = true
c_flags = [
'-mmcu=atmega32u4',
'-DUSE_LUFA_CONFIG_HEADER',
]
ld_flags = [
'-mmcu=atmega32u4',
]

[dependencies]
1 change: 1 addition & 0 deletions external/lufa
Submodule lufa added at fa2cc3
24 changes: 24 additions & 0 deletions src/platform/avr/boot.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* SPDX-License-Identifier: MIT
* SPDX-FileCopyrightText: 2022 Rafael Silva <[email protected]>
*/

#include <avr/wdt.h>

// 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();
}
8 changes: 8 additions & 0 deletions src/platform/avr/boot.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* SPDX-License-Identifier: MIT
* SPDX-FileCopyrightText: 2022 Rafael Silva <[email protected]>
*/

void reset() __attribute__((noreturn));
void bootloader() __attribute__((noreturn));
;
21 changes: 21 additions & 0 deletions src/platform/avr/hal/hid.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* SPDX-License-Identifier: MIT
* SPDX-FileCopyrightText: 2021 Rafael Silva <[email protected]>
*/

#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;
}
11 changes: 11 additions & 0 deletions src/platform/avr/hal/hid.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* SPDX-License-Identifier: MIT
* SPDX-FileCopyrightText: 2021 Rafael Silva <[email protected]>
*/

#pragma once

#include "hal/hid.h"
#include "util/types.h"

struct hid_hal_t hid_hal_init(void);
209 changes: 209 additions & 0 deletions src/platform/avr/usb.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
/*
* SPDX-License-Identifier: MIT
* SPDX-FileCopyrightText: 2022 Rafael Silva <[email protected]>
*/

#include <avr/interrupt.h>

#include "protocol/reports.h"
#include "usb.h"
#include "util/hid_descriptors.h"
#include "util/types.h"

#include <USB.h>

/** 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 = {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this aliased to const? I don't see changes to this memory so perhaps this can be on .data section or other read-only memory.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just me playing with lufa and committing to save progress, this is will get a clean up

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The struct itself i don't think is written, so yes, although on avrs, consts are always fetched to ram anyway i believe

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The struct itself i don't think is written, so yes, although on avrs, consts are always fetched to ram anyway i believe

Well, yeah, I'm not aware of those embedded cases. Godbolt https://godbolt.org/z/soo4eaaev tells me it goes to .rodata and the compiler can optimize it to immediate loads instead of an offset. This is not always the case although, as I believe the jump cost is a trade-off the compiler takes into consideration, depending on size of the data/non-natural alignment.

.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) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would use a switch case here.

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);
}
}
13 changes: 13 additions & 0 deletions src/platform/avr/usb.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* SPDX-License-Identifier: MIT
* SPDX-FileCopyrightText: 2022 Rafael Silva <[email protected]>
*/

#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);
Loading