// SPDX-License-Identifier: GPL-2.0+
/*
* Support for emulating SAT ( ata pass through ) on devices based
* on the Cypress USB / ATA bridge supporting ATACB .
*
* Copyright ( c ) 2008 Matthieu Castet ( castet . matthieu @ free . fr )
*/
#include <linux/module.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_eh.h>
#include <linux/ata.h>
#include "usb.h"
#include "protocol.h"
#include "scsiglue.h"
#include "debug.h"
#define DRV_NAME "ums-cypress"
MODULE_DESCRIPTION("SAT support for Cypress USB/ATA bridges with ATACB" );
MODULE_AUTHOR("Matthieu Castet <castet.matthieu@free.fr>" );
MODULE_LICENSE("GPL" );
MODULE_IMPORT_NS("USB_STORAGE" );
/*
* The table of devices
*/
#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
.driver_info = (flags) }
static const struct usb_device_id cypress_usb_ids[] = {
# include "unusual_cypress.h"
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, cypress_usb_ids);
#undef UNUSUAL_DEV
/*
* The flags table
*/
#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \
vendor_name, product_name, use_protocol, use_transport, \
init_function, Flags) \
{ \
.vendorName = vendor_name, \
.productName = product_name, \
.useProtocol = use_protocol, \
.useTransport = use_transport, \
.initFunction = init_function, \
}
static const struct us_unusual_dev cypress_unusual_dev_list[] = {
# include "unusual_cypress.h"
{ } /* Terminating entry */
};
#undef UNUSUAL_DEV
/*
* ATACB is a protocol used on cypress usb < - > ata bridge to
* send raw ATA command over mass storage
* There is a ATACB2 protocol that support LBA48 on newer chip .
* More info that be found on cy7c68310_8 . pdf and cy7c68300c_8 . pdf
* datasheet from cypress . com .
*/
static void cypress_atacb_passthrough(struct scsi_cmnd *srb, struct us_data *us)
{
unsigned char save_cmnd[MAX_COMMAND_SIZE];
if (likely(srb->cmnd[0 ] != ATA_16 && srb->cmnd[0 ] != ATA_12)) {
usb_stor_transparent_scsi_command(srb, us);
return ;
}
memcpy(save_cmnd, srb->cmnd, sizeof (save_cmnd));
memset(srb->cmnd, 0 , MAX_COMMAND_SIZE);
/* check if we support the command */
if (save_cmnd[1 ] >> 5 ) /* MULTIPLE_COUNT */
goto invalid_fld;
/* check protocol */
switch ((save_cmnd[1 ] >> 1 ) & 0 xf) {
case 3 : /*no DATA */
case 4 : /* PIO in */
case 5 : /* PIO out */
break ;
default :
goto invalid_fld;
}
/* first build the ATACB command */
srb->cmd_len = 16 ;
srb->cmnd[0 ] = 0 x24; /*
* bVSCBSignature : vendor - specific command
* this value can change , but most ( all ? ) manufacturers
* keep the cypress default : 0 x24
*/
srb->cmnd[1 ] = 0 x24; /* bVSCBSubCommand : 0x24 for ATACB */
srb->cmnd[3 ] = 0 xff - 1 ; /*
* features , sector count , lba low , lba med
* lba high , device , command are valid
*/
srb->cmnd[4 ] = 1 ; /* TransferBlockCount : 512 */
if (save_cmnd[0 ] == ATA_16) {
srb->cmnd[ 6 ] = save_cmnd[ 4 ]; /* features */
srb->cmnd[ 7 ] = save_cmnd[ 6 ]; /* sector count */
srb->cmnd[ 8 ] = save_cmnd[ 8 ]; /* lba low */
srb->cmnd[ 9 ] = save_cmnd[10 ]; /* lba med */
srb->cmnd[10 ] = save_cmnd[12 ]; /* lba high */
srb->cmnd[11 ] = save_cmnd[13 ]; /* device */
srb->cmnd[12 ] = save_cmnd[14 ]; /* command */
if (save_cmnd[1 ] & 0 x01) {/* extended bit set for LBA48 */
/* this could be supported by atacb2 */
if (save_cmnd[3 ] || save_cmnd[5 ] || save_cmnd[7 ] || save_cmnd[9 ]
|| save_cmnd[11 ])
goto invalid_fld;
}
} else { /* ATA12 */
srb->cmnd[ 6 ] = save_cmnd[3 ]; /* features */
srb->cmnd[ 7 ] = save_cmnd[4 ]; /* sector count */
srb->cmnd[ 8 ] = save_cmnd[5 ]; /* lba low */
srb->cmnd[ 9 ] = save_cmnd[6 ]; /* lba med */
srb->cmnd[10 ] = save_cmnd[7 ]; /* lba high */
srb->cmnd[11 ] = save_cmnd[8 ]; /* device */
srb->cmnd[12 ] = save_cmnd[9 ]; /* command */
}
/* Filter SET_FEATURES - XFER MODE command */
if ((srb->cmnd[12 ] == ATA_CMD_SET_FEATURES)
&& (srb->cmnd[6 ] == SETFEATURES_XFER))
goto invalid_fld;
if (srb->cmnd[12 ] == ATA_CMD_ID_ATA || srb->cmnd[12 ] == ATA_CMD_ID_ATAPI)
srb->cmnd[2 ] |= (1 <<7 ); /* set IdentifyPacketDevice for these cmds */
usb_stor_transparent_scsi_command(srb, us);
/* if the device doesn't support ATACB */
if (srb->result == SAM_STAT_CHECK_CONDITION &&
memcmp(srb->sense_buffer, usb_stor_sense_invalidCDB,
sizeof (usb_stor_sense_invalidCDB)) == 0 ) {
usb_stor_dbg(us, "cypress atacb not supported ???\n" );
goto end;
}
/*
* if ck_cond flags is set , and there wasn ' t critical error ,
* build the special sense
*/
if ((srb->result != (DID_ERROR << 16 ) &&
srb->result != (DID_ABORT << 16 )) &&
save_cmnd[2 ] & 0 x20) {
struct scsi_eh_save ses;
unsigned char regs[8 ];
unsigned char *sb = srb->sense_buffer;
unsigned char *desc = sb + 8 ;
int tmp_result;
/* build the command for reading the ATA registers */
scsi_eh_prep_cmnd(srb, &ses, NULL, 0 , sizeof (regs));
/*
* we use the same command as before , but we set
* the read taskfile bit , for not executing atacb command ,
* but reading register selected in srb - > cmnd [ 4 ]
*/
srb->cmd_len = 16 ;
srb->cmnd[2 ] = 1 ;
usb_stor_transparent_scsi_command(srb, us);
memcpy(regs, srb->sense_buffer, sizeof (regs));
tmp_result = srb->result;
scsi_eh_restore_cmnd(srb, &ses);
/* we fail to get registers, report invalid command */
if (tmp_result != SAM_STAT_GOOD)
goto invalid_fld;
/* build the sense */
memset(sb, 0 , SCSI_SENSE_BUFFERSIZE);
/* set sk, asc for a good command */
sb[1 ] = RECOVERED_ERROR;
sb[2 ] = 0 ; /* ATA PASS THROUGH INFORMATION AVAILABLE */
sb[3 ] = 0 x1D;
/*
* XXX we should generate sk , asc , ascq from status and error
* regs
* ( see 11 . 1 Error translation ATA device error to SCSI error
* map , and ata_to_sense_error from libata . )
*/
/* Sense data is current and format is descriptor. */
sb[0 ] = 0 x72;
desc[0 ] = 0 x09; /* ATA_RETURN_DESCRIPTOR */
/* set length of additional sense data */
sb[7 ] = 14 ;
desc[1 ] = 12 ;
/* Copy registers into sense buffer. */
desc[ 2 ] = 0 x00;
desc[ 3 ] = regs[1 ]; /* features */
desc[ 5 ] = regs[2 ]; /* sector count */
desc[ 7 ] = regs[3 ]; /* lba low */
desc[ 9 ] = regs[4 ]; /* lba med */
desc[11 ] = regs[5 ]; /* lba high */
desc[12 ] = regs[6 ]; /* device */
desc[13 ] = regs[7 ]; /* command */
srb->result = SAM_STAT_CHECK_CONDITION;
}
goto end;
invalid_fld:
srb->result = SAM_STAT_CHECK_CONDITION;
memcpy(srb->sense_buffer,
usb_stor_sense_invalidCDB,
sizeof (usb_stor_sense_invalidCDB));
end:
memcpy(srb->cmnd, save_cmnd, sizeof (save_cmnd));
if (srb->cmnd[0 ] == ATA_12)
srb->cmd_len = 12 ;
}
static struct scsi_host_template cypress_host_template;
static int cypress_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct us_data *us;
int result;
struct usb_device *device;
result = usb_stor_probe1(&us, intf, id,
(id - cypress_usb_ids) + cypress_unusual_dev_list,
&cypress_host_template);
if (result)
return result;
/*
* Among CY7C68300 chips , the A revision does not support Cypress ATACB
* Filter out this revision from EEPROM default descriptor values
*/
device = interface_to_usbdev(intf);
if (device->descriptor.iManufacturer != 0 x38 ||
device->descriptor.iProduct != 0 x4e ||
device->descriptor.iSerialNumber != 0 x64) {
us->protocol_name = "Transparent SCSI with Cypress ATACB" ;
us->proto_handler = cypress_atacb_passthrough;
} else {
us->protocol_name = "Transparent SCSI" ;
us->proto_handler = usb_stor_transparent_scsi_command;
}
result = usb_stor_probe2(us);
return result;
}
static struct usb_driver cypress_driver = {
.name = DRV_NAME,
.probe = cypress_probe,
.disconnect = usb_stor_disconnect,
.suspend = usb_stor_suspend,
.resume = usb_stor_resume,
.reset_resume = usb_stor_reset_resume,
.pre_reset = usb_stor_pre_reset,
.post_reset = usb_stor_post_reset,
.id_table = cypress_usb_ids,
.soft_unbind = 1 ,
.no_dynamic_id = 1 ,
};
module_usb_stor_driver(cypress_driver, cypress_host_template, DRV_NAME);
Messung V0.5 in Prozent C=81 H=80 G=80
¤ Dauer der Verarbeitung: 0.2 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland