Skip to content

sam0_common: Add USB peripheral driver#10915

Merged
dylad merged 4 commits intoRIOT-OS:masterfrom
bergzand:pr/usb/sam0
Mar 26, 2019
Merged

sam0_common: Add USB peripheral driver#10915
dylad merged 4 commits intoRIOT-OS:masterfrom
bergzand:pr/usb/sam0

Conversation

@bergzand
Copy link
Copy Markdown
Member

@bergzand bergzand commented Jan 31, 2019

Contribution description

This PR adds a USB peripheral driver for the sam0 device class. The driver is much WIP as some details still have to be thought out.

Testing procedure

Nothing yet, a follow-up is required to actually initialize the usb device.

Issues/PRs references

depends on #9830

image

@bergzand bergzand added State: WIP State: The PR is still work-in-progress and its code is not in its final presentable form yet Type: new feature The issue requests / The PR implemements a new feature for RIOT Area: drivers Area: Device drivers labels Jan 31, 2019
@bergzand bergzand requested a review from dylad January 31, 2019 21:56
Copy link
Copy Markdown
Member

@dylad dylad left a comment

Choose a reason for hiding this comment

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

some comments for the first round. I'll test on arduino-zero like board soon.

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2018 Freie Universität Berlin
* Copyright (C) 2018 Inria
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You should probably update these copyrights.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed, thanks

* @{
*
* @file
* @brief [RFC 4122](https://tools.ietf.org/html/rfc4122) UUID functions
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please also update Doxygen

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed

#define SAM_USB_NUM_EP USBDEV_NUM_ENDPOINTS

typedef struct {
usbdev_t usbdev;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

missing description

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

On the TODO

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

fixed

return 2 * num + (dir == USB_EP_DIR_OUT ? 0 : 1);
}

static inline unsigned _get_ep_num2(usbdev_ep_t *ep)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why is there two get_ep_num functions ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Probably just bad naming, but will check.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Refactored these a bit to make more sense

val = 0x7;
break;
default:
return;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What about a single line to directly compute this value with a nice comment ? :)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Should be possible, but have to think about it a bit, the last case (1023) makes it a bit annoying.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I've been thinking a bit about this. I'd rather keep it this way as signalling error back in case of invalid size is easier to do with the switch case statement

int res = -ENOTSUP;
assert(ep != NULL);
switch (opt) {
case USBOPT_EP_ENABLE:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

add fall-through comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

removed as they are covered by the default option

Copy link
Copy Markdown
Member

@dylad dylad left a comment

Choose a reason for hiding this comment

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

Another small round !

/* Reset peripheral */
USB->DEVICE.CTRLA.reg |= USB_CTRLA_SWRST;
while (usb_swrst_syncing()) {}
while (USB->DEVICE.CTRLA.bit.SWRST) {}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You can remove this line. The datasheet states that both bit will be clearer once reset is done, so the line above should be enough.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Duplicate something something...

USB_PADCAL_TRIM((*(uint32_t *)USB_FUSES_TRIM_ADDR >>
USB_FUSES_TRIM_Pos));

USB->DEVICE.CTRLB.bit.SPDCONF = 0x0;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Replaces 0 by USB_DEVICE_CTRLB_SPDCONF_FS

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ack

ep_reg->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ1;
}
else {
ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSSET_STALLRQ1;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSSET_STALLRQ1;
ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ1;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

fixed 😊

ep_reg->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ0;
}
else {
ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSSET_STALLRQ0;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSSET_STALLRQ0;
ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ0;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

fixed 😊

}
res = sizeof(usbopt_enable_t);
break;
default:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

DEBUG line ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Added

UsbDeviceEndpoint *ep_reg = &USB->DEVICE.DeviceEndpoint[ep->num];

if (ep->dir == USB_EP_DIR_IN) {
ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSSET_BK1RDY;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSSET_BK1RDY;
ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK1RDY;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

fixed 😊

ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSSET_BK1RDY;
}
else {
ep_reg->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSCLR_BK0RDY;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
ep_reg->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSCLR_BK0RDY;
ep_reg->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_BK0RDY;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

fixed 😊


/* Enable USB device */
USB->DEVICE.DESCADD.reg = (uint32_t)banks;
USB->DEVICE.CTRLA.reg |= USB_CTRLA_ENABLE | USB_CTRLA_RUNSTDBY;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think you can remove USB_CTRLA_RUNSTDBY for now or make it user defined in periph_conf.h

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Removed for now

struct usbdev {
const struct usbdev_driver *driver; /**< usbdev driver struct */
usbdev_event_cb_t cb; /**< Event callback supplied by
* upper layer */
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What about adding UsbDevice struct from atmel vendor files ? In the current state, this driver works fine with one USB device instance but maybe one day we will have several USB within a MCU.
This way we can replace all USB->DEVICE from the driver by a more generic call.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Agreed

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I assume you want to add that to the sam0_common_usb_t and not to the generic usbdev struct.

I think your point is valid, but I have to check if the current API allows this. If it doesn't, I would assume this to be a shortcomming of the API.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I assume you want to add that to the sam0_common_usb_t and not to the generic usbdev struct.

Yes

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done, I'm not fully content with the current implementation for retrieving the context in the isr, but this can be reworked as soon as we actually have a device with multiple usb peripherals

@bergzand
Copy link
Copy Markdown
Member Author

Rebased and reworked to the current state of #9830

Also fixed a number of issues/suggestions as proposed by @dylad

@bergzand bergzand changed the title [WIP] sam0_common: Add USB peripheral driver sam0_common: Add USB peripheral driver Feb 13, 2019
@bergzand bergzand removed the State: WIP State: The PR is still work-in-progress and its code is not in its final presentable form yet label Feb 13, 2019
@bergzand
Copy link
Copy Markdown
Member Author

Removed WIP status btw, ready for review (I hope)

@bergzand
Copy link
Copy Markdown
Member Author

rebased

@dylad
Copy link
Copy Markdown
Member

dylad commented Mar 15, 2019

@bergzand i'll give it another round this weekend. Could you rebase please ?

@bergzand
Copy link
Copy Markdown
Member Author

Reworked and rebased!

@bergzand bergzand added the Area: USB Area: Universal Serial Bus label Mar 18, 2019
Copy link
Copy Markdown
Member

@dylad dylad left a comment

Choose a reason for hiding this comment

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

@bergzand We're almost ready, just a few comments.

/**
* USB endpoint buffer space
*
* @todo global configurable?
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

still todo ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Nope, actually is globally configurable now (for a while)

case 512:
val = 0x6;
break;
case 1023:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@bergzand I think I've read somewhere that some endpoint size can be 1023 or 1024 in specific case. Am I wrong ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

1023 bytes is the max size for USB 2.0 full speed isochronous transfers, USB 2.0 high speed is allowed to have transfers of 1024 bytes with isochronous endpoint types.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Alright then we're all good here :)

GCLK_CLKCTRL_GEN_GCLK0 |
(GCLK_CLKCTRL_ID(USB_GCLK_ID)));
#elif defined(CPU_FAM_SAML21)
MCLK->AHBMASK.reg |= MCLK_AHBMASK_USB;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You missed MCLK->APBBMASK for SAML21 family

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks!

GCLK->CLKCTRL.reg = (uint32_t)(GCLK_CLKCTRL_CLKEN |
GCLK_CLKCTRL_GEN_GCLK0 |
(GCLK_CLKCTRL_ID(USB_GCLK_ID)));
#elif defined(CPU_FAM_SAML21)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

#elif defined(CPU_FAM_SAML21) || defined(CPU_FAM_SAMR30)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks!

gpio_init(GPIO_PIN(PA, 24), GPIO_IN);
gpio_init(GPIO_PIN(PA, 25), GPIO_IN);
gpio_init_mux(GPIO_PIN(PA, 24), GPIO_MUX_G);
gpio_init_mux(GPIO_PIN(PA, 25), GPIO_MUX_G);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You should reused your config struct defined in periph_conf.h here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

So much effort spent on the config struct, only to forget to use it in the implementation…

usbdev->config->device->CTRLB.bit.SPDCONF = USB_DEVICE_CTRLB_SPDCONF_FS;
_enable_irq(usbdev);

gpio_init_int(GPIO_PIN(PA, 7), GPIO_IN_PD, GPIO_BOTH,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same comment as above

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed

sam0_common_usb_t *usbdev = (sam0_common_usb_t*)dev;
if (usbdev->connect_change) {
usbdev->connect_change = false;
if (gpio_read(GPIO_PIN(PA, 7))) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same comment as above

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Also fixed

@bergzand
Copy link
Copy Markdown
Member Author

@dylad fixed your remarks, also uncrustified the file (but should have done that in a separate fixup commit :'( )

@dylad
Copy link
Copy Markdown
Member

dylad commented Mar 18, 2019

also uncrustified the file (but should have done that in a separate fixup commit :'( )

I don't mind, I'll test it on hardware asap so we can move forward.

@bergzand
Copy link
Copy Markdown
Member Author

I don't mind, I'll test it on hardware asap so we can move forward.

Thanks, appreciating your reviews!

@bergzand
Copy link
Copy Markdown
Member Author

I messed up the GPIO things :(

@bergzand
Copy link
Copy Markdown
Member Author

I messed up the GPIO things :(

fixed

@bergzand
Copy link
Copy Markdown
Member Author

@dylad I've reworked the driver with the ideas as discussed out-of-band today. Sorry for the force-push, messed up my commit order a bit.

  • usbdev is extended with suspend and resume event types. I can split these out into a separate PR if you like
  • the sam0_common usbdev driver makes use of these. Low power modes are unblocked when the device is suspended. Detaching the device from a host is also registered as a "suspend" here.
  • I've added config for the arduino-zero board.

Copy link
Copy Markdown
Member

@dylad dylad left a comment

Choose a reason for hiding this comment

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

Seems like we're close to an end.

return res;
}

static int _ep_unready(usbdev_ep_t *ep)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nitpick: why _ep_unready and _usbdev_ep_ready ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Arbitrary, nothing specific, position of the moon etc.

Will fix to _ep_ready

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Never mind the above, I remember now, all the static _usbdev_$SOMETHING calls are used in the instantiated usbdev_driver_t struct

static void _usbdev_ep_esr(usbdev_ep_t *ep)
{
UsbDeviceEndpoint *ep_reg = _ep_reg_from_ep(ep);
signed event = -1;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

int32 or int16 ? I'm sure this one will be troublesome outside of ARM.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

But this driver is closely tied to an ARM based core…

I can change it to int32_t if you like

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

But same as this comment, there is not really a need for a specific variable size here, so I'd rather give the compiler more freedom for possible optimizations.

@dylad dylad added the Reviewed: 3-testing The PR was tested according to the maintainer guidelines label Mar 25, 2019
/**
* @brief USB suspend condition no longer active
*/
USBDEV_EVENT_RESUME,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

dump question here:
Is this suppose to be random or alphabetical order ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good question. The only "order" I had in mind was something that appeared logical to me. Which at the moment is to group the similar events together as much as possible.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Alright, I don't think the coding guideline force us to change something here. Let's keep it this way.

@bergzand
Copy link
Copy Markdown
Member Author

rebased to current master

Copy link
Copy Markdown
Member

@dylad dylad left a comment

Choose a reason for hiding this comment

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

This is my last round. We're are almost good here !

GCLK->CLKCTRL.reg = (uint32_t)(GCLK_CLKCTRL_CLKEN |
GCLK_CLKCTRL_GEN_GCLK0 |
(GCLK_CLKCTRL_ID(USB_GCLK_ID)));
#elif defined(CPU_FAM_SAML21) || defined(CPU_FAM_SAMR30)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would change this line to

Suggested change
#elif defined(CPU_FAM_SAML21) || defined(CPU_FAM_SAMR30)
#elif defined(CPU_SAML21)

This will prevent us to add new sub-family like the upcoming SAMR34

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Huh? Are you sure? I changed this to the current situation based on your comment. I don't mind changing it back to what you are suggesting, just want to make sure that we get this right.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You can go ahead, this is the way to go.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

See 4b9b90b

case USB_EP_TYPE_INTERRUPT:
type = 0x04;
break;
case USB_EP_TYPE_NONE:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

add default statement at the same time here ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

How about I add an

default:
  assert(false);

here. Anything else than the types already handled in the switch is an error.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Never mind, I can just change the case USB_EP_TYPE_NONE: to default:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

See 279e844

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That would be perfect.

@dylad dylad added Reviewed: 1-fundamentals The fundamentals of the PR were reviewed according to the maintainer guidelines Reviewed: 2-code-design The code design of the PR was reviewed according to the maintainer guidelines Reviewed: 4-code-style The adherence to coding conventions by the PR were reviewed according to the maintainer guidelines Reviewed: 5-documentation The documentation details of the PR were reviewed according to the maintainer guidelines labels Mar 25, 2019
@dylad
Copy link
Copy Markdown
Member

dylad commented Mar 25, 2019

ACK.
arduino-zero is happy. Please squash !

@bergzand bergzand added the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label Mar 25, 2019
@dylad
Copy link
Copy Markdown
Member

dylad commented Mar 26, 2019

@bergzand
Feel free to squash when all static-tests will be fixed.

@bergzand
Copy link
Copy Markdown
Member Author

@dylad Squashed, all green here! 🎉

@dylad
Copy link
Copy Markdown
Member

dylad commented Mar 26, 2019

Here we go !

@dylad dylad merged commit 93bff48 into RIOT-OS:master Mar 26, 2019
@bergzand
Copy link
Copy Markdown
Member Author

@dylad Thanks for the review and testing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: drivers Area: Device drivers Area: USB Area: Universal Serial Bus CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Reviewed: 1-fundamentals The fundamentals of the PR were reviewed according to the maintainer guidelines Reviewed: 2-code-design The code design of the PR was reviewed according to the maintainer guidelines Reviewed: 3-testing The PR was tested according to the maintainer guidelines Reviewed: 4-code-style The adherence to coding conventions by the PR were reviewed according to the maintainer guidelines Reviewed: 5-documentation The documentation details of the PR were reviewed according to the maintainer guidelines Type: new feature The issue requests / The PR implemements a new feature for RIOT

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants