patch-2.1.70 linux/drivers/cdrom/cdu31a.c

Next file: linux/drivers/cdrom/cdu31a.h
Previous file: linux/drivers/cdrom/cdrom.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.69/linux/drivers/cdrom/cdu31a.c linux/drivers/cdrom/cdu31a.c
@@ -111,6 +111,13 @@
  *    Heiko Eissfeldt <heiko@colossus.escape.de>
  *         For finding abug in the return of the track numbers.
  */
+/* conversion to Uniform cdrom layer.
+   TOC processing redone for proper multisession support.
+
+   TODO: 
+         CDs with form1 and form2 sectors cause problems
+         with current read-ahead strategy.
+   Heiko Eissfeldt Sep 97 */
 
 /*
  *
@@ -173,7 +180,13 @@
  *             status and be reset.  It recovers, though.
  *
  *  03/07/97 - Fixed a problem with timers.
- */
+ *
+ *
+ *  18 Spetember 1997 -- Ported to Uniform CD-ROM driver by 
+ *                 Heiko Eissfeldt <heiko@colossus.escape.de> with additional
+ *                 changes by Erik Andersen <andersee@debian.org>
+ *
+*/
 
 #include <linux/major.h>
 
@@ -198,16 +211,16 @@
 #include <asm/dma.h>
 
 #include <linux/cdrom.h>
-#include <linux/cdu31a.h>
+#include "cdu31a.h"
 
 #define MAJOR_NR CDU31A_CDROM_MAJOR
 #include <linux/blk.h>
 
-#define DEBUG 0
-
-#define CDU31A_READAHEAD 128  /* 128 sector, 64kB, 32 reads read-ahead */
+#define CDU31A_READAHEAD 4  /* 128 sector, 64kB, 32 reads read-ahead */
 #define CDU31A_MAX_CONSECUTIVE_ATTENTIONS 10
 
+#define DEBUG 0
+
 /* Define the following if you have data corruption problems. */
 #undef SONY_POLL_EACH_BYTE
 
@@ -240,7 +253,8 @@
 static int handle_sony_cd_attention(void);
 static int read_subcode(void);
 static void sony_get_toc(void);
-static int scd_open(struct inode *inode, struct file *filp);
+/*static int scd_open(struct inode *inode, struct file *filp);*/
+static int scd_open(struct cdrom_device_info *, int);
 static void do_sony_cd_cmd(unsigned char cmd,
                            unsigned char *params,
                            unsigned int num_params,
@@ -276,6 +290,8 @@
 
 static int sony_spun_up = 0;               /* Has the drive been spun up? */
 
+static int sony_speed = 0;                 /* Last wanted speed */
+
 static int sony_xa_mode = 0;               /* Is an XA disk in the drive
 					      and the drive a CDU31A? */
 
@@ -288,9 +304,12 @@
 static int sony_pas_init = 0;		   /* Initialize the Pro-Audio
 					      Spectrum card? */
 
-static struct s_sony_session_toc sony_toc;  /* Holds the
-					       table of
-					       contents. */
+static struct s_sony_session_toc single_toc;  /* Holds the
+					         table of
+					         contents. */
+
+static struct s_all_sessions_toc sony_toc; /* entries gathered from all
+                                                sessions */
 
 static int sony_toc_read = 0;		   /* Has the TOC been read for
 					      the drive? */
@@ -307,7 +326,8 @@
 						   using the CDROM drive, or
 						   NULL if none. */
 
-static int is_double_speed = 0; /* Is the drive a CDU33A? */
+static int is_double_speed = 0; /* does the drive support double speed ? */
+static int is_a_cdu31a = 1;     /* Is the drive a CDU31A? */
 
 static int is_auto_eject = 1;   /* Door has been locked? 1=No/0=Yes */
 
@@ -375,6 +395,30 @@
    return retval;
 }
 
+/*
+ * Uniform cdrom interface function
+ * report back, if disc has changed from time of last request.
+ */
+static int
+scd_media_changed(struct cdrom_device_info *cdi, int disc_nr)
+{
+  return scd_disk_change(cdi->dev);
+}
+
+/*
+ * Uniform cdrom interface function
+ * report back, if drive is ready
+ */
+static int scd_drive_status(struct cdrom_device_info *cdi, int slot_nr)
+{
+  if (CDSL_CURRENT != slot_nr) {
+     /* we have no changer support */
+     return -EINVAL;
+  }
+
+  return sony_spun_up ? CDS_DISC_OK : CDS_DRIVE_NOT_READY;
+}
+
 static inline void
 enable_interrupts(void)
 {
@@ -474,6 +518,25 @@
    outb(SONY_DRIVE_RESET_BIT, sony_cd_control_reg);
 }
 
+/*
+ * Uniform cdrom interface function
+ * reset drive and return when it is ready
+ */
+static int scd_reset(struct cdrom_device_info * cdi)
+{
+  int retry_count;
+
+  reset_drive();
+
+  retry_count = jiffies + SONY_RESET_TIMEOUT;
+  while ((retry_count > jiffies) && (!is_attention()))
+  {
+     sony_sleep();
+  }
+
+  return 0;
+}
+
 static inline void
 clear_attention(void)
 {
@@ -574,11 +637,65 @@
 }
 
 /*
+ * give more verbose error messages
+ */
+static unsigned char *translate_error( unsigned char err_code )
+{
+   static unsigned char errbuf[80];
+
+   switch (err_code) {
+     case 0x10: return "illegal command ";
+     case 0x11: return "illegal parameter ";
+
+     case 0x20: return "not loaded ";
+     case 0x21: return "no disc ";
+     case 0x22: return "not spinning ";
+     case 0x23: return "spinning ";
+     case 0x25: return "spindle servo ";
+     case 0x26: return "focus servo ";
+     case 0x29: return "eject mechanism ";
+     case 0x2a: return "audio playing ";
+     case 0x2c: return "emergency eject ";
+
+     case 0x30: return "focus ";
+     case 0x31: return "frame sync ";
+     case 0x32: return "subcode address ";
+     case 0x33: return "block sync ";
+     case 0x34: return "header address ";
+
+     case 0x40: return "illegal track read ";
+     case 0x41: return "mode 0 read ";
+     case 0x42: return "illegal mode read ";
+     case 0x43: return "illegal block size read ";
+     case 0x44: return "mode read ";
+     case 0x45: return "form read ";
+     case 0x46: return "leadout read ";
+     case 0x47: return "buffer overrun ";
+
+     case 0x53: return "unrecoverable CIRC ";
+     case 0x57: return "unrecoverable LECC ";
+
+     case 0x60: return "no TOC ";
+     case 0x61: return "invalid subcode data ";
+     case 0x63: return "focus on TOC read ";
+     case 0x64: return "frame sync on TOC read ";
+     case 0x65: return "TOC data ";
+
+     case 0x70: return "hardware failure ";
+     case 0x91: return "leadin ";
+     case 0x92: return "leadout ";
+     case 0x93: return "data track ";
+   }
+   sprintf(errbuf, "unknown 0x%02x ", err_code);
+   return errbuf;
+}
+
+/*
  * Set the drive parameters so the drive will auto-spin-up when a
  * disk is inserted.
  */
 static void
-set_drive_params(void)
+set_drive_params(int want_doublespeed)
 {
    unsigned char res_reg[12];
    unsigned int res_size;
@@ -602,7 +719,7 @@
 
    if (is_auto_eject) params[1] |= SONY_AUTO_EJECT_BIT;
    
-   if (is_double_speed)
+   if (is_double_speed && want_doublespeed)
    {
       params[1] |= SONY_DOUBLE_SPEED_BIT; /* Set the drive to double speed if 
                                              possible */
@@ -619,6 +736,38 @@
 }
 
 /*
+ * Uniform cdrom interface function
+ * select reading speed for data access
+ */
+static int scd_select_speed(struct cdrom_device_info *cdi, int speed)
+{
+  if (speed == 0)
+    sony_speed = 1;
+  else 
+    sony_speed = speed - 1;
+
+  set_drive_params(sony_speed);
+  return 0;
+}
+
+/*
+ * Uniform cdrom interface function
+ * lock or unlock eject button
+ */
+static int scd_lock_door(struct cdrom_device_info *cdi, int lock)
+{
+   if (lock == 0 && sony_usage == 1)
+   {
+      /* Unlock the door, only if nobody is using the drive */
+      is_auto_eject = 1;
+   } else {
+      is_auto_eject = 0;
+   }
+   set_drive_params(sony_speed);
+   return 0;
+}
+
+/*
  * This code will reset the drive and attempt to restore sane parameters.
  */
 static void
@@ -636,7 +785,7 @@
    {
       sony_sleep();
    }
-   set_drive_params();
+   set_drive_params(sony_speed);
    do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
    if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
    {
@@ -1069,9 +1218,9 @@
    unsigned int log;
 
 
-   log = bcd_to_int(msf[2]);
-   log += bcd_to_int(msf[1]) * 75;
-   log += bcd_to_int(msf[0]) * 4500;
+   log = msf[2];
+   log += msf[1] * 75;
+   log += msf[0] * 4500;
    log = log - LOG_START_OFFSET;
 
    return log;
@@ -1092,10 +1241,37 @@
    buf[2] = size % 256;
 }
 
+/* Uniform cdrom interface function.
+   Return the status of the current disc:
+   If it is recognized as CD-I -> return XA Mode 2 Form 2
+   If it is recognized as XA   -> return XA Mode 2 Form 1
+   If there is at least one data track return Mode 1
+   else return type AUDIO
+ */
+static int scd_disc_status(struct cdrom_device_info *cdi)
+{
+  if (sony_spun_up)
+  {
+    int i;
+
+    sony_get_toc();
+    /* look into the TOC */
+    if (sony_toc.disk_type == 0x10)   /* this is a CD-I disc */
+      return CDS_XA_2_2;
+    if (sony_toc.disk_type == 0x20)   /* this is a XA disc */
+      return CDS_XA_2_1;
+    for (i = 0; i < sony_toc.track_entries; i++)
+      if (sony_toc.tracks[i].control & CDROM_DATA_TRACK)
+        return CDS_DATA_1;
+    return CDS_AUDIO;
+  } else 
+    return CDS_NO_INFO;
+}
+
 /* Starts a read operation. Returns 0 on success and 1 on failure. 
    The read operation used here allows multiple sequential sectors 
    to be read and status returned for each sector.  The driver will
-   read the out one at a time as the requests come and abort the
+   read the output one at a time as the requests come and abort the
    operation if the requested sector is not the next one from the
    drive. */
 static int
@@ -1128,7 +1304,7 @@
    /* Read the full readahead amount. */
    else
    {
-      read_size = CDU31A_READAHEAD;
+      read_size = CDU31A_READAHEAD / 4;
    }
    size_to_buf(read_size, &params[3]);
 
@@ -1192,8 +1368,9 @@
    do_sony_cd_cmd(SONY_ABORT_CMD, NULL, 0, result_reg, &result_size);
    if ((result_reg[0] & 0xf0) == 0x20)
    {
-      printk("CDU31A: Error aborting read, error = 0x%2.2x\n",
-             result_reg[1]);
+      printk("CDU31A: Error aborting read, %s error\n",
+             translate_error(
+             result_reg[1]));
    }
 
    while (is_result_reg_not_empty())
@@ -1525,6 +1702,8 @@
 #endif
 }
 
+static int scd_spinup(void);
+
 /*
  * The OS calls this to perform a read or write operation to the drive.
  * Write obviously fail.  Reads to a read ahead of sony_buffer_size
@@ -1599,13 +1778,7 @@
 
       if (!sony_spun_up)
       {
-         struct inode in;
-
-         /* This is a kludge to get a valid dev in an inode that
-            scd_open can take.  That's the only thing scd_open()
-            uses the inode for. */
-         in.i_rdev = CURRENT->rq_dev;
-         scd_open(&in,NULL);
+         scd_spinup();
       }
 
       /* I don't use INIT_REQUEST because it calls return, which would
@@ -1633,16 +1806,6 @@
 	 goto cdu31a_request_startover;
       }
 
-      /* Check for base read of multi-session disk.  This will still work
-         for single session disks, so just do it.  Blocks less than 80
-         are for the volume info, so offset them by the start track (which
-         should be zero for a single-session disk). */
-      if (block < 80)
-      {
-	 /* Offset the request into the session. */
-	 block += (sony_toc.start_track_lba * 4);
-      }
-
       switch(CURRENT->cmd)
       {
       case READ:
@@ -1735,7 +1898,7 @@
             }
             else
             {
-               printk("CDU31A: Read error: 0x%2.2x\n", res_reg[1]);
+               printk("CDU31A: %s error for block %d, nblock %d\n", translate_error(res_reg[1]), block, nblock);
             }
             goto try_read_again;
          }
@@ -1775,23 +1938,6 @@
 #endif
 }
 
-/* Copy overlapping buffers. */
-static void
-mcovlp(char *dst,
-       char *src,
-       int  size)
-{
-   src += (size - 1);
-   dst += (size - 1);
-   while (size > 0)
-   {
-      *dst = *src;
-      size--;
-      dst--;
-      src--;
-   }
-}
-
 
 /*
  * Read the table of contents from the drive and set up TOC if
@@ -1805,7 +1951,9 @@
    unsigned char parms[1];
    int session;
    int num_spin_ups;
-
+   int totaltracks = 0;
+   int mint = 99;
+   int maxt = 0;
 
 #if DEBUG
    printk("Entering sony_get_toc\n");
@@ -1839,9 +1987,8 @@
             goto respinup_on_gettoc;
          }
 
-         printk("cdu31a: Error reading TOC: %x %x\n",
-                sony_toc.exec_status[0],
-                sony_toc.exec_status[1]);
+         printk("cdu31a: Error reading TOC: %x %s\n",
+                res_reg[0], translate_error(res_reg[1]));
 	 return;
       }
 
@@ -1851,9 +1998,12 @@
 	 fails.  Then we know what the last valid session on the disk is.
 	 No need to check session 0, since session 0 is the same as session
          1; the command returns different information if you give it 0. 
-         Don't check session 1 because that is the first session, it must
-         be there. */
-      session = 2;
+        */
+#if DEBUG
+      memset(&sony_toc, 0x0e, sizeof(sony_toc));
+      memset(&single_toc, 0x0f, sizeof(single_toc));
+#endif
+      session = 1;
       while (1)
       {
 #if DEBUG
@@ -1875,95 +2025,265 @@
 	    /* An error reading the TOC, this must be past the last session. */
 	    break;
          }
-
-         session++;
-
-         /* Let's not get carried away... */
-         if (session > 20)
-         {
-            printk("cdu31a: too many sessions: %d\n", session);
-            return;
-         }
-      }
-
-      session--;
-
 #if DEBUG
-      printk("Reading session %d\n", session);
+         printk("Reading session %d\n", session);
 #endif
 
-      parms[0] = session;
-      do_sony_cd_cmd(SONY_REQ_TOC_DATA_SPEC_CMD,
+         parms[0] = session;
+         do_sony_cd_cmd(SONY_REQ_TOC_DATA_SPEC_CMD,
 		     parms,
 		     1, 
-		     (unsigned char *) &sony_toc, 
+		     (unsigned char *) &single_toc, 
 		     &res_size);
-      if ((res_size < 2) || ((sony_toc.exec_status[0] & 0xf0) == 0x20))
-      {
-         printk("cdu31a: Error reading session %d: %x %x\n",
+         if ((res_size < 2) || ((single_toc.exec_status[0] & 0xf0) == 0x20))
+         {
+            printk("cdu31a: Error reading session %d: %x %s\n",
                 session,
-                sony_toc.exec_status[0],
-                sony_toc.exec_status[1]);
-	 /* An error reading the TOC.  Return without sony_toc_read
-	    set. */
-	 return;
-      }
-      
-      sony_toc_read = 1;
+                single_toc.exec_status[0],
+                translate_error(single_toc.exec_status[1]));
+	    /* An error reading the TOC.  Return without sony_toc_read
+	       set. */
+	    return;
+         }
+#if DEBUG
+      printk("add0 %01x, con0 %01x, poi0 %02x, 1st trk %d, dsktyp %x, dum0 %x\n",
+              single_toc.address0, single_toc.control0, single_toc.point0,
+              bcd_to_int(single_toc.first_track_num), single_toc.disk_type, single_toc.dummy0);
+      printk("add1 %01x, con1 %01x, poi1 %02x, lst trk %d, dummy1 %x, dum2 %x\n",
+              single_toc.address1, single_toc.control1, single_toc.point1,
+              bcd_to_int(single_toc.last_track_num), single_toc.dummy1, single_toc.dummy2);
+      printk("add2 %01x, con2 %01x, poi2 %02x leadout start min %d, sec %d, frame %d\n",
+              single_toc.address2, single_toc.control2, single_toc.point2,
+              bcd_to_int(single_toc.lead_out_start_msf[0]),
+              bcd_to_int(single_toc.lead_out_start_msf[1]),
+              bcd_to_int(single_toc.lead_out_start_msf[2]));
+      if (res_size > 18 && single_toc.pointb0 > 0xaf)
+      printk("addb0 %01x, conb0 %01x, poib0 %02x, nextsession min %d, sec %d, frame %d\n"
+             "#mode5_ptrs %02d, max_start_outer_leadout_msf min %d, sec %d, frame %d\n",
+              single_toc.addressb0, single_toc.controlb0, single_toc.pointb0,
+              bcd_to_int(single_toc.next_poss_prog_area_msf[0]),
+              bcd_to_int(single_toc.next_poss_prog_area_msf[1]),
+              bcd_to_int(single_toc.next_poss_prog_area_msf[2]),
+              single_toc.num_mode_5_pointers,
+              bcd_to_int(single_toc.max_start_outer_leadout_msf[0]),
+              bcd_to_int(single_toc.max_start_outer_leadout_msf[1]),
+              bcd_to_int(single_toc.max_start_outer_leadout_msf[2]));
+      if (res_size > 27 && single_toc.pointb1 > 0xaf)
+      printk("addb1 %01x, conb1 %01x, poib1 %02x, %x %x %x %x #skipint_ptrs %d, #skiptrkassign %d %x\n",
+              single_toc.addressb1, single_toc.controlb1, single_toc.pointb1,
+              single_toc.dummyb0_1[0],
+              single_toc.dummyb0_1[1],
+              single_toc.dummyb0_1[2],
+              single_toc.dummyb0_1[3],
+              single_toc.num_skip_interval_pointers,
+              single_toc.num_skip_track_assignments,
+              single_toc.dummyb0_2);
+      if (res_size > 36 && single_toc.pointb2 > 0xaf)
+      printk("addb2 %01x, conb2 %01x, poib2 %02x, %02x %02x %02x %02x %02x %02x %02x\n",
+              single_toc.addressb2, single_toc.controlb2, single_toc.pointb2,
+              single_toc.tracksb2[0],
+              single_toc.tracksb2[1],
+              single_toc.tracksb2[2],
+              single_toc.tracksb2[3],
+              single_toc.tracksb2[4],
+              single_toc.tracksb2[5],
+              single_toc.tracksb2[6]);
+      if (res_size > 45 && single_toc.pointb3 > 0xaf)
+      printk("addb3 %01x, conb3 %01x, poib3 %02x, %02x %02x %02x %02x %02x %02x %02x\n",
+              single_toc.addressb3, single_toc.controlb3, single_toc.pointb3,
+              single_toc.tracksb3[0],
+              single_toc.tracksb3[1],
+              single_toc.tracksb3[2],
+              single_toc.tracksb3[3],
+              single_toc.tracksb3[4],
+              single_toc.tracksb3[5],
+              single_toc.tracksb3[6]);
+      if (res_size > 54 && single_toc.pointb4 > 0xaf)
+      printk("addb4 %01x, conb4 %01x, poib4 %02x, %02x %02x %02x %02x %02x %02x %02x\n",
+              single_toc.addressb4, single_toc.controlb4, single_toc.pointb4,
+              single_toc.tracksb4[0],
+              single_toc.tracksb4[1],
+              single_toc.tracksb4[2],
+              single_toc.tracksb4[3],
+              single_toc.tracksb4[4],
+              single_toc.tracksb4[5],
+              single_toc.tracksb4[6]);
+      if (res_size > 63 && single_toc.pointc0 > 0xaf)
+      printk("addc0 %01x, conc0 %01x, poic0 %02x, %02x %02x %02x %02x %02x %02x %02x\n",
+              single_toc.addressc0, single_toc.controlc0, single_toc.pointc0,
+              single_toc.dummyc0[0],
+              single_toc.dummyc0[1],
+              single_toc.dummyc0[2],
+              single_toc.dummyc0[3],
+              single_toc.dummyc0[4],
+              single_toc.dummyc0[5],
+              single_toc.dummyc0[6]);
+#endif
+#undef DEBUG
+#define DEBUG 0
+
+      sony_toc.lead_out_start_msf[0] = bcd_to_int(single_toc.lead_out_start_msf[0]);
+      sony_toc.lead_out_start_msf[1] = bcd_to_int(single_toc.lead_out_start_msf[1]);
+      sony_toc.lead_out_start_msf[2] = bcd_to_int(single_toc.lead_out_start_msf[2]);
+      sony_toc.lead_out_start_lba = single_toc.lead_out_start_lba =
+                    msf_to_log(sony_toc.lead_out_start_msf);
 
       /* For points that do not exist, move the data over them
 	 to the right location. */
-      if (sony_toc.pointb0 != 0xb0)
+      if (single_toc.pointb0 != 0xb0)
       {
-	 mcovlp(((char *) &sony_toc) + 27,
-		((char *) &sony_toc) + 18,
-		res_size - 18);
+	 memmove(((char *) &single_toc) + 27,
+		 ((char *) &single_toc) + 18,
+		 res_size - 18);
 	 res_size += 9;
       }
-      if (sony_toc.pointb1 != 0xb1)
+      else if (res_size > 18) {
+        sony_toc.lead_out_start_msf[0] = bcd_to_int(single_toc.max_start_outer_leadout_msf[0]);
+        sony_toc.lead_out_start_msf[1] = bcd_to_int(single_toc.max_start_outer_leadout_msf[1]);
+        sony_toc.lead_out_start_msf[2] = bcd_to_int(single_toc.max_start_outer_leadout_msf[2]);
+        sony_toc.lead_out_start_lba = msf_to_log(sony_toc.lead_out_start_msf);
+      }
+      if (single_toc.pointb1 != 0xb1)
       {
-	 mcovlp(((char *) &sony_toc) + 36,
-		((char *) &sony_toc) + 27,
+	 memmove(((char *) &single_toc) + 36,
+		((char *) &single_toc) + 27,
 		res_size - 27);
 	 res_size += 9;
       }
-      if (sony_toc.pointb2 != 0xb2)
+      if (single_toc.pointb2 != 0xb2)
       {
-	 mcovlp(((char *) &sony_toc) + 45,
-		((char *) &sony_toc) + 36,
+	 memmove(((char *) &single_toc) + 45,
+		((char *) &single_toc) + 36,
 		res_size - 36);
 	 res_size += 9;
       }
-      if (sony_toc.pointb3 != 0xb3)
+      if (single_toc.pointb3 != 0xb3)
       {
-	 mcovlp(((char *) &sony_toc) + 54,
-		((char *) &sony_toc) + 45,
+	 memmove(((char *) &single_toc) + 54,
+		((char *) &single_toc) + 45,
 		res_size - 45);
 	 res_size += 9;
       }
-      if (sony_toc.pointb4 != 0xb4)
+      if (single_toc.pointb4 != 0xb4)
       {
-	 mcovlp(((char *) &sony_toc) + 63,
-		((char *) &sony_toc) + 54,
+	 memmove(((char *) &single_toc) + 63,
+		((char *) &single_toc) + 54,
 		res_size - 54);
 	 res_size += 9;
       }
-      if (sony_toc.pointc0 != 0xc0)
+      if (single_toc.pointc0 != 0xc0)
       {
-	 mcovlp(((char *) &sony_toc) + 72,
-		((char *) &sony_toc) + 63,
+	 memmove(((char *) &single_toc) + 72,
+		((char *) &single_toc) + 63,
 		res_size - 63);
 	 res_size += 9;
       }
 
-      sony_toc.start_track_lba = msf_to_log(sony_toc.tracks[0].track_start_msf);
-      sony_toc.lead_out_start_lba = msf_to_log(sony_toc.lead_out_start_msf);
+#if DEBUG
+      printk("start track lba %u,  leadout start lba %u\n",
+              single_toc.start_track_lba, single_toc.lead_out_start_lba);
+      { int i;
+        for (i = 0; i < 1 + bcd_to_int(single_toc.last_track_num)
+                          - bcd_to_int(single_toc.first_track_num); i++) {
+          printk("trk %02d: add 0x%01x, con 0x%01x,  track %02d, start min %02d, sec %02d, frame %02d\n",
+                 i, single_toc.tracks[i].address, single_toc.tracks[i].control,
+                 bcd_to_int(single_toc.tracks[i].track),
+                 bcd_to_int(single_toc.tracks[i].track_start_msf[0]),
+                 bcd_to_int(single_toc.tracks[i].track_start_msf[1]),
+                 bcd_to_int(single_toc.tracks[i].track_start_msf[2]));
+          if (mint > bcd_to_int(single_toc.tracks[i].track))
+            mint = bcd_to_int(single_toc.tracks[i].track);
+          if (maxt < bcd_to_int(single_toc.tracks[i].track))
+            maxt = bcd_to_int(single_toc.tracks[i].track);
+        }
+        printk("min track number %d,   max track number %d\n", mint, maxt);
+      }
+#endif
+
+         /* prepare a special table of contents for a CD-I disc. They don't have one. */
+         if (single_toc.disk_type == 0x10 &&
+             single_toc.first_track_num == 2 &&
+             single_toc.last_track_num == 2 /* CD-I */)
+         {
+            sony_toc.tracks[totaltracks].address = 1;
+	    sony_toc.tracks[totaltracks].control = 4; /* force data tracks */
+            sony_toc.tracks[totaltracks].track = 1;
+            sony_toc.tracks[totaltracks].track_start_msf[0] = 0;
+            sony_toc.tracks[totaltracks].track_start_msf[1] = 2;
+            sony_toc.tracks[totaltracks].track_start_msf[2] = 0;
+            mint = maxt = 1;
+            totaltracks++;
+         } else
+         /* gather track entries from this session */
+         { int i;
+	   for (i = 0; i < 1 + bcd_to_int(single_toc.last_track_num)
+                          - bcd_to_int(single_toc.first_track_num); i++, totaltracks++) {
+             sony_toc.tracks[totaltracks].address = single_toc.tracks[i].address;
+             sony_toc.tracks[totaltracks].control = single_toc.tracks[i].control;
+             sony_toc.tracks[totaltracks].track = bcd_to_int(single_toc.tracks[i].track);
+             sony_toc.tracks[totaltracks].track_start_msf[0] = 
+                  bcd_to_int(single_toc.tracks[i].track_start_msf[0]);
+             sony_toc.tracks[totaltracks].track_start_msf[1] = 
+                  bcd_to_int(single_toc.tracks[i].track_start_msf[1]);
+             sony_toc.tracks[totaltracks].track_start_msf[2] = 
+                  bcd_to_int(single_toc.tracks[i].track_start_msf[2]);
+             if (i == 0)
+               single_toc.start_track_lba = msf_to_log(sony_toc.tracks[totaltracks].track_start_msf);
+             if (mint > sony_toc.tracks[totaltracks].track)
+               mint = sony_toc.tracks[totaltracks].track;
+             if (maxt < sony_toc.tracks[totaltracks].track)
+               maxt = sony_toc.tracks[totaltracks].track;
+           }
+         }
+         sony_toc.first_track_num = mint;
+         sony_toc.last_track_num = maxt;
+         /* Disk type of last session wins. For example:
+            CD-Extra has disk type 0 for the first session, so
+            a dumb HiFi CD player thinks it is a plain audio CD.
+	    We are interested in the disk type of the last session,
+            which is 0x20 (XA) for CD-Extra, so we can access the
+            data track ... */
+         sony_toc.disk_type = single_toc.disk_type;
+         sony_toc.sessions = session;
+
+         /* don't believe everything :-) */
+         if (session == 1)
+            single_toc.start_track_lba = 0;
+         sony_toc.start_track_lba = single_toc.start_track_lba;
 
+         if (session > 1 && single_toc.pointb0 == 0xb0 &&
+             sony_toc.lead_out_start_lba == single_toc.lead_out_start_lba)
+	 {
+            break;
+         }
+
+         /* Let's not get carried away... */
+         if (session > 40)
+         {
+            printk("cdu31a: too many sessions: %d\n", session);
+            break;
+         }
+         session++;
+      }
+      sony_toc.track_entries = totaltracks;
+      /* add one entry for the LAST track with track number CDROM_LEADOUT */ 
+      sony_toc.tracks[totaltracks].address = single_toc.address2;
+      sony_toc.tracks[totaltracks].control = single_toc.control2;
+      sony_toc.tracks[totaltracks].track = CDROM_LEADOUT;
+      sony_toc.tracks[totaltracks].track_start_msf[0] = 
+             sony_toc.lead_out_start_msf[0];
+      sony_toc.tracks[totaltracks].track_start_msf[1] = 
+             sony_toc.lead_out_start_msf[1];
+      sony_toc.tracks[totaltracks].track_start_msf[2] = 
+             sony_toc.lead_out_start_msf[2];
+
+      sony_toc_read = 1;
+#undef DEBUG
 #if DEBUG
    printk("Disk session %d, start track: %d, stop track: %d\n",
 	  session,
-	  sony_toc.start_track_lba,
-	  sony_toc.lead_out_start_lba);
+	  single_toc.start_track_lba,
+	  single_toc.lead_out_start_lba);
 #endif
    }
 #if DEBUG
@@ -1973,19 +2293,35 @@
 
 
 /*
+ * Uniform cdrom interface function
+ * return multisession offset and sector information
+ */
+static int scd_get_last_session(struct cdrom_device_info *cdi, 
+                                struct cdrom_multisession *ms_info)
+{
+   if (ms_info == NULL)
+      return 1;
+
+   if (!sony_toc_read)
+      sony_get_toc();
+
+   ms_info->addr_format = CDROM_LBA;
+   ms_info->addr.lba    = sony_toc.start_track_lba;
+   ms_info->xa_flag     = sony_toc.disk_type == SONY_XA_DISK_TYPE ||
+                          sony_toc.disk_type == 0x10 /* CDI */;
+
+   return 0;
+}
+
+/*
  * Search for a specific track in the table of contents.
  */
 static int
 find_track(int track)
 {
    int i;
-   int num_tracks;
-
 
-   num_tracks = (  bcd_to_int(sony_toc.last_track_num)
-                 - bcd_to_int(sony_toc.first_track_num)
-		 + 1);
-   for (i = 0; i < num_tracks; i++)
+   for (i = 0; i <= sony_toc.track_entries; i++)
    {
       if (sony_toc.tracks[i].track == track)
       {
@@ -1998,7 +2334,7 @@
 
 
 /*
- * Read the subcode and put it int last_sony_subcode for future use.
+ * Read the subcode and put it in last_sony_subcode for future use.
  */
 static int
 read_subcode(void)
@@ -2013,11 +2349,60 @@
                   &res_size);
    if ((res_size < 2) || ((last_sony_subcode.exec_status[0] & 0xf0) == 0x20))
    {
-      printk("Sony CDROM error 0x%2.2x (read_subcode)\n",
-             last_sony_subcode.exec_status[1]);
+      printk("Sony CDROM error %s (read_subcode)\n",
+             translate_error(last_sony_subcode.exec_status[1]));
       return -EIO;
    }
 
+   last_sony_subcode.track_num = bcd_to_int(last_sony_subcode.track_num);
+   last_sony_subcode.index_num = bcd_to_int(last_sony_subcode.index_num);
+   last_sony_subcode.abs_msf[0] = bcd_to_int(last_sony_subcode.abs_msf[0]);
+   last_sony_subcode.abs_msf[1] = bcd_to_int(last_sony_subcode.abs_msf[1]);
+   last_sony_subcode.abs_msf[2] = bcd_to_int(last_sony_subcode.abs_msf[2]);
+
+   last_sony_subcode.rel_msf[0] = bcd_to_int(last_sony_subcode.rel_msf[0]);
+   last_sony_subcode.rel_msf[1] = bcd_to_int(last_sony_subcode.rel_msf[1]);
+   last_sony_subcode.rel_msf[2] = bcd_to_int(last_sony_subcode.rel_msf[2]);
+   return 0;
+}
+
+/*
+ * Uniform cdrom interface function
+ * return the media catalog number found on some older audio cds
+ */
+static int
+scd_get_mcn(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn)
+{
+   unsigned char resbuffer[2 + 14];
+   unsigned char *mcnp = mcn->medium_catalog_number;
+   unsigned char *resp = resbuffer + 3;
+   unsigned int res_size;
+
+   memset(mcn->medium_catalog_number, 0, 14);
+   do_sony_cd_cmd(SONY_REQ_UPC_EAN_CMD,
+                  NULL,
+                  0, 
+                  resbuffer,
+                  &res_size);
+   if ((res_size < 2) || ((resbuffer[0] & 0xf0) == 0x20))
+     ;
+   else {
+     /* packed bcd to single ASCII digits */
+     *mcnp++ = (*resp >> 4)     + '0'; 
+     *mcnp++ = (*resp++ & 0x0f) + '0'; 
+     *mcnp++ = (*resp >> 4)     + '0'; 
+     *mcnp++ = (*resp++ & 0x0f) + '0'; 
+     *mcnp++ = (*resp >> 4)     + '0'; 
+     *mcnp++ = (*resp++ & 0x0f) + '0'; 
+     *mcnp++ = (*resp >> 4)     + '0'; 
+     *mcnp++ = (*resp++ & 0x0f) + '0'; 
+     *mcnp++ = (*resp >> 4)     + '0'; 
+     *mcnp++ = (*resp++ & 0x0f) + '0'; 
+     *mcnp++ = (*resp >> 4)     + '0'; 
+     *mcnp++ = (*resp++ & 0x0f) + '0'; 
+     *mcnp++ = (*resp >> 4)     + '0'; 
+   }
+   *mcnp = '\0';
    return 0;
 }
 
@@ -2030,12 +2415,8 @@
  * (not BCD), so all the conversions are done.
  */
 static int
-sony_get_subchnl_info(long arg)
+sony_get_subchnl_info(struct cdrom_subchnl *schi)
 {
-   int err;
-   struct cdrom_subchnl schi;
-
-
    /* Get attention stuff */
    while (handle_sony_cd_attention())
       ;
@@ -2046,13 +2427,9 @@
       return -EIO;
    }
 
-   err = verify_area(VERIFY_WRITE, (char *) arg, sizeof(schi));
-   if (err) return err;
-
-   copy_from_user(&schi, (char *) arg, sizeof(schi));
-   
    switch (sony_audio_status)
    {
+   case CDROM_AUDIO_NO_STATUS:
    case CDROM_AUDIO_PLAY:
       if (read_subcode() < 0)
       {
@@ -2064,40 +2441,39 @@
    case CDROM_AUDIO_COMPLETED:
       break;
 
+#if 0
    case CDROM_AUDIO_NO_STATUS:
-      schi.cdsc_audiostatus = sony_audio_status;
-      copy_to_user((char *) arg, &schi, sizeof(schi));
+      schi->cdsc_audiostatus = sony_audio_status;
       return 0;
       break;
-
+#endif
    case CDROM_AUDIO_INVALID:
    case CDROM_AUDIO_ERROR:
    default:
       return -EIO;
    }
 
-   schi.cdsc_audiostatus = sony_audio_status;
-   schi.cdsc_adr = last_sony_subcode.address;
-   schi.cdsc_ctrl = last_sony_subcode.control;
-   schi.cdsc_trk = bcd_to_int(last_sony_subcode.track_num);
-   schi.cdsc_ind = bcd_to_int(last_sony_subcode.index_num);
-   if (schi.cdsc_format == CDROM_MSF)
-   {
-      schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode.abs_msf[0]);
-      schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode.abs_msf[1]);
-      schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode.abs_msf[2]);
-
-      schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode.rel_msf[0]);
-      schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode.rel_msf[1]);
-      schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode.rel_msf[2]);
+   schi->cdsc_audiostatus = sony_audio_status;
+   schi->cdsc_adr = last_sony_subcode.address;
+   schi->cdsc_ctrl = last_sony_subcode.control;
+   schi->cdsc_trk = last_sony_subcode.track_num;
+   schi->cdsc_ind = last_sony_subcode.index_num;
+   if (schi->cdsc_format == CDROM_MSF)
+   {
+      schi->cdsc_absaddr.msf.minute = last_sony_subcode.abs_msf[0];
+      schi->cdsc_absaddr.msf.second = last_sony_subcode.abs_msf[1];
+      schi->cdsc_absaddr.msf.frame = last_sony_subcode.abs_msf[2];
+
+      schi->cdsc_reladdr.msf.minute = last_sony_subcode.rel_msf[0];
+      schi->cdsc_reladdr.msf.second = last_sony_subcode.rel_msf[1];
+      schi->cdsc_reladdr.msf.frame = last_sony_subcode.rel_msf[2];
    }
-   else if (schi.cdsc_format == CDROM_LBA)
+   else if (schi->cdsc_format == CDROM_LBA)
    {
-      schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode.abs_msf);
-      schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode.rel_msf);
+      schi->cdsc_absaddr.lba = msf_to_log(last_sony_subcode.abs_msf);
+      schi->cdsc_reladdr.lba = msf_to_log(last_sony_subcode.rel_msf);
    }
    
-   copy_to_user((char *) arg, &schi, sizeof(schi));
    return 0;
 }
 
@@ -2172,7 +2548,7 @@
       /* If data block, then get 2340 bytes offset by 12. */
       if (sony_raw_data_mode)
       {
-         insb(sony_cd_read_reg, buffer + CD_XA_HEAD, CD_FRAMESIZE_XA);
+         insb(sony_cd_read_reg, buffer + CD_XA_HEAD, CD_FRAMESIZE_RAW1);
       }
       else
       {
@@ -2243,8 +2619,7 @@
 /* Perform a raw data read.  This will automatically detect the
    track type and read the proper data (audio or data). */
 static int
-read_audio(struct cdrom_read_audio *ra,
-           struct inode            *inode)
+read_audio(struct cdrom_read_audio *ra)
 {
    int retval;
    unsigned char params[2];
@@ -2274,7 +2649,7 @@
 
    if (!sony_spun_up)
    {
-      scd_open (inode, NULL);
+     scd_spinup();
    }
 
    /* Set the drive to do raw operations. */
@@ -2355,9 +2730,9 @@
                }
                else
                {
-                  printk("CDU31A: Error reading audio data on sector %d: 0x%x\n",
+                  printk("CDU31A: Error reading audio data on sector %d: %s\n",
                          ra->addr.lba + cframe,
-                         res_reg[1]);
+                         translate_error(res_reg[1]));
                   retval = -EIO;
                   goto exit_read_audio;
                }
@@ -2371,9 +2746,9 @@
          }
          else
          {
-            printk("CDU31A: Error reading audio data on sector %d: 0x%x\n",
+            printk("CDU31A: Error reading audio data on sector %d: %s\n",
                    ra->addr.lba + cframe,
-                   res_reg[1]);
+                   translate_error(res_reg[1]));
             retval = -EIO;
             goto exit_read_audio;
          }
@@ -2391,8 +2766,8 @@
    get_result(res_reg, &res_size);
    if ((res_reg[0] & 0xf0) == 0x20)
    {
-      printk("CDU31A: Error return from audio read: 0x%x\n",
-             res_reg[1]);
+      printk("CDU31A: Error return from audio read: %s\n",
+             translate_error(res_reg[1]));
       retval = -EIO;
       goto exit_read_audio;
    }
@@ -2438,19 +2813,38 @@
       do_sony_cd_cmd(cmd, params, num_params, result_buffer, result_size);
       if ((*result_size < 2) || ((result_buffer[0] & 0xf0) == 0x20))
       {
-         printk("Sony CDROM error 0x%2.2x (CDROM%s)\n", result_buffer[1], name);
+         printk("Sony CDROM error %s (CDROM%s)\n", translate_error(result_buffer[1]), name);
          return -EIO;
       }
       return 0;
 }
  
 /*
+ * Uniform cdrom interface function
+ * open the tray
+ */
+static int scd_tray_move(struct cdrom_device_info *cdi, int position)
+{
+  if (position == 1 /* open tray */)
+  {
+    unsigned char res_reg[12];
+    unsigned int res_size;
+
+    do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size);
+    do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+
+    sony_audio_status = CDROM_AUDIO_INVALID;
+    return do_sony_cd_cmd_chk("EJECT",SONY_EJECT_CMD, NULL, 0, res_reg, &res_size);
+  } else
+  return 0;
+}
+
+/*
  * The big ugly ioctl handler.
  */
-static int scd_ioctl(struct inode *inode,
-          struct file  *file,
+static int scd_audio_ioctl(struct cdrom_device_info *cdi,
           unsigned int  cmd,
-          unsigned long arg)
+          void * arg)
 {
    unsigned char res_reg[12];
    unsigned int res_size;
@@ -2458,16 +2852,10 @@
    int i;
 
 
-   if (!inode)
-   {
-      return -EINVAL;
-   }
-
    switch (cmd)
    {
    case CDROMSTART:     /* Spin up the drive */
       return do_sony_cd_cmd_chk("START",SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
-      return 0;
       break;
       
    case CDROMSTOP:      /* Spin down the drive */
@@ -2504,12 +2892,12 @@
       do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
       
       /* Start the drive at the saved position. */
-      params[1] = cur_pos_msf[0];
-      params[2] = cur_pos_msf[1];
-      params[3] = cur_pos_msf[2];
-      params[4] = final_pos_msf[0];
-      params[5] = final_pos_msf[1];
-      params[6] = final_pos_msf[2];
+      params[1] = int_to_bcd(cur_pos_msf[0]);
+      params[2] = int_to_bcd(cur_pos_msf[1]);
+      params[3] = int_to_bcd(cur_pos_msf[2]);
+      params[4] = int_to_bcd(final_pos_msf[0]);
+      params[5] = int_to_bcd(final_pos_msf[1]);
+      params[6] = int_to_bcd(final_pos_msf[2]);
       params[0] = 0x03;
       if(do_sony_cd_cmd_chk("RESUME",SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size)<0)
       	return -EIO;
@@ -2517,32 +2905,27 @@
       return 0;
 
    case CDROMPLAYMSF:   /* Play starting at the given MSF address. */
-      i=verify_area(VERIFY_READ, (char *) arg, 6);
-      if(i)
-      	return i;
       do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
-      copy_from_user(&(params[1]), (void *) arg, 6);
       
       /* The parameters are given in int, must be converted */
       for (i=1; i<7; i++)
       {
-         params[i] = int_to_bcd(params[i]);
+         params[i] = int_to_bcd(((unsigned char *)arg)[i-1]);
       }
       params[0] = 0x03;
       if(do_sony_cd_cmd_chk("PLAYMSF",SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size)<0)
       	return -EIO;
       
       /* Save the final position for pauses and resumes */
-      final_pos_msf[0] = params[4];
-      final_pos_msf[1] = params[5];
-      final_pos_msf[2] = params[6];
+      final_pos_msf[0] = bcd_to_int(params[4]);
+      final_pos_msf[1] = bcd_to_int(params[5]);
+      final_pos_msf[2] = bcd_to_int(params[6]);
       sony_audio_status = CDROM_AUDIO_PLAY;
       return 0;
 
    case CDROMREADTOCHDR:        /* Read the table of contents header */
       {
          struct cdrom_tochdr *hdr;
-         struct cdrom_tochdr loc_hdr;
          
          sony_get_toc();
          if (!sony_toc_read)
@@ -2551,19 +2934,14 @@
          }
          
          hdr = (struct cdrom_tochdr *) arg;
-         i=verify_area(VERIFY_WRITE, hdr, sizeof(*hdr));
-         if(i<0)
-         	return i;
-         loc_hdr.cdth_trk0 = bcd_to_int(sony_toc.first_track_num);
-         loc_hdr.cdth_trk1 = bcd_to_int(sony_toc.last_track_num);
-         copy_to_user(hdr, &loc_hdr, sizeof(*hdr));
+         hdr->cdth_trk0 = sony_toc.first_track_num;
+         hdr->cdth_trk1 = sony_toc.last_track_num;
       }
       return 0;
 
    case CDROMREADTOCENTRY:      /* Read a given table of contents entry */
       {
          struct cdrom_tocentry *entry;
-         struct cdrom_tocentry loc_entry;
          int track_idx;
          unsigned char *msf_val = NULL;
          
@@ -2574,54 +2952,35 @@
          }
          
          entry = (struct cdrom_tocentry *) arg;
-         i=verify_area(VERIFY_READ, entry, sizeof(*entry));
-         if(i<0)
-         	return i;
-         i=verify_area(VERIFY_WRITE, entry, sizeof(*entry));
-         if(i<0)
-         	return i;
          
-         copy_from_user(&loc_entry, entry, sizeof(loc_entry));
-         
-         /* Lead out is handled separately since it is special. */
-         if (loc_entry.cdte_track == CDROM_LEADOUT)
+         track_idx = find_track(entry->cdte_track);
+         if (track_idx < 0)
          {
-            loc_entry.cdte_adr = sony_toc.address2;
-            loc_entry.cdte_ctrl = sony_toc.control2;
-            msf_val = sony_toc.lead_out_start_msf;
+            return -EINVAL;
          }
-         else
-         {
-            track_idx = find_track(int_to_bcd(loc_entry.cdte_track));
-            if (track_idx < 0)
-            {
-               return -EINVAL;
-            }
             
-            loc_entry.cdte_adr = sony_toc.tracks[track_idx].address;
-            loc_entry.cdte_ctrl = sony_toc.tracks[track_idx].control;
-            msf_val = sony_toc.tracks[track_idx].track_start_msf;
-         }
+         entry->cdte_adr = sony_toc.tracks[track_idx].address;
+         entry->cdte_ctrl = sony_toc.tracks[track_idx].control;
+         msf_val = sony_toc.tracks[track_idx].track_start_msf;
          
          /* Logical buffer address or MSF format requested? */
-         if (loc_entry.cdte_format == CDROM_LBA)
+         if (entry->cdte_format == CDROM_LBA)
          {
-            loc_entry.cdte_addr.lba = msf_to_log(msf_val);
+            entry->cdte_addr.lba = msf_to_log(msf_val);
          }
-         else if (loc_entry.cdte_format == CDROM_MSF)
+         else if (entry->cdte_format == CDROM_MSF)
          {
-            loc_entry.cdte_addr.msf.minute = bcd_to_int(*msf_val);
-            loc_entry.cdte_addr.msf.second = bcd_to_int(*(msf_val+1));
-            loc_entry.cdte_addr.msf.frame = bcd_to_int(*(msf_val+2));
+            entry->cdte_addr.msf.minute = *msf_val;
+            entry->cdte_addr.msf.second = *(msf_val+1);
+            entry->cdte_addr.msf.frame = *(msf_val+2);
          }
-         copy_to_user(entry, &loc_entry, sizeof(*entry));
       }
       return 0;
       break;
 
    case CDROMPLAYTRKIND:     /* Play a track.  This currently ignores index. */
       {
-         struct cdrom_ti ti;
+         struct cdrom_ti *ti = (struct cdrom_ti *) arg;
          int track_idx;
          
          sony_get_toc();
@@ -2630,46 +2989,41 @@
             return -EIO;
          }
          
-         i=verify_area(VERIFY_READ, (char *) arg, sizeof(ti));
-         if(i<0)
-         	return i;
-         
-         copy_from_user(&ti, (char *) arg, sizeof(ti));
-         if (   (ti.cdti_trk0 < bcd_to_int(sony_toc.first_track_num))
-             || (ti.cdti_trk0 > bcd_to_int(sony_toc.last_track_num))
-             || (ti.cdti_trk1 < ti.cdti_trk0))
+         if (   (ti->cdti_trk0 < sony_toc.first_track_num)
+             || (ti->cdti_trk0 > sony_toc.last_track_num)
+             || (ti->cdti_trk1 < ti->cdti_trk0))
          {
             return -EINVAL;
          }
          
-         track_idx = find_track(int_to_bcd(ti.cdti_trk0));
+         track_idx = find_track(ti->cdti_trk0);
          if (track_idx < 0)
          {
             return -EINVAL;
          }
-         params[1] = sony_toc.tracks[track_idx].track_start_msf[0];
-         params[2] = sony_toc.tracks[track_idx].track_start_msf[1];
-         params[3] = sony_toc.tracks[track_idx].track_start_msf[2];
+         params[1] = int_to_bcd(sony_toc.tracks[track_idx].track_start_msf[0]);
+         params[2] = int_to_bcd(sony_toc.tracks[track_idx].track_start_msf[1]);
+         params[3] = int_to_bcd(sony_toc.tracks[track_idx].track_start_msf[2]);
          
          /*
           * If we want to stop after the last track, use the lead-out
           * MSF to do that.
           */
-         if (ti.cdti_trk1 >= bcd_to_int(sony_toc.last_track_num))
+         if (ti->cdti_trk1 >= sony_toc.last_track_num)
          {
-            log_to_msf(msf_to_log(sony_toc.lead_out_start_msf)-1,
-                       &(params[4]));
+            track_idx = find_track(CDROM_LEADOUT);
          }
          else
          {
-            track_idx = find_track(int_to_bcd(ti.cdti_trk1+1));
-            if (track_idx < 0)
-            {
-               return -EINVAL;
-            }
-            log_to_msf(msf_to_log(sony_toc.tracks[track_idx].track_start_msf)-1,
-                       &(params[4]));
+            track_idx = find_track(ti->cdti_trk1+1);
+         }
+         if (track_idx < 0)
+         {
+            return -EINVAL;
          }
+         params[4] = int_to_bcd(sony_toc.tracks[track_idx].track_start_msf[0]);
+         params[5] = int_to_bcd(sony_toc.tracks[track_idx].track_start_msf[1]);
+         params[6] = int_to_bcd(sony_toc.tracks[track_idx].track_start_msf[2]);
          params[0] = 0x03;
          
          do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
@@ -2680,43 +3034,44 @@
          {
             printk("Params: %x %x %x %x %x %x %x\n", params[0], params[1],
                    params[2], params[3], params[4], params[5], params[6]);
-            printk("Sony CDROM error 0x%2.2x (CDROMPLAYTRKIND\n", res_reg[1]);
+            printk("Sony CDROM error %s (CDROMPLAYTRKIND)\n", translate_error(res_reg[1]));
             return -EIO;
          }
          
          /* Save the final position for pauses and resumes */
-         final_pos_msf[0] = params[4];
-         final_pos_msf[1] = params[5];
-         final_pos_msf[2] = params[6];
+         final_pos_msf[0] = bcd_to_int(params[4]);
+         final_pos_msf[1] = bcd_to_int(params[5]);
+         final_pos_msf[2] = bcd_to_int(params[6]);
          sony_audio_status = CDROM_AUDIO_PLAY;
          return 0;
       }
      
-   case CDROMSUBCHNL:   /* Get subchannel info */
-      return sony_get_subchnl_info(arg);
-
    case CDROMVOLCTRL:   /* Volume control.  What volume does this change, anyway? */
       {
-         struct cdrom_volctrl volctrl;
-         
-         i=verify_area(VERIFY_READ, (char *) arg, sizeof(volctrl));
-         if(i<0)
-         	return i;
+         struct cdrom_volctrl *volctrl = (struct cdrom_volctrl *) arg;
          
-         copy_from_user(&volctrl, (char *) arg, sizeof(volctrl));
          params[0] = SONY_SD_AUDIO_VOLUME;
-         params[1] = volctrl.channel0;
-         params[2] = volctrl.channel1;
+         params[1] = volctrl->channel0;
+         params[2] = volctrl->channel1;
          return do_sony_cd_cmd_chk("VOLCTRL",SONY_SET_DRIVE_PARAM_CMD, params, 3, res_reg, &res_size);
       }
-   case CDROMEJECT:     /* Eject the drive */
-      do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size);
-      do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+   case CDROMSUBCHNL:   /* Get subchannel info */
+      return sony_get_subchnl_info((struct cdrom_subchnl *)arg);
 
-      sony_audio_status = CDROM_AUDIO_INVALID;
-      return do_sony_cd_cmd_chk("EJECT",SONY_EJECT_CMD, NULL, 0, res_reg, &res_size);
-     
-    case CDROMREADAUDIO:      /* Read 2352 byte audio tracks and 2340 byte
+   default:
+      return -EINVAL;
+   }
+}
+
+static int scd_dev_ioctl(struct cdrom_device_info *cdi,
+          unsigned int  cmd,
+          unsigned long arg)
+{
+   int i;
+
+   switch (cmd)
+   {
+   case CDROMREADAUDIO:      /* Read 2352 byte audio tracks and 2340 byte
 				 raw data tracks. */
       {
          struct cdrom_read_audio ra;
@@ -2728,14 +3083,15 @@
             return -EIO;
          }
          
-         i=verify_area(VERIFY_READ, (char *) arg, sizeof(ra));
-         if(i<0)
-         	return i;
-         copy_from_user(&ra, (char *) arg, sizeof(ra));
+         if (ra.nframes == 0)
+         {
+            return 0;
+         }
 
          i=verify_area(VERIFY_WRITE, ra.buf, CD_FRAMESIZE_RAW * ra.nframes);
          if(i<0)
          	return i;
+         copy_from_user(&ra, (char *) arg, sizeof(ra));
 
          if (ra.addr_format == CDROM_LBA)
          {
@@ -2773,92 +3129,93 @@
             return -EINVAL;
          }
 
-         return(read_audio(&ra, inode));
+         return(read_audio(&ra));
       }
       return 0;
       break;
 
-   case CDROMEJECT_SW:
-      is_auto_eject = arg;
-      set_drive_params();
-      return 0;
-      break;
-
    default:
       return -EINVAL;
    }
 }
 
-
-/*
- * Open the drive for operations.  Spin the drive up and read the table of
- * contents if these have not already been done.
- */
-static int
-scd_open(struct inode *inode,
-         struct file *filp)
+static int scd_spinup(void)
 {
    unsigned char res_reg[12];
    unsigned int res_size;
    int num_spin_ups;
-   unsigned char params[2];
 
+   num_spin_ups = 0;
 
-   if ((filp) && filp->f_mode & 2)
-      return -EROFS;
+respinup_on_open:
+   do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
 
-   if (!sony_spun_up)
+   /* The drive sometimes returns error 0.  I don't know why, but ignore
+      it.  It seems to mean the drive has already done the operation. */
+   if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0)))
    {
-      num_spin_ups = 0;
-
-respinup_on_open:
-      do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
+      printk("Sony CDROM %s error (scd_open, spin up)\n", translate_error(res_reg[1]));
+      return 1;
+   }
+      
+   do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size);
 
-      /* The drive sometimes returns error 0.  I don't know why, but ignore
-         it.  It seems to mean the drive has already done the operation. */
-      if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0)))
+   /* The drive sometimes returns error 0.  I don't know why, but ignore
+      it.  It seems to mean the drive has already done the operation. */
+   if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0)))
+   {
+      /* If the drive is already playing, it's ok.  */
+      if ((res_reg[1] == SONY_AUDIO_PLAYING_ERR) || (res_reg[1] == 0))
       {
-         printk("Sony CDROM error 0x%2.2x (scd_open, spin up)\n", res_reg[1]);
-         return -EIO;
+         return 0;
       }
-      
-      do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size);
 
-      /* The drive sometimes returns error 0.  I don't know why, but ignore
-         it.  It seems to mean the drive has already done the operation. */
-      if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0)))
+      /* If the drive says it is not spun up (even though we just did it!)
+         then retry the operation at least a few times. */
+      if (   (res_reg[1] == SONY_NOT_SPIN_ERR)
+          && (num_spin_ups < MAX_CDU31A_RETRIES))
       {
-         /* If the drive is already playing, it's ok.  */
-         if ((res_reg[1] == SONY_AUDIO_PLAYING_ERR) || (res_reg[1] == 0))
-         {
-            goto drive_spinning;
-         }
+         num_spin_ups++;
+         goto respinup_on_open;
+      }
 
-         /* If the drive says it is not spun up (even though we just did it!)
-            then retry the operation at least a few times. */
-         if (   (res_reg[1] == SONY_NOT_SPIN_ERR)
-             && (num_spin_ups < MAX_CDU31A_RETRIES))
-         {
-            num_spin_ups++;
-            goto respinup_on_open;
-         }
+      printk("Sony CDROM error %s (scd_open, read toc)\n", translate_error(res_reg[1]));
+      do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+      return 1;  
+   }
+   return 0;
+}
 
-         printk("Sony CDROM error 0x%2.2x (scd_open, read toc)\n", res_reg[1]);
-         do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
-         
+/*
+ * Open the drive for operations.  Spin the drive up and read the table of
+ * contents if these have not already been done.
+ */
+static int
+scd_open(struct cdrom_device_info *cdi, int openmode)
+{
+   unsigned char res_reg[12];
+   unsigned int res_size;
+   unsigned char params[2];
+
+   MOD_INC_USE_COUNT;
+   if (sony_usage == 0)
+   {
+      if (scd_spinup() != 0) {
+         MOD_DEC_USE_COUNT;
          return -EIO;
       }
-
       sony_get_toc();
       if (!sony_toc_read)
       {
          do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+         MOD_DEC_USE_COUNT;
          return -EIO;
       }
 
       /* For XA on the CDU31A only, we have to do special reads.
          The CDU33A handles XA automagically. */
-      if (   (sony_toc.disk_type == SONY_XA_DISK_TYPE)
+      /* if (   (sony_toc.disk_type == SONY_XA_DISK_TYPE) */
+      if (   (sony_toc.disk_type != 0x00)
           && (!is_double_speed))
       {
          params[0] = SONY_SD_DECODE_PARAM;
@@ -2873,6 +3230,7 @@
             printk("CDU31A: Unable to set XA params: 0x%2.2x\n", res_reg[1]);
          }
          sony_xa_mode = 1;
+printk("sony_xa_mode is set\n");
       }
       /* A non-XA disk.  Set the parms back if necessary. */
       else if (sony_xa_mode)
@@ -2889,25 +3247,13 @@
             printk("CDU31A: Unable to reset XA params: 0x%2.2x\n", res_reg[1]);
          }
          sony_xa_mode = 0;
+printk("sony_xa_mode is reset\n");
       }
 
       sony_spun_up = 1;
    }
 
-drive_spinning:
-
-   /* If filp is not NULL (standard open), try a disk change. */
-   if (filp)
-   {
-      check_disk_change(inode->i_rdev);
-   }
-
    sony_usage++;
-   MOD_INC_USE_COUNT;
-
-   /* If all is OK (until now...), then lock the door */
-   is_auto_eject = 0;
-   set_drive_params();
 
    return 0;
 }
@@ -2917,51 +3263,54 @@
  * Close the drive.  Spin it down if no task is using it.  The spin
  * down will fail if playing audio, so audio play is OK.
  */
-static int
-scd_release(struct inode *inode,
-         struct file *filp)
+static void
+scd_release(struct cdrom_device_info *cdi)
 {
-   unsigned char res_reg[12];
-   unsigned int  res_size;
-
-
-   if (sony_usage > 0)
+   if (sony_usage == 1)
    {
-      sony_usage--;
-      MOD_DEC_USE_COUNT;
-   }
-   if (sony_usage == 0)
-   {
-      sync_dev(inode->i_rdev);
-
-      /* Unlock the door, only if nobody is using the drive */
-      is_auto_eject = 1;
-      set_drive_params();
+      unsigned char res_reg[12];
+      unsigned int  res_size;
 
       do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
 
       sony_spun_up = 0;
    }
-   return 0;
+   sony_usage--;
+   MOD_DEC_USE_COUNT;
 }
 
-
-static struct file_operations scd_fops = {
-   NULL,                   /* lseek - default */
-   block_read,             /* read - general block-dev read */
-   block_write,            /* write - general block-dev write */
-   NULL,                   /* readdir - bad */
-   NULL,                   /* poll */
-   scd_ioctl,              /* ioctl */
-   NULL,                   /* mmap */
-   scd_open,               /* open */
-   scd_release,            /* release */
-   NULL,                   /* fsync */
-   NULL,                   /* fasync */
-   scd_disk_change,        /* media_change */
-   NULL                    /* revalidate */
+static struct cdrom_device_ops scd_dops = {
+  scd_open,                   /* open */
+  scd_release,                /* release */
+  scd_drive_status,           /* drive status */
+  scd_media_changed,          /* media changed */
+  scd_tray_move,              /* tray move */
+  scd_lock_door,              /* lock door */
+  scd_select_speed,           /* select speed */
+  NULL,                       /* select disc */
+  scd_get_last_session,       /* get last session */
+  scd_get_mcn,                /* get universal product code */
+  scd_reset,                  /* hard reset */
+  scd_audio_ioctl,            /* audio ioctl */
+  scd_dev_ioctl,              /* device-specific ioctl */
+  CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED | CDC_MULTI_SESSION | 
+  CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO |
+  CDC_RESET | CDC_IOCTLS | CDC_DRIVE_STATUS, /* capability */
+  1,                            /* number of minor devices */
 };
 
+static struct cdrom_device_info scd_info = {
+  &scd_dops,                  /* device operations */
+  NULL,                       /* link */
+  NULL,                       /* handle */
+  MKDEV(MAJOR_NR,0),          /* dev */
+  0,                          /* mask */
+  2,                          /* maximum speed */
+  1,                          /* number of discs */
+  0,                          /* options, not owned */
+  0,                          /* mc_flags, not owned */
+  0                           /* use count, not owned */
+};
 
 /* The different types of disc loading mechanisms supported */
 static const char *load_mech[] __initdata = { "caddy", "tray", "pop-up", "unknown" };
@@ -3069,6 +3418,8 @@
 {
    struct s_sony_drive_config drive_config;
    unsigned int res_size;
+   char msg[255];
+   char buf[40];
    int i;
    int drive_found;
    int tmp_irq;
@@ -3136,12 +3487,14 @@
 
    if (drive_found)
    {
+      int deficiency=0;
+
       request_region(cdu31a_port, 4,"cdu31a");
       
-      if (register_blkdev(MAJOR_NR,"cdu31a",&scd_fops))
+      if (register_blkdev(MAJOR_NR,"cdu31a",&cdrom_fops))
       {
 	 printk("Unable to get major %d for CDU-31a\n", MAJOR_NR);
-	 return -EIO;
+         goto errout2;
       }
 
       if (SONY_HWC_DOUBLE_SPEED(drive_config))
@@ -3152,7 +3505,7 @@
       tmp_irq = cdu31a_irq; /* Need IRQ 0 because we can't sleep here. */
       cdu31a_irq = 0;
 
-      set_drive_params();
+      set_drive_params(sony_speed);
 
       cdu31a_irq = tmp_irq;
       
@@ -3165,41 +3518,48 @@
 	 }
       }
       
-      printk(KERN_INFO "Sony I/F CDROM : %8.8s %16.16s %8.8s\n",
+      sprintf(msg, "Sony I/F CDROM : %8.8s %16.16s %8.8s\n",
 	     drive_config.vendor_id,
 	     drive_config.product_id,
 	     drive_config.product_rev_level);
-      printk(KERN_INFO "  Capabilities: %s",
+      sprintf(buf, "  Capabilities: %s",
 	     load_mech[SONY_HWC_GET_LOAD_MECH(drive_config)]);
+      strcat(msg, buf);
       if (SONY_HWC_AUDIO_PLAYBACK(drive_config))
       {
-	 printk(", audio");
-      }
+         strcat(msg, ", audio");
+      } else
+	 deficiency |= CDC_PLAY_AUDIO;
       if (SONY_HWC_EJECT(drive_config))
       {
-	 printk(", eject");
-      }
+         strcat(msg, ", eject");
+      } else
+	 deficiency |= CDC_OPEN_TRAY;
       if (SONY_HWC_LED_SUPPORT(drive_config))
       {
-	 printk(", LED");
+         strcat(msg, ", LED");
       }
       if (SONY_HWC_ELECTRIC_VOLUME(drive_config))
       {
-	 printk(", elec. Vol");
+         strcat(msg, ", elec. Vol");
       }
       if (SONY_HWC_ELECTRIC_VOLUME_CTL(drive_config))
       {
-	 printk(", sep. Vol");
+         strcat(msg, ", sep. Vol");
       }
       if (is_double_speed)
       {
-	 printk(", double speed");
-      }
+         strcat(msg, ", double speed");
+      } else
+	 deficiency |= CDC_SELECT_SPEED;
       if (cdu31a_irq > 0)
       {
-	 printk(", irq %d", cdu31a_irq);
+	 sprintf(buf, ", irq %d", cdu31a_irq);
+         strcat(msg, buf);
       }
-      printk("\n");
+      strcat(msg, "\n");
+
+      is_a_cdu31a = strcmp("CD-ROM CDU31A", drive_config.product_id) == 0;
 
       blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
       read_ahead[MAJOR_NR] = CDU31A_READAHEAD;
@@ -3209,6 +3569,13 @@
       
       init_timer(&cdu31a_abort_timer);
       cdu31a_abort_timer.function = handle_abort_timeout;
+
+      scd_info.mask = deficiency;
+
+      if (register_cdrom(&scd_info))
+      {
+         goto errout0;
+      }
    }
 
 
@@ -3220,8 +3587,18 @@
    }
    else
    {
-      return -EIO;
+      goto errout3;
    }
+errout0:
+   printk("Unable to register CDU-31a with Uniform cdrom driver\n");
+   if (unregister_blkdev(MAJOR_NR, "cdu31a"))    
+   {
+      printk("Can't unregister block device for cdu31a\n");
+   }
+errout2:
+   release_region(cdu31a_port,4);
+errout3:
+   return -EIO;
 }
 
 #ifdef MODULE
@@ -3235,6 +3612,11 @@
 void
 cleanup_module(void)
 {
+   if (unregister_cdrom(&scd_info))    
+   {
+      printk("Can't unregister cdu31a from Uniform cdrom driver\n");
+      return;
+   }
    if ((unregister_blkdev(MAJOR_NR, "cdu31a") == -EINVAL))    
    {
       printk("Can't unregister cdu31a\n");

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov