usb: umass: add SCSIEJECT quirk and fix RTW8821CU_CD (USB mode switch)

Several Realtek (and lots other) USB dongles present themselves as
CDROM device first.  Upon eject they do a mode switch and suddenly
are a different kind of device (sometimes even with different IDs),
e.g., a wireless dongle.

In order to avoid the CDROM stage and rather than adding the quirk
handling to more drivers, add support to umass and if enabled
automatically eject the "CDROM" to make it the real device.

Longer-term some other drivers could stop using their hand-rolled
support for this.  It is unclear as-to how much we need the list of
(eject) quirks from u3g here, or if these are very specific to that
kind of devices.

Sponsored by:	The FreeBSD Foundation
Fixes:		b3b6a959c8, 9c0cce3283
Reviewed by:	imp
Differential Revision: https://reviews.freebsd.org/D54901
This commit is contained in:
Bjoern A. Zeeb
2026-01-26 13:19:37 +00:00
parent 02fd9fa295
commit b4daeded66
2 changed files with 57 additions and 2 deletions
+1 -1
View File
@@ -532,7 +532,7 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = {
UQ_MSC_NO_INQUIRY, UQ_CFG_INDEX_0),
USB_QUIRK(SMART2, G2MEMKEY, UQ_MSC_NO_INQUIRY),
USB_QUIRK_REV(RALINK, RT_STOR, 0x0001, 0x0001, UQ_MSC_IGNORE),
USB_QUIRK(REALTEK, RTW8821CU_CD, UQ_MSC_IGNORE),
USB_QUIRK(REALTEK, RTW8821CU_CD, UQ_MSC_EJECT_SCSIEJECT),
/* Non-standard USB MIDI devices */
USB_QUIRK(ROLAND, UM1, UQ_AU_VENDOR_CLASS),
USB_QUIRK(ROLAND, SC8850, UQ_AU_VENDOR_CLASS),
+56 -1
View File
@@ -115,6 +115,7 @@
#include <sys/sx.h>
#include <sys/unistd.h>
#include <sys/callout.h>
#include <sys/eventhandler.h>
#include <sys/malloc.h>
#include <sys/priv.h>
@@ -124,6 +125,7 @@
#include "usbdevs.h"
#include <dev/usb/quirk/usb_quirk.h>
#include <dev/usb/usb_msctest.h>
#include <cam/cam.h>
#include <cam/cam_ccb.h>
@@ -705,6 +707,59 @@ static const uint8_t fake_inq_data[SHORT_INQUIRY_LENGTH] = {
#define UFI_COMMAND_LENGTH 12 /* UFI commands are always 12 bytes */
#define ATAPI_COMMAND_LENGTH 12 /* ATAPI commands are always 12 bytes */
static void
umass_autoinst_eject_quirks(void *arg __unused, struct usb_device *udev,
struct usb_attach_arg *uaa)
{
struct usb_interface *iface;
struct usb_interface_descriptor *id;
if (uaa->dev_state != UAA_DEV_READY)
return;
iface = usbd_get_iface(udev, 0);
if (iface == NULL)
return;
id = iface->idesc;
if (id == NULL || id->bInterfaceClass != UICLASS_MASS)
return;
if (usb_test_quirk(uaa, UQ_MSC_EJECT_SCSIEJECT)) {
int error;
error = usb_msc_eject(uaa->device, 0, MSC_EJECT_STOPUNIT);
if (error == 0)
uaa->dev_state = UAA_DEV_EJECTING;
else
printf("UMASS failed to eject by SCSI eject STOPUNIT "
"command based on quirk: %d\n", error);
}
}
static eventhandler_tag umass_drv_evh_tag;
static int
umass_driver_evh(struct module *mod, int what, void *arg)
{
switch (what) {
case MOD_LOAD:
umass_drv_evh_tag = EVENTHANDLER_REGISTER(usb_dev_configured,
umass_autoinst_eject_quirks, NULL, EVENTHANDLER_PRI_ANY);
break;
case MOD_UNLOAD:
if (umass_drv_evh_tag != NULL)
EVENTHANDLER_DEREGISTER(usb_dev_configured,
umass_drv_evh_tag);
break;
default:
return (EOPNOTSUPP);
}
return (0);
}
static device_method_t umass_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, umass_probe),
@@ -725,7 +780,7 @@ static const STRUCT_USB_HOST_ID __used umass_devs[] = {
{USB_IFACE_CLASS(UICLASS_MASS),},
};
DRIVER_MODULE(umass, uhub, umass_driver, NULL, NULL);
DRIVER_MODULE(umass, uhub, umass_driver, umass_driver_evh, NULL);
MODULE_DEPEND(umass, usb, 1, 1, 1);
MODULE_DEPEND(umass, cam, 1, 1, 1);
MODULE_VERSION(umass, 1);