Sophia Li wrote:

> Juergen Keil wrote:
> > 
> >>I'm not yet sure if J. Schilling's cdrecord is able to record 
> >>a CD using CUE SHEETS, using one of these cue sheet data modes
> >>0x01, 0x14, 0x24 or 0x34.
> > 
> > 
> > After a quick look into cdrecord's driver for MMC drives
> > (cdrecord/drv_mmc.c, function gen_cue_mmc()):
> > Nope, it seems cdrecord has no support for these data modes - it
> > only uses them in the lead-in and lead-out area.
> > 
> > 
> > % cdrecord dev=6,0,0 -dummy -vv -sao -data /tmp/test.iso
> > ...
> > Sending CUE sheet...
> >  41 00 00 14 00 00 00 00  <-- Lead-in
> >  41 01 00 10 00 00 00 00  <-- Track 1, Index 0 == TOC  (00:00:00)
> >  41 01 01 10 00 00 02 00  <-- Track 1, Index 1 == Data (00:02:00)
> >  41 AA 01 14 00 00 06 00  <-- Lead-out
> >           ^^ Data mode
> > ...
> > 
> > The fourth byte is the data mode, and J. Schilling's cdrecord 
> > uses mode 0x14 for the lead-in and lead-out area.  But for these
> > areas no data is tranfered to the drive, anyway.
> > 
> > 
> > Both the TOC area and the data track is recorded by cdrecord using
> > data mode 0x10.  That is, the host sends 2048 bytes of data for each
> > sector that should be recorded.
> > 
> > 
> > 
> > AFAIK, cdrw doesn't support the DAO/SAO (disk-at-once, session-at-once)
> > recording mode for CDs, anyway.
> > 
> > So at least for cdrecord and cdrw, working zero byte write support
> > is not required.
> > 
> > But it is a legitimate MMC feature for CD mastering.
> > 
> 
> Thanks for the detail. It seems existing Soalris tools cannot generate a 
> zero byte write. What did you use to generate the scsi trace? Is it a 
> propriatary tool?

The first scsi protocol trace was produced by the attached dtrace script.

To use it, you have to change the cdrom_major / cdrom_minor assignment
in the BEGIN block of the script to the major/minor of the scsi device
that you want to trace, and run the modified dtrace script.  (You may also
have to adapt it for sparc vs x86, or 32 vs 64-bit kernel)


% eject -l
/dev/dsk/c6t0d0s2    cdrom1,cd1,sr1
/dev/dsk/c2t1d0s2    cdrom,cdrom0,cd,cd0,sr,sr0
/dev/dsk/c0t0d1p0:1  rmdisk1,NO NAME,/media/NO NAME
/dev/diskette0       
floppy,floppy0,fd,fd0,diskette,diskette0,rdiskette,rdiskette0


% ls -lL /dev/dsk/c6t0d0s2
br--------   1 root     root      31, 450 Apr  2 16:52 /dev/dsk/c6t0d0s2


==> cdrom_major = 31;
    cdrom_minor = 450;
    


The scsi.d dtrace script works best when only one cpu (core) is online.
(On multiprocessor or dual-core machines the sdintr() function may
be run on a different cpu core than scsi_transport(), and that mixes
the dtrace output for the submitted CDBs and the scsi pkt completion
callbacks).


The second scsi protocol trace was produced by scsi.d-1.9, available for
download here:

   http://blogs.sun.com/chrisg/page/scsi.d


>  From what you described, we can possibly use sector count == 0 or > 0 
> as a judging condition. We may also use dtype to differentiate the 
> situations as sd code does.

Yes, that sounds ok for me.
Something like this, in scsa2usb_check_bulkonly_blacklist_attrs() /
scsa2usb.c:


   switch (opcode) {
   ...
   
   case SCMD_WRITE_G1:
       /*
        * Bug 6576064:
        * Lacie Biggest F800 USB drive does not support zero byte write(10),
        * so we just accept write commands with a sector count of 0 and do
        * not send them to the device.
        *
        * MMC optical devices may use zero byte write(10), with a sector
        * count > 0 and an empty data buffer, so be sure not filter these
        * out.
        */
       if (( (inq->inq_dtype & DTYPE_MASK) == DTYPE_DIRECT /* 0x00 */ ||
             (inq->inq_dtype & DTYPE_MASK) == DTYPE_RBC /* 0x0E */ )
           && "num_sectors_from_write_g1_cdb" == 0)
                return (SCSA2USB_JUST_ACCEPT);
       break;

   ...
   }
   


Didn't fritS add usb tape support to scsa2usb some time ago? 
I think when using variable tape block sizes it could also be important
not to filter out writes to an usb tape device that have a zero sized
data buffer.  If we test for DTYPE_DIRECT, we should be ok and don't
break usb tapes, either.




Btw. are we sure that it is a write(10) that is sent to / received by
the Lacie usb device?

Maybe somewhere on the way down from scsi_watch_thread() to the usb device
the write(10) is translated to a write(6) command / or vice versa.
With read/write(6) a sector count of "0" was special and means: read or write
256 sectors.  (Confusing this could easily break the usb mass storage 
protocol, I guess)


Looking at the code in scsa2usb_rw_transport(), I'd say this special case
isn't handled correctly, when translating from SCMD_READ/SCMD_WRITE to
SCMD_READ_G1/SCMD_WRITE_G1.

I think for a correct read/write(6) implementation, we need something
like this:

        switch (opcode) {
        case SCMD_READ:
                /*
                 * Note that READ/WRITE(6) are not supported by the drive.
                 * convert it into a 10 byte read/write.
                 */
                lba = SCSA2USB_LBA_6BYTE(pkt);
                len = SCSA2USB_LEN_6BYTE(pkt);
                if (len == 0) len = 256;             <<<<<<<<<<<<<<<<<<<<<<<<<<
                opcode = SCMD_READ_G1;  /* Overwrite it w/ byte 10 cmd val */
                dir = USB_EP_DIR_IN;
                break;
        case SCMD_WRITE:
                lba = SCSA2USB_LBA_6BYTE(pkt);
                len = SCSA2USB_LEN_6BYTE(pkt);
                if (len == 0) len = 256;             <<<<<<<<<<<<<<<<<<<<<<<<<<
                opcode = SCMD_WRITE_G1; /* Overwrite it w/ byte 10 cmd val */
                dir = USB_EP_DIR_OUT;
                break;


> Or we simply add the Lacie disk drive to the 
> scsa2usb blacklist. Which method do you think is better?


Hmm, how many different models of Lacie's mass storage devices are affected?
Should we filter out zero byte write for usb vendor = Lacie, product = "*" ?

Or should we filter them out for a specific vendor/product pair ?  And when
the next generation of Lacie drives still has the same bug, the blacklist
must be changed over and over again...


-------------- next part --------------
#!/usr/sbin/dtrace -s

/*#pragma D option flowindent*/


BEGIN
{
/* sparc: */
        /* SDPART_MASK = 7; */
        /* SDPART_SHIFT = 3; */ 
/* i86pc: */
        SDPART_MASK = 63;
        SDPART_SHIFT = 6;

        cdrom_major = 31;
        cdrom_minor = 450;

/* 32-bit kernel */
        /* cdrom_dev = (cdrom_major << 18 | cdrom_minor) & ~SDPART_MASK; */
/* 64-bit kernel */
        cdrom_dev = (cdrom_major << 32 | cdrom_minor) & ~SDPART_MASK;

        scsi_op[0x00] = "TEST_UNIT_READY";
        scsi_op[0x01] = "REZERO_UNIT";
        scsi_op[0x03] = "REQUEST_SENSE";
        scsi_op[0x04] = "FORMAT";
        scsi_op[0x08] = "READ(6)";
        scsi_op[0x0a] = "WRITE(6)";
        scsi_op[0x12] = "INQUIRY";
        scsi_op[0x15] = "MODE_SELECT";
        scsi_op[0x16] = "RESERVE";
        scsi_op[0x17] = "RELEASE";
        scsi_op[0x18] = "COPY";
        scsi_op[0x19] = "ERASE";
        scsi_op[0x1a] = "MODE_SENSE";
        scsi_op[0x1b] = "START_STOP";
        scsi_op[0x1e] = "DOORLOCK";
        scsi_op[0x25] = "READ_CAPACITY";
        scsi_op[0x28] = "READ(10)";
        scsi_op[0x2a] = "WRITE(10)";
        scsi_op[0x35] = "SYNCHRONIZE_CACHE";
        scsi_op[0x3e] = "READ LONG";
        scsi_op[0x3f] = "WRITE LONG";
        scsi_op[0x42] = "READ_SUBCHANNEL";
        scsi_op[0x43] = "READ_TOC";
        scsi_op[0x44] = "READ_HEADER";
        scsi_op[0x45] = "PLAYAUDIO10";
        scsi_op[0x46] = "GET_CONFIGURATION";
        scsi_op[0x47] = "PLAYAUDIO_MSF";
        scsi_op[0x48] = "PLAYAUDIO_TI";
        scsi_op[0x49] = "PLAYTRACK_REL10";
        scsi_op[0x4b] = "PAUSE_RESUME";
        scsi_op[0x51] = "READ_DISC_INFORMATION";
        scsi_op[0x52] = "READ_TRACK_INFORMATION";
        scsi_op[0x53] = "RESERVE_TRACK";
        scsi_op[0x54] = "SEND_OPC_INFORMATION";
        scsi_op[0x55] = "MODE_SELECT_G1";
        scsi_op[0x5a] = "MODE_SENSE_G1";
        scsi_op[0x5b] = "CLOSE_TRACK";
        scsi_op[0x5c] = "READ_BUFFER_CAPACITY";
        scsi_op[0x5d] = "SEND_CUE_SHEET";
        scsi_op[0x5e] = "PERSISTENT_RESERVE_IN";
        scsi_op[0x5f] = "PERSISTENT_RESERVE_OUT";

        scsi_op[0xa5] = "PLAYAUDIO12";
        scsi_op[0xa9] = "PLAYTRACK_REL12";
        scsi_op[0xbb] = "SET_CDROM_SPEED";
        scsi_op[0xbe] = "READ_CD";
        scsi_op[-66] = "-- READ_CD";
}

fbt::scsi_transport:entry
/((struct scsi_pkt*)arg0)->pkt_private != 0
 && (((struct buf *)((struct scsi_pkt*)arg0)->pkt_private)->b_edev & 
~SDPART_MASK) == cdrom_dev/
{
        pkt = (struct scsi_pkt *)arg0;
        buf = (struct buf *) pkt->pkt_private;
        cdb = pkt->pkt_cdbp;
        printf("sd%d DATA=%05x %02x %02x %02x %02x %02x %02x %02x %02x %02x 
%02x / %s",
                (buf->b_edev & 0x3ffff) >> SDPART_SHIFT,
                buf->b_bcount,
                cdb[0], cdb[1], cdb[2], cdb[3], cdb[4],
                cdb[5], cdb[6], cdb[7], cdb[8], cdb[9],
                scsi_op[cdb[0] & 0xff] != 0 ? scsi_op[cdb[0] & 0xff] : "???");
        start_time = timestamp;
}

fbt::sdintr:entry
/((struct scsi_pkt*)arg0)->pkt_private != 0
 && (((struct buf *)((struct scsi_pkt*)arg0)->pkt_private)->b_edev & 
~SDPART_MASK) == cdrom_dev/
{
        tim = (timestamp - start_time) / 1000000;
        pkt = (struct scsi_pkt *)arg0;
        scb = (struct scsi_arq_status *)pkt->pkt_scbp;
        printf("state %x, reason %x, t/ms %d, to/s %d, %sarq sk/asc/ascq %x %x 
%x",
                pkt->pkt_state, pkt->pkt_reason, tim, pkt->pkt_time,
                pkt->pkt_state & 0x20 ? "" : "no ",
                /* pkt->pkt_state & 0x20 ? scb->sts_sensedata.es_key : 0, */
                pkt->pkt_state & 0x20 ? pkt->pkt_scbp[12+2]&0xf : 0,
                pkt->pkt_state & 0x20 ? scb->sts_sensedata.es_add_code : 0,
                pkt->pkt_state & 0x20 ? scb->sts_sensedata.es_qual_code : 0);
}

Reply via email to