From a1030e92c1507eb4a3c15d0a7d62987f671b609c Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sun, 13 Aug 2006 12:15:29 +0100 Subject: [PATCH] [PATCH] zd1211rw: Convert installer CDROM device into WLAN device Some devices identify themselves as a virtual USB CDROM drive. The virtual CD includes the windows driver. We aren't interested in this, so we eject the virtual CDROM and then the real wireless device appears. Patch fixed over the earlier version to not leak cmd, thanks to Michael Buesch for spotting that. Signed-off-by: Daniel Drake Signed-off-by: John W. Linville --- drivers/net/wireless/zd1211rw/zd_usb.c | 59 ++++++++++++++++++++++++++ drivers/net/wireless/zd1211rw/zd_usb.h | 1 + 2 files changed, 60 insertions(+) diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index 347ebc17bd74..83e23c320ece 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -48,6 +48,8 @@ static struct usb_device_id usb_ids[] = { { USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x079b, 0x0062), .driver_info = DEVICE_ZD1211B }, + /* "Driverless" devices that need ejecting */ + { USB_DEVICE(0x0ace, 0x2011), .driver_info = DEVICE_INSTALLER }, {} }; @@ -951,6 +953,55 @@ static void print_id(struct usb_device *udev) #define print_id(udev) do { } while (0) #endif +static int eject_installer(struct usb_interface *intf) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_host_interface *iface_desc = &intf->altsetting[0]; + struct usb_endpoint_descriptor *endpoint; + unsigned char *cmd; + u8 bulk_out_ep; + int r; + + /* Find bulk out endpoint */ + endpoint = &iface_desc->endpoint[1].desc; + if ((endpoint->bEndpointAddress & USB_TYPE_MASK) == USB_DIR_OUT && + (endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK) { + bulk_out_ep = endpoint->bEndpointAddress; + } else { + dev_err(&udev->dev, + "zd1211rw: Could not find bulk out endpoint\n"); + return -ENODEV; + } + + cmd = kzalloc(31, GFP_KERNEL); + if (cmd == NULL) + return -ENODEV; + + /* USB bulk command block */ + cmd[0] = 0x55; /* bulk command signature */ + cmd[1] = 0x53; /* bulk command signature */ + cmd[2] = 0x42; /* bulk command signature */ + cmd[3] = 0x43; /* bulk command signature */ + cmd[14] = 6; /* command length */ + + cmd[15] = 0x1b; /* SCSI command: START STOP UNIT */ + cmd[19] = 0x2; /* eject disc */ + + dev_info(&udev->dev, "Ejecting virtual installer media...\n"); + r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, bulk_out_ep), + cmd, 31, NULL, 2000); + kfree(cmd); + if (r) + return r; + + /* At this point, the device disconnects and reconnects with the real + * ID numbers. */ + + usb_set_intfdata(intf, NULL); + return 0; +} + static int probe(struct usb_interface *intf, const struct usb_device_id *id) { int r; @@ -959,6 +1010,9 @@ static int probe(struct usb_interface *intf, const struct usb_device_id *id) print_id(udev); + if (id->driver_info & DEVICE_INSTALLER) + return eject_installer(intf); + switch (udev->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: @@ -1024,6 +1078,11 @@ static void disconnect(struct usb_interface *intf) struct zd_mac *mac = zd_netdev_mac(netdev); struct zd_usb *usb = &mac->chip.usb; + /* Either something really bad happened, or we're just dealing with + * a DEVICE_INSTALLER. */ + if (netdev == NULL) + return; + dev_dbg_f(zd_usb_dev(usb), "\n"); zd_netdev_disconnect(netdev); diff --git a/drivers/net/wireless/zd1211rw/zd_usb.h b/drivers/net/wireless/zd1211rw/zd_usb.h index d6420283bd5a..92746f76239a 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.h +++ b/drivers/net/wireless/zd1211rw/zd_usb.h @@ -30,6 +30,7 @@ enum devicetype { DEVICE_ZD1211 = 0, DEVICE_ZD1211B = 1, + DEVICE_INSTALLER = 2, }; enum endpoints { -- 2.20.1