Files
dymo-twin-turbo/README.md
2025-08-05 22:43:48 -05:00

62 lines
3.9 KiB
Markdown

It appears the Dymo 400 Twin Turbo does not report the correct device serial number. This prevents us from using more than one of these devices on a system because the device URI is not properly formed.
This will possibly need a USB device quirk added to the Linux kernel.
An alternative is to possibly implement a fix in dymo-cups-drivers. However, I think the problem occurs higher up the software stack.
How to identify the problem
1. Load wireshark and start recording packets from the master USB bus (bus:0) just prior to connecting the printer
2. Connect a Dymo 400 Twin Turbo printer to the system with USB
3. Stop logging USB packets after a minute
4. Review packet data and notice a packet received from the device has a malformed string
- The packet will be a GET DESCRIPTOR Response STRING with bLength: 30, bDescriptorType: 0x03 (String), and malformed bString: [malformed]
5. In terminal, `lsusb -v -d 0922:0018`
6. Notice the gibberish value in the iSerial field.
7. When setting up the printer, notice the device URI is `usb://DYMO/LabelWriter%20Twin%20Turbo?serial=??????????????`
The problem occurs because the default format for USB device descriptor STRINGs is uint-16-le. However, the Dymo-TT includes an extra byte at the start of the data returned from the device.
For example, the serial bString from a Dymo 450 is received as `b'\x31\x00\x37\x00\x31\x00\x30\x00\x30\x00\x36\x00\x31\x00\x36\x00\x31\x00\x38\x00\x34\x00\x32\x00\x33\x00\x30\x00'`.
However, an example from the Dymo-TT is `b'\x30\x30\x00\x36\x00\x31\x00\x30\x00\x33\x00\x30\x00\x31\x00\x30\x00\x31\x00\x36\x00\x31\x00\x35\x00\x37\x00\x35'`.
Decoding the Dymo 450 string using uint-16-le yields "17100616184230", whereas the Dymo-TT string yields `b''`
```python
# from pyUSB
import usb.core
import usb.util
vid = 0x0922 # Dymo VID
pid450 = 0x0020 # Dymo 450 PID
pidtt = 0x0018 # Dymo Twin Turbo PID
dymo450 = usb.core.find(idVendor=vid, idProduct=pid450)
dymott = usb.core.find(idVendor=vid, idProduct=pidtt)
print(f"Dymo 450 Serial: {dymo450.serial_number}") # yields 17100616184230
print(f"Dymo-Twin-Turbo Serial: {dymott.serial_number}") # yields gibberish
print(f"Dymo 450 Raw Serial value: {usb.control.get_descriptor(dymo450,254,0x03,dymo450.iSerialNumber,dymo450.langids[0])[2:30].tobytes().decode('utf-16-le')}") # returns 17100616184230
print(f"Dymo-TT Raw Serial value: {usb.control.get_descriptor(dymott,254,0x03,dymott.iSerialNumber,dymott.langids[0])[2:30].tobytes().decode('utf-16-le')}") # returns gibberish
print(f"Dymo 450 string result as bytes: {usb.control.get_descriptor(dymo450,254,0x03,dymo450.iSerialNumber,dymo450.langids[0])[2:30].tobytes().decode('utf-16-le').encode('utf-8')}") # returns b'17100616184230'
print(f"Dymo 450 string result as bytes: {usb.control.get_descriptor(dymott,254,0x03,dymott.iSerialNumber,dymott.langids[0])[2:30].tobytes().decode('utf-16-le').encode('utf-8')}") # returns b'\xe3\x80\xb0\xe3\x98\x80\xe3\x84\x80\xe3\x80\x80\xe3\x8c\x80\xe3\x80\x80\xe3\x84\x80\xe3\x80\x80\xe3\x84\x80\xe3\x98\x80\xe3\x84\x80\xe3\x94\x80\xe3\x9c\x80\xe3\x94\x80'
```
The Twin-Turbo serial is supposed to be 006103010161575. Due to the extra 0 byte `b'\x30'`, the rest of the return data becomes shifted and is interpreted like
`b'\x30\x30\x00\x36\x00\x31\x00\x30\x00\x33\x00\x30\x00\x31\x00\x30\x00\x31\x00\x36\x00\x31\x00\x35\x00\x37\x00\x35'.decode('utf-16-le')` or literally unicode values `\x3500\x3700\x3500\x3100\x3600\x3100\x3000\x3100\x3000\x3300\x3000\x3100\x3600\x3030`
Note: Linux Mint 22.1 <- Ubuntu 24.04 Noble <- Debian Trixie/sid <- Kernel 6.8.0-71-generic; pyusb version 1.3.1; python version 3.12.3
References
https://www.beyondlogic.org/usbnutshell/usb5.shtml#StringDescriptors
https://github.com/pyusb/pyusb/issues/154
https://github.com/pyusb/pyusb/blob/master/usb/util.py#L320
https://github.com/torvalds/linux/blob/master/drivers/usb/core/quirks.c
https://docs.kernel.org/admin-guide/reporting-issues.html
https://kernel.org/category/faq.html