ath9k_htc: Handle storage devices
Some AR7010 based devices are recognized as storage media. Sending a CD-EJECT command to the device will 'convert' it into a WLAN device. Do this within the driver itself, removing the dependancy on an external program (usb_modeswitch). Signed-off-by: Sujith Manoharan <Sujith.Manoharan@atheros.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
85c9205c79
commit
36bcce4306
2 changed files with 69 additions and 6 deletions
|
@ -52,6 +52,9 @@ static struct usb_device_id ath9k_hif_usb_ids[] = {
|
||||||
{ USB_DEVICE(0x083A, 0xA704),
|
{ USB_DEVICE(0x083A, 0xA704),
|
||||||
.driver_info = AR9280_USB }, /* SMC Networks */
|
.driver_info = AR9280_USB }, /* SMC Networks */
|
||||||
|
|
||||||
|
{ USB_DEVICE(0x0cf3, 0x20ff),
|
||||||
|
.driver_info = STORAGE_DEVICE },
|
||||||
|
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -934,6 +937,61 @@ static void ath9k_hif_usb_dev_deinit(struct hif_device_usb *hif_dev)
|
||||||
release_firmware(hif_dev->firmware);
|
release_firmware(hif_dev->firmware);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An exact copy of the function from zd1211rw.
|
||||||
|
*/
|
||||||
|
static int send_eject_command(struct usb_interface *interface)
|
||||||
|
{
|
||||||
|
struct usb_device *udev = interface_to_usbdev(interface);
|
||||||
|
struct usb_host_interface *iface_desc = &interface->altsetting[0];
|
||||||
|
struct usb_endpoint_descriptor *endpoint;
|
||||||
|
unsigned char *cmd;
|
||||||
|
u8 bulk_out_ep;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
/* Find bulk out endpoint */
|
||||||
|
for (r = 1; r >= 0; r--) {
|
||||||
|
endpoint = &iface_desc->endpoint[r].desc;
|
||||||
|
if (usb_endpoint_dir_out(endpoint) &&
|
||||||
|
usb_endpoint_xfer_bulk(endpoint)) {
|
||||||
|
bulk_out_ep = endpoint->bEndpointAddress;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (r == -1) {
|
||||||
|
dev_err(&udev->dev,
|
||||||
|
"ath9k_htc: 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 storage device...\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(interface, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int ath9k_hif_usb_probe(struct usb_interface *interface,
|
static int ath9k_hif_usb_probe(struct usb_interface *interface,
|
||||||
const struct usb_device_id *id)
|
const struct usb_device_id *id)
|
||||||
{
|
{
|
||||||
|
@ -941,6 +999,9 @@ static int ath9k_hif_usb_probe(struct usb_interface *interface,
|
||||||
struct hif_device_usb *hif_dev;
|
struct hif_device_usb *hif_dev;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
if (id->driver_info == STORAGE_DEVICE)
|
||||||
|
return send_eject_command(interface);
|
||||||
|
|
||||||
hif_dev = kzalloc(sizeof(struct hif_device_usb), GFP_KERNEL);
|
hif_dev = kzalloc(sizeof(struct hif_device_usb), GFP_KERNEL);
|
||||||
if (!hif_dev) {
|
if (!hif_dev) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
|
@ -1027,12 +1088,13 @@ static void ath9k_hif_usb_disconnect(struct usb_interface *interface)
|
||||||
struct hif_device_usb *hif_dev = usb_get_intfdata(interface);
|
struct hif_device_usb *hif_dev = usb_get_intfdata(interface);
|
||||||
bool unplugged = (udev->state == USB_STATE_NOTATTACHED) ? true : false;
|
bool unplugged = (udev->state == USB_STATE_NOTATTACHED) ? true : false;
|
||||||
|
|
||||||
if (hif_dev) {
|
if (!hif_dev)
|
||||||
ath9k_htc_hw_deinit(hif_dev->htc_handle, unplugged);
|
return;
|
||||||
ath9k_htc_hw_free(hif_dev->htc_handle);
|
|
||||||
ath9k_hif_usb_dev_deinit(hif_dev);
|
ath9k_htc_hw_deinit(hif_dev->htc_handle, unplugged);
|
||||||
usb_set_intfdata(interface, NULL);
|
ath9k_htc_hw_free(hif_dev->htc_handle);
|
||||||
}
|
ath9k_hif_usb_dev_deinit(hif_dev);
|
||||||
|
usb_set_intfdata(interface, NULL);
|
||||||
|
|
||||||
if (!unplugged && (hif_dev->flags & HIF_USB_START))
|
if (!unplugged && (hif_dev->flags & HIF_USB_START))
|
||||||
ath9k_hif_usb_reboot(udev);
|
ath9k_hif_usb_reboot(udev);
|
||||||
|
|
|
@ -878,6 +878,7 @@
|
||||||
enum ath_usb_dev {
|
enum ath_usb_dev {
|
||||||
AR9280_USB = 1, /* AR7010 + AR9280, UB94 */
|
AR9280_USB = 1, /* AR7010 + AR9280, UB94 */
|
||||||
AR9287_USB = 2, /* AR7010 + AR9287, UB95 */
|
AR9287_USB = 2, /* AR7010 + AR9287, UB95 */
|
||||||
|
STORAGE_DEVICE = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define AR_DEVID_7010(_ah) \
|
#define AR_DEVID_7010(_ah) \
|
||||||
|
|
Loading…
Add table
Reference in a new issue