The importance of including a unique iSerialNumber in your USB MIDI Devices

Pete Brown

The importance of including a unique iSerialNumber in your USB MIDI Devices

Uniquely identifying connected devices is something all operating systems must do in order to function. But persistently identifying them, across moves, reboots, and more, can be more challenging. This post covers why this topic is so important to Windows MIDI Services and what manufacturers and customers can expect to see in Windows MIDI Services.

Please note that the scope of this post is just USB MIDI devices. Other devices, like storage, have different requirements and different approaches to identification.

TLDR; Please provide a valid and unique iSerialNumber string descriptor for your USB devices. If you cannot provide that value, do not provide any string descriptor for that (set the iSerialNumber field value to 0x00 so it does not point to any string descriptor). If you want to learn more, please read on.

Note: When I mention the iSerialNumber field in the rest of this post, I’m generally referring to the string descriptor the iSerialNumber field points to.

How Windows identifies USB devices

Have you ever noticed that when you unplug a device from one USB port, and then plug it into a different USB port, you’ll sometimes see it pop up as a new device, and maybe even get a driver installed?

That can happen when a USB device doesn’t include the iSerialNumber field, and you plug it into a port which shows up as a different parent device, impacting the calculated unique id for that USB device. When a device doesn’t include an iSerialNumber, operating systems use various techniques to uniquely identify the device, as best as they can.

Although implementations vary, this process isn’t unique to Windows. macOS, Linux, and other operating systems have similar algorithms to try to smartly identify a device in the absence of a serial number. They also have similar limitations.

Here are some low-level details about how devices are uniquely identified in Windows. This is quoted from our MS Learn docs (links below):

… For BusQueryInstanceID, a bus driver should supply a string that contains the instance ID for the device. Setup and bus drivers use the instance ID, with other information, to differentiate between two identical devices on the computer. The instance ID is either unique across the whole computer or just unique on the device’s parent bus.

If an instance ID is only unique on the bus, the bus driver specifies that string for BusQueryInstanceID but also specifies a UniqueID value of FALSE in response to an IRP_MN_QUERY_CAPABILITIES request for the device. If UniqueID is FALSE, the PnP manager enhances the instance ID by adding information about the device’s parent and thus makes the ID unique on the computer. In this case the bus driver should not take extra steps to make its devices’ instance IDs globally unique; just return the appropriate capabilities information and the operating system takes care of it.

If a bus driver can supply a globally unique ID for each child device, such as a serial number, the bus driver specifies those strings for BusQueryInstanceID and specifies a UniqueID value of TRUE in response to an IRP_MN_QUERY_CAPABILITIES request for each device. …

So you can see in the above description, that if an iSerialNumber is not supplied, Windows will use the device’s parent information as part of the differentiator to uniquely identify the device. If a device is moved to a new parent (plugged into a different port, plugged into a USB hub, etc.), that id changes and the device appears as a new device. Any link to its past is effectively lost.

Just as a reminder, storage and some other device classes have additional mechanisms for unique identification (a storage device can, well, store a supplied id). We’re talking about MIDI devices here.

References

How to check your own devices

As a developer, or a curious customer, you can check connected devices to see what they report using a number of free tools.

Plug & Play utility

First is the command-line tool pnputil, which is included with Windows. If you are on recent Windows 11 releases, there’s a /properties flag which will display additional information about devices, including the full Id of the device.

To list all connected USB devices, and all their properties, here’s the command line

>pnputil /enum-devices /properties /connected /bus USB

Here’s some pnputil information from the Arturia Keystep 32 I have here.

DEVPKEY_Device_Capabilities [UINT32]:
        0x00000094 (148)
DEVPKEY_Device_InstanceId [String]:
        USB\VID_1C75&PID_0288\00000000001A
DEVPKEY_Device_BusReportedDeviceDesc [String]:
        Arturia KeyStep 32
DEVPKEY_NAME [String]:
        USB Composite Device

And here’s a Modal Skulpt Synth. I don’t have more than one Skulpt synth handy to test, but it seems likely that they’ve hard-coded the iSerialNumber to just “ffff”, which is not helpful.

DEVPKEY_Device_Capabilities [UINT32]:
        0x000000B4 (180)
DEVPKEY_Device_InstanceId [String]:
        USB\VID_04D8&PID_EEFE\ffff
DEVPKEY_Device_FriendlyName [String]:
        Skulpt Synth
DEVPKEY_NAME [String]:
        Skulpt Synth

In this, we can see the Device Capabilities flag shows that the device has declared CM_DEVCAP_REMOVABLE, CM_DEVCAP_SILENTINSTALL, CM_DEVCAP_SURPRISEREMOVALOK, and importantly, we’ve set CM_DEVCAP_UNIQUEID because an iSerialNumber was provided. The actual values can be found in cfgmgr32.h:

#define CM_DEVCAP_LOCKSUPPORTED     (0x00000001)
#define CM_DEVCAP_EJECTSUPPORTED    (0x00000002)
#define CM_DEVCAP_REMOVABLE         (0x00000004)
#define CM_DEVCAP_DOCKDEVICE        (0x00000008)
#define CM_DEVCAP_UNIQUEID          (0x00000010)
#define CM_DEVCAP_SILENTINSTALL     (0x00000020)
#define CM_DEVCAP_RAWDEVICEOK       (0x00000040)
#define CM_DEVCAP_SURPRISEREMOVALOK (0x00000080)
#define CM_DEVCAP_HARDWAREDISABLED  (0x00000100)
#define CM_DEVCAP_NONDYNAMIC        (0x00000200)

If a device is going to declare that it has a unique ID by providing data in the iSerialNumber string, it needs to provide an iSerialNumber that is unique amongst other instances of the same device. Again, I can’t verify that "ffff" is or is not the same across all the devices, but it looks pretty sus. Modal, if you’re reading this, and I got it wrong, please accept my apologies. You know I love your synths. 🙂

Quite a bit more information is reported, but I won’t paste it all in here. Try out pnputil, it comes with every recent version of Windows.

USB viewer

PnP Util provides a view of what Windows sees for a device. Another tool, USB Viewer, shows more detail about what the device reports, and how the descriptors are structured. It’s free with the Windows SDK, or available on GitHub. You can find information about it here.

If you don’t have the SDK, or don’t want to use the version on GitHub, or simply want something with more reporting, there’s an enhanced version of the USB Treeview app created by a third-party. This is not an official endorsement, but this is the version I personally use. It’s available here.

Here’s a segment of the USB Tree Viewer output showing the Modal Skulpt

Modal Skulpt USB Tree View

And here’s a device (MOTU Express 128 MIDI Interface) which does not implement iSerialNumber. Note that it has a 0x00 index for that field, as is proper.

MOTU Express 128 USB Tree View

Finally, here’s a good implementation of iSerialNumber, from the PreSonus ATOM.

PreSonus ATOM USB Tree View

You can use either of these tools to check to see what your device is reporting to Windows, and how Windows is interpreting what it sees.

Windows MIDI Services Features that work best with unique ids

Now we know how devices are identified, and how to verify that information, but how does this impact Windows MIDI Services?

The first defined and approved transport for MIDI 2.0 is USB. There’s a new device class specification, and Windows also has a new USB MIDI Class 2 driver to support it. There are also other transports in development, with Network MIDI 2.0 the most likely one to hit next. That transport also includes a way to uniquely identify devices, as does the MIDI 2.0 protocol itself. But those are in-protocol mechanisms which, although we will take advantage of them in Windows MIDI Services and use them in much the same way for MIDI, are not at the same level as Kernel-level PnP information from a driver.

The new Windows MIDI Services has a number of planned features which work best when we can uniquely identify a MIDI Device, no matter which USB port it’s connected on the PC. Not all of these features will be available on day 1, but we will be rolling them out over time as we’re sure the experience is what you want. Here are two of the more important ones:

Renaming MIDI Devices / UMP Endpoints

One of the most requested features for Windows MIDI Services is the ability to rename devices / UMP Endpoints. For this to be meaningful, we need to be able to uniquely identify the MIDI device you want to rename.

Renaming a device isn’t a ton of effort for a user, but it would be super confusing if they had two of the same synth, and one was set up with bass patches, and the other with pads, and Windows got confused about which one was which, and applied the wrong names, after they were unplugged and brought to a performance.

User-supplied metadata

We’re looking at ways to provide MIDI 2.0 profiles and properties for devices which don’t natively support those features. That would enable you to, for example, indicate that the synthesizer you have fits the organ profile and can be discovered as such, or that that Piano module you have can conform to the Piano profile.

There can be quite a bit of work involved in supplying this metadata, so we want to make it worth the customer’s time, and not have any confusion over which connected device the metadata applies to.

Some scenarios

There are some scenarios where calculated vs provided unique identifiers really come into play. We will do our best to optimize for a good user experience here, but valid iSerialNumber values will really help everyone.

MIDI Device connected to PC and never moved or unplugged

The generated identifiers persist across reboots, so there are no immediate issues here, until devices are moved around, put on a hub, etc. We don’t want customers to be afraid to unplug and rearrange their studio.

If the device implements iSerialNumber properly, then no problem. If it does not, we generate an identifier, which is likely to change especially if you plug it into a different port, or into a hub.

Setting up that live performance

This is a more common problem. Let’s say you have a couple controllers and synthesizers you bring to your performance. If you don’t plug those devices into the same ports they were in when you practiced at home, especially if you have multiples of any one device, there’s potential for the OS to get them mixed up. Without an iSerialNumber, there’s no guarantee that the generated identifier will persist or will be applied to the same device.

Many club performers get little to no time to set up their equipment. They need to rush up on stage while the last performer is wrapping up so there’s no gap between the previous performance and their performance. It’s not like the roadies get there the day before and get everything set up and configured properly in advance. They shouldn’t need to struggle with recreating their performance setup, in the dark, while everyone is staring at them. This is just stress a live performer doesn’t need when setting up. And as a device manufacturer, you want to encourage your customers to buy more than one of your MIDI device, right? 🙂

Some options for iSerialNumber

The USB iSerialNumber field is a pointer to a text record, just like iManufacturer and other USB strings. There are practical limits on how long the string can be. The entire Windows Device Instance Id, which includes other information in addition to the serial, needs to be MAX_DEVICE_ID_LEN characters or less — currently 200 characters per cfgmgr32.h. This means you should keep your number to something in the range of 32 alphanumeric digits. It can be larger (quite a bit, as long as you check that the Device Instance Id is still valid), but 32 digits should be plenty for this use.

#define MAX_DEVICE_ID_LEN     200
#define MAX_DEVNODE_ID_LEN    MAX_DEVICE_ID_LEN

MIDI 2.0 allows for ProductInstanceId values, the in-protocol iSerialNumber equivalent here, to be up to 255 characters in length. But the practical limit here is much lower if you want to support Windows devices. We recommend following the same rules for both iSerialNumber and the ProductInstanceId, and keeping them in sync when you provide both.

When I recently met with manufacturers, one revelation they had was that the iSerialNumber doesn’t need to match the serial number on the box; it just needs to be a unique id. Understandably, manufacturers were concerned that the serial number matching the box would add a costly step to the manufacturing process. When I pointed out that we just need something that will uniquely identify a device within the domain of devices in a customer’s setup, the problem became much simpler to solve.

There’s no prescription here, as each device manufacturer has their own unique circumstances. However, here are some ideas for how to generate this number.

Value Approach
USB controller-provided Id Some embedded USB / Serial converter ICs also include support for iSerialNumber, and a small amount of memory to persist that value in
Processor Unique Id Some processors have unique Ids which can be read by code on the device. Simply report this number.
MAC Address If the device has a network port, the MAC address (with special characters removed), or a hash of it, will work as a unique Id here
GUID/UUID There are established algorithms for generating GUID/UUID values. In some cases, this is part of the libraries included with microcontrollers. Strip out the special characters, and you end up with a 32 character unique Id that has a very low chance of colliding with any other in the world.
Random Number Random number generators are included with almost all microcontroller libraries. Generating something reasonable like a 10 digit number would be unique enough for use here.
User-supplied If your device has a user interface for entering data (a web server or touch-screen display, for examples), you may also consider allowing the customer to provide/change the serial number themselves

When we were discussing random number generators, one developer started going through how common collisions are with UUIDs. That’s mostly a non-issue here. The number doesn’t need to be unique in the world, just unique within the domain of the devices he user has connected to their computer. If you use a sufficiently large domain of numbers to draw from, you’ll be fine.

When creating the id, we recommend require removing any special characters, and leave it to just letters and numbers. Specifically, character values less than or equal to 0x20 (space), greater than 0x7F, or equal to 0x2C (comma) are not allowed unless you really want to bugcheck the PC. You can find more information in the IRP_MN_QUERY_ID kernel driver docs.

What happens when you can’t support a serial number?

There are cases when a device cannot support an iSerialNumber because it has no available persistent storage for this. That is far less common these days, especially with devices which can support MIDI 2.0, but it does happen. In addition, the body of devices already available without an iSerialNumber need to continue to function in Windows without a degraded experience.

Excellent user experience is top priority for Windows MIDI Services.

At a minimum, everything will continue using the algorithms the Windows USB stack uses today. In the tools, we may warn the user that the device does not support a serial number so they can consider how much time/effort they want to invest in customization. We’re optimizing for the user experience here, and investing time in changing names, adding metadata, etc. only to have it disassociated from the device, or applied to another instance of the device, would not be good there.

In addition, in the absence of iSerialNumber, we are working on identifying MIDI devices by other properties reported by the driver. For example, the device name as reported by the hardware (although device naming is an entirely different and convoluted discussion). This can work if you only own one of those devices, but does not work well when you have more than one of the devices connected at a time, or more than one of the device with other unique values (like patch sets on a synth or sound module, or firmware-stored CC mapping on controllers).

Our request for device manufacturers and makers

Including and reporting iSerialNumber on your USB devices will improve the user experience across all operating systems, not just Windows, so please considering this as part of your device firmware and/or provisioning.

Did I say please? PLEASE properly implement iSerialNumber. Your customers will love you for it, even if they don’t quite realize why 🙂

6 comments

Discussion is closed. Login to edit/delete existing comments.

  • Bug 0

    Manufacturers may start with 0000 as a SN for each product. The Serial# can still cause lots of conflicts. Perhaps define a SYSEX message for GET/SET_PERMANENT_SERIALNUMBER or GET/SET_PERMANENT_DEVICE_ID in the MID 2.0 SPEC that the device uses to store it in it’s NVRAM.

    • Pete BrownMicrosoft employee 0

      The serial number only needs to be unique for the specific type of device from that manufacturer. We differentiate first by VID/PID in the case of USB.

      I agree that being able to remotely set the serial or even the name would be helpful, using property exchange or a new stream message. Those are proposals I mentioned to the working group that I may make for a future revision.

      Pete

      • Bug 0

        It seems we are a step closer today with the updated UMP specification, but not fully there.

        7.1.5 Product Instance Id Notification Message: Devices should declare a Product Instance Id. Product Instance Id should be, where possible, the same as the Serial Number of the Device and should be a unique number per Manufacturer/Family/Model.

        Now
        [1] its a “should”, Not all (probably very few) manufacturer may be capable/willing to do this at the manufacturing floor.
        [2] There is no “Set Product Instance ID” function either by SYSEX or UMP to set this unique ID (in case of two identical devices, the same VID/PID),
        [3] An easy option is to either add one or more DIP switches on the device or set it via an GUI. I have some some (old) MIDI 1.0 1980’s synths where you could set this device ID via the 7 segment display or a 40 digit LCD. Remember Universal MIDI: F0 7F [,device id]
        [4] It only needs to be done once by the user for the lifetime of the device as long a the device keeps the ID in NVRAM and thus can be a very simple MIDI utility.
        [5] In case OS drivers add this call to obtain the instance ID and add it into the port data, then OS applications won’t need to add this call into the (re-)enumeration of the ports, this specially handy to easily identify which device was added/removed in case of identical devices.

        • Pete BrownMicrosoft employee 0

          Yep. ProductInstanceId was added for these reasons, but especially for non-USB transports. It’s useful for Network, for example. USB has additional layers of consideration.

          The reason it’s optional in UMP still (although I have argued for it to be required for the upcoming Network protocol) is because not all devices can supply it. Personally, I think any device which can handle MIDI 2.0 can also supply a unique Id, but I am not a device manufacturer.

          Note that in Windows, every USB device ends up with a unique Id anyway. It’s about whether or not that Id persists when the device is moved between USB ports/hubs, or if it survives unplug/replug.

          FWIW, I also have a (shelved for the moment) proposal to set the instance ID, name, and possibly some other properties in UMP or Property Exchange (some is there already). This would allow the OS to provide the UI for super small utility devices which don’t need the overhead of creating a web server, or have space for an on-device UI.

          All in good time, though. MIDI 1.0 has already lasted for 40 years, and MIDI 2.0 is even better positioned for growth.

          Pete

          • Bug 0

            [1] I think any device which can handle MIDI 2.0 can also supply a unique Id,
            Many (specially small) devices hard code their USB descriptors and flash the device with that image and the serial-number field may not be easily (if at all) modifiable from code.
            [2] USB device ends up with a unique Id anyway.
            As long as the endpoint’s (ports) ID does not change when another device gets plugged in/out, then that is perfect ,if not, then that causes issues (as in MIDI 1.0 UWP). There is a big difference between the port (ID) information and the device USB descriptor /ProductInstanceID

          • Pete BrownMicrosoft employee 0

            Bug

            (replying to June 16 comment, but WP won’t let me nest that deep)

            [1] Fair enough. I know some companies are going to change this practice, though, in as many devices as they can.

            [2] MIDI 2.0 has no concept of ports. In MIDI 1.0, a port is just a remapped cable number on an endpoint. That abstraction goes away with MIDI 2.0 where cable numbers are conceptually the same as the group for this use. Still need a persistent unique id for the device which is connected over USB.

            We will map groups->ports for the older WinMM and WinRT APIs, but anything which talks to the native MIDI 2.0 API will deal directly with UMP Endpoints.

            Note that the unique Id that the USB device ends up with is not persistent if there’s no iSerialNumber. It changes based on where it’s plugged in, whether or not there’s a USB hub, etc. We do our best to keep it the same, but it gets especially messy if you have more than one of the same device.

            Pete

Feedback usabilla icon