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

📄 cdu31a.c

📁 LINUX 1.0 内核c源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/*
 * Sony CDU-31A CDROM interface device driver.
 *
 * Corey Minyard (minyard@wf-rch.cirr.com)
 *
 * Colossians 3:17
 *
 * The Sony interface device driver handles Sony interface CDROM
 * drives and provides a complete block-level interface as well as an
 * ioctl() interface compatible with the Sun (as specified in
 * include/linux/cdrom.h).  With this interface, CDROMs can be
 * accessed and standard audio CDs can be played back normally.
 *
 * This interface is (unfortunatly) a polled interface.  This is
 * because most Sony interfaces are set up with DMA and interrupts
 * disables.  Some (like mine) do not even have the capability to
 * handle interrupts or DMA.  For this reason you will see a lot of
 * the following:
 *
 *   retry_count = jiffies+ SONY_JIFFIES_TIMEOUT;
 *   while ((retry_count > jiffies) && (! <some condition to wait for))
 *   {
 *      while (handle_sony_cd_attention())
 *         ;
 *
 *      sony_sleep();
 *   }
 *   if (the condition not met)
 *   {
 *      return an error;
 *   }
 *
 * This ugly hack waits for something to happen, sleeping a little
 * between every try.  it also handles attentions, which are
 * asyncronous events from the drive informing the driver that a disk
 * has been inserted, removed, etc.
 *
 * One thing about these drives: They talk in MSF (Minute Second Frame) format.
 * There are 75 frames a second, 60 seconds a minute, and up to 75 minutes on a
 * disk.  The funny thing is that these are sent to the drive in BCD, but the
 * interface wants to see them in decimal.  A lot of conversion goes on.
 *
 *  Copyright (C) 1993  Corey Minyard
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */



#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/hdreg.h>
#include <linux/genhd.h>
#include <linux/ioport.h>

#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>

#include <linux/cdrom.h>
#include <linux/cdu31a.h>

#define MAJOR_NR CDU31A_CDROM_MAJOR
#include "blk.h"

#define CDU31A_MAX_CONSECUTIVE_ATTENTIONS 10

static unsigned short cdu31a_addresses[] =
{
   0x340,	/* Standard configuration Sony Interface */
   0x1f88,	/* Fusion CD-16 */
   0x230,	/* SoundBlaster 16 card */
   0x360,	/* Secondary standard Sony Interface */
   0x320,	/* Secondary standard Sony Interface */
   0x330,	/* Secondary standard Sony Interface */
   0
};


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 void do_sony_cd_cmd(unsigned char cmd,
                           unsigned char *params,
                           unsigned int num_params,
                           unsigned char *result_buffer,
                           unsigned int *result_size);
static void size_to_buf(unsigned int size,
                        unsigned char *buf);


/* The base I/O address of the Sony Interface.  This is a variable (not a
   #define) so it can be easily changed via some future ioctl() */
static unsigned short sony_cd_base_io = 0;

/*
 * The following are I/O addresses of the various registers for the drive.  The
 * comment for the base address also applies here.
 */
static volatile unsigned short sony_cd_cmd_reg;
static volatile unsigned short sony_cd_param_reg;
static volatile unsigned short sony_cd_write_reg;
static volatile unsigned short sony_cd_control_reg;
static volatile unsigned short sony_cd_status_reg;
static volatile unsigned short sony_cd_result_reg;
static volatile unsigned short sony_cd_read_reg;
static volatile unsigned short sony_cd_fifost_reg;


static int sony_disc_changed = 1;          /* Has the disk been changed
                                              since the last check? */
static int sony_toc_read = 0;              /* Has the table of contents been
                                              read? */
static int sony_spun_up = 0;               /* Has the drive been spun up? */
static unsigned int sony_buffer_size;      /* Size in bytes of the read-ahead
                                              buffer. */
static unsigned int sony_buffer_sectors;   /* Size (in 2048 byte records) of
                                              the read-ahead buffer. */
static unsigned int sony_usage = 0;        /* How many processes have the
                                              drive open. */

static volatile int sony_first_block = -1; /* First OS block (512 byte) in
                                              the read-ahead buffer */
static volatile int sony_last_block = -1;  /* Last OS block (512 byte) in
                                              the read-ahead buffer */

static struct s_sony_toc *sony_toc;              /* Points to the table of
                                                    contents. */
static struct s_sony_subcode * volatile last_sony_subcode; /* Points to the last
                                                    subcode address read */
static unsigned char * volatile sony_buffer;     /* Points to the read-ahead
                                                    buffer */

static volatile int sony_inuse = 0;  /* Is the drive in use?  Only one operation at a time
                                        allowed */

static struct wait_queue * sony_wait = NULL;

static struct task_struct *has_cd_task = NULL;  /* The task that is currently using the
                                                   CDROM drive, or NULL if none. */

/*
 * The audio status uses the values from read subchannel data as specified
 * in include/linux/cdrom.h.
 */
static volatile int sony_audio_status = CDROM_AUDIO_NO_STATUS;

/*
 * The following are a hack for pausing and resuming audio play.  The drive
 * does not work as I would expect it, if you stop it then start it again,
 * the drive seeks back to the beginning and starts over.  This holds the
 * position during a pause so a resume can restart it.  It uses the
 * audio status variable above to tell if it is paused.
 */
unsigned volatile char cur_pos_msf[3] = { 0, 0, 0 };
unsigned volatile char final_pos_msf[3] = { 0, 0, 0 };

/*
 * This routine returns 1 if the disk has been changed since the last
 * check or 0 if it hasn't.  Setting flag to 0 resets the changed flag.
 */
int
check_cdu31a_media_change(int full_dev, int flag)
{
   int retval, target;


   target = MINOR(full_dev);

   if (target > 0) {
      printk("Sony CD-ROM request error: invalid device.\n");
      return 0;
   }

   retval = sony_disc_changed;
   if (!flag)
   {
      sony_disc_changed = 0;
   }

   return retval;
}


/*
 * Wait a little while (used for polling the drive).  If in initialization,
 * setting a timeout doesn't work, so just loop for a while.
 */
static inline void
sony_sleep(void)
{
   current->state = TASK_INTERRUPTIBLE;
   current->timeout = jiffies;
   schedule();
}


/*
 * The following are convenience routine to read various status and set
 * various conditions in the drive.
 */
static inline int
is_attention(void)
{
   return((inb(sony_cd_status_reg) & SONY_ATTN_BIT) != 0);
}

static inline int
is_busy(void)
{
   return((inb(sony_cd_status_reg) & SONY_BUSY_BIT) != 0);
}

static inline int
is_data_ready(void)
{
   return((inb(sony_cd_status_reg) & SONY_DATA_RDY_BIT) != 0);
}

static inline int
is_data_requested(void)
{
   return((inb(sony_cd_status_reg) & SONY_DATA_REQUEST_BIT) != 0);
}

static inline int
is_result_ready(void)
{
   return((inb(sony_cd_status_reg) & SONY_RES_RDY_BIT) != 0);
}

static inline int
is_param_write_rdy(void)
{
   return((inb(sony_cd_fifost_reg) & SONY_PARAM_WRITE_RDY_BIT) != 0);
}

static inline void
reset_drive(void)
{
   outb(SONY_DRIVE_RESET_BIT, sony_cd_control_reg);
}

static inline void
clear_attention(void)
{
   outb(SONY_ATTN_CLR_BIT, sony_cd_control_reg);
}

static inline void
clear_result_ready(void)
{
   outb(SONY_RES_RDY_CLR_BIT, sony_cd_control_reg);
}

static inline void
clear_data_ready(void)
{
   outb(SONY_DATA_RDY_CLR_BIT, sony_cd_control_reg);
}

static inline void
clear_param_reg(void)
{
   outb(SONY_PARAM_CLR_BIT, sony_cd_control_reg);
}

static inline unsigned char
read_status_register(void)
{
   return(inb(sony_cd_status_reg));
}

static inline unsigned char
read_result_register(void)
{
   return(inb(sony_cd_result_reg));
}

static inline unsigned char
read_data_register(void)
{
   return(inb(sony_cd_read_reg));
}

static inline void
write_param(unsigned char param)
{
   outb(param, sony_cd_param_reg);
}

static inline void
write_cmd(unsigned char cmd)
{
   outb(cmd, sony_cd_cmd_reg);
   outb(SONY_RES_RDY_INT_EN_BIT, sony_cd_control_reg);
}

/*
 * Set the drive parameters so the drive will auto-spin-up when a
 * disk is inserted.
 */
static void
set_drive_params(void)
{
   unsigned char res_reg[2];
   unsigned int res_size;
   unsigned char params[3];


   params[0] = SONY_SD_MECH_CONTROL;
   params[1] = 0x03;
   do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
                  params,
                  2,
                  res_reg,
                  &res_size);
   if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
   {
      printk("  Unable to set mechanical parameters: 0x%2.2x\n", res_reg[1]);
   }
}

/*
 * This code will reset the drive and attempt to restore sane parameters.
 */
static void
restart_on_error(void)
{
   unsigned char res_reg[2];
   unsigned int res_size;
   unsigned int retry_count;


   printk("cdu31a: Resetting drive on error\n");
   reset_drive();
   retry_count = jiffies + SONY_RESET_TIMEOUT;
   while ((retry_count > jiffies) && (!is_attention()))
   {
      sony_sleep();
   }
   set_drive_params();
   do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
   if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
   {
      printk("cdu31a: Unable to spin up drive: 0x%2.2x\n", res_reg[1]);
   }

   current->state = TASK_INTERRUPTIBLE;
   current->timeout = jiffies + 200;
   schedule();

   do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size);
   if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
   {
      printk("cdu31a: Unable to read TOC: 0x%2.2x\n", res_reg[1]);
   }
   sony_get_toc();
   if (!sony_toc_read)
   {
      printk("cdu31a: Unable to get TOC data\n");
   }
}

/*
 * This routine writes data to the parameter register.  Since this should
 * happen fairly fast, it is polled with no OS waits between.
 */
static int
write_params(unsigned char *params,
             int num_params)
{
   unsigned int retry_count;


   retry_count = SONY_READY_RETRIES;
   while ((retry_count > 0) && (!is_param_write_rdy()))
   {
      retry_count--;
   }
   if (!is_param_write_rdy())
   {
      return -EIO;
   }

   while (num_params > 0)
   {
      write_param(*params);
      params++;
      num_params--;
   }

   return 0;
}


/*
 * The following reads data from the command result register.  It is a
 * fairly complex routine, all status info flows back through this
 * interface.  The algorithm is stolen directly from the flowcharts in
 * the drive manual.
 */
static void
get_result(unsigned char *result_buffer,
           unsigned int *result_size)
{
   unsigned char a, b;
   int i;
   unsigned int retry_count;


   while (handle_sony_cd_attention())
      ;
   /* Wait for the result data to be ready */
   retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
   while ((retry_count > jiffies) && (is_busy() || (!(is_result_ready()))))
   {
      sony_sleep();

      while (handle_sony_cd_attention())
         ;
   }
   if (is_busy() || (!(is_result_ready())))
   {
      result_buffer[0] = 0x20;
      result_buffer[1] = SONY_TIMEOUT_OP_ERR;
      *result_size = 2;
      return;
   }

   /*
    * Get the first two bytes.  This determines what else needs
    * to be done.
    */
   clear_result_ready();
   a = read_result_register();
   *result_buffer = a;
   result_buffer++;
   b = read_result_register();
   *result_buffer = b;
   result_buffer++;
   *result_size = 2;

   /*
    * 0x20 means an error occured.  Byte 2 will have the error code.
    * Otherwise, the command succeded, byte 2 will have the count of

⌨️ 快捷键说明

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