📄 sbpcd.c
字号:
/*
* sbpcd.c CD-ROM device driver for the whole family of IDE-style
* Kotobuki/Matsushita/Panasonic CR-5xx drives for
* SoundBlaster ("Pro" or "16 ASP" or compatible) cards
* and for "no-sound" interfaces like Lasermate and the
* Panasonic CI-101P.
*
* NOTE: This is release 1.3.
* It works with my SbPro & drive CR-521 V2.11 from 2/92
* and with the new CR-562-B V0.75 on a "naked" Panasonic
* CI-101P interface. And vice versa.
*
*
* VERSION HISTORY
*
* 0.1 initial release, April/May 93, after mcd.c (Martin Harriss)
*
* 0.2 the "repeat:"-loop in do_sbpcd_request did not check for
* end-of-request_queue (resulting in kernel panic).
* Flow control seems stable, but throughput is not better.
*
* 0.3 interrupt locking totally eliminated (maybe "inb" and "outb"
* are still locking) - 0.2 made keyboard-type-ahead losses.
* check_sbpcd_media_change added (to use by isofs/inode.c)
* - but it detects almost nothing.
*
* 0.4 use MAJOR 25 definitely.
* Almost total re-design to support double-speed drives and
* "naked" (no sound) interface cards.
* Flow control should be exact now (tell me if not).
* Don't occupy the SbPro IRQ line (not needed either); will
* live together with Hannu Savolainen's sndkit now.
* Speeded up data transfer to 150 kB/sec, with help from Kai
* Makisara, the "provider" of the "mt" tape utility.
* Give "SpinUp" command if necessary.
* First steps to support up to 4 drives (but currently only one).
* Implemented audio capabilities - workman should work, xcdplayer
* gives some problems.
* This version is still consuming too much CPU time, and
* sleeping still has to be worked on.
* During "long" implied seeks, it seems possible that a
* ReadStatus command gets ignored. That gives the message
* "ResponseStatus timed out" (happens about 6 times here during
* a "ls -alR" of the YGGDRASIL LGX-Beta CD). Such a case is
* handled without data error, but it should get done better.
*
* 0.5 Free CPU during waits (again with help from Kai Makisara).
* Made it work together with the LILO/kernel setup standard.
* Included auto-probing code, as suggested by YGGDRASIL.
* Formal redesign to add DDI debugging.
* There are still flaws in IOCTL (workman with double speed drive).
*
* 1.0 Added support for all drive ids (0...3, no longer only 0)
* and up to 4 drives on one controller.
* Added "#define MANY_SESSION" for "old" multi session CDs.
*
* 1.1 Do SpinUp for new drives, too.
* Revised for clean compile under "old" kernels (pl9).
*
* 1.2 Found the "workman with double-speed drive" bug: use the driver's
* audio_state, not what the drive is reporting with ReadSubQ.
*
* 1.3 Minor cleanups.
* Refinements regarding Workman.
*
* special thanks to Kai Makisara (kai.makisara@vtt.fi) for his fine
* elaborated speed-up experiments (and the fabulous results!), for
* the "push" towards load-free wait loops, and for the extensive mail
* thread which brought additional hints and bug fixes.
*
*
* Copyright (C) 1993, 1994 Eberhard Moenkeberg <emoenke@gwdg.de>
* or <eberhard_moenkeberg@rollo.central.de>
*
* The FTP-home of this driver is
* ftp.gwdg.de:/pub/linux/cdrom/drivers/sbpcd/.
*
* If you change this software, you should mail a .diff
* file with some description lines to emoenke@gwdg.de.
* I want to know about it.
*
* If you are the editor of a Linux CD, you should
* enable sbpcd.c within your boot floppy kernel and
* send me one of your CDs for free.
*
* 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, or (at your option)
* any later version.
*
* You should have received a copy of the GNU General Public License
* (for example /usr/src/linux/COPYING); if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/cdrom.h>
#include <linux/ioport.h>
#include <linux/sbpcd.h>
#if SBPCD_USE_IRQ
#include <linux/signal.h>
#endif SBPCD_USE_IRQ
#include <linux/ddi.h>
#include <linux/major.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <stdarg.h>
#define MAJOR_NR MATSUSHITA_CDROM_MAJOR
#include "blk.h"
#define VERSION "1.3 Eberhard Moenkeberg <emoenke@gwdg.de>"
#define SBPCD_DEBUG
#ifndef CONFIG_ISO9660_FS
#error "SBPCD: \"make config\" again. File system iso9660 is necessary."
#endif
/*
* still testing around...
*/
#define MANY_SESSION 0
#define CDMKE
#undef FUTURE
#define WORKMAN 1 /* some testing stuff to make it better */
/*==========================================================================*/
/*==========================================================================*/
/*
* auto-probing address list
* inspired by Adam J. Richter from Yggdrasil
*
* still not good enough - can cause a hang.
* example: a NE 2000 ethernet card at 300 will cause a hang probing 310.
* if that happens, reboot and use the LILO (kernel) command line.
* The possibly conflicting ethernet card addresses get NOT probed
* by default - to minimize the hang possibilities.
*
* The SB Pro addresses get "mirrored" at 0x6xx - to avoid a type error,
* the 0x2xx-addresses must get checked before 0x6xx.
*
* send mail to emoenke@gwdg.de if your interface card is not FULLY
* represented here.
*/
static int autoprobe[] =
{
CDROM_PORT, SBPRO, /* probe with user's setup first */
0x230, 1, /* Soundblaster Pro and 16 (default) */
0x300, 0, /* CI-101P (default), Galaxy (default), Reveal (one default) */
0x250, 1, /* OmniCD default, Soundblaster Pro and 16 */
0x260, 1, /* OmniCD */
0x320, 0, /* Lasermate, CI-101P, Galaxy, Reveal (other default) */
0x340, 0, /* Lasermate, CI-101P */
0x360, 0, /* Lasermate, CI-101P */
0x270, 1, /* Soundblaster 16 */
0x630, 0, /* "sound card #9" (default) */
0x650, 0, /* "sound card #9" */
0x670, 0, /* "sound card #9" */
0x690, 0, /* "sound card #9" */
#if 0
/* some "hazardous" locations (ethernet cards) */
0x330, 0, /* Lasermate, CI-101P */
0x350, 0, /* Lasermate, CI-101P */
0x370, 0, /* Lasermate, CI-101P */
0x290, 1, /* Soundblaster 16 */
0x310, 0, /* Lasermate, CI-101P */
#endif
};
#define NUM_AUTOPROBE (sizeof(autoprobe) / sizeof(int))
/*==========================================================================*/
/*
* the forward references:
*/
static void sbp_read_cmd(void);
static int sbp_data(void);
/*==========================================================================*/
/*
* pattern for printk selection:
*
* (1<<DBG_INF) necessary information
* (1<<DBG_IRQ) interrupt trace
* (1<<DBG_REA) "read" status trace
* (1<<DBG_CHK) "media check" trace
* (1<<DBG_TIM) datarate timer test
* (1<<DBG_INI) initialization trace
* (1<<DBG_TOC) tell TocEntry values
* (1<<DBG_IOC) ioctl trace
* (1<<DBG_STA) "ResponseStatus" trace
* (1<<DBG_ERR) "xx_ReadError" trace
* (1<<DBG_CMD) "cmd_out" trace
* (1<<DBG_WRN) give explanation before auto-probing
* (1<<DBG_MUL) multi session code test
* (1<<DBG_ID) "drive_id != 0" test code
* (1<<DBG_IOX) some special information
* (1<<DBG_DID) drive ID test
* (1<<DBG_RES) drive reset info
* (1<<DBG_SPI) SpinUp test info
* (1<<DBG_IOS) ioctl trace: "subchannel"
* (1<<DBG_IO2) ioctl trace: general
* (1<<DBG_000) unnecessary information
*/
#if 1
static int sbpcd_debug = (1<<DBG_INF) | (1<<DBG_WRN);
#else
static int sbpcd_debug = (1<<DBG_INF) |
(1<<DBG_TOC) |
(1<<DBG_IOC) |
(1<<DBG_IOX);
#endif
static int sbpcd_ioaddr = CDROM_PORT; /* default I/O base address */
static int sbpro_type = SBPRO;
static int CDo_command, CDo_reset;
static int CDo_sel_d_i, CDo_enable;
static int CDi_info, CDi_status, CDi_data;
static int MIXER_addr, MIXER_data;
static struct cdrom_msf msf;
static struct cdrom_ti ti;
static struct cdrom_tochdr tochdr;
static struct cdrom_tocentry tocentry;
static struct cdrom_subchnl SC;
static struct cdrom_volctrl volctrl;
char *str_sb = "SoundBlaster";
char *str_lm = "LaserMate";
char *type;
/*==========================================================================*/
#if FUTURE
static struct wait_queue *sbp_waitq = NULL;
#endif FUTURE
/*==========================================================================*/
#define SBP_BUFFER_FRAMES 4 /* driver's own read_ahead */
/*==========================================================================*/
static u_char drive_family[]="CR-5";
static u_char drive_vendor[]="MATSHITA";
static u_int response_count=0;
static u_int flags_cmd_out;
static u_char cmd_type=0;
static u_char drvcmd[7];
static u_char infobuf[20];
static u_char timed_out=0;
static u_int datarate= 1000000;
static u_int maxtim16=16000000;
static u_int maxtim04= 4000000;
static u_int maxtim02= 2000000;
static u_int maxtim_8= 30000;
#if MANY_SESSION
static u_int maxtim_data= 9000;
#else
static u_int maxtim_data= 3000;
#endif MANY_SESSION
/*==========================================================================*/
static int ndrives=0;
static u_char drv_pattern[4]={ 0x80, 0x80, 0x80, 0x80 }; /* auto speed */
/* /X:... drv_pattern[0] |= (sax_n1|sax_n2); */
/* /A:... for (i=0;i<4;i++) drv_pattern[i] |= sax_a; */
/* /N:... ndrives=i-'0'; */
/*==========================================================================*/
/*
* drive space begins here (needed separate for each unit)
*/
static int d=0; /* DS index: drive number */
static struct {
char drv_minor; /* minor number or -1 */
char drive_model[4];
char firmware_version[4];
u_char *sbp_buf; /* Pointer to internal data buffer,
space allocated during sbpcd_init() */
int sbp_first_frame; /* First frame in buffer */
int sbp_last_frame; /* Last frame in buffer */
int sbp_read_frames; /* Number of frames being read to buffer */
int sbp_current; /* Frame being currently read */
u_char drv_type;
u_char drv_options;
u_char status_byte;
u_char diskstate_flags;
u_char sense_byte;
u_char CD_changed;
u_char error_byte;
u_char f_multisession;
u_int lba_multi;
u_char audio_state;
u_int pos_audio_start;
u_int pos_audio_end;
char vol_chan0;
u_char vol_ctrl0;
char vol_chan1;
u_char vol_ctrl1;
#if 000
char vol_chan2;
u_char vol_ctrl2;
char vol_chan3;
u_char vol_ctrl3;
#endif 000
u_char SubQ_audio;
u_char SubQ_ctl_adr;
u_char SubQ_trk;
u_char SubQ_pnt_idx;
u_int SubQ_run_tot;
u_int SubQ_run_trk;
u_char SubQ_whatisthis;
u_char UPC_ctl_adr;
u_char UPC_buf[7];
int CDsize_blk;
int frame_size;
int CDsize_frm;
u_char xa_byte; /* 0x20: XA capabilities */
u_char n_first_track; /* binary */
u_char n_last_track; /* binary (not bcd), 0x01...0x63 */
u_int size_msf; /* time of whole CD, position of LeadOut track */
u_int size_blk;
u_char TocEnt_nixbyte; /* em */
u_char TocEnt_ctl_adr;
u_char TocEnt_number;
u_char TocEnt_format; /* em */
u_int TocEnt_address;
u_char ored_ctl_adr; /* to detect if CDROM contains data tracks */
struct {
u_char nixbyte; /* em */
u_char ctl_adr; /* 0x4x: data, 0x0x: audio */
u_char number;
u_char format; /* em */ /* 0x00: lba, 0x01: msf */
u_int address;
} TocBuffer[MAX_TRACKS+1]; /* last entry faked */
int in_SpinUp;
} DS[4];
/*
* drive space ends here (needed separate for each unit)
*/
/*==========================================================================*/
/*==========================================================================*/
/*
* DDI interface definitions
*/
#ifdef SBPCD_DEBUG
# define DPRINTF(x) sbpcd_dprintf x
void sbpcd_dprintf(int level, char *fmt, ...)
{
char buff[256];
va_list args;
extern int vsprintf(char *buf, const char *fmt, va_list args);
if (! (sbpcd_debug & (1 << level))) return;
va_start(args, fmt);
vsprintf(buff, fmt, args);
va_end(args);
printk(buff);
}
#else
# define DPRINTF(x) /* nothing */
#endif SBPCD_DEBUG
/*
* maintain trace bit pattern
*/
static int sbpcd_dbg_ioctl(unsigned long arg, int level)
{
int val;
val = get_fs_long((int *) arg);
switch(val)
{
case 0: /* OFF */
sbpcd_debug = 0;
break;
default:
if (val >= 128) sbpcd_debug &= ~(1 << (val - 128));
else sbpcd_debug |= (1 << val);
}
return(0);
}
/*==========================================================================*/
/*==========================================================================*/
/*
* 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 sbp_sleep(u_int jifs)
{
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + jifs;
schedule();
}
/*==========================================================================*/
/*==========================================================================*/
/*
* convert logical_block_address to m-s-f_number (3 bytes only)
*/
static void lba2msf(int lba, u_char *msf)
{
lba += CD_BLOCK_OFFSET;
msf[0] = lba / (CD_SECS*CD_FRAMES);
lba %= CD_SECS*CD_FRAMES;
msf[1] = lba / CD_FRAMES;
msf[2] = lba % CD_FRAMES;
}
/*==========================================================================*/
/*==========================================================================*/
/*
* convert msf-bin to msf-bcd
*/
static void bin2bcdx(u_char *p) /* must work only up to 75 or 99 */
{
*p=((*p/10)<<4)|(*p%10);
}
/*==========================================================================*/
static u_int blk2msf(u_int blk)
{
MSF msf;
u_int mm;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -