📄 sonycd535.c
字号:
/* * Sony CDU-535 interface device driver * * This is a modified version of the CDU-31A device driver (see below). * Changes were made using documentation for the CDU-531 (which Sony * assures me is very similar to the 535) and partial disassembly of the * DOS driver. I used Minyard's driver and replaced the CDU-31A * commands with the CDU-531 commands. This was complicated by a different * interface protocol with the drive. The driver is still polled. * * Data transfer rate is about 110 Kb/sec, theoretical maximum is 150 Kb/sec. * I tried polling without the sony_sleep during the data transfers but * it did not speed things up any. * * 1993-05-23 (rgj) changed the major number to 21 to get rid of conflict * with CDU-31A driver. This is the also the number from the Linux * Device Driver Registry for the Sony Drive. Hope nobody else is using it. * * 1993-08-29 (rgj) remove the configuring of the interface board address * from the top level configuration, you have to modify it in this file. * * 1995-01-26 Made module-capable (Joel Katz <Stimpson@Panix.COM>) * * 1995-05-20 * Modified to support CDU-510/515 series * (Claudio Porfiri<C.Porfiri@nisms.tei.ericsson.se>) * Fixed to report verify_area() failures * (Heiko Eissfeldt <heiko@colossus.escape.de>) * * 1995-06-01 * More changes to support CDU-510/515 series * (Claudio Porfiri<C.Porfiri@nisms.tei.ericsson.se>) * * Things to do: * - handle errors and status better, put everything into a single word * - use interrupts (code mostly there, but a big hole still missing) * - handle multi-session CDs? * - use DMA? * * Known Bugs: * - * * Ken Pizzini (ken@halcyon.com) * * Original by: * Ron Jeppesen (ronj.an@site007.saic.com) * * *------------------------------------------------------------------------ * Sony CDROM interface device driver. * * Corey Minyard (minyard@wf-rch.cirr.com) (CDU-535 complaints to Ken above) * * 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 (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 bit of * the following: * * snap = jiffies; * while (jiffies-snap < SONY_JIFFIES_TIMEOUT) * { * if (some_condition()) * break; * sony_sleep(); * } * if (some_condition not met) * { * return an_error; * } * * This ugly hack waits for something to happen, sleeping a little * between every try. (The conditional is written so that jiffies * wrap-around is handled properly.) * * 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/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/ioport.h>#include <linux/hdreg.h>#include <linux/genhd.h>#include <linux/mm.h>#include <linux/malloc.h>#include <linux/init.h>#define REALLY_SLOW_IO#include <asm/system.h>#include <asm/io.h>#include <asm/uaccess.h>#include <linux/cdrom.h>#define MAJOR_NR CDU535_CDROM_MAJOR# include <linux/blk.h>#define sony535_cd_base_io sonycd535 /* for compatible parameter passing with "insmod" */#include "sonycd535.h"/* * this is the base address of the interface card for the Sony CDU-535 * CDROM drive. If your jumpers are set for an address other than * this one (the default), change the following line to the * proper address. */#ifndef CDU535_ADDRESS# define CDU535_ADDRESS 0x340#endif#ifndef CDU535_INTERRUPT# define CDU535_INTERRUPT 0#endif#ifndef CDU535_HANDLE# define CDU535_HANDLE "cdu535"#endif#ifndef CDU535_MESSAGE_NAME# define CDU535_MESSAGE_NAME "Sony CDU-535"#endif#define CDU535_BLOCK_SIZE 2048 #ifndef MAX_SPINUP_RETRY# define MAX_SPINUP_RETRY 3 /* 1 is sufficient for most drives... */#endif#ifndef RETRY_FOR_BAD_STATUS# define RETRY_FOR_BAD_STATUS 100 /* in 10th of second */#endif#ifndef DEBUG# define DEBUG 1#endif/* * SONY535_BUFFER_SIZE determines the size of internal buffer used * by the drive. It must be at least 2K and the larger the buffer * the better the transfer rate. It does however take system memory. * On my system I get the following transfer rates using dd to read * 10 Mb off /dev/cdrom. * * 8K buffer 43 Kb/sec * 16K buffer 66 Kb/sec * 32K buffer 91 Kb/sec * 64K buffer 111 Kb/sec * 128K buffer 123 Kb/sec * 512K buffer 123 Kb/sec */#define SONY535_BUFFER_SIZE (64*1024)/* * if LOCK_DOORS is defined then the eject button is disabled while * the device is open. */#ifndef NO_LOCK_DOORS# define LOCK_DOORS#endifstatic int read_subcode(void);static void sony_get_toc(void);static int cdu_open(struct inode *inode, struct file *filp);static inline unsigned int int_to_bcd(unsigned int val);static unsigned int bcd_to_int(unsigned int bcd);static int do_sony_cmd(Byte * cmd, int nCmd, Byte status[2], Byte * response, int n_response, int ignoreStatusBit7);/* 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 sony535_cd_base_io = CDU535_ADDRESS;MODULE_PARM(sony535_cd_base_io, "i");/* * The following are I/O addresses of the various registers for the drive. The * comment for the base address also applies here. */static unsigned short select_unit_reg;static unsigned short result_reg;static unsigned short command_reg;static unsigned short read_status_reg;static unsigned short data_reg;static int initialized = 0; /* Has the drive been initialized? */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 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 int sony_first_block = -1; /* First OS block (512 byte) in the read-ahead buffer */static int sony_last_block = -1; /* Last OS block (512 byte) in the read-ahead buffer */static struct s535_sony_toc *sony_toc; /* Points to the table of contents. */static struct s535_sony_subcode *last_sony_subcode; /* Points to the last subcode address read */static Byte **sony_buffer; /* Points to the pointers to the sector buffers */static int sony_inuse = 0; /* is the drive in use? Only one open at a time allowed *//* * The audio status uses the values from read subchannel data as specified * in include/linux/cdrom.h. */static 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. * I just kept the CDU-31A driver behavior rather than using the PAUSE * command on the CDU-535. */static Byte cur_pos_msf[3] = {0, 0, 0};static Byte final_pos_msf[3] = {0, 0, 0};/* What IRQ is the drive using? 0 if none. */static int sony535_irq_used = CDU535_INTERRUPT;/* The interrupt handler will wake this queue up when it gets an interrupt. */static struct wait_queue *cdu535_irq_wait = NULL;/* * 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. */static intcdu535_check_media_change(kdev_t full_dev){ int retval; if (MINOR(full_dev) != 0) { printk(CDU535_MESSAGE_NAME " request error: invalid device.\n"); return 0; } /* if driver is not initialized, always return 0 */ retval = initialized ? sony_disc_changed : 0; sony_disc_changed = 0; return retval;}static inline voidenable_interrupts(void){#ifdef USE_IRQ /* * This code was taken from cdu31a.c; it will not * directly work for the cdu535 as written... */ 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);#endif}static inline voiddisable_interrupts(void){#ifdef USE_IRQ /* * This code was taken from cdu31a.c; it will not * directly work for the cdu535 as written... */ 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);#endif}static voidcdu535_interrupt(int irq, void *dev_id, struct pt_regs *regs){ disable_interrupts(); if (cdu535_irq_wait != NULL) wake_up(&cdu535_irq_wait); else printk(CDU535_MESSAGE_NAME ": Got an interrupt but nothing was waiting\n");}/* * Wait a little while. */static inline voidsony_sleep(void){ if (sony535_irq_used <= 0) { /* poll */ current->state = TASK_INTERRUPTIBLE; schedule_timeout(0); } else { /* Interrupt driven */ cli(); enable_interrupts(); interruptible_sleep_on(&cdu535_irq_wait); sti(); }}/*------------------start of SONY CDU535 very specific ---------------------*//**************************************************************************** * void select_unit( int unit_no ) * * Select the specified unit (0-3) so that subsequent commands reference it ****************************************************************************/static voidselect_unit(int unit_no){ unsigned int select_mask = ~(1 << unit_no); outb(select_mask, select_unit_reg);}/*************************************************************************** * int read_result_reg( Byte *data_ptr ) * * Read a result byte from the Sony CDU controller, store in location pointed * to by data_ptr. Return zero on success, TIME_OUT if we did not receive * data. ***************************************************************************/static intread_result_reg(Byte *data_ptr){ unsigned long snap; int read_status; snap = jiffies; while (jiffies-snap < SONY_JIFFIES_TIMEOUT) { read_status = inb(read_status_reg); if ((read_status & SONY535_RESULT_NOT_READY_BIT) == 0) {#if DEBUG > 1 printk(CDU535_MESSAGE_NAME ": read_result_reg(): readStatReg = 0x%x\n", read_status);#endif *data_ptr = inb(result_reg); return 0; } else { sony_sleep(); } } printk(CDU535_MESSAGE_NAME " read_result_reg: TIME OUT!\n"); return TIME_OUT;}/**************************************************************************** * int read_exec_status( Byte status[2] ) * * Read the execution status of the last command and put into status. * Handles reading second status word if available. Returns 0 on success, * TIME_OUT on failure. ****************************************************************************/static intread_exec_status(Byte status[2]){ status[1] = 0; if (read_result_reg(&(status[0])) != 0) return TIME_OUT; if ((status[0] & 0x80) != 0) { /* byte two follows */ if (read_result_reg(&(status[1])) != 0) return TIME_OUT; }#if DEBUG > 1 printk(CDU535_MESSAGE_NAME ": read_exec_status: read 0x%x 0x%x\n", status[0], status[1]);#endif return 0;}/**************************************************************************** * int check_drive_status( void ) * * Check the current drive status. Using this before executing a command * takes care of the problem of unsolicited drive status-2 messages. * Add a check of the audio status if we think the disk is playing. ****************************************************************************/static intcheck_drive_status(void){ Byte status, e_status[2];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -