2013-10-21 08:53:31 +01:00
# include <linux/usb.h>
# include <linux/usb/hcd.h>
2013-11-16 12:19:36 +01:00
# include "usb.h"
2013-10-21 08:53:31 +01:00
static int uas_is_interface ( struct usb_host_interface * intf )
{
return ( intf - > desc . bInterfaceClass = = USB_CLASS_MASS_STORAGE & &
intf - > desc . bInterfaceSubClass = = USB_SC_SCSI & &
intf - > desc . bInterfaceProtocol = = USB_PR_UAS ) ;
}
static int uas_find_uas_alt_setting ( struct usb_interface * intf )
{
int i ;
for ( i = 0 ; i < intf - > num_altsetting ; i + + ) {
struct usb_host_interface * alt = & intf - > altsetting [ i ] ;
2014-07-25 22:01:26 +02:00
if ( uas_is_interface ( alt ) )
2013-10-21 08:53:31 +01:00
return alt - > desc . bAlternateSetting ;
}
return - ENODEV ;
}
2013-10-25 17:04:33 +01:00
2013-10-29 10:03:34 +01:00
static int uas_find_endpoints ( struct usb_host_interface * alt ,
struct usb_host_endpoint * eps [ ] )
{
struct usb_host_endpoint * endpoint = alt - > endpoint ;
unsigned i , n_endpoints = alt - > desc . bNumEndpoints ;
for ( i = 0 ; i < n_endpoints ; i + + ) {
unsigned char * extra = endpoint [ i ] . extra ;
int len = endpoint [ i ] . extralen ;
while ( len > = 3 ) {
if ( extra [ 1 ] = = USB_DT_PIPE_USAGE ) {
unsigned pipe_id = extra [ 2 ] ;
if ( pipe_id > 0 & & pipe_id < 5 )
eps [ pipe_id - 1 ] = & endpoint [ i ] ;
break ;
}
len - = extra [ 0 ] ;
extra + = extra [ 0 ] ;
}
}
if ( ! eps [ 0 ] | | ! eps [ 1 ] | | ! eps [ 2 ] | | ! eps [ 3 ] )
return - ENODEV ;
return 0 ;
}
2013-10-25 17:04:33 +01:00
static int uas_use_uas_driver ( struct usb_interface * intf ,
2015-04-21 11:20:30 +02:00
const struct usb_device_id * id ,
unsigned long * flags_ret )
2013-10-25 17:04:33 +01:00
{
2013-11-16 11:37:41 +01:00
struct usb_host_endpoint * eps [ 4 ] = { } ;
2013-11-16 12:19:36 +01:00
struct usb_device * udev = interface_to_usbdev ( intf ) ;
2014-02-11 20:36:04 +01:00
struct usb_hcd * hcd = bus_to_hcd ( udev - > bus ) ;
2013-10-25 17:04:33 +01:00
unsigned long flags = id - > driver_info ;
2013-11-16 11:37:41 +01:00
int r , alt ;
2013-10-25 17:04:33 +01:00
2013-11-16 11:37:41 +01:00
alt = uas_find_uas_alt_setting ( intf ) ;
if ( alt < 0 )
return 0 ;
r = uas_find_endpoints ( & intf - > altsetting [ alt ] , eps ) ;
if ( r < 0 )
return 0 ;
2014-09-10 10:51:36 +02:00
/*
2015-01-08 15:15:14 +01:00
* ASMedia has a number of usb3 to sata bridge chips , at the time of
* this writing the following versions exist :
* ASM1051 - no uas support version
* ASM1051 - with broken ( * ) uas support
2015-04-21 11:20:32 +02:00
* ASM1053 - with working uas support , but problems with large xfers
2015-01-08 15:15:14 +01:00
* ASM1153 - with working uas support
*
* Devices with these chips re - use a number of device - ids over the
* entire line , so the device - id is useless to determine if we ' re
* dealing with an ASM1051 ( which we want to avoid ) .
*
* The ASM1153 can be identified by config . MaxPower = = 0 ,
* where as the ASM105x models have config . MaxPower = = 36.
*
* Differentiating between the ASM1053 and ASM1051 is trickier , when
* connected over USB - 3 we can look at the number of streams supported ,
* ASM1051 supports 32 streams , where as early ASM1053 versions support
* 16 streams , newer ASM1053 - s also support 32 streams , but have a
* different prod - id .
*
* ( * ) ASM1051 chips do work with UAS with some disks ( with the
* US_FL_NO_REPORT_OPCODES quirk ) , but are broken with other disks
2014-09-10 10:51:36 +02:00
*/
2014-09-11 11:06:12 +02:00
if ( le16_to_cpu ( udev - > descriptor . idVendor ) = = 0x174c & &
2015-01-08 15:15:14 +01:00
( le16_to_cpu ( udev - > descriptor . idProduct ) = = 0x5106 | |
le16_to_cpu ( udev - > descriptor . idProduct ) = = 0x55aa ) ) {
if ( udev - > actconfig - > desc . bMaxPower = = 0 ) {
/* ASM1153, do nothing */
} else if ( udev - > speed < USB_SPEED_SUPER ) {
2014-09-10 10:51:36 +02:00
/* No streams info, assume ASM1051 */
flags | = US_FL_IGNORE_UAS ;
} else if ( usb_ss_max_streams ( & eps [ 1 ] - > ss_ep_comp ) = = 32 ) {
2015-01-08 15:15:14 +01:00
/* Possibly an ASM1051, disable uas */
2014-09-10 10:51:36 +02:00
flags | = US_FL_IGNORE_UAS ;
2015-04-21 11:20:32 +02:00
} else {
/* ASM1053, these have issues with large transfers */
flags | = US_FL_MAX_SECTORS_240 ;
2014-09-10 10:51:36 +02:00
}
}
usb_stor_adjust_quirks ( udev , & flags ) ;
if ( flags & US_FL_IGNORE_UAS ) {
dev_warn ( & udev - > dev ,
" UAS is blacklisted for this device, using usb-storage instead \n " ) ;
return 0 ;
}
2014-07-25 22:01:26 +02:00
if ( udev - > bus - > sg_tablesize = = 0 ) {
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 other USB controller if you wish to use UAS. \n " ) ;
return 0 ;
}
2014-07-25 22:01:27 +02:00
if ( udev - > speed > = USB_SPEED_SUPER & & ! hcd - > can_do_streams ) {
dev_warn ( & udev - > dev ,
" USB controller %s does not support streams, which are required by the UAS driver. \n " ,
hcd_to_bus ( hcd ) - > bus_name ) ;
dev_warn ( & udev - > dev ,
" Please try an other USB controller if you wish to use UAS. \n " ) ;
return 0 ;
}
2015-04-21 11:20:30 +02:00
if ( flags_ret )
* flags_ret = flags ;
2013-11-16 11:37:41 +01:00
return 1 ;
2013-10-25 17:04:33 +01:00
}