⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cdu31a.c

📁 Linux 1.0 内核C源代码 Linux最早版本代码 由Linus Torvalds亲自书写的
💻 C
📖 第 1 页 / 共 4 页
字号:
{
   log = log + LOG_START_OFFSET;
   msf[0] = int_to_bcd(log / 4500);
   log = log % 4500;
   msf[1] = int_to_bcd(log / 75);
   msf[2] = int_to_bcd(log % 75);
}


/*
 * Convert an MSF format to a logical sector.
 */
static unsigned int
msf_to_log(unsigned char *msf)
{
   unsigned int log;


   log = bcd_to_int(msf[2]);
   log += bcd_to_int(msf[1]) * 75;
   log += bcd_to_int(msf[0]) * 4500;
   log = log - LOG_START_OFFSET;

   return log;
}


/*
 * Take in integer size value and put it into a buffer like
 * the drive would want to see a number-of-sector value.
 */
static void
size_to_buf(unsigned int size,
            unsigned char *buf)
{
   buf[0] = size / 65536;
   size = size % 65536;
   buf[1] = size / 256;
   buf[2] = size % 256;
}


/*
 * 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
 * bytes to help speed operations.  This especially helps since the OS
 * uses 1024 byte blocks and the drive uses 2048 byte blocks.  Since most
 * data access on a CD is done sequentially, this saves a lot of operations.
 */
static void
do_cdu31a_request(void)
{
   int block;
   unsigned int dev;
   int nsect;
   unsigned char params[10];
   unsigned char res_reg[2];
   unsigned int res_size;
   int copyoff;
   int spin_up_retry;
   unsigned int read_size;


   if (!sony_spun_up)
   {
      scd_open (NULL,NULL);
   }

   while (1)
   {
cdu31a_request_startover:
      /*
       * The beginning here is stolen from the hard disk driver.  I hope
       * its right.
       */
      if (!(CURRENT) || CURRENT->dev < 0)
      {
         return;
      }

      INIT_REQUEST;
      dev = MINOR(CURRENT->dev);
      block = CURRENT->sector;
      nsect = CURRENT->nr_sectors;
      if (dev != 0)
      {
         end_request(0);
         goto cdu31a_request_startover;
      }

      switch(CURRENT->cmd)
      {
      case READ:
         /*
          * If the block address is invalid or the request goes beyond the end of
          * the media, return an error.
          */
         if ((block / 4) >= sony_toc->lead_out_start_lba)
         {
            end_request(0);
            goto cdu31a_request_startover;
         }
         if (((block + nsect) / 4) >= sony_toc->lead_out_start_lba)
         {
            end_request(0);
            goto cdu31a_request_startover;
         }

         while (nsect > 0)
         {
            /*
             * If the requested sector is not currently in the read-ahead buffer,
             * it must be read in.
             */
            if ((block < sony_first_block) || (block > sony_last_block))
            {
               sony_first_block = (block / 4) * 4;
               log_to_msf(block/4, params);

               /*
                * If the full read-ahead would go beyond the end of the media, trim
                * it back to read just till the end of the media.
                */
               if (((block / 4) + sony_buffer_sectors) >= sony_toc->lead_out_start_lba)
               {
                  read_size = sony_toc->lead_out_start_lba - (block / 4);
               }
               else
               {
                  read_size = sony_buffer_sectors;
               }
               size_to_buf(read_size, &params[3]);

               /*
                * Read the data.  If the drive was not spinning, spin it up and try
                * once more.  I know, the goto is ugly, but I am too lazy to fix it.
                */
               spin_up_retry = 0;
try_read_again:
               sony_last_block =   sony_first_block
                                 + (get_data(sony_buffer,
                                             params,
                                             (read_size * 2048),
                                             res_reg,
                                             &res_size) * 4) - 1;
               if ((res_size < 2) || (res_reg[0] != 0))
               {
                  if ((res_reg[1] == SONY_NOT_SPIN_ERR) && (!spin_up_retry))
                  {
                     do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
                     spin_up_retry = 1;
                     goto try_read_again;
                  }

                  printk("Sony CDROM Read error: 0x%2.2x\n", res_reg[1]);
                  sony_first_block = -1;
                  sony_last_block = -1;
                  end_request(0);
                  goto cdu31a_request_startover;
               }
            }
   
            /*
             * The data is in memory now, copy it to the buffer and advance to the
             * next block to read.
             */
            copyoff = (block - sony_first_block) * 512;
            memcpy(CURRENT->buffer, sony_buffer+copyoff, 512);
               
            block += 1;
            nsect -= 1;
            CURRENT->buffer += 512;
         }
               
         end_request(1);
         break;
            
      case WRITE:
         end_request(0);
         break;
            
      default:
         panic("Unkown SONY CD cmd");
      }
   }
}


/*
 * Read the table of contents from the drive and set sony_toc_read if
 * successful.
 */
static void
sony_get_toc(void)
{
   unsigned int res_size;


   if (!sony_toc_read)
   {
      do_sony_cd_cmd(SONY_REQ_TOC_DATA_CMD,
                     NULL,
                     0, 
                     (unsigned char *) sony_toc, 
                     &res_size);
      if ((res_size < 2) || ((sony_toc->exec_status[0] & 0x20) == 0x20))
      {
         return;
      }
      sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf);
      sony_toc_read = 1;
   }
}


/*
 * Search for a specific track in the table of contents.
 */
static int
find_track(int track)
{
   int i;
   int num_tracks;


   num_tracks = sony_toc->last_track_num + sony_toc->first_track_num + 1;
   for (i = 0; i < num_tracks; i++)
   {
      if (sony_toc->tracks[i].track == track)
      {
         return i;
      }
   }

   return -1;
}


/*
 * Read the subcode and put it int last_sony_subcode for future use.
 */
static int
read_subcode(void)
{
   unsigned int res_size;


   do_sony_cd_cmd(SONY_REQ_SUBCODE_ADDRESS_CMD,
                  NULL,
                  0, 
                  (unsigned char *) last_sony_subcode, 
                  &res_size);
   if ((res_size < 2) || ((last_sony_subcode->exec_status[0] & 0x20) == 0x20))
   {
      printk("Sony CDROM error 0x%2.2x (read_subcode)\n",
             last_sony_subcode->exec_status[1]);
      return -EIO;
   }

   return 0;
}


/*
 * Get the subchannel info like the CDROMSUBCHNL command wants to see it.  If
 * the drive is playing, the subchannel needs to be read (since it would be
 * changing).  If the drive is paused or completed, the subcode information has
 * already been stored, just use that.  The ioctl call wants things in decimal
 * (not BCD), so all the conversions are done.
 */
static int
sony_get_subchnl_info(long arg)
{
   struct cdrom_subchnl schi;


   /* Get attention stuff */
   while (handle_sony_cd_attention())
      ;

   sony_get_toc();
   if (!sony_toc_read)
   {
      return -EIO;
   }

   verify_area(VERIFY_READ, (char *) arg, sizeof(schi));
   verify_area(VERIFY_WRITE, (char *) arg, sizeof(schi));

   memcpy_fromfs(&schi, (char *) arg, sizeof(schi));
   
   switch (sony_audio_status)
   {
   case CDROM_AUDIO_PLAY:
      if (read_subcode() < 0)
      {
         return -EIO;
      }
      break;

   case CDROM_AUDIO_PAUSED:
   case CDROM_AUDIO_COMPLETED:
      break;

   case CDROM_AUDIO_NO_STATUS:
      schi.cdsc_audiostatus = sony_audio_status;
      memcpy_tofs((char *) arg, &schi, sizeof(schi));
      return 0;
      break;

   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]);
   }
   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);
   }
   
   memcpy_tofs((char *) arg, &schi, sizeof(schi));
   return 0;
}


/*
 * The big ugly ioctl handler.
 */
static int
scd_ioctl(struct inode *inode,
          struct file  *file,
          unsigned int  cmd,
          unsigned long arg)
{
   unsigned int dev;
   unsigned char res_reg[2];
   unsigned int res_size;
   unsigned char params[7];
   int i;


   if (!inode)
   {
      return -EINVAL;
   }
   dev = MINOR(inode->i_rdev) >> 6;
   if (dev != 0)
   {
      return -EINVAL;
   }

   switch (cmd)
   {
   case CDROMSTART:     /* Spin up the drive */
      do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
      if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
      {
         printk("Sony CDROM error 0x%2.2x (CDROMSTART)\n", res_reg[1]);
         return -EIO;
      }
      return 0;
      break;
      
   case CDROMSTOP:      /* Spin down the drive */
      do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size);

      /*
       * Spin the drive down, ignoring the error if the disk was
       * already not spinning.
       */
      sony_audio_status = CDROM_AUDIO_NO_STATUS;
      do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
      if (   ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
          && (res_reg[1] != SONY_NOT_SPIN_ERR))
      {
         printk("Sony CDROM error 0x%2.2x (CDROMSTOP)\n", res_reg[1]);
         return -EIO;
      }
      
      return 0;
      break;

   case CDROMPAUSE:     /* Pause the drive */
      do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size);
      if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
      {
         printk("Sony CDROM error 0x%2.2x (CDROMPAUSE)\n", res_reg[1]);
         return -EIO;
      }

      /* Get the current position and save it for resuming */
      if (read_subcode() < 0)
      {
         return -EIO;
      }
      cur_pos_msf[0] = last_sony_subcode->abs_msf[0];
      cur_pos_msf[1] = last_sony_subcode->abs_msf[1];
      cur_pos_msf[2] = last_sony_subcode->abs_msf[2];
      sony_audio_status = CDROM_AUDIO_PAUSED;
      return 0;
      break;

   case CDROMRESUME:    /* Start the drive after being paused */
      if (sony_audio_status != CDROM_AUDIO_PAUSED)
      {
         return -EINVAL;
      }
      
      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[0] = 0x03;
      do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size);
      if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
      {
         printk("Sony CDROM error 0x%2.2x (CDROMRESUME)\n", res_reg[1]);
         return -EIO;
      }
      sony_audio_status = CDROM_AUDIO_PLAY;
      return 0;
      break;

   case CDROMPLAYMSF:   /* Play starting at the given MSF address. */
      verify_area(VERIFY_READ, (char *) arg, 6);
      do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
      memcpy_fromfs(&(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[0] = 0x03;
      do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size);
      if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
      {
         printk("Sony CDROM error 0x%2.2x (CDROMPLAYMSF)\n", res_reg[1]);
         return -EIO;
      }
      
      /* Save the final position for pauses and resumes */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -