mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-17 18:36:00 +00:00
Merge branch 'usb-3.3-rc4' into usb-next
This is to pull in the xhci changes and the other fixes and device id updates that were done in Linus's tree. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
commit
c69263c66e
@ -182,3 +182,14 @@ Description:
|
|||||||
USB2 hardware LPM is enabled for the device. Developer can
|
USB2 hardware LPM is enabled for the device. Developer can
|
||||||
write y/Y/1 or n/N/0 to the file to enable/disable the
|
write y/Y/1 or n/N/0 to the file to enable/disable the
|
||||||
feature.
|
feature.
|
||||||
|
|
||||||
|
What: /sys/bus/usb/devices/.../removable
|
||||||
|
Date: February 2012
|
||||||
|
Contact: Matthew Garrett <mjg@redhat.com>
|
||||||
|
Description:
|
||||||
|
Some information about whether a given USB device is
|
||||||
|
physically fixed to the platform can be inferred from a
|
||||||
|
combination of hub decriptor bits and platform-specific data
|
||||||
|
such as ACPI. This file will read either "removable" or
|
||||||
|
"fixed" if the information is available, and "unknown"
|
||||||
|
otherwise.
|
@ -1295,6 +1295,7 @@ EXPORT_SYMBOL(int_to_scsilun);
|
|||||||
* LUNs even if it's older than SCSI-3.
|
* LUNs even if it's older than SCSI-3.
|
||||||
* If BLIST_NOREPORTLUN is set, return 1 always.
|
* If BLIST_NOREPORTLUN is set, return 1 always.
|
||||||
* If BLIST_NOLUN is set, return 0 always.
|
* If BLIST_NOLUN is set, return 0 always.
|
||||||
|
* If starget->no_report_luns is set, return 1 always.
|
||||||
*
|
*
|
||||||
* Return:
|
* Return:
|
||||||
* 0: scan completed (or no memory, so further scanning is futile)
|
* 0: scan completed (or no memory, so further scanning is futile)
|
||||||
@ -1321,6 +1322,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags,
|
|||||||
* Only support SCSI-3 and up devices if BLIST_NOREPORTLUN is not set.
|
* Only support SCSI-3 and up devices if BLIST_NOREPORTLUN is not set.
|
||||||
* Also allow SCSI-2 if BLIST_REPORTLUN2 is set and host adapter does
|
* Also allow SCSI-2 if BLIST_REPORTLUN2 is set and host adapter does
|
||||||
* support more than 8 LUNs.
|
* support more than 8 LUNs.
|
||||||
|
* Don't attempt if the target doesn't support REPORT LUNS.
|
||||||
*/
|
*/
|
||||||
if (bflags & BLIST_NOREPORTLUN)
|
if (bflags & BLIST_NOREPORTLUN)
|
||||||
return 1;
|
return 1;
|
||||||
@ -1332,6 +1334,8 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags,
|
|||||||
return 1;
|
return 1;
|
||||||
if (bflags & BLIST_NOLUN)
|
if (bflags & BLIST_NOLUN)
|
||||||
return 0;
|
return 0;
|
||||||
|
if (starget->no_report_luns)
|
||||||
|
return 1;
|
||||||
|
|
||||||
if (!(sdev = scsi_device_lookup_by_target(starget, 0))) {
|
if (!(sdev = scsi_device_lookup_by_target(starget, 0))) {
|
||||||
sdev = scsi_alloc_sdev(starget, 0, NULL);
|
sdev = scsi_alloc_sdev(starget, 0, NULL);
|
||||||
|
@ -2349,7 +2349,7 @@ static int sd_try_extended_inquiry(struct scsi_device *sdp)
|
|||||||
* some USB ones crash on receiving them, and the pages
|
* some USB ones crash on receiving them, and the pages
|
||||||
* we currently ask for are for SPC-3 and beyond
|
* we currently ask for are for SPC-3 and beyond
|
||||||
*/
|
*/
|
||||||
if (sdp->scsi_level > SCSI_SPC_2)
|
if (sdp->scsi_level > SCSI_SPC_2 && !sdp->skip_vpd_pages)
|
||||||
return 1;
|
return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,7 @@ config USB_ARCH_HAS_EHCI
|
|||||||
default y if MICROBLAZE
|
default y if MICROBLAZE
|
||||||
default y if SPARC_LEON
|
default y if SPARC_LEON
|
||||||
default y if ARCH_MMP
|
default y if ARCH_MMP
|
||||||
|
default y if MACH_LOONGSON1
|
||||||
default PCI
|
default PCI
|
||||||
|
|
||||||
# some non-PCI HCDs implement xHCI
|
# some non-PCI HCDs implement xHCI
|
||||||
|
@ -31,6 +31,8 @@
|
|||||||
#define DRIVER_AUTHOR "Oliver Neukum"
|
#define DRIVER_AUTHOR "Oliver Neukum"
|
||||||
#define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management"
|
#define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management"
|
||||||
|
|
||||||
|
#define HUAWEI_VENDOR_ID 0x12D1
|
||||||
|
|
||||||
static const struct usb_device_id wdm_ids[] = {
|
static const struct usb_device_id wdm_ids[] = {
|
||||||
{
|
{
|
||||||
.match_flags = USB_DEVICE_ID_MATCH_INT_CLASS |
|
.match_flags = USB_DEVICE_ID_MATCH_INT_CLASS |
|
||||||
@ -38,6 +40,20 @@ static const struct usb_device_id wdm_ids[] = {
|
|||||||
.bInterfaceClass = USB_CLASS_COMM,
|
.bInterfaceClass = USB_CLASS_COMM,
|
||||||
.bInterfaceSubClass = USB_CDC_SUBCLASS_DMM
|
.bInterfaceSubClass = USB_CDC_SUBCLASS_DMM
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Huawei E392, E398 and possibly other Qualcomm based modems
|
||||||
|
* embed the Qualcomm QMI protocol inside CDC on CDC ECM like
|
||||||
|
* control interfaces. Userspace access to this is required
|
||||||
|
* to configure the accompanying data interface
|
||||||
|
*/
|
||||||
|
.match_flags = USB_DEVICE_ID_MATCH_VENDOR |
|
||||||
|
USB_DEVICE_ID_MATCH_INT_INFO,
|
||||||
|
.idVendor = HUAWEI_VENDOR_ID,
|
||||||
|
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
|
||||||
|
.bInterfaceSubClass = 1,
|
||||||
|
.bInterfaceProtocol = 9, /* NOTE: CDC ECM control interface! */
|
||||||
|
},
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -54,6 +70,7 @@ MODULE_DEVICE_TABLE (usb, wdm_ids);
|
|||||||
#define WDM_POLL_RUNNING 6
|
#define WDM_POLL_RUNNING 6
|
||||||
#define WDM_RESPONDING 7
|
#define WDM_RESPONDING 7
|
||||||
#define WDM_SUSPENDING 8
|
#define WDM_SUSPENDING 8
|
||||||
|
#define WDM_RESETTING 9
|
||||||
|
|
||||||
#define WDM_MAX 16
|
#define WDM_MAX 16
|
||||||
|
|
||||||
@ -82,7 +99,6 @@ struct wdm_device {
|
|||||||
u16 bufsize;
|
u16 bufsize;
|
||||||
u16 wMaxCommand;
|
u16 wMaxCommand;
|
||||||
u16 wMaxPacketSize;
|
u16 wMaxPacketSize;
|
||||||
u16 bMaxPacketSize0;
|
|
||||||
__le16 inum;
|
__le16 inum;
|
||||||
int reslength;
|
int reslength;
|
||||||
int length;
|
int length;
|
||||||
@ -162,11 +178,9 @@ static void wdm_int_callback(struct urb *urb)
|
|||||||
int rv = 0;
|
int rv = 0;
|
||||||
int status = urb->status;
|
int status = urb->status;
|
||||||
struct wdm_device *desc;
|
struct wdm_device *desc;
|
||||||
struct usb_ctrlrequest *req;
|
|
||||||
struct usb_cdc_notification *dr;
|
struct usb_cdc_notification *dr;
|
||||||
|
|
||||||
desc = urb->context;
|
desc = urb->context;
|
||||||
req = desc->irq;
|
|
||||||
dr = (struct usb_cdc_notification *)desc->sbuf;
|
dr = (struct usb_cdc_notification *)desc->sbuf;
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
@ -213,24 +227,6 @@ static void wdm_int_callback(struct urb *urb)
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
req->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
|
|
||||||
req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
|
|
||||||
req->wValue = 0;
|
|
||||||
req->wIndex = desc->inum;
|
|
||||||
req->wLength = cpu_to_le16(desc->wMaxCommand);
|
|
||||||
|
|
||||||
usb_fill_control_urb(
|
|
||||||
desc->response,
|
|
||||||
interface_to_usbdev(desc->intf),
|
|
||||||
/* using common endpoint 0 */
|
|
||||||
usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0),
|
|
||||||
(unsigned char *)req,
|
|
||||||
desc->inbuf,
|
|
||||||
desc->wMaxCommand,
|
|
||||||
wdm_in_callback,
|
|
||||||
desc
|
|
||||||
);
|
|
||||||
desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
|
||||||
spin_lock(&desc->iuspin);
|
spin_lock(&desc->iuspin);
|
||||||
clear_bit(WDM_READ, &desc->flags);
|
clear_bit(WDM_READ, &desc->flags);
|
||||||
set_bit(WDM_RESPONDING, &desc->flags);
|
set_bit(WDM_RESPONDING, &desc->flags);
|
||||||
@ -279,14 +275,8 @@ static void free_urbs(struct wdm_device *desc)
|
|||||||
|
|
||||||
static void cleanup(struct wdm_device *desc)
|
static void cleanup(struct wdm_device *desc)
|
||||||
{
|
{
|
||||||
usb_free_coherent(interface_to_usbdev(desc->intf),
|
kfree(desc->sbuf);
|
||||||
desc->wMaxPacketSize,
|
kfree(desc->inbuf);
|
||||||
desc->sbuf,
|
|
||||||
desc->validity->transfer_dma);
|
|
||||||
usb_free_coherent(interface_to_usbdev(desc->intf),
|
|
||||||
desc->bMaxPacketSize0,
|
|
||||||
desc->inbuf,
|
|
||||||
desc->response->transfer_dma);
|
|
||||||
kfree(desc->orq);
|
kfree(desc->orq);
|
||||||
kfree(desc->irq);
|
kfree(desc->irq);
|
||||||
kfree(desc->ubuf);
|
kfree(desc->ubuf);
|
||||||
@ -351,6 +341,10 @@ static ssize_t wdm_write
|
|||||||
else
|
else
|
||||||
if (test_bit(WDM_IN_USE, &desc->flags))
|
if (test_bit(WDM_IN_USE, &desc->flags))
|
||||||
r = -EAGAIN;
|
r = -EAGAIN;
|
||||||
|
|
||||||
|
if (test_bit(WDM_RESETTING, &desc->flags))
|
||||||
|
r = -EIO;
|
||||||
|
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
goto out;
|
goto out;
|
||||||
@ -430,6 +424,10 @@ retry:
|
|||||||
rv = -ENODEV;
|
rv = -ENODEV;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
if (test_bit(WDM_RESETTING, &desc->flags)) {
|
||||||
|
rv = -EIO;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
usb_mark_last_busy(interface_to_usbdev(desc->intf));
|
usb_mark_last_busy(interface_to_usbdev(desc->intf));
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
rv = -ERESTARTSYS;
|
rv = -ERESTARTSYS;
|
||||||
@ -631,7 +629,6 @@ static void wdm_rxwork(struct work_struct *work)
|
|||||||
static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||||
{
|
{
|
||||||
int rv = -EINVAL;
|
int rv = -EINVAL;
|
||||||
struct usb_device *udev = interface_to_usbdev(intf);
|
|
||||||
struct wdm_device *desc;
|
struct wdm_device *desc;
|
||||||
struct usb_host_interface *iface;
|
struct usb_host_interface *iface;
|
||||||
struct usb_endpoint_descriptor *ep;
|
struct usb_endpoint_descriptor *ep;
|
||||||
@ -692,7 +689,6 @@ next_desc:
|
|||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
desc->wMaxPacketSize = usb_endpoint_maxp(ep);
|
desc->wMaxPacketSize = usb_endpoint_maxp(ep);
|
||||||
desc->bMaxPacketSize0 = udev->descriptor.bMaxPacketSize0;
|
|
||||||
|
|
||||||
desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
|
desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
|
||||||
if (!desc->orq)
|
if (!desc->orq)
|
||||||
@ -717,19 +713,13 @@ next_desc:
|
|||||||
if (!desc->ubuf)
|
if (!desc->ubuf)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
desc->sbuf = usb_alloc_coherent(interface_to_usbdev(intf),
|
desc->sbuf = kmalloc(desc->wMaxPacketSize, GFP_KERNEL);
|
||||||
desc->wMaxPacketSize,
|
|
||||||
GFP_KERNEL,
|
|
||||||
&desc->validity->transfer_dma);
|
|
||||||
if (!desc->sbuf)
|
if (!desc->sbuf)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
desc->inbuf = usb_alloc_coherent(interface_to_usbdev(intf),
|
desc->inbuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);
|
||||||
desc->wMaxCommand,
|
|
||||||
GFP_KERNEL,
|
|
||||||
&desc->response->transfer_dma);
|
|
||||||
if (!desc->inbuf)
|
if (!desc->inbuf)
|
||||||
goto err2;
|
goto err;
|
||||||
|
|
||||||
usb_fill_int_urb(
|
usb_fill_int_urb(
|
||||||
desc->validity,
|
desc->validity,
|
||||||
@ -741,30 +731,39 @@ next_desc:
|
|||||||
desc,
|
desc,
|
||||||
ep->bInterval
|
ep->bInterval
|
||||||
);
|
);
|
||||||
desc->validity->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
|
||||||
|
desc->irq->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
|
||||||
|
desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
|
||||||
|
desc->irq->wValue = 0;
|
||||||
|
desc->irq->wIndex = desc->inum;
|
||||||
|
desc->irq->wLength = cpu_to_le16(desc->wMaxCommand);
|
||||||
|
|
||||||
|
usb_fill_control_urb(
|
||||||
|
desc->response,
|
||||||
|
interface_to_usbdev(intf),
|
||||||
|
/* using common endpoint 0 */
|
||||||
|
usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0),
|
||||||
|
(unsigned char *)desc->irq,
|
||||||
|
desc->inbuf,
|
||||||
|
desc->wMaxCommand,
|
||||||
|
wdm_in_callback,
|
||||||
|
desc
|
||||||
|
);
|
||||||
|
|
||||||
usb_set_intfdata(intf, desc);
|
usb_set_intfdata(intf, desc);
|
||||||
rv = usb_register_dev(intf, &wdm_class);
|
rv = usb_register_dev(intf, &wdm_class);
|
||||||
if (rv < 0)
|
if (rv < 0)
|
||||||
goto err3;
|
goto err2;
|
||||||
else
|
else
|
||||||
dev_info(&intf->dev, "cdc-wdm%d: USB WDM device\n",
|
dev_info(&intf->dev, "%s: USB WDM device\n", dev_name(intf->usb_dev));
|
||||||
intf->minor - WDM_MINOR_BASE);
|
|
||||||
out:
|
out:
|
||||||
return rv;
|
return rv;
|
||||||
err3:
|
|
||||||
usb_set_intfdata(intf, NULL);
|
|
||||||
usb_free_coherent(interface_to_usbdev(desc->intf),
|
|
||||||
desc->bMaxPacketSize0,
|
|
||||||
desc->inbuf,
|
|
||||||
desc->response->transfer_dma);
|
|
||||||
err2:
|
err2:
|
||||||
usb_free_coherent(interface_to_usbdev(desc->intf),
|
usb_set_intfdata(intf, NULL);
|
||||||
desc->wMaxPacketSize,
|
|
||||||
desc->sbuf,
|
|
||||||
desc->validity->transfer_dma);
|
|
||||||
err:
|
err:
|
||||||
free_urbs(desc);
|
free_urbs(desc);
|
||||||
|
kfree(desc->inbuf);
|
||||||
|
kfree(desc->sbuf);
|
||||||
kfree(desc->ubuf);
|
kfree(desc->ubuf);
|
||||||
kfree(desc->orq);
|
kfree(desc->orq);
|
||||||
kfree(desc->irq);
|
kfree(desc->irq);
|
||||||
@ -869,10 +868,6 @@ static int wdm_pre_reset(struct usb_interface *intf)
|
|||||||
{
|
{
|
||||||
struct wdm_device *desc = usb_get_intfdata(intf);
|
struct wdm_device *desc = usb_get_intfdata(intf);
|
||||||
|
|
||||||
mutex_lock(&desc->rlock);
|
|
||||||
mutex_lock(&desc->wlock);
|
|
||||||
kill_urbs(desc);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* we notify everybody using poll of
|
* we notify everybody using poll of
|
||||||
* an exceptional situation
|
* an exceptional situation
|
||||||
@ -880,9 +875,16 @@ static int wdm_pre_reset(struct usb_interface *intf)
|
|||||||
* message from the device is lost
|
* message from the device is lost
|
||||||
*/
|
*/
|
||||||
spin_lock_irq(&desc->iuspin);
|
spin_lock_irq(&desc->iuspin);
|
||||||
|
set_bit(WDM_RESETTING, &desc->flags); /* inform read/write */
|
||||||
|
set_bit(WDM_READ, &desc->flags); /* unblock read */
|
||||||
|
clear_bit(WDM_IN_USE, &desc->flags); /* unblock write */
|
||||||
desc->rerr = -EINTR;
|
desc->rerr = -EINTR;
|
||||||
spin_unlock_irq(&desc->iuspin);
|
spin_unlock_irq(&desc->iuspin);
|
||||||
wake_up_all(&desc->wait);
|
wake_up_all(&desc->wait);
|
||||||
|
mutex_lock(&desc->rlock);
|
||||||
|
mutex_lock(&desc->wlock);
|
||||||
|
kill_urbs(desc);
|
||||||
|
cancel_work_sync(&desc->rxwork);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -891,6 +893,7 @@ static int wdm_post_reset(struct usb_interface *intf)
|
|||||||
struct wdm_device *desc = usb_get_intfdata(intf);
|
struct wdm_device *desc = usb_get_intfdata(intf);
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
|
clear_bit(WDM_RESETTING, &desc->flags);
|
||||||
rv = recover_from_urb_loss(desc);
|
rv = recover_from_urb_loss(desc);
|
||||||
mutex_unlock(&desc->wlock);
|
mutex_unlock(&desc->wlock);
|
||||||
mutex_unlock(&desc->rlock);
|
mutex_unlock(&desc->rlock);
|
||||||
|
@ -958,13 +958,8 @@ void usb_rebind_intf(struct usb_interface *intf)
|
|||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Delayed unbind of an existing driver */
|
/* Delayed unbind of an existing driver */
|
||||||
if (intf->dev.driver) {
|
if (intf->dev.driver)
|
||||||
struct usb_driver *driver =
|
usb_forced_unbind_intf(intf);
|
||||||
to_usb_driver(intf->dev.driver);
|
|
||||||
|
|
||||||
dev_dbg(&intf->dev, "forced unbind\n");
|
|
||||||
usb_driver_release_interface(driver, intf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try to rebind the interface */
|
/* Try to rebind the interface */
|
||||||
if (!intf->dev.power.is_prepared) {
|
if (!intf->dev.power.is_prepared) {
|
||||||
@ -977,15 +972,13 @@ void usb_rebind_intf(struct usb_interface *intf)
|
|||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
|
|
||||||
#define DO_UNBIND 0
|
/* Unbind drivers for @udev's interfaces that don't support suspend/resume
|
||||||
#define DO_REBIND 1
|
* There is no check for reset_resume here because it can be determined
|
||||||
|
* only during resume whether reset_resume is needed.
|
||||||
/* Unbind drivers for @udev's interfaces that don't support suspend/resume,
|
|
||||||
* or rebind interfaces that have been unbound, according to @action.
|
|
||||||
*
|
*
|
||||||
* The caller must hold @udev's device lock.
|
* The caller must hold @udev's device lock.
|
||||||
*/
|
*/
|
||||||
static void do_unbind_rebind(struct usb_device *udev, int action)
|
static void unbind_no_pm_drivers_interfaces(struct usb_device *udev)
|
||||||
{
|
{
|
||||||
struct usb_host_config *config;
|
struct usb_host_config *config;
|
||||||
int i;
|
int i;
|
||||||
@ -996,23 +989,53 @@ static void do_unbind_rebind(struct usb_device *udev, int action)
|
|||||||
if (config) {
|
if (config) {
|
||||||
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
|
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
|
||||||
intf = config->interface[i];
|
intf = config->interface[i];
|
||||||
switch (action) {
|
|
||||||
case DO_UNBIND:
|
if (intf->dev.driver) {
|
||||||
if (intf->dev.driver) {
|
drv = to_usb_driver(intf->dev.driver);
|
||||||
drv = to_usb_driver(intf->dev.driver);
|
if (!drv->suspend || !drv->resume)
|
||||||
if (!drv->suspend || !drv->resume)
|
usb_forced_unbind_intf(intf);
|
||||||
usb_forced_unbind_intf(intf);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DO_REBIND:
|
|
||||||
if (intf->needs_binding)
|
|
||||||
usb_rebind_intf(intf);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Unbind drivers for @udev's interfaces that failed to support reset-resume.
|
||||||
|
* These interfaces have the needs_binding flag set by usb_resume_interface().
|
||||||
|
*
|
||||||
|
* The caller must hold @udev's device lock.
|
||||||
|
*/
|
||||||
|
static void unbind_no_reset_resume_drivers_interfaces(struct usb_device *udev)
|
||||||
|
{
|
||||||
|
struct usb_host_config *config;
|
||||||
|
int i;
|
||||||
|
struct usb_interface *intf;
|
||||||
|
|
||||||
|
config = udev->actconfig;
|
||||||
|
if (config) {
|
||||||
|
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
|
||||||
|
intf = config->interface[i];
|
||||||
|
if (intf->dev.driver && intf->needs_binding)
|
||||||
|
usb_forced_unbind_intf(intf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_rebind_interfaces(struct usb_device *udev)
|
||||||
|
{
|
||||||
|
struct usb_host_config *config;
|
||||||
|
int i;
|
||||||
|
struct usb_interface *intf;
|
||||||
|
|
||||||
|
config = udev->actconfig;
|
||||||
|
if (config) {
|
||||||
|
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
|
||||||
|
intf = config->interface[i];
|
||||||
|
if (intf->needs_binding)
|
||||||
|
usb_rebind_intf(intf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int usb_suspend_device(struct usb_device *udev, pm_message_t msg)
|
static int usb_suspend_device(struct usb_device *udev, pm_message_t msg)
|
||||||
{
|
{
|
||||||
struct usb_device_driver *udriver;
|
struct usb_device_driver *udriver;
|
||||||
@ -1302,35 +1325,48 @@ int usb_suspend(struct device *dev, pm_message_t msg)
|
|||||||
{
|
{
|
||||||
struct usb_device *udev = to_usb_device(dev);
|
struct usb_device *udev = to_usb_device(dev);
|
||||||
|
|
||||||
do_unbind_rebind(udev, DO_UNBIND);
|
unbind_no_pm_drivers_interfaces(udev);
|
||||||
|
|
||||||
|
/* From now on we are sure all drivers support suspend/resume
|
||||||
|
* but not necessarily reset_resume()
|
||||||
|
* so we may still need to unbind and rebind upon resume
|
||||||
|
*/
|
||||||
choose_wakeup(udev, msg);
|
choose_wakeup(udev, msg);
|
||||||
return usb_suspend_both(udev, msg);
|
return usb_suspend_both(udev, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The device lock is held by the PM core */
|
||||||
|
int usb_resume_complete(struct device *dev)
|
||||||
|
{
|
||||||
|
struct usb_device *udev = to_usb_device(dev);
|
||||||
|
|
||||||
|
/* For PM complete calls, all we do is rebind interfaces
|
||||||
|
* whose needs_binding flag is set
|
||||||
|
*/
|
||||||
|
if (udev->state != USB_STATE_NOTATTACHED)
|
||||||
|
do_rebind_interfaces(udev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* The device lock is held by the PM core */
|
/* The device lock is held by the PM core */
|
||||||
int usb_resume(struct device *dev, pm_message_t msg)
|
int usb_resume(struct device *dev, pm_message_t msg)
|
||||||
{
|
{
|
||||||
struct usb_device *udev = to_usb_device(dev);
|
struct usb_device *udev = to_usb_device(dev);
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
/* For PM complete calls, all we do is rebind interfaces */
|
/* For all calls, take the device back to full power and
|
||||||
if (msg.event == PM_EVENT_ON) {
|
|
||||||
if (udev->state != USB_STATE_NOTATTACHED)
|
|
||||||
do_unbind_rebind(udev, DO_REBIND);
|
|
||||||
status = 0;
|
|
||||||
|
|
||||||
/* For all other calls, take the device back to full power and
|
|
||||||
* tell the PM core in case it was autosuspended previously.
|
* tell the PM core in case it was autosuspended previously.
|
||||||
* Unbind the interfaces that will need rebinding later.
|
* Unbind the interfaces that will need rebinding later,
|
||||||
|
* because they fail to support reset_resume.
|
||||||
|
* (This can't be done in usb_resume_interface()
|
||||||
|
* above because it doesn't own the right set of locks.)
|
||||||
*/
|
*/
|
||||||
} else {
|
status = usb_resume_both(udev, msg);
|
||||||
status = usb_resume_both(udev, msg);
|
if (status == 0) {
|
||||||
if (status == 0) {
|
pm_runtime_disable(dev);
|
||||||
pm_runtime_disable(dev);
|
pm_runtime_set_active(dev);
|
||||||
pm_runtime_set_active(dev);
|
pm_runtime_enable(dev);
|
||||||
pm_runtime_enable(dev);
|
unbind_no_reset_resume_drivers_interfaces(udev);
|
||||||
do_unbind_rebind(udev, DO_REBIND);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Avoid PM error messages for devices disconnected while suspended
|
/* Avoid PM error messages for devices disconnected while suspended
|
||||||
|
@ -62,6 +62,8 @@ struct usb_hub {
|
|||||||
resumed */
|
resumed */
|
||||||
unsigned long removed_bits[1]; /* ports with a "removed"
|
unsigned long removed_bits[1]; /* ports with a "removed"
|
||||||
device present */
|
device present */
|
||||||
|
unsigned long wakeup_bits[1]; /* ports that have signaled
|
||||||
|
remote wakeup */
|
||||||
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
|
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
|
||||||
#error event_bits[] is too short!
|
#error event_bits[] is too short!
|
||||||
#endif
|
#endif
|
||||||
@ -411,6 +413,29 @@ void usb_kick_khubd(struct usb_device *hdev)
|
|||||||
kick_khubd(hub);
|
kick_khubd(hub);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Let the USB core know that a USB 3.0 device has sent a Function Wake Device
|
||||||
|
* Notification, which indicates it had initiated remote wakeup.
|
||||||
|
*
|
||||||
|
* USB 3.0 hubs do not report the port link state change from U3 to U0 when the
|
||||||
|
* device initiates resume, so the USB core will not receive notice of the
|
||||||
|
* resume through the normal hub interrupt URB.
|
||||||
|
*/
|
||||||
|
void usb_wakeup_notification(struct usb_device *hdev,
|
||||||
|
unsigned int portnum)
|
||||||
|
{
|
||||||
|
struct usb_hub *hub;
|
||||||
|
|
||||||
|
if (!hdev)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hub = hdev_to_hub(hdev);
|
||||||
|
if (hub) {
|
||||||
|
set_bit(portnum, hub->wakeup_bits);
|
||||||
|
kick_khubd(hub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(usb_wakeup_notification);
|
||||||
|
|
||||||
/* completion function, fires on port status changes and various faults */
|
/* completion function, fires on port status changes and various faults */
|
||||||
static void hub_irq(struct urb *urb)
|
static void hub_irq(struct urb *urb)
|
||||||
@ -823,12 +848,6 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
|||||||
clear_port_feature(hub->hdev, port1,
|
clear_port_feature(hub->hdev, port1,
|
||||||
USB_PORT_FEAT_C_ENABLE);
|
USB_PORT_FEAT_C_ENABLE);
|
||||||
}
|
}
|
||||||
if (portchange & USB_PORT_STAT_C_LINK_STATE) {
|
|
||||||
need_debounce_delay = true;
|
|
||||||
clear_port_feature(hub->hdev, port1,
|
|
||||||
USB_PORT_FEAT_C_PORT_LINK_STATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
|
if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
|
||||||
hub_is_superspeed(hub->hdev)) {
|
hub_is_superspeed(hub->hdev)) {
|
||||||
need_debounce_delay = true;
|
need_debounce_delay = true;
|
||||||
@ -850,12 +869,19 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
|||||||
set_bit(port1, hub->change_bits);
|
set_bit(port1, hub->change_bits);
|
||||||
|
|
||||||
} else if (portstatus & USB_PORT_STAT_ENABLE) {
|
} else if (portstatus & USB_PORT_STAT_ENABLE) {
|
||||||
|
bool port_resumed = (portstatus &
|
||||||
|
USB_PORT_STAT_LINK_STATE) ==
|
||||||
|
USB_SS_PORT_LS_U0;
|
||||||
/* The power session apparently survived the resume.
|
/* The power session apparently survived the resume.
|
||||||
* If there was an overcurrent or suspend change
|
* If there was an overcurrent or suspend change
|
||||||
* (i.e., remote wakeup request), have khubd
|
* (i.e., remote wakeup request), have khubd
|
||||||
* take care of it.
|
* take care of it. Look at the port link state
|
||||||
|
* for USB 3.0 hubs, since they don't have a suspend
|
||||||
|
* change bit, and they don't set the port link change
|
||||||
|
* bit on device-initiated resume.
|
||||||
*/
|
*/
|
||||||
if (portchange)
|
if (portchange || (hub_is_superspeed(hub->hdev) &&
|
||||||
|
port_resumed))
|
||||||
set_bit(port1, hub->change_bits);
|
set_bit(port1, hub->change_bits);
|
||||||
|
|
||||||
} else if (udev->persist_enabled) {
|
} else if (udev->persist_enabled) {
|
||||||
@ -1293,14 +1319,8 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
|||||||
desc = intf->cur_altsetting;
|
desc = intf->cur_altsetting;
|
||||||
hdev = interface_to_usbdev(intf);
|
hdev = interface_to_usbdev(intf);
|
||||||
|
|
||||||
/* Hubs have proper suspend/resume support. USB 3.0 device suspend is
|
/* Hubs have proper suspend/resume support. */
|
||||||
* different from USB 2.0/1.1 device suspend, and unfortunately we
|
usb_enable_autosuspend(hdev);
|
||||||
* don't support it yet. So leave autosuspend disabled for USB 3.0
|
|
||||||
* external hubs for now. Enable autosuspend for USB 3.0 roothubs,
|
|
||||||
* since that isn't a "real" hub.
|
|
||||||
*/
|
|
||||||
if (!hub_is_superspeed(hdev) || !hdev->parent)
|
|
||||||
usb_enable_autosuspend(hdev);
|
|
||||||
|
|
||||||
if (hdev->level == MAX_TOPO_LEVEL) {
|
if (hdev->level == MAX_TOPO_LEVEL) {
|
||||||
dev_err(&intf->dev,
|
dev_err(&intf->dev,
|
||||||
@ -1842,6 +1862,37 @@ fail:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void set_usb_port_removable(struct usb_device *udev)
|
||||||
|
{
|
||||||
|
struct usb_device *hdev = udev->parent;
|
||||||
|
struct usb_hub *hub;
|
||||||
|
u8 port = udev->portnum;
|
||||||
|
u16 wHubCharacteristics;
|
||||||
|
bool removable = true;
|
||||||
|
|
||||||
|
if (!hdev)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hub = hdev_to_hub(udev->parent);
|
||||||
|
|
||||||
|
wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
|
||||||
|
|
||||||
|
if (!(wHubCharacteristics & HUB_CHAR_COMPOUND))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (hub_is_superspeed(hdev)) {
|
||||||
|
if (hub->descriptor->u.ss.DeviceRemovable & (1 << port))
|
||||||
|
removable = false;
|
||||||
|
} else {
|
||||||
|
if (hub->descriptor->u.hs.DeviceRemovable[port / 8] & (1 << (port % 8)))
|
||||||
|
removable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removable)
|
||||||
|
udev->removable = USB_DEVICE_REMOVABLE;
|
||||||
|
else
|
||||||
|
udev->removable = USB_DEVICE_FIXED;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* usb_new_device - perform initial device setup (usbcore-internal)
|
* usb_new_device - perform initial device setup (usbcore-internal)
|
||||||
@ -1900,6 +1951,15 @@ int usb_new_device(struct usb_device *udev)
|
|||||||
announce_device(udev);
|
announce_device(udev);
|
||||||
|
|
||||||
device_enable_async_suspend(&udev->dev);
|
device_enable_async_suspend(&udev->dev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check whether the hub marks this port as non-removable. Do it
|
||||||
|
* now so that platform-specific data can override it in
|
||||||
|
* device_add()
|
||||||
|
*/
|
||||||
|
if (udev->parent)
|
||||||
|
set_usb_port_removable(udev);
|
||||||
|
|
||||||
/* Register the device. The device driver is responsible
|
/* Register the device. The device driver is responsible
|
||||||
* for configuring the device and invoking the add-device
|
* for configuring the device and invoking the add-device
|
||||||
* notifier chain (used by usbfs and possibly others).
|
* notifier chain (used by usbfs and possibly others).
|
||||||
@ -2385,11 +2445,27 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
|
|||||||
* we don't explicitly enable it here.
|
* we don't explicitly enable it here.
|
||||||
*/
|
*/
|
||||||
if (udev->do_remote_wakeup) {
|
if (udev->do_remote_wakeup) {
|
||||||
status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
if (!hub_is_superspeed(hub->hdev)) {
|
||||||
USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
|
status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||||||
USB_DEVICE_REMOTE_WAKEUP, 0,
|
USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
|
||||||
NULL, 0,
|
USB_DEVICE_REMOTE_WAKEUP, 0,
|
||||||
USB_CTRL_SET_TIMEOUT);
|
NULL, 0,
|
||||||
|
USB_CTRL_SET_TIMEOUT);
|
||||||
|
} else {
|
||||||
|
/* Assume there's only one function on the USB 3.0
|
||||||
|
* device and enable remote wake for the first
|
||||||
|
* interface. FIXME if the interface association
|
||||||
|
* descriptor shows there's more than one function.
|
||||||
|
*/
|
||||||
|
status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||||||
|
USB_REQ_SET_FEATURE,
|
||||||
|
USB_RECIP_INTERFACE,
|
||||||
|
USB_INTRF_FUNC_SUSPEND,
|
||||||
|
USB_INTRF_FUNC_SUSPEND_RW |
|
||||||
|
USB_INTRF_FUNC_SUSPEND_LP,
|
||||||
|
NULL, 0,
|
||||||
|
USB_CTRL_SET_TIMEOUT);
|
||||||
|
}
|
||||||
if (status) {
|
if (status) {
|
||||||
dev_dbg(&udev->dev, "won't remote wakeup, status %d\n",
|
dev_dbg(&udev->dev, "won't remote wakeup, status %d\n",
|
||||||
status);
|
status);
|
||||||
@ -2679,6 +2755,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
|
|||||||
struct usb_hub *hub = usb_get_intfdata (intf);
|
struct usb_hub *hub = usb_get_intfdata (intf);
|
||||||
struct usb_device *hdev = hub->hdev;
|
struct usb_device *hdev = hub->hdev;
|
||||||
unsigned port1;
|
unsigned port1;
|
||||||
|
int status;
|
||||||
|
|
||||||
/* Warn if children aren't already suspended */
|
/* Warn if children aren't already suspended */
|
||||||
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
|
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
|
||||||
@ -2691,6 +2768,17 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
|
|||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (hub_is_superspeed(hdev) && hdev->do_remote_wakeup) {
|
||||||
|
/* Enable hub to send remote wakeup for all ports. */
|
||||||
|
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
|
||||||
|
status = set_port_feature(hdev,
|
||||||
|
port1 |
|
||||||
|
USB_PORT_FEAT_REMOTE_WAKE_CONNECT |
|
||||||
|
USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT |
|
||||||
|
USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT,
|
||||||
|
USB_PORT_FEAT_REMOTE_WAKE_MASK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dev_dbg(&intf->dev, "%s\n", __func__);
|
dev_dbg(&intf->dev, "%s\n", __func__);
|
||||||
|
|
||||||
@ -3424,6 +3512,46 @@ done:
|
|||||||
hcd->driver->relinquish_port(hcd, port1);
|
hcd->driver->relinquish_port(hcd, port1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Returns 1 if there was a remote wakeup and a connect status change. */
|
||||||
|
static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
|
||||||
|
u16 portstatus, u16 portchange)
|
||||||
|
{
|
||||||
|
struct usb_device *hdev;
|
||||||
|
struct usb_device *udev;
|
||||||
|
int connect_change = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
hdev = hub->hdev;
|
||||||
|
udev = hdev->children[port-1];
|
||||||
|
if (!hub_is_superspeed(hdev)) {
|
||||||
|
if (!(portchange & USB_PORT_STAT_C_SUSPEND))
|
||||||
|
return 0;
|
||||||
|
clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND);
|
||||||
|
} else {
|
||||||
|
if (!udev || udev->state != USB_STATE_SUSPENDED ||
|
||||||
|
(portstatus & USB_PORT_STAT_LINK_STATE) !=
|
||||||
|
USB_SS_PORT_LS_U0)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (udev) {
|
||||||
|
/* TRSMRCY = 10 msec */
|
||||||
|
msleep(10);
|
||||||
|
|
||||||
|
usb_lock_device(udev);
|
||||||
|
ret = usb_remote_wakeup(udev);
|
||||||
|
usb_unlock_device(udev);
|
||||||
|
if (ret < 0)
|
||||||
|
connect_change = 1;
|
||||||
|
} else {
|
||||||
|
ret = -ENODEV;
|
||||||
|
hub_port_disable(hub, port, 1);
|
||||||
|
}
|
||||||
|
dev_dbg(hub->intfdev, "resume on port %d, status %d\n",
|
||||||
|
port, ret);
|
||||||
|
return connect_change;
|
||||||
|
}
|
||||||
|
|
||||||
static void hub_events(void)
|
static void hub_events(void)
|
||||||
{
|
{
|
||||||
struct list_head *tmp;
|
struct list_head *tmp;
|
||||||
@ -3436,7 +3564,7 @@ static void hub_events(void)
|
|||||||
u16 portstatus;
|
u16 portstatus;
|
||||||
u16 portchange;
|
u16 portchange;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
int connect_change;
|
int connect_change, wakeup_change;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We restart the list every time to avoid a deadlock with
|
* We restart the list every time to avoid a deadlock with
|
||||||
@ -3515,8 +3643,9 @@ static void hub_events(void)
|
|||||||
if (test_bit(i, hub->busy_bits))
|
if (test_bit(i, hub->busy_bits))
|
||||||
continue;
|
continue;
|
||||||
connect_change = test_bit(i, hub->change_bits);
|
connect_change = test_bit(i, hub->change_bits);
|
||||||
|
wakeup_change = test_and_clear_bit(i, hub->wakeup_bits);
|
||||||
if (!test_and_clear_bit(i, hub->event_bits) &&
|
if (!test_and_clear_bit(i, hub->event_bits) &&
|
||||||
!connect_change)
|
!connect_change && !wakeup_change)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ret = hub_port_status(hub, i,
|
ret = hub_port_status(hub, i,
|
||||||
@ -3557,31 +3686,10 @@ static void hub_events(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (portchange & USB_PORT_STAT_C_SUSPEND) {
|
if (hub_handle_remote_wakeup(hub, i,
|
||||||
struct usb_device *udev;
|
portstatus, portchange))
|
||||||
|
connect_change = 1;
|
||||||
|
|
||||||
clear_port_feature(hdev, i,
|
|
||||||
USB_PORT_FEAT_C_SUSPEND);
|
|
||||||
udev = hdev->children[i-1];
|
|
||||||
if (udev) {
|
|
||||||
/* TRSMRCY = 10 msec */
|
|
||||||
msleep(10);
|
|
||||||
|
|
||||||
usb_lock_device(udev);
|
|
||||||
ret = usb_remote_wakeup(hdev->
|
|
||||||
children[i-1]);
|
|
||||||
usb_unlock_device(udev);
|
|
||||||
if (ret < 0)
|
|
||||||
connect_change = 1;
|
|
||||||
} else {
|
|
||||||
ret = -ENODEV;
|
|
||||||
hub_port_disable(hub, i, 1);
|
|
||||||
}
|
|
||||||
dev_dbg (hub_dev,
|
|
||||||
"resume on port %d, status %d\n",
|
|
||||||
i, ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
|
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
|
||||||
u16 status = 0;
|
u16 status = 0;
|
||||||
u16 unused;
|
u16 unused;
|
||||||
|
@ -230,6 +230,28 @@ show_urbnum(struct device *dev, struct device_attribute *attr, char *buf)
|
|||||||
}
|
}
|
||||||
static DEVICE_ATTR(urbnum, S_IRUGO, show_urbnum, NULL);
|
static DEVICE_ATTR(urbnum, S_IRUGO, show_urbnum, NULL);
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
show_removable(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct usb_device *udev;
|
||||||
|
char *state;
|
||||||
|
|
||||||
|
udev = to_usb_device(dev);
|
||||||
|
|
||||||
|
switch (udev->removable) {
|
||||||
|
case USB_DEVICE_REMOVABLE:
|
||||||
|
state = "removable";
|
||||||
|
break;
|
||||||
|
case USB_DEVICE_FIXED:
|
||||||
|
state = "fixed";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
state = "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf(buf, "%s\n", state);
|
||||||
|
}
|
||||||
|
static DEVICE_ATTR(removable, S_IRUGO, show_removable, NULL);
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
|
|
||||||
@ -626,6 +648,7 @@ static struct attribute *dev_attrs[] = {
|
|||||||
&dev_attr_avoid_reset_quirk.attr,
|
&dev_attr_avoid_reset_quirk.attr,
|
||||||
&dev_attr_authorized.attr,
|
&dev_attr_authorized.attr,
|
||||||
&dev_attr_remove.attr,
|
&dev_attr_remove.attr,
|
||||||
|
&dev_attr_removable.attr,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
static struct attribute_group dev_attr_grp = {
|
static struct attribute_group dev_attr_grp = {
|
||||||
|
@ -403,20 +403,17 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
|||||||
* cause problems in HCDs if they get it wrong.
|
* cause problems in HCDs if they get it wrong.
|
||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
unsigned int orig_flags = urb->transfer_flags;
|
|
||||||
unsigned int allowed;
|
unsigned int allowed;
|
||||||
static int pipetypes[4] = {
|
static int pipetypes[4] = {
|
||||||
PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT
|
PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Check that the pipe's type matches the endpoint's type */
|
/* Check that the pipe's type matches the endpoint's type */
|
||||||
if (usb_pipetype(urb->pipe) != pipetypes[xfertype]) {
|
if (usb_pipetype(urb->pipe) != pipetypes[xfertype])
|
||||||
dev_err(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n",
|
dev_WARN(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n",
|
||||||
usb_pipetype(urb->pipe), pipetypes[xfertype]);
|
usb_pipetype(urb->pipe), pipetypes[xfertype]);
|
||||||
return -EPIPE; /* The most suitable error code :-) */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* enforce simple/standard policy */
|
/* Check against a simple/standard policy */
|
||||||
allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT | URB_DIR_MASK |
|
allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT | URB_DIR_MASK |
|
||||||
URB_FREE_BUFFER);
|
URB_FREE_BUFFER);
|
||||||
switch (xfertype) {
|
switch (xfertype) {
|
||||||
@ -435,14 +432,12 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
|
|||||||
allowed |= URB_ISO_ASAP;
|
allowed |= URB_ISO_ASAP;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
urb->transfer_flags &= allowed;
|
allowed &= urb->transfer_flags;
|
||||||
|
|
||||||
/* fail if submitter gave bogus flags */
|
/* warn if submitter gave bogus flags */
|
||||||
if (urb->transfer_flags != orig_flags) {
|
if (allowed != urb->transfer_flags)
|
||||||
dev_err(&dev->dev, "BOGUS urb flags, %x --> %x\n",
|
dev_WARN(&dev->dev, "BOGUS urb flags, %x --> %x\n",
|
||||||
orig_flags, urb->transfer_flags);
|
urb->transfer_flags, allowed);
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
/*
|
/*
|
||||||
|
@ -274,7 +274,7 @@ static int usb_dev_prepare(struct device *dev)
|
|||||||
static void usb_dev_complete(struct device *dev)
|
static void usb_dev_complete(struct device *dev)
|
||||||
{
|
{
|
||||||
/* Currently used only for rebinding interfaces */
|
/* Currently used only for rebinding interfaces */
|
||||||
usb_resume(dev, PMSG_ON); /* FIXME: change to PMSG_COMPLETE */
|
usb_resume_complete(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int usb_dev_suspend(struct device *dev)
|
static int usb_dev_suspend(struct device *dev)
|
||||||
|
@ -56,6 +56,7 @@ extern void usb_major_cleanup(void);
|
|||||||
|
|
||||||
extern int usb_suspend(struct device *dev, pm_message_t msg);
|
extern int usb_suspend(struct device *dev, pm_message_t msg);
|
||||||
extern int usb_resume(struct device *dev, pm_message_t msg);
|
extern int usb_resume(struct device *dev, pm_message_t msg);
|
||||||
|
extern int usb_resume_complete(struct device *dev);
|
||||||
|
|
||||||
extern int usb_port_suspend(struct usb_device *dev, pm_message_t msg);
|
extern int usb_port_suspend(struct usb_device *dev, pm_message_t msg);
|
||||||
extern int usb_port_resume(struct usb_device *dev, pm_message_t msg);
|
extern int usb_port_resume(struct usb_device *dev, pm_message_t msg);
|
||||||
|
@ -323,7 +323,9 @@ static int ehci_fsl_setup(struct usb_hcd *hcd)
|
|||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
int retval;
|
int retval;
|
||||||
struct fsl_usb2_platform_data *pdata;
|
struct fsl_usb2_platform_data *pdata;
|
||||||
|
struct device *dev;
|
||||||
|
|
||||||
|
dev = hcd->self.controller;
|
||||||
pdata = hcd->self.controller->platform_data;
|
pdata = hcd->self.controller->platform_data;
|
||||||
ehci->big_endian_desc = pdata->big_endian_desc;
|
ehci->big_endian_desc = pdata->big_endian_desc;
|
||||||
ehci->big_endian_mmio = pdata->big_endian_mmio;
|
ehci->big_endian_mmio = pdata->big_endian_mmio;
|
||||||
@ -353,6 +355,16 @@ static int ehci_fsl_setup(struct usb_hcd *hcd)
|
|||||||
|
|
||||||
ehci_reset(ehci);
|
ehci_reset(ehci);
|
||||||
|
|
||||||
|
if (of_device_is_compatible(dev->parent->of_node,
|
||||||
|
"fsl,mpc5121-usb2-dr")) {
|
||||||
|
/*
|
||||||
|
* set SBUSCFG:AHBBRST so that control msgs don't
|
||||||
|
* fail when doing heavy PATA writes.
|
||||||
|
*/
|
||||||
|
ehci_writel(ehci, SBUSCFG_INCR8,
|
||||||
|
hcd->regs + FSL_SOC_USB_SBUSCFG);
|
||||||
|
}
|
||||||
|
|
||||||
retval = ehci_fsl_reinit(ehci);
|
retval = ehci_fsl_reinit(ehci);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
@ -476,6 +488,8 @@ static int ehci_fsl_mpc512x_drv_resume(struct device *dev)
|
|||||||
ehci_writel(ehci, ISIPHYCTRL_PXE | ISIPHYCTRL_PHYE,
|
ehci_writel(ehci, ISIPHYCTRL_PXE | ISIPHYCTRL_PHYE,
|
||||||
hcd->regs + FSL_SOC_USB_ISIPHYCTRL);
|
hcd->regs + FSL_SOC_USB_ISIPHYCTRL);
|
||||||
|
|
||||||
|
ehci_writel(ehci, SBUSCFG_INCR8, hcd->regs + FSL_SOC_USB_SBUSCFG);
|
||||||
|
|
||||||
/* restore EHCI registers */
|
/* restore EHCI registers */
|
||||||
ehci_writel(ehci, pdata->pm_command, &ehci->regs->command);
|
ehci_writel(ehci, pdata->pm_command, &ehci->regs->command);
|
||||||
ehci_writel(ehci, pdata->pm_intr_enable, &ehci->regs->intr_enable);
|
ehci_writel(ehci, pdata->pm_intr_enable, &ehci->regs->intr_enable);
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
#define _EHCI_FSL_H
|
#define _EHCI_FSL_H
|
||||||
|
|
||||||
/* offsets for the non-ehci registers in the FSL SOC USB controller */
|
/* offsets for the non-ehci registers in the FSL SOC USB controller */
|
||||||
|
#define FSL_SOC_USB_SBUSCFG 0x90
|
||||||
|
#define SBUSCFG_INCR8 0x02 /* INCR8, specified */
|
||||||
#define FSL_SOC_USB_ULPIVP 0x170
|
#define FSL_SOC_USB_ULPIVP 0x170
|
||||||
#define FSL_SOC_USB_PORTSC1 0x184
|
#define FSL_SOC_USB_PORTSC1 0x184
|
||||||
#define PORT_PTS_MSK (3<<30)
|
#define PORT_PTS_MSK (3<<30)
|
||||||
|
@ -1376,6 +1376,11 @@ MODULE_LICENSE ("GPL");
|
|||||||
#define PLATFORM_DRIVER ehci_mv_driver
|
#define PLATFORM_DRIVER ehci_mv_driver
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_MACH_LOONGSON1
|
||||||
|
#include "ehci-ls1x.c"
|
||||||
|
#define PLATFORM_DRIVER ehci_ls1x_driver
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
|
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
|
||||||
!defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
|
!defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
|
||||||
!defined(XILINX_OF_PLATFORM_DRIVER)
|
!defined(XILINX_OF_PLATFORM_DRIVER)
|
||||||
|
@ -107,7 +107,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
|
|||||||
ehci->owned_ports = 0;
|
ehci->owned_ports = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ehci_port_change(struct ehci_hcd *ehci)
|
static int __maybe_unused ehci_port_change(struct ehci_hcd *ehci)
|
||||||
{
|
{
|
||||||
int i = HCS_N_PORTS(ehci->hcs_params);
|
int i = HCS_N_PORTS(ehci->hcs_params);
|
||||||
|
|
||||||
@ -1076,7 +1076,8 @@ error_exit:
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ehci_relinquish_port(struct usb_hcd *hcd, int portnum)
|
static void __maybe_unused ehci_relinquish_port(struct usb_hcd *hcd,
|
||||||
|
int portnum)
|
||||||
{
|
{
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
|
|
||||||
@ -1085,7 +1086,8 @@ static void ehci_relinquish_port(struct usb_hcd *hcd, int portnum)
|
|||||||
set_owner(ehci, --portnum, PORT_OWNER);
|
set_owner(ehci, --portnum, PORT_OWNER);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ehci_port_handed_over(struct usb_hcd *hcd, int portnum)
|
static int __maybe_unused ehci_port_handed_over(struct usb_hcd *hcd,
|
||||||
|
int portnum)
|
||||||
{
|
{
|
||||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
u32 __iomem *reg;
|
u32 __iomem *reg;
|
||||||
|
159
drivers/usb/host/ehci-ls1x.c
Normal file
159
drivers/usb/host/ehci-ls1x.c
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
* Bus Glue for Loongson LS1X built-in EHCI controller.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012 Zhang, Keguang <keguang.zhang@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 as published
|
||||||
|
* by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
|
static int ehci_ls1x_reset(struct usb_hcd *hcd)
|
||||||
|
{
|
||||||
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ehci->caps = hcd->regs;
|
||||||
|
|
||||||
|
ret = ehci_setup(hcd);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ehci_port_power(ehci, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct hc_driver ehci_ls1x_hc_driver = {
|
||||||
|
.description = hcd_name,
|
||||||
|
.product_desc = "LOONGSON1 EHCI",
|
||||||
|
.hcd_priv_size = sizeof(struct ehci_hcd),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* generic hardware linkage
|
||||||
|
*/
|
||||||
|
.irq = ehci_irq,
|
||||||
|
.flags = HCD_MEMORY | HCD_USB2,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* basic lifecycle operations
|
||||||
|
*/
|
||||||
|
.reset = ehci_ls1x_reset,
|
||||||
|
.start = ehci_run,
|
||||||
|
.stop = ehci_stop,
|
||||||
|
.shutdown = ehci_shutdown,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* managing i/o requests and associated device resources
|
||||||
|
*/
|
||||||
|
.urb_enqueue = ehci_urb_enqueue,
|
||||||
|
.urb_dequeue = ehci_urb_dequeue,
|
||||||
|
.endpoint_disable = ehci_endpoint_disable,
|
||||||
|
.endpoint_reset = ehci_endpoint_reset,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* scheduling support
|
||||||
|
*/
|
||||||
|
.get_frame_number = ehci_get_frame,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* root hub support
|
||||||
|
*/
|
||||||
|
.hub_status_data = ehci_hub_status_data,
|
||||||
|
.hub_control = ehci_hub_control,
|
||||||
|
.relinquish_port = ehci_relinquish_port,
|
||||||
|
.port_handed_over = ehci_port_handed_over,
|
||||||
|
|
||||||
|
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ehci_hcd_ls1x_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct usb_hcd *hcd;
|
||||||
|
struct resource *res;
|
||||||
|
int irq;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
pr_debug("initializing loongson1 ehci USB Controller\n");
|
||||||
|
|
||||||
|
if (usb_disabled())
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||||
|
if (!res) {
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"Found HC with no IRQ. Check %s setup!\n",
|
||||||
|
dev_name(&pdev->dev));
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
irq = res->start;
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
if (!res) {
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"Found HC with no register addr. Check %s setup!\n",
|
||||||
|
dev_name(&pdev->dev));
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
hcd = usb_create_hcd(&ehci_ls1x_hc_driver, &pdev->dev,
|
||||||
|
dev_name(&pdev->dev));
|
||||||
|
if (!hcd)
|
||||||
|
return -ENOMEM;
|
||||||
|
hcd->rsrc_start = res->start;
|
||||||
|
hcd->rsrc_len = resource_size(res);
|
||||||
|
|
||||||
|
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
|
||||||
|
dev_dbg(&pdev->dev, "controller already in use\n");
|
||||||
|
ret = -EBUSY;
|
||||||
|
goto err_put_hcd;
|
||||||
|
}
|
||||||
|
|
||||||
|
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
|
||||||
|
if (hcd->regs == NULL) {
|
||||||
|
dev_dbg(&pdev->dev, "error mapping memory\n");
|
||||||
|
ret = -EFAULT;
|
||||||
|
goto err_release_region;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
|
||||||
|
if (ret)
|
||||||
|
goto err_iounmap;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
err_iounmap:
|
||||||
|
iounmap(hcd->regs);
|
||||||
|
err_release_region:
|
||||||
|
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||||
|
err_put_hcd:
|
||||||
|
usb_put_hcd(hcd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ehci_hcd_ls1x_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
usb_remove_hcd(hcd);
|
||||||
|
iounmap(hcd->regs);
|
||||||
|
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||||
|
usb_put_hcd(hcd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver ehci_ls1x_driver = {
|
||||||
|
.probe = ehci_hcd_ls1x_probe,
|
||||||
|
.remove = ehci_hcd_ls1x_remove,
|
||||||
|
.shutdown = usb_hcd_platform_shutdown,
|
||||||
|
.driver = {
|
||||||
|
.name = "ls1x-ehci",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
MODULE_ALIAS(PLATFORM_MODULE_PREFIX "ls1x-ehci");
|
@ -239,7 +239,7 @@ static int debug_status_show(struct seq_file *s, void *v)
|
|||||||
"ETDs allocated: %d/%d (max=%d)\n"
|
"ETDs allocated: %d/%d (max=%d)\n"
|
||||||
"ETDs in use sw: %d\n"
|
"ETDs in use sw: %d\n"
|
||||||
"ETDs in use hw: %d\n"
|
"ETDs in use hw: %d\n"
|
||||||
"DMEM alocated: %d/%d (max=%d)\n"
|
"DMEM allocated: %d/%d (max=%d)\n"
|
||||||
"DMEM blocks: %d\n"
|
"DMEM blocks: %d\n"
|
||||||
"Queued waiting for ETD: %d\n"
|
"Queued waiting for ETD: %d\n"
|
||||||
"Queued waiting for DMEM: %d\n",
|
"Queued waiting for DMEM: %d\n",
|
||||||
|
@ -565,6 +565,9 @@ static int uhci_start(struct usb_hcd *hcd)
|
|||||||
struct dentry __maybe_unused *dentry;
|
struct dentry __maybe_unused *dentry;
|
||||||
|
|
||||||
hcd->uses_new_polling = 1;
|
hcd->uses_new_polling = 1;
|
||||||
|
/* Accept arbitrarily long scatter-gather lists */
|
||||||
|
if (!(hcd->driver->flags & HCD_LOCAL_MEM))
|
||||||
|
hcd->self.sg_tablesize = ~0;
|
||||||
|
|
||||||
spin_lock_init(&uhci->lock);
|
spin_lock_init(&uhci->lock);
|
||||||
setup_timer(&uhci->fsbr_timer, uhci_fsbr_timeout,
|
setup_timer(&uhci->fsbr_timer, uhci_fsbr_timeout,
|
||||||
|
@ -422,6 +422,32 @@ void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array,
|
|||||||
xhci_writel(xhci, temp, port_array[port_id]);
|
xhci_writel(xhci, temp, port_array[port_id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void xhci_set_remote_wake_mask(struct xhci_hcd *xhci,
|
||||||
|
__le32 __iomem **port_array, int port_id, u16 wake_mask)
|
||||||
|
{
|
||||||
|
u32 temp;
|
||||||
|
|
||||||
|
temp = xhci_readl(xhci, port_array[port_id]);
|
||||||
|
temp = xhci_port_state_to_neutral(temp);
|
||||||
|
|
||||||
|
if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_CONNECT)
|
||||||
|
temp |= PORT_WKCONN_E;
|
||||||
|
else
|
||||||
|
temp &= ~PORT_WKCONN_E;
|
||||||
|
|
||||||
|
if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT)
|
||||||
|
temp |= PORT_WKDISC_E;
|
||||||
|
else
|
||||||
|
temp &= ~PORT_WKDISC_E;
|
||||||
|
|
||||||
|
if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT)
|
||||||
|
temp |= PORT_WKOC_E;
|
||||||
|
else
|
||||||
|
temp &= ~PORT_WKOC_E;
|
||||||
|
|
||||||
|
xhci_writel(xhci, temp, port_array[port_id]);
|
||||||
|
}
|
||||||
|
|
||||||
/* Test and clear port RWC bit */
|
/* Test and clear port RWC bit */
|
||||||
void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array,
|
void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array,
|
||||||
int port_id, u32 port_bit)
|
int port_id, u32 port_bit)
|
||||||
@ -448,6 +474,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|||||||
int slot_id;
|
int slot_id;
|
||||||
struct xhci_bus_state *bus_state;
|
struct xhci_bus_state *bus_state;
|
||||||
u16 link_state = 0;
|
u16 link_state = 0;
|
||||||
|
u16 wake_mask = 0;
|
||||||
|
|
||||||
max_ports = xhci_get_ports(hcd, &port_array);
|
max_ports = xhci_get_ports(hcd, &port_array);
|
||||||
bus_state = &xhci->bus_state[hcd_index(hcd)];
|
bus_state = &xhci->bus_state[hcd_index(hcd)];
|
||||||
@ -593,6 +620,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|||||||
case SetPortFeature:
|
case SetPortFeature:
|
||||||
if (wValue == USB_PORT_FEAT_LINK_STATE)
|
if (wValue == USB_PORT_FEAT_LINK_STATE)
|
||||||
link_state = (wIndex & 0xff00) >> 3;
|
link_state = (wIndex & 0xff00) >> 3;
|
||||||
|
if (wValue == USB_PORT_FEAT_REMOTE_WAKE_MASK)
|
||||||
|
wake_mask = wIndex & 0xff00;
|
||||||
wIndex &= 0xff;
|
wIndex &= 0xff;
|
||||||
if (!wIndex || wIndex > max_ports)
|
if (!wIndex || wIndex > max_ports)
|
||||||
goto error;
|
goto error;
|
||||||
@ -703,6 +732,14 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|||||||
temp = xhci_readl(xhci, port_array[wIndex]);
|
temp = xhci_readl(xhci, port_array[wIndex]);
|
||||||
xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp);
|
xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp);
|
||||||
break;
|
break;
|
||||||
|
case USB_PORT_FEAT_REMOTE_WAKE_MASK:
|
||||||
|
xhci_set_remote_wake_mask(xhci, port_array,
|
||||||
|
wIndex, wake_mask);
|
||||||
|
temp = xhci_readl(xhci, port_array[wIndex]);
|
||||||
|
xhci_dbg(xhci, "set port remote wake mask, "
|
||||||
|
"actual port %d status = 0x%x\n",
|
||||||
|
wIndex, temp);
|
||||||
|
break;
|
||||||
case USB_PORT_FEAT_BH_PORT_RESET:
|
case USB_PORT_FEAT_BH_PORT_RESET:
|
||||||
temp |= PORT_WR;
|
temp |= PORT_WR;
|
||||||
xhci_writel(xhci, temp, port_array[wIndex]);
|
xhci_writel(xhci, temp, port_array[wIndex]);
|
||||||
@ -883,6 +920,10 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
|
|||||||
t2 |= PORT_LINK_STROBE | XDEV_U3;
|
t2 |= PORT_LINK_STROBE | XDEV_U3;
|
||||||
set_bit(port_index, &bus_state->bus_suspended);
|
set_bit(port_index, &bus_state->bus_suspended);
|
||||||
}
|
}
|
||||||
|
/* USB core sets remote wake mask for USB 3.0 hubs,
|
||||||
|
* including the USB 3.0 roothub, but only if CONFIG_USB_SUSPEND
|
||||||
|
* is enabled, so also enable remote wake here.
|
||||||
|
*/
|
||||||
if (hcd->self.root_hub->do_remote_wakeup) {
|
if (hcd->self.root_hub->do_remote_wakeup) {
|
||||||
if (t1 & PORT_CONNECT) {
|
if (t1 & PORT_CONNECT) {
|
||||||
t2 |= PORT_WKOC_E | PORT_WKDISC_E;
|
t2 |= PORT_WKOC_E | PORT_WKDISC_E;
|
||||||
|
@ -2157,7 +2157,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
|||||||
unsigned int val, val2;
|
unsigned int val, val2;
|
||||||
u64 val_64;
|
u64 val_64;
|
||||||
struct xhci_segment *seg;
|
struct xhci_segment *seg;
|
||||||
u32 page_size;
|
u32 page_size, temp;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
page_size = xhci_readl(xhci, &xhci->op_regs->page_size);
|
page_size = xhci_readl(xhci, &xhci->op_regs->page_size);
|
||||||
@ -2340,6 +2340,15 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
|||||||
|
|
||||||
INIT_LIST_HEAD(&xhci->lpm_failed_devs);
|
INIT_LIST_HEAD(&xhci->lpm_failed_devs);
|
||||||
|
|
||||||
|
/* Enable USB 3.0 device notifications for function remote wake, which
|
||||||
|
* is necessary for allowing USB 3.0 devices to do remote wakeup from
|
||||||
|
* U3 (device suspend).
|
||||||
|
*/
|
||||||
|
temp = xhci_readl(xhci, &xhci->op_regs->dev_notification);
|
||||||
|
temp &= ~DEV_NOTE_MASK;
|
||||||
|
temp |= DEV_NOTE_FWAKE;
|
||||||
|
xhci_writel(xhci, temp, &xhci->op_regs->dev_notification);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
@ -1237,6 +1237,26 @@ static unsigned int find_faked_portnum_from_hw_portnum(struct usb_hcd *hcd,
|
|||||||
return num_similar_speed_ports;
|
return num_similar_speed_ports;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void handle_device_notification(struct xhci_hcd *xhci,
|
||||||
|
union xhci_trb *event)
|
||||||
|
{
|
||||||
|
u32 slot_id;
|
||||||
|
struct usb_device *udev;
|
||||||
|
|
||||||
|
slot_id = TRB_TO_SLOT_ID(event->generic.field[3]);
|
||||||
|
if (!xhci->devs[slot_id]) {
|
||||||
|
xhci_warn(xhci, "Device Notification event for "
|
||||||
|
"unused slot %u\n", slot_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
xhci_dbg(xhci, "Device Wake Notification event for slot ID %u\n",
|
||||||
|
slot_id);
|
||||||
|
udev = xhci->devs[slot_id]->udev;
|
||||||
|
if (udev && udev->parent)
|
||||||
|
usb_wakeup_notification(udev->parent, udev->portnum);
|
||||||
|
}
|
||||||
|
|
||||||
static void handle_port_status(struct xhci_hcd *xhci,
|
static void handle_port_status(struct xhci_hcd *xhci,
|
||||||
union xhci_trb *event)
|
union xhci_trb *event)
|
||||||
{
|
{
|
||||||
@ -1321,20 +1341,21 @@ static void handle_port_status(struct xhci_hcd *xhci,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (DEV_SUPERSPEED(temp)) {
|
if (DEV_SUPERSPEED(temp)) {
|
||||||
xhci_dbg(xhci, "resume SS port %d\n", port_id);
|
xhci_dbg(xhci, "remote wake SS port %d\n", port_id);
|
||||||
|
/* Set a flag to say the port signaled remote wakeup,
|
||||||
|
* so we can tell the difference between the end of
|
||||||
|
* device and host initiated resume.
|
||||||
|
*/
|
||||||
|
bus_state->port_remote_wakeup |= 1 << faked_port_index;
|
||||||
|
xhci_test_and_clear_bit(xhci, port_array,
|
||||||
|
faked_port_index, PORT_PLC);
|
||||||
xhci_set_link_state(xhci, port_array, faked_port_index,
|
xhci_set_link_state(xhci, port_array, faked_port_index,
|
||||||
XDEV_U0);
|
XDEV_U0);
|
||||||
slot_id = xhci_find_slot_id_by_port(hcd, xhci,
|
/* Need to wait until the next link state change
|
||||||
faked_port_index + 1);
|
* indicates the device is actually in U0.
|
||||||
if (!slot_id) {
|
*/
|
||||||
xhci_dbg(xhci, "slot_id is zero\n");
|
bogus_port_status = true;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
|
||||||
xhci_ring_device(xhci, slot_id);
|
|
||||||
xhci_dbg(xhci, "resume SS port %d finished\n", port_id);
|
|
||||||
/* Clear PORT_PLC */
|
|
||||||
xhci_test_and_clear_bit(xhci, port_array,
|
|
||||||
faked_port_index, PORT_PLC);
|
|
||||||
} else {
|
} else {
|
||||||
xhci_dbg(xhci, "resume HS port %d\n", port_id);
|
xhci_dbg(xhci, "resume HS port %d\n", port_id);
|
||||||
bus_state->resume_done[faked_port_index] = jiffies +
|
bus_state->resume_done[faked_port_index] = jiffies +
|
||||||
@ -1345,6 +1366,32 @@ static void handle_port_status(struct xhci_hcd *xhci,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_U0 &&
|
||||||
|
DEV_SUPERSPEED(temp)) {
|
||||||
|
xhci_dbg(xhci, "resume SS port %d finished\n", port_id);
|
||||||
|
/* We've just brought the device into U0 through either the
|
||||||
|
* Resume state after a device remote wakeup, or through the
|
||||||
|
* U3Exit state after a host-initiated resume. If it's a device
|
||||||
|
* initiated remote wake, don't pass up the link state change,
|
||||||
|
* so the roothub behavior is consistent with external
|
||||||
|
* USB 3.0 hub behavior.
|
||||||
|
*/
|
||||||
|
slot_id = xhci_find_slot_id_by_port(hcd, xhci,
|
||||||
|
faked_port_index + 1);
|
||||||
|
if (slot_id && xhci->devs[slot_id])
|
||||||
|
xhci_ring_device(xhci, slot_id);
|
||||||
|
if (bus_state->port_remote_wakeup && (1 << faked_port_index)) {
|
||||||
|
bus_state->port_remote_wakeup &=
|
||||||
|
~(1 << faked_port_index);
|
||||||
|
xhci_test_and_clear_bit(xhci, port_array,
|
||||||
|
faked_port_index, PORT_PLC);
|
||||||
|
usb_wakeup_notification(hcd->self.root_hub,
|
||||||
|
faked_port_index + 1);
|
||||||
|
bogus_port_status = true;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (hcd->speed != HCD_USB3)
|
if (hcd->speed != HCD_USB3)
|
||||||
xhci_test_and_clear_bit(xhci, port_array, faked_port_index,
|
xhci_test_and_clear_bit(xhci, port_array, faked_port_index,
|
||||||
PORT_PLC);
|
PORT_PLC);
|
||||||
@ -2277,6 +2324,9 @@ static int xhci_handle_event(struct xhci_hcd *xhci)
|
|||||||
else
|
else
|
||||||
update_ptrs = 0;
|
update_ptrs = 0;
|
||||||
break;
|
break;
|
||||||
|
case TRB_TYPE(TRB_DEV_NOTE):
|
||||||
|
handle_device_notification(xhci, event);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
if ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK) >=
|
if ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK) >=
|
||||||
TRB_TYPE(48))
|
TRB_TYPE(48))
|
||||||
|
@ -1344,6 +1344,7 @@ struct xhci_bus_state {
|
|||||||
/* ports suspend status arrays - max 31 ports for USB2, 15 for USB3 */
|
/* ports suspend status arrays - max 31 ports for USB2, 15 for USB3 */
|
||||||
u32 port_c_suspend;
|
u32 port_c_suspend;
|
||||||
u32 suspended_ports;
|
u32 suspended_ports;
|
||||||
|
u32 port_remote_wakeup;
|
||||||
unsigned long resume_done[USB_MAXCHILDREN];
|
unsigned long resume_done[USB_MAXCHILDREN];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -286,7 +286,7 @@ static int cp210x_get_config(struct usb_serial_port *port, u8 request,
|
|||||||
|
|
||||||
if (result != size) {
|
if (result != size) {
|
||||||
dbg("%s - Unable to send config request, "
|
dbg("%s - Unable to send config request, "
|
||||||
"request=0x%x size=%d result=%d\n",
|
"request=0x%x size=%d result=%d",
|
||||||
__func__, request, size, result);
|
__func__, request, size, result);
|
||||||
if (result > 0)
|
if (result > 0)
|
||||||
result = -EPROTO;
|
result = -EPROTO;
|
||||||
@ -340,7 +340,7 @@ static int cp210x_set_config(struct usb_serial_port *port, u8 request,
|
|||||||
|
|
||||||
if ((size > 2 && result != size) || result < 0) {
|
if ((size > 2 && result != size) || result < 0) {
|
||||||
dbg("%s - Unable to send request, "
|
dbg("%s - Unable to send request, "
|
||||||
"request=0x%x size=%d result=%d\n",
|
"request=0x%x size=%d result=%d",
|
||||||
__func__, request, size, result);
|
__func__, request, size, result);
|
||||||
if (result > 0)
|
if (result > 0)
|
||||||
result = -EPROTO;
|
result = -EPROTO;
|
||||||
@ -683,13 +683,13 @@ static void cp210x_set_termios(struct tty_struct *tty,
|
|||||||
default:
|
default:
|
||||||
dbg("cp210x driver does not "
|
dbg("cp210x driver does not "
|
||||||
"support the number of bits requested,"
|
"support the number of bits requested,"
|
||||||
" using 8 bit mode\n");
|
" using 8 bit mode");
|
||||||
bits |= BITS_DATA_8;
|
bits |= BITS_DATA_8;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
|
if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
|
||||||
dbg("Number of data bits requested "
|
dbg("Number of data bits requested "
|
||||||
"not supported by device\n");
|
"not supported by device");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((cflag & (PARENB|PARODD|CMSPAR)) !=
|
if ((cflag & (PARENB|PARODD|CMSPAR)) !=
|
||||||
@ -716,8 +716,7 @@ static void cp210x_set_termios(struct tty_struct *tty,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
|
if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
|
||||||
dbg("Parity mode not supported "
|
dbg("Parity mode not supported by device");
|
||||||
"by device\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) {
|
if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) {
|
||||||
@ -732,7 +731,7 @@ static void cp210x_set_termios(struct tty_struct *tty,
|
|||||||
}
|
}
|
||||||
if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
|
if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
|
||||||
dbg("Number of stop bits requested "
|
dbg("Number of stop bits requested "
|
||||||
"not supported by device\n");
|
"not supported by device");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
|
if ((cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
|
||||||
|
@ -800,7 +800,7 @@ send:
|
|||||||
cypress_write_int_callback, port, priv->write_urb_interval);
|
cypress_write_int_callback, port, priv->write_urb_interval);
|
||||||
result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC);
|
result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC);
|
||||||
if (result) {
|
if (result) {
|
||||||
dev_err(&port->dev,
|
dev_err_console(port,
|
||||||
"%s - failed submitting write urb, error %d\n",
|
"%s - failed submitting write urb, error %d\n",
|
||||||
__func__, result);
|
__func__, result);
|
||||||
priv->write_urb_in_use = 0;
|
priv->write_urb_in_use = 0;
|
||||||
|
@ -995,7 +995,7 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port,
|
|||||||
/* return length of new data written, or error */
|
/* return length of new data written, or error */
|
||||||
spin_unlock_irqrestore(&priv->dp_port_lock, flags);
|
spin_unlock_irqrestore(&priv->dp_port_lock, flags);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
dev_err(&port->dev,
|
dev_err_console(port,
|
||||||
"%s: usb_submit_urb failed, ret=%d, port=%d\n",
|
"%s: usb_submit_urb failed, ret=%d, port=%d\n",
|
||||||
__func__, ret, priv->dp_port_num);
|
__func__, ret, priv->dp_port_num);
|
||||||
dbg("digi_write: returning %d", ret);
|
dbg("digi_write: returning %d", ret);
|
||||||
@ -1065,7 +1065,7 @@ static void digi_write_bulk_callback(struct urb *urb)
|
|||||||
|
|
||||||
spin_unlock(&priv->dp_port_lock);
|
spin_unlock(&priv->dp_port_lock);
|
||||||
if (ret && ret != -EPERM)
|
if (ret && ret != -EPERM)
|
||||||
dev_err(&port->dev,
|
dev_err_console(port,
|
||||||
"%s: usb_submit_urb failed, ret=%d, port=%d\n",
|
"%s: usb_submit_urb failed, ret=%d, port=%d\n",
|
||||||
__func__, ret, priv->dp_port_num);
|
__func__, ret, priv->dp_port_num);
|
||||||
}
|
}
|
||||||
|
@ -217,7 +217,7 @@ retry:
|
|||||||
clear_bit(i, &port->write_urbs_free);
|
clear_bit(i, &port->write_urbs_free);
|
||||||
result = usb_submit_urb(urb, GFP_ATOMIC);
|
result = usb_submit_urb(urb, GFP_ATOMIC);
|
||||||
if (result) {
|
if (result) {
|
||||||
dev_err(&port->dev, "%s - error submitting urb: %d\n",
|
dev_err_console(port, "%s - error submitting urb: %d\n",
|
||||||
__func__, result);
|
__func__, result);
|
||||||
set_bit(i, &port->write_urbs_free);
|
set_bit(i, &port->write_urbs_free);
|
||||||
spin_lock_irqsave(&port->lock, flags);
|
spin_lock_irqsave(&port->lock, flags);
|
||||||
|
@ -1286,7 +1286,7 @@ static void send_more_port_data(struct edgeport_serial *edge_serial,
|
|||||||
count = fifo->count;
|
count = fifo->count;
|
||||||
buffer = kmalloc(count+2, GFP_ATOMIC);
|
buffer = kmalloc(count+2, GFP_ATOMIC);
|
||||||
if (buffer == NULL) {
|
if (buffer == NULL) {
|
||||||
dev_err(&edge_port->port->dev,
|
dev_err_console(edge_port->port,
|
||||||
"%s - no more kernel memory...\n", __func__);
|
"%s - no more kernel memory...\n", __func__);
|
||||||
edge_port->write_in_progress = false;
|
edge_port->write_in_progress = false;
|
||||||
goto exit_send;
|
goto exit_send;
|
||||||
@ -1331,7 +1331,7 @@ static void send_more_port_data(struct edgeport_serial *edge_serial,
|
|||||||
status = usb_submit_urb(urb, GFP_ATOMIC);
|
status = usb_submit_urb(urb, GFP_ATOMIC);
|
||||||
if (status) {
|
if (status) {
|
||||||
/* something went wrong */
|
/* something went wrong */
|
||||||
dev_err(&edge_port->port->dev,
|
dev_err_console(edge_port->port,
|
||||||
"%s - usb_submit_urb(write bulk) failed, status = %d, data lost\n",
|
"%s - usb_submit_urb(write bulk) failed, status = %d, data lost\n",
|
||||||
__func__, status);
|
__func__, status);
|
||||||
edge_port->write_in_progress = false;
|
edge_port->write_in_progress = false;
|
||||||
|
@ -1817,7 +1817,7 @@ static void edge_bulk_out_callback(struct urb *urb)
|
|||||||
__func__, status);
|
__func__, status);
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
dev_err(&urb->dev->dev, "%s - nonzero write bulk status "
|
dev_err_console(port, "%s - nonzero write bulk status "
|
||||||
"received: %d\n", __func__, status);
|
"received: %d\n", __func__, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2111,7 +2111,7 @@ static void edge_send(struct tty_struct *tty)
|
|||||||
/* send the data out the bulk port */
|
/* send the data out the bulk port */
|
||||||
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
|
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
|
||||||
if (result) {
|
if (result) {
|
||||||
dev_err(&port->dev,
|
dev_err_console(port,
|
||||||
"%s - failed submitting write urb, error %d\n",
|
"%s - failed submitting write urb, error %d\n",
|
||||||
__func__, result);
|
__func__, result);
|
||||||
edge_port->ep_write_urb_in_use = 0;
|
edge_port->ep_write_urb_in_use = 0;
|
||||||
|
@ -1294,7 +1294,7 @@ static int mos7720_write(struct tty_struct *tty, struct usb_serial_port *port,
|
|||||||
urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
|
urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (urb->transfer_buffer == NULL) {
|
if (urb->transfer_buffer == NULL) {
|
||||||
dev_err(&port->dev, "%s no more kernel memory...\n",
|
dev_err_console(port, "%s no more kernel memory...\n",
|
||||||
__func__);
|
__func__);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@ -1315,7 +1315,7 @@ static int mos7720_write(struct tty_struct *tty, struct usb_serial_port *port,
|
|||||||
/* send it down the pipe */
|
/* send it down the pipe */
|
||||||
status = usb_submit_urb(urb, GFP_ATOMIC);
|
status = usb_submit_urb(urb, GFP_ATOMIC);
|
||||||
if (status) {
|
if (status) {
|
||||||
dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed "
|
dev_err_console(port, "%s - usb_submit_urb(write bulk) failed "
|
||||||
"with status = %d\n", __func__, status);
|
"with status = %d\n", __func__, status);
|
||||||
bytes_sent = status;
|
bytes_sent = status;
|
||||||
goto exit;
|
goto exit;
|
||||||
|
@ -1509,7 +1509,7 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,
|
|||||||
kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
|
kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
|
||||||
|
|
||||||
if (urb->transfer_buffer == NULL) {
|
if (urb->transfer_buffer == NULL) {
|
||||||
dev_err(&port->dev, "%s no more kernel memory...\n",
|
dev_err_console(port, "%s no more kernel memory...\n",
|
||||||
__func__);
|
__func__);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@ -1535,7 +1535,7 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,
|
|||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
mos7840_port->busy[i] = 0;
|
mos7840_port->busy[i] = 0;
|
||||||
dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed "
|
dev_err_console(port, "%s - usb_submit_urb(write bulk) failed "
|
||||||
"with status = %d\n", __func__, status);
|
"with status = %d\n", __func__, status);
|
||||||
bytes_sent = status;
|
bytes_sent = status;
|
||||||
goto exit;
|
goto exit;
|
||||||
|
@ -254,7 +254,7 @@ static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port,
|
|||||||
result = usb_submit_urb(wport->write_urb, GFP_ATOMIC);
|
result = usb_submit_urb(wport->write_urb, GFP_ATOMIC);
|
||||||
if (result) {
|
if (result) {
|
||||||
set_bit(0, &wport->write_urbs_free);
|
set_bit(0, &wport->write_urbs_free);
|
||||||
dev_err(&port->dev,
|
dev_err_console(port,
|
||||||
"%s - failed submitting write urb, error %d\n",
|
"%s - failed submitting write urb, error %d\n",
|
||||||
__func__, result);
|
__func__, result);
|
||||||
} else
|
} else
|
||||||
|
@ -302,7 +302,7 @@ static void send_data(struct work_struct *work)
|
|||||||
if (count != 0) {
|
if (count != 0) {
|
||||||
allow = kmalloc(1, GFP_KERNEL);
|
allow = kmalloc(1, GFP_KERNEL);
|
||||||
if (!allow) {
|
if (!allow) {
|
||||||
dev_err(&port->dev, "%s(): kmalloc failed\n",
|
dev_err_console(port, "%s(): kmalloc failed\n",
|
||||||
__func__);
|
__func__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -334,7 +334,7 @@ static void send_data(struct work_struct *work)
|
|||||||
port->write_urb->transfer_buffer_length = count;
|
port->write_urb->transfer_buffer_length = count;
|
||||||
result = usb_submit_urb(port->write_urb, GFP_NOIO);
|
result = usb_submit_urb(port->write_urb, GFP_NOIO);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
dev_err(&port->dev, "%s(): usb_submit_urb() failed"
|
dev_err_console(port, "%s(): usb_submit_urb() failed"
|
||||||
" with error %d\n", __func__, result);
|
" with error %d\n", __func__, result);
|
||||||
priv->flags.write_urb_in_use = 0;
|
priv->flags.write_urb_in_use = 0;
|
||||||
}
|
}
|
||||||
@ -938,7 +938,7 @@ static void oti6858_write_bulk_callback(struct urb *urb)
|
|||||||
port->write_urb->transfer_buffer_length = 1;
|
port->write_urb->transfer_buffer_length = 1;
|
||||||
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
|
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
|
||||||
if (result) {
|
if (result) {
|
||||||
dev_err(&port->dev, "%s(): usb_submit_urb() failed,"
|
dev_err_console(port, "%s(): usb_submit_urb() failed,"
|
||||||
" error %d\n", __func__, result);
|
" error %d\n", __func__, result);
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
|
@ -1250,7 +1250,6 @@ static void ti_bulk_out_callback(struct urb *urb)
|
|||||||
{
|
{
|
||||||
struct ti_port *tport = urb->context;
|
struct ti_port *tport = urb->context;
|
||||||
struct usb_serial_port *port = tport->tp_port;
|
struct usb_serial_port *port = tport->tp_port;
|
||||||
struct device *dev = &urb->dev->dev;
|
|
||||||
int status = urb->status;
|
int status = urb->status;
|
||||||
|
|
||||||
dbg("%s - port %d", __func__, port->number);
|
dbg("%s - port %d", __func__, port->number);
|
||||||
@ -1268,7 +1267,7 @@ static void ti_bulk_out_callback(struct urb *urb)
|
|||||||
wake_up_interruptible(&tport->tp_write_wait);
|
wake_up_interruptible(&tport->tp_write_wait);
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
dev_err(dev, "%s - nonzero urb status, %d\n",
|
dev_err_console(port, "%s - nonzero urb status, %d\n",
|
||||||
__func__, status);
|
__func__, status);
|
||||||
tport->tp_tdev->td_urb_error = 1;
|
tport->tp_tdev->td_urb_error = 1;
|
||||||
wake_up_interruptible(&tport->tp_write_wait);
|
wake_up_interruptible(&tport->tp_write_wait);
|
||||||
@ -1337,7 +1336,7 @@ static void ti_send(struct ti_port *tport)
|
|||||||
|
|
||||||
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
|
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
|
||||||
if (result) {
|
if (result) {
|
||||||
dev_err(&port->dev, "%s - submit write urb failed, %d\n",
|
dev_err_console(port, "%s - submit write urb failed, %d\n",
|
||||||
__func__, result);
|
__func__, result);
|
||||||
tport->tp_write_urb_in_use = 0;
|
tport->tp_write_urb_in_use = 0;
|
||||||
/* TODO: reschedule ti_send */
|
/* TODO: reschedule ti_send */
|
||||||
|
@ -740,7 +740,7 @@ static int whiteheat_write(struct tty_struct *tty,
|
|||||||
urb->transfer_buffer_length = bytes;
|
urb->transfer_buffer_length = bytes;
|
||||||
result = usb_submit_urb(urb, GFP_ATOMIC);
|
result = usb_submit_urb(urb, GFP_ATOMIC);
|
||||||
if (result) {
|
if (result) {
|
||||||
dev_err(&port->dev,
|
dev_err_console(port,
|
||||||
"%s - failed submitting write urb, error %d\n",
|
"%s - failed submitting write urb, error %d\n",
|
||||||
__func__, result);
|
__func__, result);
|
||||||
sent = result;
|
sent = result;
|
||||||
|
@ -1276,6 +1276,7 @@ static struct usb_driver alauda_driver = {
|
|||||||
.post_reset = usb_stor_post_reset,
|
.post_reset = usb_stor_post_reset,
|
||||||
.id_table = alauda_usb_ids,
|
.id_table = alauda_usb_ids,
|
||||||
.soft_unbind = 1,
|
.soft_unbind = 1,
|
||||||
|
.no_dynamic_id = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_usb_driver(alauda_driver);
|
module_usb_driver(alauda_driver);
|
||||||
|
@ -272,6 +272,7 @@ static struct usb_driver cypress_driver = {
|
|||||||
.post_reset = usb_stor_post_reset,
|
.post_reset = usb_stor_post_reset,
|
||||||
.id_table = cypress_usb_ids,
|
.id_table = cypress_usb_ids,
|
||||||
.soft_unbind = 1,
|
.soft_unbind = 1,
|
||||||
|
.no_dynamic_id = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_usb_driver(cypress_driver);
|
module_usb_driver(cypress_driver);
|
||||||
|
@ -751,6 +751,7 @@ static struct usb_driver datafab_driver = {
|
|||||||
.post_reset = usb_stor_post_reset,
|
.post_reset = usb_stor_post_reset,
|
||||||
.id_table = datafab_usb_ids,
|
.id_table = datafab_usb_ids,
|
||||||
.soft_unbind = 1,
|
.soft_unbind = 1,
|
||||||
|
.no_dynamic_id = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_usb_driver(datafab_driver);
|
module_usb_driver(datafab_driver);
|
||||||
|
@ -2407,6 +2407,7 @@ static struct usb_driver ene_ub6250_driver = {
|
|||||||
.post_reset = usb_stor_post_reset,
|
.post_reset = usb_stor_post_reset,
|
||||||
.id_table = ene_ub6250_usb_ids,
|
.id_table = ene_ub6250_usb_ids,
|
||||||
.soft_unbind = 1,
|
.soft_unbind = 1,
|
||||||
|
.no_dynamic_id = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_usb_driver(ene_ub6250_driver);
|
module_usb_driver(ene_ub6250_driver);
|
||||||
|
@ -553,6 +553,7 @@ static struct usb_driver freecom_driver = {
|
|||||||
.post_reset = usb_stor_post_reset,
|
.post_reset = usb_stor_post_reset,
|
||||||
.id_table = freecom_usb_ids,
|
.id_table = freecom_usb_ids,
|
||||||
.soft_unbind = 1,
|
.soft_unbind = 1,
|
||||||
|
.no_dynamic_id = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_usb_driver(freecom_driver);
|
module_usb_driver(freecom_driver);
|
||||||
|
@ -1566,6 +1566,7 @@ static struct usb_driver isd200_driver = {
|
|||||||
.post_reset = usb_stor_post_reset,
|
.post_reset = usb_stor_post_reset,
|
||||||
.id_table = isd200_usb_ids,
|
.id_table = isd200_usb_ids,
|
||||||
.soft_unbind = 1,
|
.soft_unbind = 1,
|
||||||
|
.no_dynamic_id = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_usb_driver(isd200_driver);
|
module_usb_driver(isd200_driver);
|
||||||
|
@ -677,6 +677,7 @@ static struct usb_driver jumpshot_driver = {
|
|||||||
.post_reset = usb_stor_post_reset,
|
.post_reset = usb_stor_post_reset,
|
||||||
.id_table = jumpshot_usb_ids,
|
.id_table = jumpshot_usb_ids,
|
||||||
.soft_unbind = 1,
|
.soft_unbind = 1,
|
||||||
|
.no_dynamic_id = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_usb_driver(jumpshot_driver);
|
module_usb_driver(jumpshot_driver);
|
||||||
|
@ -230,6 +230,7 @@ static struct usb_driver karma_driver = {
|
|||||||
.post_reset = usb_stor_post_reset,
|
.post_reset = usb_stor_post_reset,
|
||||||
.id_table = karma_usb_ids,
|
.id_table = karma_usb_ids,
|
||||||
.soft_unbind = 1,
|
.soft_unbind = 1,
|
||||||
|
.no_dynamic_id = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_usb_driver(karma_driver);
|
module_usb_driver(karma_driver);
|
||||||
|
@ -312,6 +312,7 @@ static struct usb_driver onetouch_driver = {
|
|||||||
.post_reset = usb_stor_post_reset,
|
.post_reset = usb_stor_post_reset,
|
||||||
.id_table = onetouch_usb_ids,
|
.id_table = onetouch_usb_ids,
|
||||||
.soft_unbind = 1,
|
.soft_unbind = 1,
|
||||||
|
.no_dynamic_id = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_usb_driver(onetouch_driver);
|
module_usb_driver(onetouch_driver);
|
||||||
|
@ -1100,6 +1100,7 @@ static struct usb_driver realtek_cr_driver = {
|
|||||||
.id_table = realtek_cr_ids,
|
.id_table = realtek_cr_ids,
|
||||||
.soft_unbind = 1,
|
.soft_unbind = 1,
|
||||||
.supports_autosuspend = 1,
|
.supports_autosuspend = 1,
|
||||||
|
.no_dynamic_id = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_usb_driver(realtek_cr_driver);
|
module_usb_driver(realtek_cr_driver);
|
||||||
|
@ -78,8 +78,6 @@ static const char* host_info(struct Scsi_Host *host)
|
|||||||
|
|
||||||
static int slave_alloc (struct scsi_device *sdev)
|
static int slave_alloc (struct scsi_device *sdev)
|
||||||
{
|
{
|
||||||
struct us_data *us = host_to_us(sdev->host);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the INQUIRY transfer length to 36. We don't use any of
|
* Set the INQUIRY transfer length to 36. We don't use any of
|
||||||
* the extra data and many devices choke if asked for more or
|
* the extra data and many devices choke if asked for more or
|
||||||
@ -104,18 +102,6 @@ static int slave_alloc (struct scsi_device *sdev)
|
|||||||
*/
|
*/
|
||||||
blk_queue_update_dma_alignment(sdev->request_queue, (512 - 1));
|
blk_queue_update_dma_alignment(sdev->request_queue, (512 - 1));
|
||||||
|
|
||||||
/*
|
|
||||||
* The UFI spec treates the Peripheral Qualifier bits in an
|
|
||||||
* INQUIRY result as reserved and requires devices to set them
|
|
||||||
* to 0. However the SCSI spec requires these bits to be set
|
|
||||||
* to 3 to indicate when a LUN is not present.
|
|
||||||
*
|
|
||||||
* Let the scanning code know if this target merely sets
|
|
||||||
* Peripheral Device Type to 0x1f to indicate no LUN.
|
|
||||||
*/
|
|
||||||
if (us->subclass == USB_SC_UFI)
|
|
||||||
sdev->sdev_target->pdt_1f_for_no_lun = 1;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,6 +183,9 @@ static int slave_configure(struct scsi_device *sdev)
|
|||||||
* page x08, so we will skip it. */
|
* page x08, so we will skip it. */
|
||||||
sdev->skip_ms_page_8 = 1;
|
sdev->skip_ms_page_8 = 1;
|
||||||
|
|
||||||
|
/* Some devices don't handle VPD pages correctly */
|
||||||
|
sdev->skip_vpd_pages = 1;
|
||||||
|
|
||||||
/* Some disks return the total number of blocks in response
|
/* Some disks return the total number of blocks in response
|
||||||
* to READ CAPACITY rather than the highest block number.
|
* to READ CAPACITY rather than the highest block number.
|
||||||
* If this device makes that mistake, tell the sd driver. */
|
* If this device makes that mistake, tell the sd driver. */
|
||||||
@ -217,16 +206,6 @@ static int slave_configure(struct scsi_device *sdev)
|
|||||||
if (sdev->scsi_level > SCSI_SPC_2)
|
if (sdev->scsi_level > SCSI_SPC_2)
|
||||||
us->fflags |= US_FL_SANE_SENSE;
|
us->fflags |= US_FL_SANE_SENSE;
|
||||||
|
|
||||||
/* Some devices report a SCSI revision level above 2 but are
|
|
||||||
* unable to handle the REPORT LUNS command (for which
|
|
||||||
* support is mandatory at level 3). Since we already have
|
|
||||||
* a Get-Max-LUN request, we won't lose much by setting the
|
|
||||||
* revision level down to 2. The only devices that would be
|
|
||||||
* affected are those with sparse LUNs. */
|
|
||||||
if (sdev->scsi_level > SCSI_2)
|
|
||||||
sdev->sdev_target->scsi_level =
|
|
||||||
sdev->scsi_level = SCSI_2;
|
|
||||||
|
|
||||||
/* USB-IDE bridges tend to report SK = 0x04 (Non-recoverable
|
/* USB-IDE bridges tend to report SK = 0x04 (Non-recoverable
|
||||||
* Hardware Error) when any low-level error occurs,
|
* Hardware Error) when any low-level error occurs,
|
||||||
* recoverable or not. Setting this flag tells the SCSI
|
* recoverable or not. Setting this flag tells the SCSI
|
||||||
@ -283,6 +262,33 @@ static int slave_configure(struct scsi_device *sdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int target_alloc(struct scsi_target *starget)
|
||||||
|
{
|
||||||
|
struct us_data *us = host_to_us(dev_to_shost(starget->dev.parent));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some USB drives don't support REPORT LUNS, even though they
|
||||||
|
* report a SCSI revision level above 2. Tell the SCSI layer
|
||||||
|
* not to issue that command; it will perform a normal sequential
|
||||||
|
* scan instead.
|
||||||
|
*/
|
||||||
|
starget->no_report_luns = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The UFI spec treats the Peripheral Qualifier bits in an
|
||||||
|
* INQUIRY result as reserved and requires devices to set them
|
||||||
|
* to 0. However the SCSI spec requires these bits to be set
|
||||||
|
* to 3 to indicate when a LUN is not present.
|
||||||
|
*
|
||||||
|
* Let the scanning code know if this target merely sets
|
||||||
|
* Peripheral Device Type to 0x1f to indicate no LUN.
|
||||||
|
*/
|
||||||
|
if (us->subclass == USB_SC_UFI)
|
||||||
|
starget->pdt_1f_for_no_lun = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* queue a command */
|
/* queue a command */
|
||||||
/* This is always called with scsi_lock(host) held */
|
/* This is always called with scsi_lock(host) held */
|
||||||
static int queuecommand_lck(struct scsi_cmnd *srb,
|
static int queuecommand_lck(struct scsi_cmnd *srb,
|
||||||
@ -546,6 +552,7 @@ struct scsi_host_template usb_stor_host_template = {
|
|||||||
|
|
||||||
.slave_alloc = slave_alloc,
|
.slave_alloc = slave_alloc,
|
||||||
.slave_configure = slave_configure,
|
.slave_configure = slave_configure,
|
||||||
|
.target_alloc = target_alloc,
|
||||||
|
|
||||||
/* lots of sg segments can be handled */
|
/* lots of sg segments can be handled */
|
||||||
.sg_tablesize = SCSI_MAX_SG_CHAIN_SEGMENTS,
|
.sg_tablesize = SCSI_MAX_SG_CHAIN_SEGMENTS,
|
||||||
|
@ -1787,6 +1787,7 @@ static struct usb_driver sddr09_driver = {
|
|||||||
.post_reset = usb_stor_post_reset,
|
.post_reset = usb_stor_post_reset,
|
||||||
.id_table = sddr09_usb_ids,
|
.id_table = sddr09_usb_ids,
|
||||||
.soft_unbind = 1,
|
.soft_unbind = 1,
|
||||||
|
.no_dynamic_id = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_usb_driver(sddr09_driver);
|
module_usb_driver(sddr09_driver);
|
||||||
|
@ -1006,6 +1006,7 @@ static struct usb_driver sddr55_driver = {
|
|||||||
.post_reset = usb_stor_post_reset,
|
.post_reset = usb_stor_post_reset,
|
||||||
.id_table = sddr55_usb_ids,
|
.id_table = sddr55_usb_ids,
|
||||||
.soft_unbind = 1,
|
.soft_unbind = 1,
|
||||||
|
.no_dynamic_id = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_usb_driver(sddr55_driver);
|
module_usb_driver(sddr55_driver);
|
||||||
|
@ -1863,6 +1863,7 @@ static struct usb_driver usbat_driver = {
|
|||||||
.post_reset = usb_stor_post_reset,
|
.post_reset = usb_stor_post_reset,
|
||||||
.id_table = usbat_usb_ids,
|
.id_table = usbat_usb_ids,
|
||||||
.soft_unbind = 1,
|
.soft_unbind = 1,
|
||||||
|
.no_dynamic_id = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_usb_driver(usbat_driver);
|
module_usb_driver(usbat_driver);
|
||||||
|
@ -13,7 +13,9 @@
|
|||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
|
#include <linux/usb/hcd.h>
|
||||||
#include <linux/usb/storage.h>
|
#include <linux/usb/storage.h>
|
||||||
|
#include <linux/usb/uas.h>
|
||||||
|
|
||||||
#include <scsi/scsi.h>
|
#include <scsi/scsi.h>
|
||||||
#include <scsi/scsi_dbg.h>
|
#include <scsi/scsi_dbg.h>
|
||||||
@ -22,49 +24,6 @@
|
|||||||
#include <scsi/scsi_host.h>
|
#include <scsi/scsi_host.h>
|
||||||
#include <scsi/scsi_tcq.h>
|
#include <scsi/scsi_tcq.h>
|
||||||
|
|
||||||
/* Common header for all IUs */
|
|
||||||
struct iu {
|
|
||||||
__u8 iu_id;
|
|
||||||
__u8 rsvd1;
|
|
||||||
__be16 tag;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
|
||||||
IU_ID_COMMAND = 0x01,
|
|
||||||
IU_ID_STATUS = 0x03,
|
|
||||||
IU_ID_RESPONSE = 0x04,
|
|
||||||
IU_ID_TASK_MGMT = 0x05,
|
|
||||||
IU_ID_READ_READY = 0x06,
|
|
||||||
IU_ID_WRITE_READY = 0x07,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct command_iu {
|
|
||||||
__u8 iu_id;
|
|
||||||
__u8 rsvd1;
|
|
||||||
__be16 tag;
|
|
||||||
__u8 prio_attr;
|
|
||||||
__u8 rsvd5;
|
|
||||||
__u8 len;
|
|
||||||
__u8 rsvd7;
|
|
||||||
struct scsi_lun lun;
|
|
||||||
__u8 cdb[16]; /* XXX: Overflow-checking tools may misunderstand */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Also used for the Read Ready and Write Ready IUs since they have the
|
|
||||||
* same first four bytes
|
|
||||||
*/
|
|
||||||
struct sense_iu {
|
|
||||||
__u8 iu_id;
|
|
||||||
__u8 rsvd1;
|
|
||||||
__be16 tag;
|
|
||||||
__be16 status_qual;
|
|
||||||
__u8 status;
|
|
||||||
__u8 rsvd7[7];
|
|
||||||
__be16 len;
|
|
||||||
__u8 sense[SCSI_SENSE_BUFFERSIZE];
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The r00-r01c specs define this version of the SENSE IU data structure.
|
* The r00-r01c specs define this version of the SENSE IU data structure.
|
||||||
* It's still in use by several different firmware releases.
|
* It's still in use by several different firmware releases.
|
||||||
@ -79,18 +38,6 @@ struct sense_iu_old {
|
|||||||
__u8 sense[SCSI_SENSE_BUFFERSIZE];
|
__u8 sense[SCSI_SENSE_BUFFERSIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
|
||||||
CMD_PIPE_ID = 1,
|
|
||||||
STATUS_PIPE_ID = 2,
|
|
||||||
DATA_IN_PIPE_ID = 3,
|
|
||||||
DATA_OUT_PIPE_ID = 4,
|
|
||||||
|
|
||||||
UAS_SIMPLE_TAG = 0,
|
|
||||||
UAS_HEAD_TAG = 1,
|
|
||||||
UAS_ORDERED_TAG = 2,
|
|
||||||
UAS_ACA = 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct uas_dev_info {
|
struct uas_dev_info {
|
||||||
struct usb_interface *intf;
|
struct usb_interface *intf;
|
||||||
struct usb_device *udev;
|
struct usb_device *udev;
|
||||||
@ -98,6 +45,8 @@ struct uas_dev_info {
|
|||||||
unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe;
|
unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe;
|
||||||
unsigned use_streams:1;
|
unsigned use_streams:1;
|
||||||
unsigned uas_sense_old:1;
|
unsigned uas_sense_old:1;
|
||||||
|
struct scsi_cmnd *cmnd;
|
||||||
|
struct urb *status_urb; /* used only if stream support is available */
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@ -109,6 +58,9 @@ enum {
|
|||||||
SUBMIT_DATA_OUT_URB = (1 << 5),
|
SUBMIT_DATA_OUT_URB = (1 << 5),
|
||||||
ALLOC_CMD_URB = (1 << 6),
|
ALLOC_CMD_URB = (1 << 6),
|
||||||
SUBMIT_CMD_URB = (1 << 7),
|
SUBMIT_CMD_URB = (1 << 7),
|
||||||
|
COMPLETED_DATA_IN = (1 << 8),
|
||||||
|
COMPLETED_DATA_OUT = (1 << 9),
|
||||||
|
DATA_COMPLETES_CMD = (1 << 10),
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Overrides scsi_pointer */
|
/* Overrides scsi_pointer */
|
||||||
@ -116,6 +68,7 @@ struct uas_cmd_info {
|
|||||||
unsigned int state;
|
unsigned int state;
|
||||||
unsigned int stream;
|
unsigned int stream;
|
||||||
struct urb *cmd_urb;
|
struct urb *cmd_urb;
|
||||||
|
/* status_urb is used only if stream support isn't available */
|
||||||
struct urb *status_urb;
|
struct urb *status_urb;
|
||||||
struct urb *data_in_urb;
|
struct urb *data_in_urb;
|
||||||
struct urb *data_out_urb;
|
struct urb *data_out_urb;
|
||||||
@ -125,33 +78,43 @@ struct uas_cmd_info {
|
|||||||
/* I hate forward declarations, but I actually have a loop */
|
/* I hate forward declarations, but I actually have a loop */
|
||||||
static int uas_submit_urbs(struct scsi_cmnd *cmnd,
|
static int uas_submit_urbs(struct scsi_cmnd *cmnd,
|
||||||
struct uas_dev_info *devinfo, gfp_t gfp);
|
struct uas_dev_info *devinfo, gfp_t gfp);
|
||||||
|
static void uas_do_work(struct work_struct *work);
|
||||||
|
|
||||||
|
static DECLARE_WORK(uas_work, uas_do_work);
|
||||||
static DEFINE_SPINLOCK(uas_work_lock);
|
static DEFINE_SPINLOCK(uas_work_lock);
|
||||||
static LIST_HEAD(uas_work_list);
|
static LIST_HEAD(uas_work_list);
|
||||||
|
|
||||||
static void uas_do_work(struct work_struct *work)
|
static void uas_do_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct uas_cmd_info *cmdinfo;
|
struct uas_cmd_info *cmdinfo;
|
||||||
|
struct uas_cmd_info *temp;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
int err;
|
||||||
|
|
||||||
spin_lock_irq(&uas_work_lock);
|
spin_lock_irq(&uas_work_lock);
|
||||||
list_replace_init(&uas_work_list, &list);
|
list_replace_init(&uas_work_list, &list);
|
||||||
spin_unlock_irq(&uas_work_lock);
|
spin_unlock_irq(&uas_work_lock);
|
||||||
|
|
||||||
list_for_each_entry(cmdinfo, &list, list) {
|
list_for_each_entry_safe(cmdinfo, temp, &list, list) {
|
||||||
struct scsi_pointer *scp = (void *)cmdinfo;
|
struct scsi_pointer *scp = (void *)cmdinfo;
|
||||||
struct scsi_cmnd *cmnd = container_of(scp,
|
struct scsi_cmnd *cmnd = container_of(scp,
|
||||||
struct scsi_cmnd, SCp);
|
struct scsi_cmnd, SCp);
|
||||||
uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO);
|
err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO);
|
||||||
|
if (err) {
|
||||||
|
list_del(&cmdinfo->list);
|
||||||
|
spin_lock_irq(&uas_work_lock);
|
||||||
|
list_add_tail(&cmdinfo->list, &uas_work_list);
|
||||||
|
spin_unlock_irq(&uas_work_lock);
|
||||||
|
schedule_work(&uas_work);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static DECLARE_WORK(uas_work, uas_do_work);
|
|
||||||
|
|
||||||
static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd)
|
static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd)
|
||||||
{
|
{
|
||||||
struct sense_iu *sense_iu = urb->transfer_buffer;
|
struct sense_iu *sense_iu = urb->transfer_buffer;
|
||||||
struct scsi_device *sdev = cmnd->device;
|
struct scsi_device *sdev = cmnd->device;
|
||||||
|
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
|
||||||
|
|
||||||
if (urb->actual_length > 16) {
|
if (urb->actual_length > 16) {
|
||||||
unsigned len = be16_to_cpup(&sense_iu->len);
|
unsigned len = be16_to_cpup(&sense_iu->len);
|
||||||
@ -169,16 +132,15 @@ static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmnd->result = sense_iu->status;
|
cmnd->result = sense_iu->status;
|
||||||
if (sdev->current_cmnd)
|
if (!(cmdinfo->state & DATA_COMPLETES_CMD))
|
||||||
sdev->current_cmnd = NULL;
|
cmnd->scsi_done(cmnd);
|
||||||
cmnd->scsi_done(cmnd);
|
|
||||||
usb_free_urb(urb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd)
|
static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd)
|
||||||
{
|
{
|
||||||
struct sense_iu_old *sense_iu = urb->transfer_buffer;
|
struct sense_iu_old *sense_iu = urb->transfer_buffer;
|
||||||
struct scsi_device *sdev = cmnd->device;
|
struct scsi_device *sdev = cmnd->device;
|
||||||
|
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
|
||||||
|
|
||||||
if (urb->actual_length > 8) {
|
if (urb->actual_length > 8) {
|
||||||
unsigned len = be16_to_cpup(&sense_iu->len) - 2;
|
unsigned len = be16_to_cpup(&sense_iu->len) - 2;
|
||||||
@ -196,10 +158,8 @@ static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmnd->result = sense_iu->status;
|
cmnd->result = sense_iu->status;
|
||||||
if (sdev->current_cmnd)
|
if (!(cmdinfo->state & DATA_COMPLETES_CMD))
|
||||||
sdev->current_cmnd = NULL;
|
cmnd->scsi_done(cmnd);
|
||||||
cmnd->scsi_done(cmnd);
|
|
||||||
usb_free_urb(urb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd,
|
static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd,
|
||||||
@ -208,7 +168,7 @@ static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd,
|
|||||||
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
|
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
cmdinfo->state = direction | SUBMIT_STATUS_URB;
|
cmdinfo->state = direction;
|
||||||
err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC);
|
err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC);
|
||||||
if (err) {
|
if (err) {
|
||||||
spin_lock(&uas_work_lock);
|
spin_lock(&uas_work_lock);
|
||||||
@ -221,27 +181,61 @@ static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd,
|
|||||||
static void uas_stat_cmplt(struct urb *urb)
|
static void uas_stat_cmplt(struct urb *urb)
|
||||||
{
|
{
|
||||||
struct iu *iu = urb->transfer_buffer;
|
struct iu *iu = urb->transfer_buffer;
|
||||||
struct scsi_device *sdev = urb->context;
|
struct Scsi_Host *shost = urb->context;
|
||||||
struct uas_dev_info *devinfo = sdev->hostdata;
|
struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
|
||||||
struct scsi_cmnd *cmnd;
|
struct scsi_cmnd *cmnd;
|
||||||
|
struct uas_cmd_info *cmdinfo;
|
||||||
u16 tag;
|
u16 tag;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (urb->status) {
|
if (urb->status) {
|
||||||
dev_err(&urb->dev->dev, "URB BAD STATUS %d\n", urb->status);
|
dev_err(&urb->dev->dev, "URB BAD STATUS %d\n", urb->status);
|
||||||
usb_free_urb(urb);
|
if (devinfo->use_streams)
|
||||||
|
usb_free_urb(urb);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tag = be16_to_cpup(&iu->tag) - 1;
|
tag = be16_to_cpup(&iu->tag) - 1;
|
||||||
if (sdev->current_cmnd)
|
if (tag == 0)
|
||||||
cmnd = sdev->current_cmnd;
|
cmnd = devinfo->cmnd;
|
||||||
else
|
else
|
||||||
cmnd = scsi_find_tag(sdev, tag);
|
cmnd = scsi_host_find_tag(shost, tag - 1);
|
||||||
if (!cmnd)
|
if (!cmnd) {
|
||||||
|
if (devinfo->use_streams) {
|
||||||
|
usb_free_urb(urb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ret = usb_submit_urb(urb, GFP_ATOMIC);
|
||||||
|
if (ret)
|
||||||
|
dev_err(&urb->dev->dev, "failed submit status urb\n");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
cmdinfo = (void *)&cmnd->SCp;
|
||||||
|
|
||||||
switch (iu->iu_id) {
|
switch (iu->iu_id) {
|
||||||
case IU_ID_STATUS:
|
case IU_ID_STATUS:
|
||||||
|
if (devinfo->cmnd == cmnd)
|
||||||
|
devinfo->cmnd = NULL;
|
||||||
|
|
||||||
|
if (!(cmdinfo->state & COMPLETED_DATA_IN) &&
|
||||||
|
cmdinfo->data_in_urb) {
|
||||||
|
if (devinfo->use_streams) {
|
||||||
|
cmdinfo->state |= DATA_COMPLETES_CMD;
|
||||||
|
usb_unlink_urb(cmdinfo->data_in_urb);
|
||||||
|
} else {
|
||||||
|
usb_free_urb(cmdinfo->data_in_urb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!(cmdinfo->state & COMPLETED_DATA_OUT) &&
|
||||||
|
cmdinfo->data_out_urb) {
|
||||||
|
if (devinfo->use_streams) {
|
||||||
|
cmdinfo->state |= DATA_COMPLETES_CMD;
|
||||||
|
usb_unlink_urb(cmdinfo->data_in_urb);
|
||||||
|
} else {
|
||||||
|
usb_free_urb(cmdinfo->data_out_urb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (urb->actual_length < 16)
|
if (urb->actual_length < 16)
|
||||||
devinfo->uas_sense_old = 1;
|
devinfo->uas_sense_old = 1;
|
||||||
if (devinfo->uas_sense_old)
|
if (devinfo->uas_sense_old)
|
||||||
@ -259,29 +253,70 @@ static void uas_stat_cmplt(struct urb *urb)
|
|||||||
scmd_printk(KERN_ERR, cmnd,
|
scmd_printk(KERN_ERR, cmnd,
|
||||||
"Bogus IU (%d) received on status pipe\n", iu->iu_id);
|
"Bogus IU (%d) received on status pipe\n", iu->iu_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (devinfo->use_streams) {
|
||||||
|
usb_free_urb(urb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = usb_submit_urb(urb, GFP_ATOMIC);
|
||||||
|
if (ret)
|
||||||
|
dev_err(&urb->dev->dev, "failed submit status urb\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uas_data_cmplt(struct urb *urb)
|
static void uas_data_out_cmplt(struct urb *urb)
|
||||||
{
|
{
|
||||||
struct scsi_data_buffer *sdb = urb->context;
|
struct scsi_cmnd *cmnd = urb->context;
|
||||||
|
struct scsi_data_buffer *sdb = scsi_out(cmnd);
|
||||||
|
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
|
||||||
|
|
||||||
|
cmdinfo->state |= COMPLETED_DATA_OUT;
|
||||||
|
|
||||||
sdb->resid = sdb->length - urb->actual_length;
|
sdb->resid = sdb->length - urb->actual_length;
|
||||||
usb_free_urb(urb);
|
usb_free_urb(urb);
|
||||||
|
|
||||||
|
if (cmdinfo->state & DATA_COMPLETES_CMD)
|
||||||
|
cmnd->scsi_done(cmnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uas_data_in_cmplt(struct urb *urb)
|
||||||
|
{
|
||||||
|
struct scsi_cmnd *cmnd = urb->context;
|
||||||
|
struct scsi_data_buffer *sdb = scsi_in(cmnd);
|
||||||
|
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
|
||||||
|
|
||||||
|
cmdinfo->state |= COMPLETED_DATA_IN;
|
||||||
|
|
||||||
|
sdb->resid = sdb->length - urb->actual_length;
|
||||||
|
usb_free_urb(urb);
|
||||||
|
|
||||||
|
if (cmdinfo->state & DATA_COMPLETES_CMD)
|
||||||
|
cmnd->scsi_done(cmnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp,
|
static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp,
|
||||||
unsigned int pipe, u16 stream_id,
|
unsigned int pipe, struct scsi_cmnd *cmnd,
|
||||||
struct scsi_data_buffer *sdb,
|
enum dma_data_direction dir)
|
||||||
enum dma_data_direction dir)
|
|
||||||
{
|
{
|
||||||
|
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
|
||||||
struct usb_device *udev = devinfo->udev;
|
struct usb_device *udev = devinfo->udev;
|
||||||
struct urb *urb = usb_alloc_urb(0, gfp);
|
struct urb *urb = usb_alloc_urb(0, gfp);
|
||||||
|
struct scsi_data_buffer *sdb;
|
||||||
|
usb_complete_t complete_fn;
|
||||||
|
u16 stream_id = cmdinfo->stream;
|
||||||
|
|
||||||
if (!urb)
|
if (!urb)
|
||||||
goto out;
|
goto out;
|
||||||
usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length, uas_data_cmplt,
|
if (dir == DMA_FROM_DEVICE) {
|
||||||
sdb);
|
sdb = scsi_in(cmnd);
|
||||||
if (devinfo->use_streams)
|
complete_fn = uas_data_in_cmplt;
|
||||||
urb->stream_id = stream_id;
|
} else {
|
||||||
|
sdb = scsi_out(cmnd);
|
||||||
|
complete_fn = uas_data_out_cmplt;
|
||||||
|
}
|
||||||
|
usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length,
|
||||||
|
complete_fn, cmnd);
|
||||||
|
urb->stream_id = stream_id;
|
||||||
urb->num_sgs = udev->bus->sg_tablesize ? sdb->table.nents : 0;
|
urb->num_sgs = udev->bus->sg_tablesize ? sdb->table.nents : 0;
|
||||||
urb->sg = sdb->table.sgl;
|
urb->sg = sdb->table.sgl;
|
||||||
out:
|
out:
|
||||||
@ -289,7 +324,7 @@ static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp,
|
static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp,
|
||||||
struct scsi_cmnd *cmnd, u16 stream_id)
|
struct Scsi_Host *shost, u16 stream_id)
|
||||||
{
|
{
|
||||||
struct usb_device *udev = devinfo->udev;
|
struct usb_device *udev = devinfo->udev;
|
||||||
struct urb *urb = usb_alloc_urb(0, gfp);
|
struct urb *urb = usb_alloc_urb(0, gfp);
|
||||||
@ -303,7 +338,7 @@ static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp,
|
|||||||
goto free;
|
goto free;
|
||||||
|
|
||||||
usb_fill_bulk_urb(urb, udev, devinfo->status_pipe, iu, sizeof(*iu),
|
usb_fill_bulk_urb(urb, udev, devinfo->status_pipe, iu, sizeof(*iu),
|
||||||
uas_stat_cmplt, cmnd->device);
|
uas_stat_cmplt, shost);
|
||||||
urb->stream_id = stream_id;
|
urb->stream_id = stream_id;
|
||||||
urb->transfer_flags |= URB_FREE_BUFFER;
|
urb->transfer_flags |= URB_FREE_BUFFER;
|
||||||
out:
|
out:
|
||||||
@ -334,7 +369,10 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp,
|
|||||||
goto free;
|
goto free;
|
||||||
|
|
||||||
iu->iu_id = IU_ID_COMMAND;
|
iu->iu_id = IU_ID_COMMAND;
|
||||||
iu->tag = cpu_to_be16(stream_id);
|
if (blk_rq_tagged(cmnd->request))
|
||||||
|
iu->tag = cpu_to_be16(cmnd->request->tag + 2);
|
||||||
|
else
|
||||||
|
iu->tag = cpu_to_be16(1);
|
||||||
iu->prio_attr = UAS_SIMPLE_TAG;
|
iu->prio_attr = UAS_SIMPLE_TAG;
|
||||||
iu->len = len;
|
iu->len = len;
|
||||||
int_to_scsilun(sdev->lun, &iu->lun);
|
int_to_scsilun(sdev->lun, &iu->lun);
|
||||||
@ -362,8 +400,8 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
|
|||||||
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
|
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
|
||||||
|
|
||||||
if (cmdinfo->state & ALLOC_STATUS_URB) {
|
if (cmdinfo->state & ALLOC_STATUS_URB) {
|
||||||
cmdinfo->status_urb = uas_alloc_sense_urb(devinfo, gfp, cmnd,
|
cmdinfo->status_urb = uas_alloc_sense_urb(devinfo, gfp,
|
||||||
cmdinfo->stream);
|
cmnd->device->host, cmdinfo->stream);
|
||||||
if (!cmdinfo->status_urb)
|
if (!cmdinfo->status_urb)
|
||||||
return SCSI_MLQUEUE_DEVICE_BUSY;
|
return SCSI_MLQUEUE_DEVICE_BUSY;
|
||||||
cmdinfo->state &= ~ALLOC_STATUS_URB;
|
cmdinfo->state &= ~ALLOC_STATUS_URB;
|
||||||
@ -380,8 +418,8 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
|
|||||||
|
|
||||||
if (cmdinfo->state & ALLOC_DATA_IN_URB) {
|
if (cmdinfo->state & ALLOC_DATA_IN_URB) {
|
||||||
cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, gfp,
|
cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, gfp,
|
||||||
devinfo->data_in_pipe, cmdinfo->stream,
|
devinfo->data_in_pipe, cmnd,
|
||||||
scsi_in(cmnd), DMA_FROM_DEVICE);
|
DMA_FROM_DEVICE);
|
||||||
if (!cmdinfo->data_in_urb)
|
if (!cmdinfo->data_in_urb)
|
||||||
return SCSI_MLQUEUE_DEVICE_BUSY;
|
return SCSI_MLQUEUE_DEVICE_BUSY;
|
||||||
cmdinfo->state &= ~ALLOC_DATA_IN_URB;
|
cmdinfo->state &= ~ALLOC_DATA_IN_URB;
|
||||||
@ -398,8 +436,8 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
|
|||||||
|
|
||||||
if (cmdinfo->state & ALLOC_DATA_OUT_URB) {
|
if (cmdinfo->state & ALLOC_DATA_OUT_URB) {
|
||||||
cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, gfp,
|
cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, gfp,
|
||||||
devinfo->data_out_pipe, cmdinfo->stream,
|
devinfo->data_out_pipe, cmnd,
|
||||||
scsi_out(cmnd), DMA_TO_DEVICE);
|
DMA_TO_DEVICE);
|
||||||
if (!cmdinfo->data_out_urb)
|
if (!cmdinfo->data_out_urb)
|
||||||
return SCSI_MLQUEUE_DEVICE_BUSY;
|
return SCSI_MLQUEUE_DEVICE_BUSY;
|
||||||
cmdinfo->state &= ~ALLOC_DATA_OUT_URB;
|
cmdinfo->state &= ~ALLOC_DATA_OUT_URB;
|
||||||
@ -444,13 +482,13 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
|
|||||||
|
|
||||||
BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer));
|
BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer));
|
||||||
|
|
||||||
if (!cmdinfo->status_urb && sdev->current_cmnd)
|
if (devinfo->cmnd)
|
||||||
return SCSI_MLQUEUE_DEVICE_BUSY;
|
return SCSI_MLQUEUE_DEVICE_BUSY;
|
||||||
|
|
||||||
if (blk_rq_tagged(cmnd->request)) {
|
if (blk_rq_tagged(cmnd->request)) {
|
||||||
cmdinfo->stream = cmnd->request->tag + 1;
|
cmdinfo->stream = cmnd->request->tag + 2;
|
||||||
} else {
|
} else {
|
||||||
sdev->current_cmnd = cmnd;
|
devinfo->cmnd = cmnd;
|
||||||
cmdinfo->stream = 1;
|
cmdinfo->stream = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -472,7 +510,8 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!devinfo->use_streams) {
|
if (!devinfo->use_streams) {
|
||||||
cmdinfo->state &= ~(SUBMIT_DATA_IN_URB | SUBMIT_DATA_OUT_URB);
|
cmdinfo->state &= ~(SUBMIT_DATA_IN_URB | SUBMIT_DATA_OUT_URB |
|
||||||
|
ALLOC_STATUS_URB | SUBMIT_STATUS_URB);
|
||||||
cmdinfo->stream = 0;
|
cmdinfo->stream = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -551,7 +590,7 @@ static int uas_slave_configure(struct scsi_device *sdev)
|
|||||||
{
|
{
|
||||||
struct uas_dev_info *devinfo = sdev->hostdata;
|
struct uas_dev_info *devinfo = sdev->hostdata;
|
||||||
scsi_set_tag_type(sdev, MSG_ORDERED_TAG);
|
scsi_set_tag_type(sdev, MSG_ORDERED_TAG);
|
||||||
scsi_activate_tcq(sdev, devinfo->qdepth - 1);
|
scsi_activate_tcq(sdev, devinfo->qdepth - 2);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -589,22 +628,34 @@ static int uas_is_interface(struct usb_host_interface *intf)
|
|||||||
intf->desc.bInterfaceProtocol == USB_PR_UAS);
|
intf->desc.bInterfaceProtocol == USB_PR_UAS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int uas_isnt_supported(struct usb_device *udev)
|
||||||
|
{
|
||||||
|
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
||||||
|
|
||||||
|
dev_warn(&udev->dev, "The driver for the USB controller %s does not "
|
||||||
|
"support scatter-gather which is\n",
|
||||||
|
hcd->driver->description);
|
||||||
|
dev_warn(&udev->dev, "required by the UAS driver. Please try an"
|
||||||
|
"alternative USB controller if you wish to use UAS.\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
static int uas_switch_interface(struct usb_device *udev,
|
static int uas_switch_interface(struct usb_device *udev,
|
||||||
struct usb_interface *intf)
|
struct usb_interface *intf)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
int sg_supported = udev->bus->sg_tablesize != 0;
|
||||||
if (uas_is_interface(intf->cur_altsetting))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for (i = 0; i < intf->num_altsetting; i++) {
|
for (i = 0; i < intf->num_altsetting; i++) {
|
||||||
struct usb_host_interface *alt = &intf->altsetting[i];
|
struct usb_host_interface *alt = &intf->altsetting[i];
|
||||||
if (alt == intf->cur_altsetting)
|
|
||||||
continue;
|
if (uas_is_interface(alt)) {
|
||||||
if (uas_is_interface(alt))
|
if (!sg_supported)
|
||||||
|
return uas_isnt_supported(udev);
|
||||||
return usb_set_interface(udev,
|
return usb_set_interface(udev,
|
||||||
alt->desc.bInterfaceNumber,
|
alt->desc.bInterfaceNumber,
|
||||||
alt->desc.bAlternateSetting);
|
alt->desc.bAlternateSetting);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
@ -619,6 +670,7 @@ static void uas_configure_endpoints(struct uas_dev_info *devinfo)
|
|||||||
unsigned i, n_endpoints = intf->cur_altsetting->desc.bNumEndpoints;
|
unsigned i, n_endpoints = intf->cur_altsetting->desc.bNumEndpoints;
|
||||||
|
|
||||||
devinfo->uas_sense_old = 0;
|
devinfo->uas_sense_old = 0;
|
||||||
|
devinfo->cmnd = NULL;
|
||||||
|
|
||||||
for (i = 0; i < n_endpoints; i++) {
|
for (i = 0; i < n_endpoints; i++) {
|
||||||
unsigned char *extra = endpoint[i].extra;
|
unsigned char *extra = endpoint[i].extra;
|
||||||
@ -670,6 +722,40 @@ static void uas_configure_endpoints(struct uas_dev_info *devinfo)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int uas_alloc_status_urb(struct uas_dev_info *devinfo,
|
||||||
|
struct Scsi_Host *shost)
|
||||||
|
{
|
||||||
|
if (devinfo->use_streams) {
|
||||||
|
devinfo->status_urb = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
devinfo->status_urb = uas_alloc_sense_urb(devinfo, GFP_KERNEL,
|
||||||
|
shost, 0);
|
||||||
|
if (!devinfo->status_urb)
|
||||||
|
goto err_s_urb;
|
||||||
|
|
||||||
|
if (usb_submit_urb(devinfo->status_urb, GFP_KERNEL))
|
||||||
|
goto err_submit_urb;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
err_submit_urb:
|
||||||
|
usb_free_urb(devinfo->status_urb);
|
||||||
|
err_s_urb:
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uas_free_streams(struct uas_dev_info *devinfo)
|
||||||
|
{
|
||||||
|
struct usb_device *udev = devinfo->udev;
|
||||||
|
struct usb_host_endpoint *eps[3];
|
||||||
|
|
||||||
|
eps[0] = usb_pipe_endpoint(udev, devinfo->status_pipe);
|
||||||
|
eps[1] = usb_pipe_endpoint(udev, devinfo->data_in_pipe);
|
||||||
|
eps[2] = usb_pipe_endpoint(udev, devinfo->data_out_pipe);
|
||||||
|
usb_free_streams(devinfo->intf, eps, 3, GFP_KERNEL);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* XXX: What I'd like to do here is register a SCSI host for each USB host in
|
* XXX: What I'd like to do here is register a SCSI host for each USB host in
|
||||||
* the system. Follow usb-storage's design of registering a SCSI host for
|
* the system. Follow usb-storage's design of registering a SCSI host for
|
||||||
@ -699,18 +785,33 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
|||||||
shost->max_id = 1;
|
shost->max_id = 1;
|
||||||
shost->sg_tablesize = udev->bus->sg_tablesize;
|
shost->sg_tablesize = udev->bus->sg_tablesize;
|
||||||
|
|
||||||
result = scsi_add_host(shost, &intf->dev);
|
|
||||||
if (result)
|
|
||||||
goto free;
|
|
||||||
shost->hostdata[0] = (unsigned long)devinfo;
|
|
||||||
|
|
||||||
devinfo->intf = intf;
|
devinfo->intf = intf;
|
||||||
devinfo->udev = udev;
|
devinfo->udev = udev;
|
||||||
uas_configure_endpoints(devinfo);
|
uas_configure_endpoints(devinfo);
|
||||||
|
|
||||||
|
result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 2);
|
||||||
|
if (result)
|
||||||
|
goto free;
|
||||||
|
|
||||||
|
result = scsi_add_host(shost, &intf->dev);
|
||||||
|
if (result)
|
||||||
|
goto deconfig_eps;
|
||||||
|
|
||||||
|
shost->hostdata[0] = (unsigned long)devinfo;
|
||||||
|
|
||||||
|
result = uas_alloc_status_urb(devinfo, shost);
|
||||||
|
if (result)
|
||||||
|
goto err_alloc_status;
|
||||||
|
|
||||||
scsi_scan_host(shost);
|
scsi_scan_host(shost);
|
||||||
usb_set_intfdata(intf, shost);
|
usb_set_intfdata(intf, shost);
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
|
err_alloc_status:
|
||||||
|
scsi_remove_host(shost);
|
||||||
|
shost = NULL;
|
||||||
|
deconfig_eps:
|
||||||
|
uas_free_streams(devinfo);
|
||||||
free:
|
free:
|
||||||
kfree(devinfo);
|
kfree(devinfo);
|
||||||
if (shost)
|
if (shost)
|
||||||
@ -732,18 +833,13 @@ static int uas_post_reset(struct usb_interface *intf)
|
|||||||
|
|
||||||
static void uas_disconnect(struct usb_interface *intf)
|
static void uas_disconnect(struct usb_interface *intf)
|
||||||
{
|
{
|
||||||
struct usb_device *udev = interface_to_usbdev(intf);
|
|
||||||
struct usb_host_endpoint *eps[3];
|
|
||||||
struct Scsi_Host *shost = usb_get_intfdata(intf);
|
struct Scsi_Host *shost = usb_get_intfdata(intf);
|
||||||
struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
|
struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
|
||||||
|
|
||||||
scsi_remove_host(shost);
|
scsi_remove_host(shost);
|
||||||
|
usb_kill_urb(devinfo->status_urb);
|
||||||
eps[0] = usb_pipe_endpoint(udev, devinfo->status_pipe);
|
usb_free_urb(devinfo->status_urb);
|
||||||
eps[1] = usb_pipe_endpoint(udev, devinfo->data_in_pipe);
|
uas_free_streams(devinfo);
|
||||||
eps[2] = usb_pipe_endpoint(udev, devinfo->data_out_pipe);
|
|
||||||
usb_free_streams(intf, eps, 3, GFP_KERNEL);
|
|
||||||
|
|
||||||
kfree(devinfo);
|
kfree(devinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,6 +125,9 @@ static struct us_unusual_dev us_unusual_dev_list[] = {
|
|||||||
{ } /* Terminating entry */
|
{ } /* Terminating entry */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct us_unusual_dev for_dynamic_ids =
|
||||||
|
USUAL_DEV(USB_SC_SCSI, USB_PR_BULK, 0);
|
||||||
|
|
||||||
#undef UNUSUAL_DEV
|
#undef UNUSUAL_DEV
|
||||||
#undef COMPLIANT_DEV
|
#undef COMPLIANT_DEV
|
||||||
#undef USUAL_DEV
|
#undef USUAL_DEV
|
||||||
@ -999,8 +1002,10 @@ EXPORT_SYMBOL_GPL(usb_stor_disconnect);
|
|||||||
static int storage_probe(struct usb_interface *intf,
|
static int storage_probe(struct usb_interface *intf,
|
||||||
const struct usb_device_id *id)
|
const struct usb_device_id *id)
|
||||||
{
|
{
|
||||||
|
struct us_unusual_dev *unusual_dev;
|
||||||
struct us_data *us;
|
struct us_data *us;
|
||||||
int result;
|
int result;
|
||||||
|
int size;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If libusual is configured, let it decide whether a standard
|
* If libusual is configured, let it decide whether a standard
|
||||||
@ -1019,8 +1024,19 @@ static int storage_probe(struct usb_interface *intf,
|
|||||||
* table, so we use the index of the id entry to find the
|
* table, so we use the index of the id entry to find the
|
||||||
* corresponding unusual_devs entry.
|
* corresponding unusual_devs entry.
|
||||||
*/
|
*/
|
||||||
result = usb_stor_probe1(&us, intf, id,
|
|
||||||
(id - usb_storage_usb_ids) + us_unusual_dev_list);
|
size = ARRAY_SIZE(us_unusual_dev_list);
|
||||||
|
if (id >= usb_storage_usb_ids && id < usb_storage_usb_ids + size) {
|
||||||
|
unusual_dev = (id - usb_storage_usb_ids) + us_unusual_dev_list;
|
||||||
|
} else {
|
||||||
|
unusual_dev = &for_dynamic_ids;
|
||||||
|
|
||||||
|
US_DEBUGP("%s %s 0x%04x 0x%04x\n", "Use Bulk-Only transport",
|
||||||
|
"with the Transparent SCSI protocol for dynamic id:",
|
||||||
|
id->idVendor, id->idProduct);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = usb_stor_probe1(&us, intf, id, unusual_dev);
|
||||||
if (result)
|
if (result)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
@ -1046,7 +1062,6 @@ static struct usb_driver usb_storage_driver = {
|
|||||||
.id_table = usb_storage_usb_ids,
|
.id_table = usb_storage_usb_ids,
|
||||||
.supports_autosuspend = 1,
|
.supports_autosuspend = 1,
|
||||||
.soft_unbind = 1,
|
.soft_unbind = 1,
|
||||||
.no_dynamic_id = 1,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init usb_stor_init(void)
|
static int __init usb_stor_init(void)
|
||||||
|
@ -376,6 +376,12 @@ struct usb_bus {
|
|||||||
|
|
||||||
struct usb_tt;
|
struct usb_tt;
|
||||||
|
|
||||||
|
enum usb_device_removable {
|
||||||
|
USB_DEVICE_REMOVABLE_UNKNOWN = 0,
|
||||||
|
USB_DEVICE_REMOVABLE,
|
||||||
|
USB_DEVICE_FIXED,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct usb_device - kernel's representation of a USB device
|
* struct usb_device - kernel's representation of a USB device
|
||||||
* @devnum: device number; address on a USB bus
|
* @devnum: device number; address on a USB bus
|
||||||
@ -432,6 +438,7 @@ struct usb_tt;
|
|||||||
* @wusb_dev: if this is a Wireless USB device, link to the WUSB
|
* @wusb_dev: if this is a Wireless USB device, link to the WUSB
|
||||||
* specific data for the device.
|
* specific data for the device.
|
||||||
* @slot_id: Slot ID assigned by xHCI
|
* @slot_id: Slot ID assigned by xHCI
|
||||||
|
* @removable: Device can be physically removed from this port
|
||||||
*
|
*
|
||||||
* Notes:
|
* Notes:
|
||||||
* Usbcore drivers should not set usbdev->state directly. Instead use
|
* Usbcore drivers should not set usbdev->state directly. Instead use
|
||||||
@ -509,6 +516,7 @@ struct usb_device {
|
|||||||
#endif
|
#endif
|
||||||
struct wusb_dev *wusb_dev;
|
struct wusb_dev *wusb_dev;
|
||||||
int slot_id;
|
int slot_id;
|
||||||
|
enum usb_device_removable removable;
|
||||||
};
|
};
|
||||||
#define to_usb_device(d) container_of(d, struct usb_device, dev)
|
#define to_usb_device(d) container_of(d, struct usb_device, dev)
|
||||||
|
|
||||||
|
@ -76,6 +76,11 @@
|
|||||||
#define USB_PORT_FEAT_C_BH_PORT_RESET 29
|
#define USB_PORT_FEAT_C_BH_PORT_RESET 29
|
||||||
#define USB_PORT_FEAT_FORCE_LINKPM_ACCEPT 30
|
#define USB_PORT_FEAT_FORCE_LINKPM_ACCEPT 30
|
||||||
|
|
||||||
|
/* USB 3.0 hub remote wake mask bits, see table 10-14 */
|
||||||
|
#define USB_PORT_FEAT_REMOTE_WAKE_CONNECT (1 << 8)
|
||||||
|
#define USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT (1 << 9)
|
||||||
|
#define USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT (1 << 10)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hub Status and Hub Change results
|
* Hub Status and Hub Change results
|
||||||
* See USB 2.0 spec Table 11-19 and Table 11-20
|
* See USB 2.0 spec Table 11-19 and Table 11-20
|
||||||
|
@ -412,6 +412,8 @@ extern irqreturn_t usb_hcd_irq(int irq, void *__hcd);
|
|||||||
|
|
||||||
extern void usb_hc_died(struct usb_hcd *hcd);
|
extern void usb_hc_died(struct usb_hcd *hcd);
|
||||||
extern void usb_hcd_poll_rh_status(struct usb_hcd *hcd);
|
extern void usb_hcd_poll_rh_status(struct usb_hcd *hcd);
|
||||||
|
extern void usb_wakeup_notification(struct usb_device *hdev,
|
||||||
|
unsigned int portnum);
|
||||||
|
|
||||||
/* The D0/D1 toggle bits ... USE WITH CAUTION (they're almost hcd-internal) */
|
/* The D0/D1 toggle bits ... USE WITH CAUTION (they're almost hcd-internal) */
|
||||||
#define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> (ep)) & 1)
|
#define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> (ep)) & 1)
|
||||||
|
@ -389,5 +389,20 @@ do { \
|
|||||||
printk(KERN_DEBUG "%s: " format "\n", __FILE__, ##arg); \
|
printk(KERN_DEBUG "%s: " format "\n", __FILE__, ##arg); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Macro for reporting errors in write path to avoid inifinite loop
|
||||||
|
* when port is used as a console.
|
||||||
|
*/
|
||||||
|
#define dev_err_console(usport, fmt, ...) \
|
||||||
|
do { \
|
||||||
|
static bool __print_once; \
|
||||||
|
struct usb_serial_port *__port = (usport); \
|
||||||
|
\
|
||||||
|
if (!__port->port.console || !__print_once) { \
|
||||||
|
__print_once = true; \
|
||||||
|
dev_err(&__port->dev, fmt, ##__VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#endif /* __LINUX_USB_SERIAL_H */
|
#endif /* __LINUX_USB_SERIAL_H */
|
||||||
|
|
||||||
|
69
include/linux/usb/uas.h
Normal file
69
include/linux/usb/uas.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#ifndef __USB_UAS_H__
|
||||||
|
#define __USB_UAS_H__
|
||||||
|
|
||||||
|
#include <scsi/scsi.h>
|
||||||
|
#include <scsi/scsi_cmnd.h>
|
||||||
|
|
||||||
|
/* Common header for all IUs */
|
||||||
|
struct iu {
|
||||||
|
__u8 iu_id;
|
||||||
|
__u8 rsvd1;
|
||||||
|
__be16 tag;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
IU_ID_COMMAND = 0x01,
|
||||||
|
IU_ID_STATUS = 0x03,
|
||||||
|
IU_ID_RESPONSE = 0x04,
|
||||||
|
IU_ID_TASK_MGMT = 0x05,
|
||||||
|
IU_ID_READ_READY = 0x06,
|
||||||
|
IU_ID_WRITE_READY = 0x07,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct command_iu {
|
||||||
|
__u8 iu_id;
|
||||||
|
__u8 rsvd1;
|
||||||
|
__be16 tag;
|
||||||
|
__u8 prio_attr;
|
||||||
|
__u8 rsvd5;
|
||||||
|
__u8 len;
|
||||||
|
__u8 rsvd7;
|
||||||
|
struct scsi_lun lun;
|
||||||
|
__u8 cdb[16]; /* XXX: Overflow-checking tools may misunderstand */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Also used for the Read Ready and Write Ready IUs since they have the
|
||||||
|
* same first four bytes
|
||||||
|
*/
|
||||||
|
struct sense_iu {
|
||||||
|
__u8 iu_id;
|
||||||
|
__u8 rsvd1;
|
||||||
|
__be16 tag;
|
||||||
|
__be16 status_qual;
|
||||||
|
__u8 status;
|
||||||
|
__u8 rsvd7[7];
|
||||||
|
__be16 len;
|
||||||
|
__u8 sense[SCSI_SENSE_BUFFERSIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct usb_pipe_usage_descriptor {
|
||||||
|
__u8 bLength;
|
||||||
|
__u8 bDescriptorType;
|
||||||
|
|
||||||
|
__u8 bPipeID;
|
||||||
|
__u8 Reserved;
|
||||||
|
} __attribute__((__packed__));
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CMD_PIPE_ID = 1,
|
||||||
|
STATUS_PIPE_ID = 2,
|
||||||
|
DATA_IN_PIPE_ID = 3,
|
||||||
|
DATA_OUT_PIPE_ID = 4,
|
||||||
|
|
||||||
|
UAS_SIMPLE_TAG = 0,
|
||||||
|
UAS_HEAD_TAG = 1,
|
||||||
|
UAS_ORDERED_TAG = 2,
|
||||||
|
UAS_ACA = 4,
|
||||||
|
};
|
||||||
|
#endif
|
@ -136,6 +136,7 @@ struct scsi_device {
|
|||||||
unsigned use_10_for_ms:1; /* first try 10-byte mode sense/select */
|
unsigned use_10_for_ms:1; /* first try 10-byte mode sense/select */
|
||||||
unsigned skip_ms_page_8:1; /* do not use MODE SENSE page 0x08 */
|
unsigned skip_ms_page_8:1; /* do not use MODE SENSE page 0x08 */
|
||||||
unsigned skip_ms_page_3f:1; /* do not use MODE SENSE page 0x3f */
|
unsigned skip_ms_page_3f:1; /* do not use MODE SENSE page 0x3f */
|
||||||
|
unsigned skip_vpd_pages:1; /* do not read VPD pages */
|
||||||
unsigned use_192_bytes_for_3f:1; /* ask for 192 bytes from page 0x3f */
|
unsigned use_192_bytes_for_3f:1; /* ask for 192 bytes from page 0x3f */
|
||||||
unsigned no_start_on_add:1; /* do not issue start on add */
|
unsigned no_start_on_add:1; /* do not issue start on add */
|
||||||
unsigned allow_restart:1; /* issue START_UNIT in error handler */
|
unsigned allow_restart:1; /* issue START_UNIT in error handler */
|
||||||
@ -246,8 +247,10 @@ struct scsi_target {
|
|||||||
unsigned int single_lun:1; /* Indicates we should only
|
unsigned int single_lun:1; /* Indicates we should only
|
||||||
* allow I/O to one of the luns
|
* allow I/O to one of the luns
|
||||||
* for the device at a time. */
|
* for the device at a time. */
|
||||||
unsigned int pdt_1f_for_no_lun; /* PDT = 0x1f */
|
unsigned int pdt_1f_for_no_lun:1; /* PDT = 0x1f
|
||||||
/* means no lun present */
|
* means no lun present. */
|
||||||
|
unsigned int no_report_luns:1; /* Don't use
|
||||||
|
* REPORT LUNS for scanning. */
|
||||||
/* commands actually active on LLD. protected by host lock. */
|
/* commands actually active on LLD. protected by host lock. */
|
||||||
unsigned int target_busy;
|
unsigned int target_busy;
|
||||||
/*
|
/*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user