micropython/micropython — pull request #955 by dpgeorge (2014-11-01 21:49:36+00:00)
Title: Add Python-configurable USB HID mode.

With this patch different HID modes can be configured in Python. You
can either use predefined mouse or keyboard, or write your own report
descriptor.

Eg, in boot.py you can write:
```python
pyb.usb_mode('CDC+HID', pyb.hid_keyboard)
```
This sets the USB device to CDC with HID as a keyboard. Then to send a report:
```python
hid = pyb.USB_HID()
def press_key_once(key):
buf = bytearray(8) # report is 8 bytes long
buf[2] = key
hid.send(buf) # key pressed
pyb.delay(10) # wait until report sent; can also use: select.select([], [hid], [])
buf[2] = 0
hid.send(buf) # key released
```
There is the original mouse mode:
```python
pyb.usb_mode('CDC+HID', pib.hid_mouse)
```
And now you can define your own report descriptors:
```python
pyb.usb_mode('CDC+HID', (0x1234, 0, 2, 10, b'...'))
```
Where the data in the tuple is: PID, subclass, protocol, max_packet_len, report_desc.

This is experimental, but the default mouse and keyboard work.

Some basic things need to be decided/agreed upon for the API:

1. You must now explicitly enable a USB mode using pyb.usb_mode (eg in boot.py). Otherwise the USB is not enabled. This is actually a feature, since it allows you to save power by not turning on the USB peripheral if you don't need it. The default boot.py now has `pyb.usb_mode('CDC+MSC')` uncommented. And "safe boot mode" also enables CDC+MSC mode, so it's pretty much as it was before. Just need to make people aware that if they have a blank boot.py then they won't get any USB device showing up. They can enter safe boot mode to fix this.

2. USB is a bit different from all other peripherals (eg UART), in that we don't create an instance of the top-level USB object (eg unlike uart=pyb.UART(1,9600)). In the current implementation, you set the USB mode using pyb.usb_mode(...), then create the specific objects using vcp=pyb.USB_VCP() and hid=pyb.USB_HID(). These specific objects have methods to control them (like sending a HID report). But there is no top-level USB object. That's mostly because it's not needed, since once it's initialised there's nothing more you can do. Trying to make the USB peripheral more like, eg, UART, we could make a USB object, such as usb=pyb.USB(), and then have a method usb.init('CDC+HID', ...). Then the VCP is accessed with usb.VCP(), etc. What are people's opinions on this?

3. The USB mode for CDC/VCP is called CDC, but the object is called USB_VCP. (Note CDC=communications device class, VCP=virtual comm port). VCP is kind of a subset/subclass/specialisation of CDC, and I prefer to use VCP because "virtual comm port" is much more descriptive and tells you exactly what it is. So that's why the object is named USB_VCP. It would be nice to change CDC to VCP for consistency reasons. Ie, you would select mode using `pyb.usb_mode('VCP+MSC')` or `pyb.usb_mode('VCP+HID', ...)`. This is a small change that doesn't affect anything except end-user experience and consistency for them. Opinions?