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

Overhaul USB HID for Leonardo and Micro #1803

Closed
wants to merge 27 commits into from
Closed

Overhaul USB HID for Leonardo and Micro #1803

wants to merge 27 commits into from

Conversation

obra
Copy link
Contributor

@obra obra commented Jan 13, 2014

This pull request includes the features from pull request 1488 and pull request 1391. I've somewhat overhauled the code to better match the surrounding code and a few features.

I've also added code examples for the less discoverable features.

If this pull request isn't "right" yet, I'd love feedback so I can improve it.

@@ -0,0 +1,34 @@
/* AbsoluteMouse.ino

For ATmega32U4 based boards (like the Leonardo and Micro

Choose a reason for hiding this comment

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

Missing closing bracket: )

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks. fixed.

@madrang
Copy link
Contributor

madrang commented Jan 30, 2014

Is it possible to allow users to customise the USB descriptors so libraries can add support for other types of devices ??
Like a gamepad, sound input/output, Serial, ....

Also this would allow to move the Mouse and Keyboard code in a different library that would only be included when needed. This way users would only import what they need to save space.

@obra
Copy link
Contributor Author

obra commented Jan 30, 2014

I would love to see patches for that. Not sure what the right extension mechanism for that would look like

Marc-Andre Ferland notifications@github.com wrote:

Is it possible to allow users to customise the USB descriptors so
libraries can add support for other types of devices ??
Like a gamepad, sound input/output, Serial, ....


Reply to this email directly or view it on GitHub:
#1803 (comment)

Sent from Kaiten Mail. Please excuse my brevity.

@yahesh
Copy link

yahesh commented Feb 3, 2014

It's not the keyboard that handles the locking states (NumLock, CapsLock, ScrollLock) but the operating system. Unfortunately the Arduino Leonardo does not support this. There are solutions to this in the forum ( e.g. http://forum.arduino.cc/index.php?topic=166638.0 or http://forum.arduino.cc/index.php?topic=173583.0 ) and it would be great to see this feature in the overhauled USB HID library.

@obra
Copy link
Contributor Author

obra commented Feb 3, 2014

On Sun, Feb 02, 2014 at 07:07:33PM -0800, Kenneth Newwood wrote:

It's not the keyboard that handles the locking states (NumLock, CapsLock, ScrollLock) but the operating system. Unfortunately the Arduino Leonardo does not support this. There are solutions to this in the forum ( e.g. http://forum.arduino.cc/index.php?topic=166638.0 ) and it would be great to see this feature in the overhauled USB HID library.

I'd love to see a version of that patch with slightly cleaner code, maybe some comments and an example. (Also, clarification from the author that it's ok to include in the Arduino core)

@cmaglie
Copy link
Member

cmaglie commented Feb 6, 2014

Hi @obra
sorry for the late feedback and thanks for the pull request, I've looked at it and at a first sight it looks very well done, BTW it's a fairly complex patch, it touches the 32u4 USB core and needs thorough testing at least on any combination of the most used OS/board. On which OS/board have you tested it?

Another important aspect to consider is how much this patch impact on the sketch size, I went through some of the base examples:

Sketch traditional core with pull 1803 delta
Empty sketch 4554 5404 +850
ASCIITable 5574 6424 +850
KeyboardAndMouseControl 5452 6302 +850

It seems that there is a fixed increase of 850 bytes, that is a lot. It may be due to the modified USB descriptors? can we figure out a way to reduce the space when some of the features are not used?

@obra
Copy link
Contributor Author

obra commented Feb 6, 2014

On Thu, Feb 06, 2014 at 09:26:21AM -0800, Cristian Maglie wrote:

Hi @obra
sorry for the late feedback and thanks for the pull request,

No worries!

I've looked at it and at a first sight it looks very well done

Thanks! The devil, as they say, is in the details and I very much don't want to be the guy who ruined Arduino 1.5.

BTW it's a fairly complex patch, it touches the 32u4 USB core and needs thorough testing at least on any combination of the most used OS/board. On which OS/board have you tested it?

I've only tested it with the Micro, as I don't have a Leonardo (or any other 32u4 board). If it'd help things along, I'm happy to buy additional Arduinos for testing. Just tell me what you'd like me to try.

On the host side, I've tested on Windows 7 and 8, OS X 10.7 and OS X 10.9, and Android 4.4.
(Android 4.4 doesn't really seem to take advantage of the new features, but the old stuff still works great)

Is there a documented matrix of configurations that need testing or a testing methodology?

Another important aspect to consider is how much this patch impact on the sketch size, I went through some of the base examples:

Sketch traditional core with pull 1803 delta
Empty sketch 4554 5404 +850
ASCIITable 5574 6424 +850
KeyboardAndMouseControl 5452 6302 +850

It seems that there is a fixed increase of 850 bytes, that is a lot. It may be due to the modified USB descriptors? can we figure out a way to reduce the space when some of the features are not used?

I believe you're correct on the cause of the bloat.

I suspect the USB-wakeup code is also part of it.

I would be thrilled to work to cut down on the memory footprint.
Ultimately, I'd really like to let libraries add USB HID drivers
without needing them to be in the Arduino core.

My C++ is pretty rusty, but there are two obvious-seeming ways to cut down
the default code size:

  1. surround the 'new' HID drivers in #ifdefs.

The downside of this approach is that it makes using the new features
more complex. I haven't seen a good example of an Arduino sketch that sets
a #define for the core code.

I expect that the answer is 'no' as it is in the broader C/C preprocessor world, but is there a way in Arduino C to set a compile-time define from within a run-time method. If it is possible, then this all gets a bit simpler and we could shrink the default runtime to smaller than it is today by conditionally compiling the USB keyboard and mouse HID descriptors.

  1. provide some sort of an API for libraries to extend _hidReportDescriptor at compile time.

I have no idea what that would look like, but it would let us move most of the HID code into libraries, which would be a lot more flexible and maintainable.

I'm 100% happy to do the work on this, but I'll need some coaching so I don't screw it up.

Thanks!

Jesse

@madrang
Copy link
Contributor

madrang commented Feb 7, 2014

Should be possible to make this work with virtual functions.
http://forum.arduino.cc/index.php/topic,41800.0.html

@obra
Copy link
Contributor Author

obra commented Feb 7, 2014

I'm having a hard time imagining an inheritance model (rather than plugin model) working well here. That might just be my lack of familiarity with the code base, though.

Is there anywhere else in the arduino core that does this so that I can see that it's not scary and wrong?

Best,
Jesse

On Feb 7, 2014, at 10:09 AM, Marc-Andre Ferland notifications@github.com wrote:

Should be possible to make this work withnvirtual functions.
http://forum.arduino.cc/index.php/topic,41800.0.html


Reply to this email directly or view it on GitHub.

@cmaglie
Copy link
Member

cmaglie commented Feb 7, 2014

Jesse, your test cases seems already quite wide, I didn't expected that. On the board side I would like to add the Leonardo and on the Host side Linux 32/64 and WinXP, but I can manage to make the missing tests here so don't bother trying nor buying other boards :-)

About reducing the memory footprint: I don't have a precise idea, it's a hard problem indeed, I asked hoping that someone would come up with a solution.

The most obvious approach is to use #ifdef blocks as you suggested, but we need to find a way to make it practical for users, currently there is no simple way to feed #defines constants from the sketch to the Arduino core.

Composing the USB descriptor at compile-time seems hard too, I can't think of any C/C++ trick to make it happen. An alternative may be to split USB descriptor into pieces and send them all at once at runtime with some chaining but, again, this is just another hypothesis and requires an amount of refactoring on the actual code, I don't know if it's feasible.

mmhhh

@obra
Copy link
Contributor Author

obra commented Feb 7, 2014

On Fri, Feb 07, 2014 at 09:00:07AM -0800, Cristian Maglie wrote:

Jesse, your test cases seems already quite wide, I didn't expected that.

Thanks :)

On the board side I would like to add the Leonardo and on the Host side Linux 32/64 and WinXP, but I can manage to make the missing tests here so don't bother trying nor buying other boards :-)

nod I've actually now got a tester on WinXP. And I've done some limited testing on Linux (x86 64) but I wouldn't want to claim that I've run the current code.

The problem is to "build" the
The most obvious approach is to use #ifdef blocks as you suggested, but we need to find a way to make it practical for users, currently there is no simple way to feed #defines constants from the sketch to the Arduino core.

Composing the USB descriptor at compile-time seems hard too, I can't think of any C/C++ trick to make it happen. An alternative may be to split USB descriptor into pieces and send them all at once at runtime with some chaining but, again, this is just another hypothesis and requires an amount of refactoring on the actual code, I don't know if it's feasible.

And I worry that that could actually have its own bloat associated with it.

Looking at main.cpp, there's just no hook provided before the USB device gets
initialized.

It feels like Arduino could give developers a bunch more rope if there were another pre-setup function called before that USB connection were made. I havent' tried to prototype anything since that seems like a pretty fundamental sort of change.

#include <Arduino.h>

int main(void)
{
init();

#if defined(USBCON)
USBDevice.attach();
#endif
setup();
for (;;) {
loop();
if (serialEventRun) serialEventRun();
}
return 0;
}

@madrang
Copy link
Contributor

madrang commented Feb 10, 2014

What if we made a virtual usb hub and each library register it's own device on it ?

@cmaglie
Copy link
Member

cmaglie commented Feb 10, 2014

IMHO its not a problem to add an additional function call to the main.cpp if needed (maybe USBinit() instead of init(), but you got the point).

@obra
Copy link
Contributor Author

obra commented Feb 10, 2014

On Mon, Feb 10, 2014 at 03:25:37AM -0800, Marc-Andre Ferland wrote:

What if we made a virtual usb hub and each library register it's own device on it ?

That seems like it would end up with a lot of overhead, but I'd love to see a prototype.

@obra
Copy link
Contributor Author

obra commented Feb 10, 2014

On Mon, Feb 10, 2014 at 03:30:23AM -0800, Cristian Maglie wrote:

IMHO its not a problem to add an additional function call to the main.cpp if needed (maybe USBinit() instead of init(), but you got the point).

nod I'd probably give it a name that's not USB specific, just to improve reusability.
I'm still not entirely sure what the code would look like. I'd be thrilled if someone beats me to it, but I'll start sketching something.

@@ -75,6 +76,7 @@ class Mouse_
void end(void);
void click(uint8_t b = MOUSE_LEFT);
void move(signed char x, signed char y, signed char wheel = 0);
void moveAbs(uint16_t x, uint16_t y);
Copy link
Contributor

Choose a reason for hiding this comment

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

I realize this is a very small point about a large proposed change but for consistency with other parts of the Arduino API and the API style guide (http://arduino.cc/en/Reference/APIStyleGuide), this should be called moveAbsolute(), not moveAbs(). The letters "abs" can mean a lot of different things and, in general, we use complete words in the API.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

On Mon, Feb 10, 2014 at 07:30:03AM -0800, David A. Mellis wrote:

@@ -75,6 +76,7 @@ class Mouse_
void end(void);
void click(uint8_t b = MOUSE_LEFT);
void move(signed char x, signed char y, signed char wheel = 0);

  • void moveAbs(uint16_t x, uint16_t y);

I realize this is a very small point about a large proposed change but for consistency with other parts of the Arduino API and the API style guide (http://arduino.cc/en/Reference/APIStyleGuide), this should be called moveAbsolute(), not moveAbs(). The letters "abs" can mean a lot of different things and, in general, we use complete words in the API.

I like consistency. Thank you. I'll update this in just a moment.

@obra
Copy link
Contributor Author

obra commented Feb 17, 2014

@cmaglie, I started down the road of adding new functions to main.cpp to try and work around the problem of being unable to pass preprocessor macros to the Arduino core. It seemed like an ugly work around, so I tried to fix the 'actual' problem.

The patch below looks in a sketch's directory for a directory called "core_include" - If it exists, it's added as the first entry in the include search path.

Additionally, Arduino.h now has hooks to load two new include files <before_arduino_h.h> and <after_arduino_h.h>.

Dummy files for each of those in the core make sure that nobody will get a "file not found" error.

With this infrastructure, users can now #define things from userspace programs and have the results actually do something useful in the core.

If this idea/implementation is workable, I believe that I need to do at least the following before submitting a pull request

  • Make the same changes to the SAM core
  • Add some comments to the before and after files
  • Document this somewhere/somehow

Does this seem like a valid/valuable addition to the Arduino core?

Is my implementation sane?

What else would you like to see in a pull request?

Thanks!

diff --git a/app/src/processing/app/debug/Compiler.java b/app/src/processing/app/debug/Compiler.java
index 5bdb7d2..577d3bd 100644
--- a/app/src/processing/app/debug/Compiler.java
+++ b/app/src/processing/app/debug/Compiler.java
@@ -89,6 +89,11 @@ public class Compiler implements MessageConsumer {
     // 0. include paths for core + all libraries
     sketch.setCompilingProgress(20);
     List<String> includePaths = new ArrayList<String>();
+
+    File coreIncludeOverrides = new File(sketch.getFolder(), "core_include");
+    if (coreIncludeOverrides.isDirectory()) {
+        includePaths.add(coreIncludeOverrides.getAbsolutePath());
+    }
     includePaths.add(prefs.get("build.core.path"));
     if (prefs.get("build.variant.path").length() != 0)
       includePaths.add(prefs.get("build.variant.path"));
@@ -666,8 +671,13 @@ public class Compiler implements MessageConsumer {
     String corePath = prefs.get("build.core.path");
     String variantPath = prefs.get("build.variant.path");
     String buildPath = prefs.get("build.path");
-
+    
     List<String> includePaths = new ArrayList<String>();
+
+    File coreIncludeOverrides = new File(sketch.getFolder(), "core_include");
+    if (coreIncludeOverrides.isDirectory()) {
+        includePaths.add(coreIncludeOverrides.getAbsolutePath());
+    }
     includePaths.add(corePath); // include core path only
     if (variantPath.length() != 0)
       includePaths.add(variantPath);
diff --git a/hardware/arduino/avr/cores/arduino/Arduino.h b/hardware/arduino/avr/cores/arduino/Arduino.h
index 7bf5119..f51f560 100644
--- a/hardware/arduino/avr/cores/arduino/Arduino.h
+++ b/hardware/arduino/avr/cores/arduino/Arduino.h
@@ -1,6 +1,7 @@
 #ifndef Arduino_h
 #define Arduino_h

+#include <before_arduino_h.h>
 #include <stdlib.h>
 #include <string.h>
 #include <math.h>
@@ -217,4 +218,5 @@ long map(long, long, long, long, long);

 #include "pins_arduino.h"

+#include <after_arduino_h.h>
 #endif

@matthijskooijman
Copy link
Collaborator

@obra, an approach like this seems ok to me. I don't think it's the most reliable or elegant solution, but at least it is simple and powerful. Also see #1734, which has code to do pretty much the same thing for libraries. Ideally, we'd have a single solution (or at least a single convention) that works for both.

Regarding your actual patch, if you indent it by a few spaces, github Markdown will show it as a code block, it's pretty unreadable as it is now...

@damellis
Copy link
Contributor

The idea of having a specially-named .h file in the sketch be included during compilation of the core and libraries is a long-standing one: https://code.google.com/p/arduino/issues/detail?id=27

It may or may not be a good idea (I've never quite been convinced it's worth it) but it is, I think, a big change and one that should be considered carefully on the developers list. This would open up lots of potential tweaks to the core and libraries, greatly increasing the effective API of those files (i.e. previously-internal details of the files that people will now be able to rely on and therefore will become more difficult to change). It will also add a lot of potential complexity / features that people may see in sketches they get from others. In addition, by not having a way to provide pre-processor macros / compile-time constants to the core and libraries, I think we've forced people to find easier-to-use alternatives in the form of run-time configuration (which is more consistent with other parts of the Arduino API). With this option, I worry that people will instead take the path-of-least-resistant and use compile-time configuration, complicating things for users.

In any case, I'd encourage a thoughtful discussion of this feature before adding it and it has far-reaching implications.

@obra
Copy link
Contributor Author

obra commented Feb 18, 2014

David,

Thanks very much for the feedback. I'll bring it up on the list.

Do you have thoughts about other ways to solve the problem I'm specifically trying to address for the code in this pull request? USB HID descriptors can take up a relatively large amount of space in a compiled sketch and it's pretty clear that the new features in this pull request can't go into the core until and unless there's a way to conditionally compile them.

@follower
Copy link

On 19 February 2014 05:55, Jesse Vincent notifications@github.com wrote:

USB HID descriptors can take up a relatively large amount of space in a
compiled sketch
In one of the other threads you mentioned this patch adding ~800 bytes
to sketches. Do you know what the split between code and descriptors
is, in that size increase?

Am I correct in thinking that the issue is that this extra space is
taken even when the new functionality isn't being used?

If so, then we need the code/descriptors written in such a way that
the compiler doesn't automatically include them, correct? Is there a
reason why the approaches we normally take (e.g. weakrefs,
--gc-sections use) isn't working here?

One thought I had was in relation to something that I saw when working
with the V-USB implementation: descriptors could actually be generated
on the fly from a function--is this something that we could do here in
a way that would reduce code size? The hardware USB implementation
might be completely different in a way to make this not relevant but
thought I'd at least mention it.

--Philip;

@obra
Copy link
Contributor Author

obra commented Feb 18, 2014

Am I correct in thinking that the issue is that this extra space is
taken even when the new functionality isn't being used?

Yes.

If so, then we need the code/descriptors written in such a way that
the compiler doesn't automatically include them, correct? Is there a
reason why the approaches we normally take (e.g. weakrefs,
--gc-sections use) isn't working here?

Pointers to documentation? I'm a clueless newbie on this stuff.

One thought I had was in relation to something that I saw when working
with the V-USB implementation: descriptors could actually be generated
on the fly from a function--is this something that we could do here in
a way that would reduce code size? The hardware USB implementation
might be completely different in a way to make this not relevant but
thought I'd at least mention it.

The problem is that we're taking the space hit just by compiling the descriptors in and storing them in PROGMEM . We need to do something at compile time to not compile them in. And, at least as of now, the USB connection happens before any user code is run.

@follower
Copy link

On 19 February 2014 10:16, Jesse Vincent notifications@github.com wrote:

If so, then we need the code/descriptors written in such a way that
the compiler doesn't automatically include them, correct? Is there a
reason why the approaches we normally take (e.g. weakrefs,
--gc-sections use) isn't working here?

Pointers to documentation? I'm a clueless newbie on this stuff.
Well, in theory it should already happen so you shouldn't need to do
anything different but I may be making incorrect assumptions. :) Here
is a starting point:

http://embeddedfreak.wordpress.com/2009/02/10/removing-unused-functionsdead-codes-with-gccgnu-ld/

Also, apparently the GCC term is "weak symbols" rather than weakrefs:

http://www.embedded-bits.co.uk/2008/gcc-weak-symbols/

Using this approach would presumably require having a "default"
implementation that did nothing but including a specific library would
override with an actual implementation. I have no idea whether this is
even possible in this situation.

The problem is that we're taking the space hit just by compiling the
descriptors in and storing them in PROGMEM . We need to do something
at compile time to not compile them in.
As I mentioned, I don't know if generating these dynamically at
runtime is a way past this or not--presumably if a function returning
the descriptor could be eliminated at compile time then data should be
able to be treated the same way.

And, at least as of now, the USB connection happens before any user code is run.
That shouldn't be an issue given this is probably a compile-time
issue. Although having said that, can the descriptors etc be updated
at run time by the user with the current implementation?

I'm kinda vague and hand-wavy on all of this because I've only ever
dealt with the V-USB software implementation but wanted you to get at
least some response to your enquiry. :) Maybe worth talking to the
LUFA people to see if they have a suggestion?

Good luck!

--Philip;

@matthijskooijman
Copy link
Collaborator

Also, apparently the GCC term is "weak symbols" rather than weakrefs:

Note that there's really two types of weak symbols (not sure about exact
terminology, though): weak definitions, which can be overridden by
another (weak or non-weak) definition of the same symbol and weak
declarations / references, which the linker does not try so hard to
satisfy (it won't pull in a library to satisfy the weak reference, but
if the symbol is available to satisfy another dependency, it is used)
and might be NULL at runtime.

I suspect that a similar approach to what I added for HardwareSerial
recently might apply here. See this comment and commit:

#1711 (comment)
matthijskooijman@aa584bc518d3ea68c1ffa149ec5ed02ae10085f6https://github.com/matthijskooijman/Arduino/commit/aa584bc518d3ea68c1ffa149ec5ed02ae10085f6

I'm not entirely sure what the problem is you're trying to solve, but
I'll assume it's something like this: You have a bit of USB core that is
always included and code for specific hardware types, like USB keyboard,
that is only included when actually used. In addition to code, these
hardware types include a descriptor, which is a significant amount of
data in PROGMEM.

A solution for this could look like the following.

USBCore.cpp contains a main init function, that weakly calls
descriptor-specific init functions. It also contains a function to allow
registering descriptors at runtime. e.g.,

void initUSBKeyboard() __attribute__((__weak__));
void initUSB() {
    if (initUSBKeyboard)
        initUSBKeyboard();
    ... more initialization ...
}

void addUSBDescriptor(Descriptor *d) {
    // Load d from progmem and add it to some internal list
}

Then, there is an USBKeyboard.cpp, which contains the descriptor,
the init function and any other related functions.

Descriptor keyboard_descriptor PROGMEM = { ... };

void initUSBKeyboard() {
    addUSBDescriptor(&keyboard_descriptor);
    ... more initialization ...
}

void USBKeyboardDoSomething(...) {
    ...
}

Now, when a sketch does not use any USB keyboard functions, the only
reference to USBKeyboard.cpp is a weak one from USBCore.cpp, so
USBKeyboard is not pulled in and initUSBKeyboard will be NULL at
runtime. However, if the sketch calls USBKeyboardDoSomething(), then
there is a strong dependency on USBKeyboard.cpp, so it is included.
Now, initUSBKeyboard contains the actual function address at runtime
and initUSB() will call it.

This approach needs an addUSBDescriptor() function which can be called
any number of times, so it's unknown how much memory should be allocated
for the list of descriptors, which is not very handy. We can probably
mitigate this by doing letting initUSBKeyboard() just return a
Descriptor*:

enum {
    USB_KEYBOARD_INDEX = 0,
    ...
    NUM_DESCRIPTORS = ...,
}

Descriptor *list[NUM_DESCRIPTORS] = {0};

Descriptor *initUSBKeyboard() __attribute__((__weak__));
void initUSB() {
    if (initUSBKeyboard)
        list[USB_KEYBOARD_INDEX] = initUSBKeyboard();
    ... more initialization ...
}

Note that this assumes that all descriptors have the same type and can
be just a Descriptor*, but that type might of course be more complicated
(perhaps just a uint8_t[] and a size, if USBCore doesn't care what's
in it exactly).

Also, note that the above assumes that stuff will be loaded from PROGMEM
by USBCore, meaning USBCore also needs to allocate memory for the
descriptors. This is fine when the descriptors need to be in memory for
a short while, but if they need to be available in memory all the time,
this causes a problem: USBCore should then either dynamically allocate
all memory with malloc, or it should statically allocate memory for all
possible descriptors, regardless of wether they're used.

In this case, it would be better to let the USBKeyboard.cpp statically
allocate storage and load the descriptor from PROGMEM. However, if the
descriptor really needs to be in memory all the time, then it's even
better to just drop the PROGMEM attribute - then the linker / gcc
startup code will just load the data from flash to ram for you.

Finally, I'm wondering if you can do something like this in USBCore.cpp:

extern Descriptor USBKeyboardDescriptor __attribute__((__weak__));
Descriptor *list[] = {
    &USBKeyboardDescriptor,
    ... more descriptor references ...
};

And have the compiler sort everything out automatically, either putting
NULL in the list if USBKeyboard.cpp is not used, or putting the address
of the descriptor in the list if it is.

I'm not entirely sure if this will do what I hope it does, but it looks
like at least specifying the weak attribute for variables is suported:

http://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Variable-Attributes.html

I hope this helps you figure out something cool and efficient! :-)

@follower
Copy link

On 20 February 2014 01:19, Matthijs Kooijman notifications@github.com wrote:

I hope this helps you figure out something cool and efficient! :-)
Hey, cool, thanks for filling in my hand-waving with specifics. :)

--Philip;

@obra
Copy link
Contributor Author

obra commented Feb 20, 2014

Wow. Thank you so much for that explanation, Matthijs. I suspect that's enough
to figure out how to do what i want. I think it might even let me set up Arduino to not bother to compile in Keyboard/Mouse support unless Keyboard.begin()/Mouse.begin() are called. (I'll do that as a separate patch, since I'm not sure it's desirable.)

@matthijskooijman
Copy link
Collaborator

I think it might even let me set up Arduino to not bother to compile in Keyboard/Mouse support unless Keyboard.begin()/Mouse.begin() are called.

I think that'll only work if you put the Keyboard and Mouse support in separate libraries. If you keep it in the core, it'll certainly compile them, but just not include them in the link.

However, if you move stuff into external libraries, the weak linking stuff I proposed above might become tricky again. You can still use the same trick with hardcoding symbol names in USBCore that are defined in the library, but that doesn't seem like good form. Moreover, if we want to support third-party libraries also adding extra descriptors etc., then we can't know in advance how all these symbols are named, so we can't use that approach.

So, perhaps using the dynamic memory approach with an addDescriptor() call might make sense after all? For third-party libraries, you can't (weakly) call initNameOfLibrary() from USBCore, but I think you can also use C++ constructors here: If, for example, you have for example an USBKeyboard class, which has a single instance Keyboard, then you can put an addDescriptor(&keyboard_descriptor) or something like that in the USBKeyboard constructor, which will then automatically be called at startup before main() is called. This approach might equally well work for any descriptor-specific code in the core, which removes the need for the weak linking entirely.

@cmaglie cmaglie modified the milestones: Release 1.6.1, Release 1.6.0 Feb 18, 2015
@cmaglie cmaglie added Component: USB Device Opposed to USB Host. Related to the USB subsystem (SerialUSB, HID, ...) feature request A request to make an enhancement (not a bug fix) Architecture: AVR Applies only to the AVR microcontrollers (Uno, etc.) labels Apr 15, 2015
@ffissore ffissore modified the milestones: Release 1.6.1, Release 1.6.5 May 20, 2015
@ffissore ffissore modified the milestones: Release 1.6.5, Release 1.6.6 Jun 15, 2015
facchinm added a commit to facchinm/Arduino that referenced this pull request Jun 26, 2015
cmaglie pushed a commit to cmaglie/Arduino that referenced this pull request Jul 16, 2015
@NicoHood
Copy link
Contributor

This may be closed now as well.

@facchinm
Copy link
Member

I'd close this issue as, from 1.6.6, USB-related enhancements can be implemented by libraries.
All the code from this PR was squashed in CompleteHID library which can be easily recovered from git history or using @NicoHood HID library
Thanks everyone for taking part in this great effort!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Architecture: AVR Applies only to the AVR microcontrollers (Uno, etc.) Component: Core Related to the code for the standard Arduino API Component: USB Device Opposed to USB Host. Related to the USB subsystem (SerialUSB, HID, ...) feature request A request to make an enhancement (not a bug fix)
Projects
None yet
Development

Successfully merging this pull request may close these issues.