📄 cdu31a.c
字号:
/** Sony CDU-31A CDROM interface device driver.** Corey Minyard (minyard@wf-rch.cirr.com)** Colossians 3:17** See Documentation/cdrom/cdu31a for additional details about this driver.* * 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.** WARNING - All autoprobes have been removed from the driver.* You MUST configure the CDU31A via a LILO config* at boot time or in lilo.conf. I have the* following in my lilo.conf:** append="cdu31a=0x1f88,0,PAS"** The first number is the I/O base address of the* card. The second is the interrupt (0 means none). * The third should be "PAS" if on a Pro-Audio * spectrum, or nothing if on something else. * * This interface is (unfortunately) 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 (time_before(jiffies, retry_count) && (! <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 * asynchronous events from the drive informing the driver that a disk * has been inserted, removed, etc. * * NEWS FLASH - The driver now supports interrupts but they are * turned off by default. Use of interrupts is highly encouraged, it * cuts CPU usage down to a reasonable level. I had DMA in for a while * but PC DMA is just too slow. Better to just insb() it. * * 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. * * DRIVER SPECIAL FEATURES * ----------------------- * * This section describes features beyond the normal audio and CD-ROM * functions of the drive. * * 2048 byte buffer mode * * If a disk is mounted with -o block=2048, data is copied straight * from the drive data port to the buffer. Otherwise, the readahead * buffer must be involved to hold the other 1K of data when a 1K * block operation is done. Note that with 2048 byte blocks you * cannot execute files from the CD. * * XA compatibility * * The driver should support XA disks for both the CDU31A and CDU33A. * It does this transparently, the using program doesn't need to set it. * * Multi-Session * * A multi-session disk looks just like a normal disk to the user. * Just mount one normally, and all the data should be there. * A special thanks to Koen for help with this! * * Raw sector I/O * * Using the CDROMREADAUDIO it is possible to read raw audio and data * tracks. Both operations return 2352 bytes per sector. On the data * tracks, the first 12 bytes is not returned by the drive and the value * of that data is indeterminate. * * * 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. * * TODO: * CDs with form1 and form2 sectors cause problems * with current read-ahead strategy. * * Credits: * Heiko Eissfeldt <heiko@colossus.escape.de> * For finding abug in the return of the track numbers. * TOC processing redone for proper multisession support. * * * It probably a little late to be adding a history, but I guess I * will start. * * 10/24/95 - Added support for disabling the eject button when the * drive is open. Note that there is a small problem * still here, if the eject button is pushed while the * drive light is flashing, the drive will return a bad * 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> * * 24 January 1998 -- Removed the scd_disc_status() function, which was now * just dead code left over from the port. * Erik Andersen <andersee@debian.org> * * 16 July 1998 -- Drive donated to Erik Andersen by John Kodis * <kodis@jagunet.com>. Work begun on fixing driver to * work under 2.1.X. Added temporary extra printks * which seem to slow it down enough to work. * * 9 November 1999 -- Make kernel-parameter implementation work with 2.3.x * Removed init_module & cleanup_module in favor of * module_init & module_exit. * Torben Mathiasen <tmm@image.dk>*/#include <linux/major.h>#include <linux/module.h>#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 <linux/devfs_fs_kernel.h>#include <linux/string.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/interrupt.h>#include <asm/system.h>#include <asm/io.h>#include <asm/uaccess.h>#include <asm/dma.h>#include <linux/cdrom.h>#include "cdu31a.h"#define MAJOR_NR CDU31A_CDROM_MAJOR#include <linux/blk.h>#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/*** Edit the following data to change interrupts, DMA channels, etc.** Default is polled and no DMA. DMA is not recommended for double-speed** drives.*/static struct { unsigned short base; /* I/O Base Address */ short int_num; /* Interrupt Number (-1 means scan for it, 0 means don't use) */} cdu31a_addresses[] __initdata = { {0}};static int handle_sony_cd_attention(void);static int read_subcode(void);static void sony_get_toc(void);static int scd_spinup(void);/*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, unsigned char *result_buffer, unsigned int *result_size);static void size_to_buf(unsigned int size, unsigned char *buf);/* Parameters for the read-ahead. */static unsigned int sony_next_block; /* Next 512 byte block offset */static unsigned int sony_blocks_left = 0; /* Number of 512 byte blocks left in the current read command. *//* 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 int cdu31a_port = 0;MODULE_PARM(cdu31a_port, "i");/* * 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_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? */static int sony_raw_data_mode = 1; /* 1 if data tracks, 0 if audio. For raw data reads. */static unsigned int sony_usage = 0; /* How many processes have the drive open. */static int sony_pas_init = 0; /* Initialize the Pro-Audio Spectrum card? */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? */static struct s_sony_subcode last_sony_subcode; /* Points to the last subcode address read */static volatile int sony_inuse = 0; /* Is the drive in use? Only one operation at a time allowed */static DECLARE_WAIT_QUEUE_HEAD(sony_wait); /* Things waiting for the drive */static struct task_struct *has_cd_task = NULL; /* The task that is currently using the CDROM drive, or NULL if none. */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 *//* * 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. */static unsigned volatile char cur_pos_msf[3] = { 0, 0, 0 };static unsigned volatile char final_pos_msf[3] = { 0, 0, 0 };/* What IRQ is the drive using? 0 if none. */static int cdu31a_irq = 0;MODULE_PARM(cdu31a_irq, "i");/* The interrupt handler will wake this queue up when it gets an interrupts. */DECLARE_WAIT_QUEUE_HEAD(cdu31a_irq_wait);static int curr_control_reg = 0; /* Current value of the control register *//* A disk changed variable. When a disk change is detected, it will all be set to TRUE. As the upper layers ask for disk_changed status it will be cleared. */static char disk_changed;/* Variable for using the readahead buffer. The readahead buffer is used for raw sector reads and for blocksizes that are smaller than 2048 bytes. */static char readahead_buffer[CD_FRAMESIZE_RAW];static int readahead_dataleft = 0;static int readahead_bad = 0;/* Used to time a short period to abort an operation after the drive has been idle for a while. This keeps the light on the drive from flashing for very long. */static struct timer_list cdu31a_abort_timer;/* Marks if the timeout has started an abort read. This is used on entry to the drive to tell the code to read out the status from the abort read. */static int abort_read_started = 0;/* * This routine returns 1 if the disk has been changed since the last * check or 0 if it hasn't. */static int scd_disk_change(kdev_t full_dev){ int retval; retval = disk_changed; disk_changed = 0; 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; } if (scd_spinup() == 0) { sony_spun_up = 1; } return sony_spun_up ? CDS_DISC_OK : CDS_DRIVE_NOT_READY;}static inline void enable_interrupts(void){ curr_control_reg |= (SONY_ATTN_INT_EN_BIT | SONY_RES_RDY_INT_EN_BIT | SONY_DATA_RDY_INT_EN_BIT); outb(curr_control_reg, sony_cd_control_reg);}static inline void disable_interrupts(void){ curr_control_reg &= ~(SONY_ATTN_INT_EN_BIT | SONY_RES_RDY_INT_EN_BIT | SONY_DATA_RDY_INT_EN_BIT); outb(curr_control_reg, sony_cd_control_reg);}/* * 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){ unsigned long flags; if (cdu31a_irq <= 0) { current->state = TASK_INTERRUPTIBLE; schedule_timeout(0); } else { /* Interrupt driven */ save_flags(flags); cli(); enable_interrupts(); interruptible_sleep_on(&cdu31a_irq_wait); restore_flags(flags); }}/* * 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 int is_result_reg_not_empty(void){ return ((inb(sony_cd_fifost_reg) & SONY_RES_REG_NOT_EMP_BIT) != 0);}static inline void reset_drive(void){ curr_control_reg = 0; readahead_dataleft = 0; sony_toc_read = 0; 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 (time_before(jiffies, retry_count) && (!is_attention())) { sony_sleep(); } return 0;}static inline void clear_attention(void){ outb(curr_control_reg | SONY_ATTN_CLR_BIT, sony_cd_control_reg);}static inline void clear_result_ready(void){ outb(curr_control_reg | SONY_RES_RDY_CLR_BIT, sony_cd_control_reg);}static inline void clear_data_ready(void){ outb(curr_control_reg | SONY_DATA_RDY_CLR_BIT, sony_cd_control_reg);}static inline void clear_param_reg(void){ outb(curr_control_reg | SONY_PARAM_CLR_BIT, sony_cd_control_reg);}static inline unsigned char read_status_register(void){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -