📄 cdu31a.c
字号:
/* * 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 10static 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. */intcheck_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 voidsony_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 intis_attention(void){ return((inb(sony_cd_status_reg) & SONY_ATTN_BIT) != 0);}static inline intis_busy(void){ return((inb(sony_cd_status_reg) & SONY_BUSY_BIT) != 0);}static inline intis_data_ready(void){ return((inb(sony_cd_status_reg) & SONY_DATA_RDY_BIT) != 0);}static inline intis_data_requested(void){ return((inb(sony_cd_status_reg) & SONY_DATA_REQUEST_BIT) != 0);}static inline intis_result_ready(void){ return((inb(sony_cd_status_reg) & SONY_RES_RDY_BIT) != 0);}static inline intis_param_write_rdy(void){ return((inb(sony_cd_fifost_reg) & SONY_PARAM_WRITE_RDY_BIT) != 0);}static inline voidreset_drive(void){ outb(SONY_DRIVE_RESET_BIT, sony_cd_control_reg);}static inline voidclear_attention(void){ outb(SONY_ATTN_CLR_BIT, sony_cd_control_reg);}static inline voidclear_result_ready(void){ outb(SONY_RES_RDY_CLR_BIT, sony_cd_control_reg);}static inline voidclear_data_ready(void){ outb(SONY_DATA_RDY_CLR_BIT, sony_cd_control_reg);}static inline voidclear_param_reg(void){ outb(SONY_PARAM_CLR_BIT, sony_cd_control_reg);}static inline unsigned charread_status_register(void){ return(inb(sony_cd_status_reg));}static inline unsigned charread_result_register(void){ return(inb(sony_cd_result_reg));}static inline unsigned charread_data_register(void){ return(inb(sony_cd_read_reg));}static inline voidwrite_param(unsigned char param){ outb(param, sony_cd_param_reg);}static inline voidwrite_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 voidset_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 voidrestart_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 intwrite_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 voidget_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 + -