[USBMIDI] Serial + MIDI, how to deactivate either one ?

Status
Not open for further replies.

chuckinteensy

New member
Hi in there !

I work on a MIDI Controller based on Teensy and USBMIDI and I try to find out how to switch in between a Class Compliant Device and a Serial Device. Indeed, my goal is here to be able to switch my controller between a recognised Class Compliant MIDI Device and a generic serial device that communicates with a driver i write.

When i flash the Teensy with the Serial + MIDI option enabled, I get a feature I want : my computer gets the ability to communicate either with serial or MIDI at the same time but what if I would like to get only one of this option at a time..

I don't find any option to enable/disable at setup. The purpose here is to avoid any audio software to detect my board as a MIDI Device when i'm using it with other dedicated functions.


Thanks for your help !!
 
To make USB interface settings which don't appear in the Tools > USB Type menu, you need to edit usb_desc.h. Please be aware 2 copies of that file exist, for Teensy 3.x and 4.x, so look at the folder name to make sure you're editing the right one.
 
Hey Thanks for the answer.

As I understand your answer, you suggest editing the USB Interface declaration ?
In my case, I need to declare some definition in the void setup related to a condition (a switch state for instance) that activates either USBMIDI or Serial communication. But in usb_desc.h i don't see any enable function. Maybe theres is a trick to create a declaration pointing to a null setup that is not recognised by the computer so the MIDI Device does not show up in DAWs (when not needed)?
 
This will turn out to be quite a project, since the code is designed to use only 1 configuration.

If the PC has already read the USB descriptors, the only way to make a change involves shutting off the USB port for at least 10 ms. Your PC will see that as the USB cable being unplugged. Then when you turn the USB on again, it will believe the cable has been reconnected and do the whole USB enumeration process all over.

The simplest way to do this is a soft reboot by writing to the ARM SCB AIRCR register. It's been discussed many times on this forum, so I'm sure you can find the soft reboot code with a quick search. You could also shut off just the USB peripheral without rebooting, but that isn't a well traveled path so you'll need to do more research on how to accomplish such a thing. Soft reboot is the easy way.

You will need to find a way to change the way Teensy responds to your PC's requests to read the USB descriptors. There are many ways you might do this. All of them involve significant programming effort. If you are hoping the existing code could do this for you, I'm here to say as clearly as possible that it will not. You will need to edit the source code and make significant changes to how it works.

One possible way might involve changing this table:

Code:
const usb_descriptor_list_t usb_descriptor_list[] = {
        //wValue, wIndex, address,          length
        {0x0100, 0x0000, device_descriptor, sizeof(device_descriptor)},
        {0x0600, 0x0000, qualifier_descriptor, sizeof(qualifier_descriptor)},
        {0x0200, 0x0000, usb_config_descriptor_480, CONFIG_DESC_SIZE},
        {0x0700, 0x0000, usb_config_descriptor_12, CONFIG_DESC_SIZE},
#ifdef SEREMU_INTERFACE
        {0x2200, SEREMU_INTERFACE, seremu_report_desc, sizeof(seremu_report_desc)},
        {0x2100, SEREMU_INTERFACE, usb_config_descriptor_480+SEREMU_HID_DESC_OFFSET, 9},
#endif
#ifdef KEYBOARD_INTERFACE
        {0x2200, KEYBOARD_INTERFACE, keyboard_report_desc, sizeof(keyboard_report_desc)},
//......

Maybe you would remove "const" so the table is writable, and add more of the all-zero entries if you need extra space to write more entries to the table. Before your PC reads the descriptors, maybe your code would just alter the contents of this table. Of course, if you want your PC to see 2 completely different configurations, you will need to create 2 copies of all the items this table references, especially the huge config descriptor.

Or maybe you could create 2 of these tables, and of course 2 complete copies of all the data the table references. While that is more data, it might be simpler if most of that work is just making a complete copy of all the descriptor data and just changing the names slightly. If you create 2 complete tables, you'll need to alter the all source code which actually reads this table. Fortunately, it's only actually used in 1 location in usb.c.

Code:
          case 0x0680: // GET_DESCRIPTOR
          case 0x0681:
                for (list = [B][COLOR="#B22222"]usb_descriptor_list[/COLOR][/B]; list->addr != NULL; list++) {
                        if (setup.wValue == list->wValue && setup.wIndex == list->wIndex) {
                                uint32_t datalen;
                                if ((setup.wValue >> 8) == 3) {
                                        // for string descriptors, use the descriptor's
                                        // length field, allowing runtime configured length.
                                        datalen = *(list->addr);
                                } else {
                                        datalen = list->length;
                                }
                                if (datalen > setup.wLength) datalen = setup.wLength;
//.........

So perhaps you might change this code to initialize "list" with different table names.

However you decide to do this, I again want to emphasize this is a real programming project. You will need to make design decisions, edit code, and test & debug the results. Hopefully this message helps you get started.

If you get it working reasonably well, I hope you'll consider sharing the result or anything interesting you learn along the way. Maybe others wanting to do something similar will find this thread in the future?
 
Last edited:
One other thing you should probably know about is the 300 ms startup delay. We added that years ago because so many people has problems with motion sensors and other chips that start up slowly, and Arduino libraries which were never tested on any board which boots up faster than those sensors.

If your code re-writes that table or needs to do other work before your PC begins reading the USB descriptors, you'll need to do it early. If your code is in setup() or C++ constructors, it will run (probably) too late. Your PC will probably already have read some or all of the descriptor info by that time.

You could delete that delay, or delete the starting of USB initialization, or design it to work some other way. Yet again, I want to emphasize this is a real programming project where you must make design decisions, write code, and test & debug. I'm writing this to hopefully save you some trouble with issues I can anticipate you'll find along the way. But ultimately you must make these design decisions and write the code to implement it as you decide.
 
Status
Not open for further replies.
Back
Top