$NetBSD: patch-cg,v 1.7 2003/10/20 12:07:23 grant Exp $

--- interface/scsi_interface.c.orig	2001-03-24 12:15:46.000000000 +1100
+++ interface/scsi_interface.c
@@ -23,6 +23,8 @@ static void tweak_SG_buffer(cdrom_drive 
   int table,reserved;
   char buffer[256];
 
+#ifndef __FreeBSD__
+#ifdef __linux__
   /* maximum transfer size? */
   if(ioctl(d->cdda_fd,SG_GET_RESERVED_SIZE,&reserved)){
     /* Up, guess not. */
@@ -31,6 +33,10 @@ static void tweak_SG_buffer(cdrom_drive 
   }
 
   if(ioctl(d->cdda_fd,SG_GET_SG_TABLESIZE,&table))table=1;
+#else
+  reserved = 32*1024;	/* ? */
+  table = 1;
+#endif
   {
     int cur;
 
@@ -53,14 +59,24 @@ static void tweak_SG_buffer(cdrom_drive 
     cdmessage(d,buffer);
   } 
 
+#ifdef __linux__
   /* Disable command queue; we don't need it, no reason to have it on */
   reserved=0;
   if(ioctl(d->cdda_fd,SG_SET_COMMAND_Q,&reserved)){
     cdmessage(d,"\tCouldn't disable command queue!  Continuing anyway...\n");
   }
-
+#endif
+#else /* FreeBSD */
+  d->nsectors = 26;		/* FreeBSD only supports 64K I/O transfer size */
+  d->bigbuff = d->nsectors * CD_FRAMESIZE_RAW;
+  
+  sprintf(buffer,"\tSetting default read size to %d sectors (%d bytes).\n\n",
+      d->nsectors,d->nsectors*CD_FRAMESIZE_RAW);
+  cdmessage(d,buffer);
+#endif
 }
 
+#ifdef __linux__
 static void reset_scsi(cdrom_drive *d){
   int arg;
   d->enable_cdda(d,0);
@@ -74,6 +90,44 @@ static void reset_scsi(cdrom_drive *d){
   d->enable_cdda(d,1);
 }
 
+#elif defined(__NetBSD__)
+static void reset_scsi(cdrom_drive *d){
+  int arg;
+  d->enable_cdda(d,0);
+
+  cdmessage(d,"sending SCSI reset... ");
+  if(ioctl(d->cdda_fd,CDIOCRESET,&arg))
+    cdmessage(d,"FAILED: EBUSY\n");
+  else
+    cdmessage(d,"OK\n");
+  
+  d->enable_cdda(d,1);
+}
+
+#elif defined(__FreeBSD__)
+static void reset_scsi(cdrom_drive *d) {
+	d->enable_cdda(d,0);
+
+	d->ccb->ccb_h.func_code = XPT_RESET_DEV;
+	d->ccb->ccb_h.timeout = 5000;
+
+	cdmessage(d, "sending SCSI reset... ");
+	if (cam_send_ccb(d->dev, d->ccb)) {
+		cdmessage(d, "error sending XPT_RESET_DEV CCB");
+	} else {
+	
+		if (((d->ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) ||
+		    ((d->ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_BDR_SENT))
+			cdmessage(d,"OK\n");
+		else
+			cdmessage(d,"FAILED\n");
+	}
+
+	d->enable_cdda(d,1);
+}
+#endif
+
+#ifdef __linux__
 static void clear_garbage(cdrom_drive *d){
   fd_set fdset;
   struct timeval tv;
@@ -104,21 +158,30 @@ static void clear_garbage(cdrom_drive *d
     flag=1;
   }
 }
+#endif
 
 /* process a complete scsi command. */
+#ifndef __FreeBSD__
 static int handle_scsi_cmd(cdrom_drive *d,
 			   unsigned int cmd_len, 
 			   unsigned int in_size, 
 			   unsigned int out_size,
 
-			   unsigned char bytefill,
+ 			   unsigned int bfill,
 			   int bytecheck){
+  unsigned char bytefill = bfill;
   int status = 0;
+#ifdef __linux__
   struct sg_header *sg_hd=(struct sg_header *)d->sg;
   long writebytes=SG_OFF+cmd_len+in_size;
+#endif
+#ifdef __NetBSD__
+  scsireq_t *sreq = (scsireq_t *)d->sg;
+#endif
 
   /* generic scsi device services */
 
+#ifdef __linux__
   /* clear out any possibly preexisting garbage */
   clear_garbage(d);
 
@@ -224,11 +287,53 @@ static int handle_scsi_cmd(cdrom_drive *
     if(errno==0)errno=EIO;
     return(TR_EREAD);
   }
+#endif	/* __linux__ */
 
-  if(sg_hd->sense_buffer[0]){
+#ifdef __NetBSD__
+  if (in_size && out_size) {
+    warnx("handle_scsi_cmd: in and out is not supported");
+    abort();
+  }
+  memset(sreq, 0, sizeof(scsireq_t));
+  sreq->cmdlen = cmd_len;
+  memcpy(sreq->cmd, d->sg_buffer, cmd_len);
+  if (in_size) {
+    sreq->flags = SCCMD_WRITE;
+    sreq->databuf = d->sg_buffer + cmd_len;
+    sreq->datalen = in_size;
+  }
+  if (out_size) {
+    sreq->flags = SCCMD_READ;
+    sreq->databuf = d->sg_buffer;
+    sreq->datalen = out_size;
+    if(bytecheck)
+      memset(d->sg_buffer, bytefill, out_size); 
+  }
+  sreq->senselen = SENSEBUFLEN;
+  sreq->timeout = 60000;	/* 60s */
+
+  status = ioctl(d->cdda_fd, SCIOCCOMMAND, (void *) sreq);
+  if (status < 0)
+    return(TR_ILLEGAL);
+#endif
+
+#ifdef __linux__
+  if(sg_hd->sense_buffer[0])
+#endif
+#ifdef __NetBSD__
+  if (sreq->sense[0])
+#endif
+  {
+#ifdef __linux__
     char key=sg_hd->sense_buffer[2]&0xf;
     char ASC=sg_hd->sense_buffer[12];
     char ASCQ=sg_hd->sense_buffer[13];
+#endif
+#ifdef __NetBSD__
+    char key = sreq->sense[2]&0xf;
+    char ASC = sreq->sense[12];
+    char ASCQ = sreq->sense[13];
+#endif
     switch(key){
     case 0:
       if(errno==0)errno=EIO;
@@ -266,9 +371,20 @@ static int handle_scsi_cmd(cdrom_drive *
      commands still get through.  Perhaps no data comes back even
      though the target reports success? */
 
-  if(bytecheck && in_size+cmd_len<out_size){
+#ifdef __linux__
+  if(bytecheck && in_size+cmd_len<out_size)
+#endif
+#ifdef __NetBSD__
+  if(bytecheck && out_size)
+#endif
+  {
     long i,flag=0;
+#ifdef __linux__
     for(i=in_size;i<out_size;i++)
+#endif
+#ifdef __NetBSD__
+    for(i=0;i<out_size;i++)
+#endif
       if(d->sg_buffer[i]!=bytefill){
 	flag=1;
 	break;
@@ -284,6 +400,83 @@ static int handle_scsi_cmd(cdrom_drive *
   return(0);
 }
 
+#elif defined(__FreeBSD__)
+static int handle_scsi_cmd(cdrom_drive *d,
+			   unsigned int cmd_len, 
+			   unsigned int out_size, 
+			   unsigned int in_size,
+			   unsigned char bytefill,
+			   int bytecheck) {
+	int result;
+	
+	bzero(&d->ccb->csio, sizeof(d->ccb->csio));
+
+	memcpy(d->ccb->csio.cdb_io.cdb_bytes, d->sg_buffer, cmd_len);
+
+	if (bytecheck && out_size == 0)
+		memset(d->sg_buffer, bytefill, in_size);
+
+	cam_fill_csio(&d->ccb->csio,
+	    /* retries */ 0,
+	    /* cbfcnp */ NULL,
+	    /* flags */ CAM_DEV_QFRZDIS | (out_size ? CAM_DIR_OUT : CAM_DIR_IN),
+	    /* tag_action */ MSG_SIMPLE_Q_TAG,
+	    /* data_ptr */ out_size ? d->sg_buffer + cmd_len : d->sg_buffer,
+	    /* dxfer_len */ out_size ? out_size : in_size,
+	    /* sense_len */ SSD_FULL_SIZE,
+	    /* cdb_len */ cmd_len,
+	    /* timeout */ 60000);	/* XXX */
+
+	if ((result = cam_send_ccb(d->dev, d->ccb)) < 0 ||
+	    (d->ccb->ccb_h.status & CAM_STATUS_MASK) == 0 /* hack? */)
+		return TR_EREAD;
+
+	if ((d->ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP &&
+	    (d->ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_SCSI_STATUS_ERROR) {
+		fprintf (stderr, "\t\terror returned from SCSI command:\n"
+				 "\t\tccb->ccb_h.status == %d\n", d->ccb->ccb_h.status);
+		errno = EIO;
+		return TR_UNKNOWN;
+	}
+
+	if (d->ccb->csio.dxfer_len != in_size) {
+		errno = EIO;
+		return TR_EREAD;
+	}
+
+	if (d->ccb->csio.sense_data.error_code & SSD_ERRCODE) {
+		switch (d->ccb->csio.sense_data.flags & SSD_KEY) {
+		case SSD_KEY_NO_SENSE:
+			errno = EIO;
+			return TR_UNKNOWN;
+		case SSD_KEY_RECOVERED_ERROR:
+			break;
+		case SSD_KEY_NOT_READY:
+			errno = EBUSY;
+			return TR_BUSY;
+		case SSD_KEY_MEDIUM_ERROR:
+			errno = EIO;
+			if (d->ccb->csio.sense_data.add_sense_code == 0x0c &&
+			    d->ccb->csio.sense_data.add_sense_code_qual == 0x09)
+				return TR_STREAMING;
+			else
+				return TR_MEDIUM;
+		case SSD_KEY_HARDWARE_ERROR:
+			errno = EIO;
+			return TR_FAULT;
+		case SSD_KEY_ILLEGAL_REQUEST:
+			errno = EINVAL;
+			return TR_ILLEGAL;
+		default:
+			errno = EIO;
+			return TR_UNKNOWN;
+		}
+	}
+
+	return 0;
+}
+#endif
+
 /* Group 1 (10b) command */
 
 static int mode_sense_atapi(cdrom_drive *d,int size,int page){ 
@@ -600,6 +793,23 @@ static int scsi_read_toc2 (cdrom_drive *
   return(tracks);
 }
 
+/* Set operating speed */
+static int scsi_setspeed(cdrom_drive *d, int speed)
+{
+  if (speed == 0)
+    speed = 0xffff;		/* maximum speed */
+  else if (speed < 176)
+    speed *= 176;
+
+  memset(d->sg_buffer, 0, 12); 
+  d->sg_buffer[0] = 0xbb;	/* set speed */
+  d->sg_buffer[2] = speed >> 8;
+  d->sg_buffer[3] = speed;
+  d->sg_buffer[4] = -1;
+  d->sg_buffer[5] = -1;
+  return handle_scsi_cmd(d, 12, 0, 0, 0, 0);
+}
+
 /* These do one 'extra' copy in the name of clean code */
 
 static int i_read_28 (cdrom_drive *d, void *p, long begin, long sectors){
@@ -833,30 +1043,59 @@ static long scsi_read_map (cdrom_drive *
   while(1) {
     if((err=map(d,(p?buffer:NULL),begin,sectors))){
       if(d->report_all){
+#ifdef __linux__
 	struct sg_header *sg_hd=(struct sg_header *)d->sg;
+#endif
+#ifdef __NetBSD__
+	scsireq_t *sreq=(scsireq_t *)d->sg;
+#endif
 	char b[256];
 
 	sprintf(b,"scsi_read error: sector=%ld length=%ld retry=%d\n",
 		begin,sectors,retry_count);
+	fputs(b, stderr);
 	cdmessage(d,b);
 	sprintf(b,"                 Sense key: %x ASC: %x ASCQ: %x\n",
+#ifdef __linux__
 		(int)(sg_hd->sense_buffer[2]&0xf),
 		(int)(sg_hd->sense_buffer[12]),
 		(int)(sg_hd->sense_buffer[13]));
+#elif defined(__FreeBSD__)
+		d->ccb->csio.sense_data.flags & SSD_KEY,
+		d->ccb->csio.sense_data.add_sense_code,
+		d->ccb->csio.sense_data.add_sense_code_qual);
+#elif defined(__NetBSD__)
+		(int)(sreq->sense[2]&0xf),
+		(int)(sreq->sense[12]),
+		(int)(sreq->sense[13]));
+#endif
+	fputs(b, stderr);
 	cdmessage(d,b);
 	sprintf(b,"                 Transport error: %s\n",strerror_tr[err]);
+	fputs(b, stderr);
 	cdmessage(d,b);
 	sprintf(b,"                 System error: %s\n",strerror(errno));
+	fputs(b, stderr);
 	cdmessage(d,b);
 
+#ifndef __FreeBSD__
 	fprintf(stderr,"scsi_read error: sector=%ld length=%ld retry=%d\n",
 		begin,sectors,retry_count);
 	fprintf(stderr,"                 Sense key: %x ASC: %x ASCQ: %x\n",
+#ifdef __linux__
 		(int)(sg_hd->sense_buffer[2]&0xf),
 		(int)(sg_hd->sense_buffer[12]),
-		(int)(sg_hd->sense_buffer[13]));
+		(int)(sg_hd->sense_buffer[13])
+#endif
+#ifdef __NetBSD__
+		(int)(sreq->sense[2]&0xf),
+		(int)(sreq->sense[12]),
+		(int)(sreq->sense[13])
+#endif
+		);
 	fprintf(stderr,"                 Transport error: %s\n",strerror_tr[err]);
 	fprintf(stderr,"                 System error: %s\n",strerror(errno));
+#endif
       }
 
       if(!d->error_retry)return(-7);
@@ -1307,22 +1546,45 @@ static void check_fua_bit(cdrom_drive *d
   return;
 }
 
+#ifndef __FreeBSD__
 static int check_atapi(cdrom_drive *d){
+#ifdef __linux__
   int atapiret=-1;
+#endif
+#ifdef __NetBSD__
+  struct scsi_addr scaddr;
+#endif
   int fd = d->cdda_fd; /* this is the correct fd (not ioctl_fd), as the 
 			  generic device is the device we need to check */
 			  
   cdmessage(d,"\nChecking for SCSI emulation...\n");
 
-  if (ioctl(fd,SG_EMULATED_HOST,&atapiret)){
+#ifdef __linux__
+  if (ioctl(fd,SG_EMULATED_HOST,&atapiret))
+#endif
+#ifdef __NetBSD__
+  if(ioctl(fd,SCIOCIDENTIFY,&scaddr))
+#endif
+  {
     cderror(d,"\tSG_EMULATED_HOST ioctl() failed!\n");
     return(-1);
   } else {
-    if(atapiret==1){
+#ifdef __linux__
+    if(atapiret==1)
+#endif
+#ifdef __NetBSD__
+    if(scaddr.type == TYPE_ATAPI)
+#endif
+    {
+#ifdef __linux__
       cdmessage(d,"\tDrive is ATAPI (using SCSI host adaptor emulation)\n");
       /* Disable kernel SCSI command translation layer for access through sg */
       if (ioctl(fd,SG_SET_TRANSFORM,0))
 	cderror(d,"\tCouldn't disable kernel command translation layer\n");
+#endif
+#ifdef __NetBSD__
+      cdmessage(d,"\tDrive is ATAPI\n");
+#endif
       d->is_atapi=1;
     }else{
       cdmessage(d,"\tDrive is SCSI\n");
@@ -1332,6 +1594,46 @@ static int check_atapi(cdrom_drive *d){
     return(d->is_atapi);
   }
 }  
+#elif defined(__FreeBSD__)
+static int
+check_atapi(cdrom_drive *d)
+{
+	bzero(&(&d->ccb->ccb_h)[1], sizeof(d->ccb->cpi) - sizeof(d->ccb->ccb_h));
+	
+	d->ccb->ccb_h.func_code = XPT_PATH_INQ;
+
+	cdmessage(d, "\nChecking for ATAPICAM...\n");
+
+	if (cam_send_ccb(d->dev, d->ccb) < 0) {
+		cderror(d, "\terror sending XPT_PATH_INQ CCB: ");
+		cderror(d, cam_errbuf);
+		cderror(d, "\n");
+		return -1;
+	}
+
+	if ((d->ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+		cderror(d, "\tXPT_PATH_INQ CCB failed: ");
+		cderror(d, cam_errbuf);
+		cderror(d, "\n");
+		return -1;
+	}
+
+	/*
+	 * if the bus device name is `ata', we're (obviously)
+	 * running ATAPICAM.
+	 */
+
+	if (strncmp(d->ccb->cpi.dev_name, "ata", 3) == 0) {
+		cdmessage(d, "\tDrive is ATAPI (using ATAPICAM)\n");
+		d->is_atapi = 1;
+	} else {
+		cdmessage(d, "\tDrive is SCSI\n");
+		d->is_atapi = 0;
+	}
+	
+	return d->is_atapi;
+}
+#endif
 
 static int check_mmc(cdrom_drive *d){
   char *b;
@@ -1379,6 +1681,7 @@ static void check_exceptions(cdrom_drive
   }
 }
 
+#ifndef __FreeBSD__
 /* request vendor brand and model */
 unsigned char *scsi_inquiry(cdrom_drive *d){
   memcpy(d->sg_buffer,(char[]){ 0x12,0,0,0,56,0},6);
@@ -1389,6 +1692,7 @@ unsigned char *scsi_inquiry(cdrom_drive 
   }
   return (d->sg_buffer);
 }
+#endif
 
 
 int scsi_init_drive(cdrom_drive *d){
@@ -1434,7 +1738,7 @@ int scsi_init_drive(cdrom_drive *d){
 
   d->read_toc = (!memcmp(d->drive_model, "IMS", 3) && !d->is_atapi) ? scsi_read_toc2 : 
     scsi_read_toc;
-  d->set_speed = NULL;
+  d->set_speed = scsi_setspeed;
   
 
   if(!d->is_atapi){
@@ -1458,8 +1762,12 @@ int scsi_init_drive(cdrom_drive *d){
   check_fua_bit(d);
 
   d->error_retry=1;
+#ifndef __FreeBSD__
   d->sg=realloc(d->sg,d->nsectors*CD_FRAMESIZE_RAW + SG_OFF + 128);
   d->sg_buffer=d->sg+SG_OFF;
+#else
+  d->sg_buffer = realloc(d->sg_buffer, d->nsectors * CD_FRAMESIZE_RAW);
+#endif
   d->report_all=1;
   return(0);
 }
