|  | /* | 
|  | * 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>) | 
|  | * | 
|  | * 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> | 
|  | * | 
|  | * September 2003 - Fix SMP support by removing cli/sti calls. | 
|  | *                  Using spinlocks with a wait_queue instead. | 
|  | *                  Felipe Damasio <felipewd@terra.com.br> | 
|  | * | 
|  | * 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/interrupt.h> | 
|  | #include <linux/ioport.h> | 
|  | #include <linux/hdreg.h> | 
|  | #include <linux/genhd.h> | 
|  | #include <linux/mm.h> | 
|  | #include <linux/slab.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/blkdev.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 | 
|  | #endif | 
|  |  | 
|  | static 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_param(sony535_cd_base_io, int, 0); | 
|  |  | 
|  | /* | 
|  | * 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 DEFINE_SPINLOCK(sonycd535_lock); /* queue lock */ | 
|  | static struct request_queue *sonycd535_queue; | 
|  |  | 
|  | static int initialized;			/* 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;		/* 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;		/* 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;			/* 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]; | 
|  | static Byte final_pos_msf[3]; | 
|  |  | 
|  | /* 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 DECLARE_WAIT_QUEUE_HEAD(cdu535_irq_wait); | 
|  |  | 
|  |  | 
|  | /* | 
|  | * 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 int | 
|  | cdu535_check_media_change(struct gendisk *disk) | 
|  | { | 
|  | /* if driver is not initialized, always return 0 */ | 
|  | int retval = initialized ? sony_disc_changed : 0; | 
|  | sony_disc_changed = 0; | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | static inline void | 
|  | enable_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 void | 
|  | disable_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 irqreturn_t | 
|  | cdu535_interrupt(int irq, void *dev_id) | 
|  | { | 
|  | disable_interrupts(); | 
|  | if (waitqueue_active(&cdu535_irq_wait)) { | 
|  | wake_up(&cdu535_irq_wait); | 
|  | return IRQ_HANDLED; | 
|  | } | 
|  | printk(CDU535_MESSAGE_NAME | 
|  | ": Got an interrupt but nothing was waiting\n"); | 
|  | return IRQ_NONE; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Wait a little while. | 
|  | */ | 
|  | static inline void | 
|  | sony_sleep(void) | 
|  | { | 
|  | if (sony535_irq_used <= 0) {	/* poll */ | 
|  | yield(); | 
|  | } else {	/* Interrupt driven */ | 
|  | DEFINE_WAIT(wait); | 
|  |  | 
|  | spin_lock_irq(&sonycd535_lock); | 
|  | enable_interrupts(); | 
|  | prepare_to_wait(&cdu535_irq_wait, &wait, TASK_INTERRUPTIBLE); | 
|  | spin_unlock_irq(&sonycd535_lock); | 
|  | schedule(); | 
|  | finish_wait(&cdu535_irq_wait, &wait); | 
|  | } | 
|  | } | 
|  |  | 
|  | /*------------------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 void | 
|  | select_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 int | 
|  | read_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 int | 
|  | read_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 int | 
|  | check_drive_status(void) | 
|  | { | 
|  | Byte status, e_status[2]; | 
|  | int  CDD, ATN; | 
|  | Byte cmd; | 
|  |  | 
|  | select_unit(0); | 
|  | if (sony_audio_status == CDROM_AUDIO_PLAY) {	/* check status */ | 
|  | outb(SONY535_REQUEST_AUDIO_STATUS, command_reg); | 
|  | if (read_result_reg(&status) == 0) { | 
|  | switch (status) { | 
|  | case 0x0: | 
|  | break;		/* play in progress */ | 
|  | case 0x1: | 
|  | break;		/* paused */ | 
|  | case 0x3:		/* audio play completed */ | 
|  | case 0x5:		/* play not requested */ | 
|  | sony_audio_status = CDROM_AUDIO_COMPLETED; | 
|  | read_subcode(); | 
|  | break; | 
|  | case 0x4:		/* error during play */ | 
|  | sony_audio_status = CDROM_AUDIO_ERROR; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | /* now check drive status */ | 
|  | outb(SONY535_REQUEST_DRIVE_STATUS_2, command_reg); | 
|  | if (read_result_reg(&status) != 0) | 
|  | return TIME_OUT; | 
|  |  | 
|  | #if DEBUG > 1 | 
|  | printk(CDU535_MESSAGE_NAME ": check_drive_status() got 0x%x\n", status); | 
|  | #endif | 
|  |  | 
|  | if (status == 0) | 
|  | return 0; | 
|  |  | 
|  | ATN = status & 0xf; | 
|  | CDD = (status >> 4) & 0xf; | 
|  |  | 
|  | switch (ATN) { | 
|  | case 0x0: | 
|  | break;					/* go on to CDD stuff */ | 
|  | case SONY535_ATN_BUSY: | 
|  | if (initialized) | 
|  | printk(CDU535_MESSAGE_NAME " error: drive busy\n"); | 
|  | return CD_BUSY; | 
|  | case SONY535_ATN_EJECT_IN_PROGRESS: | 
|  | printk(CDU535_MESSAGE_NAME " error: eject in progress\n"); | 
|  | sony_audio_status = CDROM_AUDIO_INVALID; | 
|  | return CD_BUSY; | 
|  | case SONY535_ATN_RESET_OCCURRED: | 
|  | case SONY535_ATN_DISC_CHANGED: | 
|  | case SONY535_ATN_RESET_AND_DISC_CHANGED: | 
|  | #if DEBUG > 0 | 
|  | printk(CDU535_MESSAGE_NAME " notice: reset occurred or disc changed\n"); | 
|  | #endif | 
|  | sony_disc_changed = 1; | 
|  | sony_toc_read = 0; | 
|  | sony_audio_status = CDROM_AUDIO_NO_STATUS; | 
|  | sony_first_block = -1; | 
|  | sony_last_block = -1; | 
|  | if (initialized) { | 
|  | cmd = SONY535_SPIN_UP; | 
|  | do_sony_cmd(&cmd, 1, e_status, NULL, 0, 0); | 
|  | sony_get_toc(); | 
|  | } | 
|  | return 0; | 
|  | default: | 
|  | printk(CDU535_MESSAGE_NAME " error: drive busy (ATN=0x%x)\n", ATN); | 
|  | return CD_BUSY; | 
|  | } | 
|  | switch (CDD) {			/* the 531 docs are not helpful in decoding this */ | 
|  | case 0x0:				/* just use the values from the DOS driver */ | 
|  | case 0x2: | 
|  | case 0xa: | 
|  | break;				/* no error */ | 
|  | case 0xc: | 
|  | printk(CDU535_MESSAGE_NAME | 
|  | ": check_drive_status(): CDD = 0xc! Not properly handled!\n"); | 
|  | return CD_BUSY;		/* ? */ | 
|  | default: | 
|  | return CD_BUSY; | 
|  | } | 
|  | return 0; | 
|  | }	/* check_drive_status() */ | 
|  |  | 
|  | /***************************************************************************** | 
|  | * int do_sony_cmd( Byte *cmd, int n_cmd, Byte status[2], | 
|  | *                Byte *response, int n_response, int ignore_status_bit7 ) | 
|  | * | 
|  | *  Generic routine for executing commands.  The command and its parameters | 
|  | *  should be placed in the cmd[] array, number of bytes in the command is | 
|  | *  stored in nCmd.  The response from the command will be stored in the | 
|  | *  response array.  The number of bytes you expect back (excluding status) | 
|  | *  should be passed in n_response.  Finally, some | 
|  | *  commands set bit 7 of the return status even when there is no second | 
|  | *  status byte, on these commands set ignoreStatusBit7 TRUE. | 
|  | *    If the command was sent and data received back, then we return 0, | 
|  | *  else we return TIME_OUT.  You still have to check the status yourself. | 
|  | *    You should call check_drive_status() before calling this routine | 
|  | *  so that you do not lose notifications of disk changes, etc. | 
|  | ****************************************************************************/ | 
|  | static int | 
|  | do_sony_cmd(Byte * cmd, int n_cmd, Byte status[2], | 
|  | Byte * response, int n_response, int ignore_status_bit7) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | /* write out the command */ | 
|  | for (i = 0; i < n_cmd; i++) | 
|  | outb(cmd[i], command_reg); | 
|  |  | 
|  | /* read back the status */ | 
|  | if (read_result_reg(status) != 0) | 
|  | return TIME_OUT; | 
|  | if (!ignore_status_bit7 && ((status[0] & 0x80) != 0)) { | 
|  | /* get second status byte */ | 
|  | if (read_result_reg(status + 1) != 0) | 
|  | return TIME_OUT; | 
|  | } else { | 
|  | status[1] = 0; | 
|  | } | 
|  | #if DEBUG > 2 | 
|  | printk(CDU535_MESSAGE_NAME ": do_sony_cmd %x: %x %x\n", | 
|  | *cmd, status[0], status[1]); | 
|  | #endif | 
|  |  | 
|  | /* do not know about when I should read set of data and when not to */ | 
|  | if ((status[0] & ((ignore_status_bit7 ? 0x7f : 0xff) & 0x8f)) != 0) | 
|  | return 0; | 
|  |  | 
|  | /* else, read in rest of data */ | 
|  | for (i = 0; 0 < n_response; n_response--, i++) | 
|  | if (read_result_reg(response + i) != 0) | 
|  | return TIME_OUT; | 
|  | return 0; | 
|  | }	/* do_sony_cmd() */ | 
|  |  | 
|  | /************************************************************************** | 
|  | * int set_drive_mode( int mode, Byte status[2] ) | 
|  | * | 
|  | *  Set the drive mode to the specified value (mode=0 is audio, mode=e0 | 
|  | * is mode-1 CDROM | 
|  | **************************************************************************/ | 
|  | static int | 
|  | set_drive_mode(int mode, Byte status[2]) | 
|  | { | 
|  | Byte cmd_buff[2]; | 
|  | Byte ret_buff[1]; | 
|  |  | 
|  | cmd_buff[0] = SONY535_SET_DRIVE_MODE; | 
|  | cmd_buff[1] = mode; | 
|  | return do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1); | 
|  | } | 
|  |  | 
|  | /*************************************************************************** | 
|  | * int seek_and_read_N_blocks( Byte params[], int n_blocks, Byte status[2], | 
|  | *                             Byte *data_buff, int buff_size ) | 
|  | * | 
|  | *  Read n_blocks of data from the CDROM starting at position params[0:2], | 
|  | *  number of blocks in stored in params[3:5] -- both these are already | 
|  | *  int bcd format. | 
|  | *  Transfer the data into the buffer pointed at by data_buff.  buff_size | 
|  | *  gives the number of bytes available in the buffer. | 
|  | *    The routine returns number of bytes read in if successful, otherwise | 
|  | *  it returns one of the standard error returns. | 
|  | ***************************************************************************/ | 
|  | static int | 
|  | seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2], | 
|  | Byte **buff, int buf_size) | 
|  | { | 
|  | Byte cmd_buff[7]; | 
|  | int  i; | 
|  | int  read_status; | 
|  | unsigned long snap; | 
|  | Byte *data_buff; | 
|  | int  sector_count = 0; | 
|  |  | 
|  | if (buf_size < CDU535_BLOCK_SIZE * n_blocks) | 
|  | return NO_ROOM; | 
|  |  | 
|  | set_drive_mode(SONY535_CDROM_DRIVE_MODE, status); | 
|  |  | 
|  | /* send command to read the data */ | 
|  | cmd_buff[0] = SONY535_SEEK_AND_READ_N_BLOCKS_1; | 
|  | for (i = 0; i < 6; i++) | 
|  | cmd_buff[i + 1] = params[i]; | 
|  | for (i = 0; i < 7; i++) | 
|  | outb(cmd_buff[i], command_reg); | 
|  |  | 
|  | /* read back the data one block at a time */ | 
|  | while (0 < n_blocks--) { | 
|  | /* wait for data to be ready */ | 
|  | int data_valid = 0; | 
|  | snap = jiffies; | 
|  | while (jiffies-snap < SONY_JIFFIES_TIMEOUT) { | 
|  | read_status = inb(read_status_reg); | 
|  | if ((read_status & SONY535_RESULT_NOT_READY_BIT) == 0) { | 
|  | read_exec_status(status); | 
|  | return BAD_STATUS; | 
|  | } | 
|  | if ((read_status & SONY535_DATA_NOT_READY_BIT) == 0) { | 
|  | /* data is ready, read it */ | 
|  | data_buff = buff[sector_count++]; | 
|  | for (i = 0; i < CDU535_BLOCK_SIZE; i++) | 
|  | *data_buff++ = inb(data_reg);	/* unrolling this loop does not seem to help */ | 
|  | data_valid = 1; | 
|  | break;			/* exit the timeout loop */ | 
|  | } | 
|  | sony_sleep();		/* data not ready, sleep a while */ | 
|  | } | 
|  | if (!data_valid) | 
|  | return TIME_OUT;	/* if we reach this stage */ | 
|  | } | 
|  |  | 
|  | /* read all the data, now read the status */ | 
|  | if ((i = read_exec_status(status)) != 0) | 
|  | return i; | 
|  | return CDU535_BLOCK_SIZE * sector_count; | 
|  | }	/* seek_and_read_N_blocks() */ | 
|  |  | 
|  | /**************************************************************************** | 
|  | * int request_toc_data( Byte status[2], struct s535_sony_toc *toc ) | 
|  | * | 
|  | *  Read in the table of contents data.  Converts all the bcd data | 
|  | * into integers in the toc structure. | 
|  | ****************************************************************************/ | 
|  | static int | 
|  | request_toc_data(Byte status[2], struct s535_sony_toc *toc) | 
|  | { | 
|  | int  to_status; | 
|  | int  i, j, n_tracks, track_no; | 
|  | int  first_track_num, last_track_num; | 
|  | Byte cmd_no = 0xb2; | 
|  | Byte track_address_buffer[5]; | 
|  |  | 
|  | /* read the fixed portion of the table of contents */ | 
|  | if ((to_status = do_sony_cmd(&cmd_no, 1, status, (Byte *) toc, 15, 1)) != 0) | 
|  | return to_status; | 
|  |  | 
|  | /* convert the data into integers so we can use them */ | 
|  | first_track_num = bcd_to_int(toc->first_track_num); | 
|  | last_track_num = bcd_to_int(toc->last_track_num); | 
|  | n_tracks = last_track_num - first_track_num + 1; | 
|  |  | 
|  | /* read each of the track address descriptors */ | 
|  | for (i = 0; i < n_tracks; i++) { | 
|  | /* read the descriptor into a temporary buffer */ | 
|  | for (j = 0; j < 5; j++) { | 
|  | if (read_result_reg(track_address_buffer + j) != 0) | 
|  | return TIME_OUT; | 
|  | if (j == 1)		/* need to convert from bcd */ | 
|  | track_no = bcd_to_int(track_address_buffer[j]); | 
|  | } | 
|  | /* copy the descriptor to proper location - sonycd.c just fills */ | 
|  | memcpy(toc->tracks + i, track_address_buffer, 5); | 
|  | } | 
|  | return 0; | 
|  | }	/* request_toc_data() */ | 
|  |  | 
|  | /*************************************************************************** | 
|  | * int spin_up_drive( Byte status[2] ) | 
|  | * | 
|  | *  Spin up the drive (unless it is already spinning). | 
|  | ***************************************************************************/ | 
|  | static int | 
|  | spin_up_drive(Byte status[2]) | 
|  | { | 
|  | Byte cmd; | 
|  |  | 
|  | /* first see if the drive is already spinning */ | 
|  | cmd = SONY535_REQUEST_DRIVE_STATUS_1; | 
|  | if (do_sony_cmd(&cmd, 1, status, NULL, 0, 0) != 0) | 
|  | return TIME_OUT; | 
|  | if ((status[0] & SONY535_STATUS1_NOT_SPINNING) == 0) | 
|  | return 0;	/* it's already spinning */ | 
|  |  | 
|  | /* otherwise, give the spin-up command */ | 
|  | cmd = SONY535_SPIN_UP; | 
|  | return do_sony_cmd(&cmd, 1, status, NULL, 0, 0); | 
|  | } | 
|  |  | 
|  | /*--------------------end of SONY CDU535 very specific ---------------------*/ | 
|  |  | 
|  | /* Convert from an integer 0-99 to BCD */ | 
|  | static inline unsigned int | 
|  | int_to_bcd(unsigned int val) | 
|  | { | 
|  | int retval; | 
|  |  | 
|  | retval = (val / 10) << 4; | 
|  | retval = retval | val % 10; | 
|  | return retval; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Convert from BCD to an integer from 0-99 */ | 
|  | static unsigned int | 
|  | bcd_to_int(unsigned int bcd) | 
|  | { | 
|  | return (((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Convert a logical sector value (like the OS would want to use for | 
|  | * a block device) to an MSF format. | 
|  | */ | 
|  | static void | 
|  | log_to_msf(unsigned int log, Byte *msf) | 
|  | { | 
|  | log = log + LOG_START_OFFSET; | 
|  | msf[0] = int_to_bcd(log / 4500); | 
|  | log = log % 4500; | 
|  | msf[1] = int_to_bcd(log / 75); | 
|  | msf[2] = int_to_bcd(log % 75); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Convert an MSF format to a logical sector. | 
|  | */ | 
|  | static unsigned int | 
|  | msf_to_log(Byte *msf) | 
|  | { | 
|  | unsigned int log; | 
|  |  | 
|  |  | 
|  | log = bcd_to_int(msf[2]); | 
|  | log += bcd_to_int(msf[1]) * 75; | 
|  | log += bcd_to_int(msf[0]) * 4500; | 
|  | log = log - LOG_START_OFFSET; | 
|  |  | 
|  | return log; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Take in integer size value and put it into a buffer like | 
|  | * the drive would want to see a number-of-sector value. | 
|  | */ | 
|  | static void | 
|  | size_to_buf(unsigned int size, Byte *buf) | 
|  | { | 
|  | buf[0] = size / 65536; | 
|  | size = size % 65536; | 
|  | buf[1] = size / 256; | 
|  | buf[2] = size % 256; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * The OS calls this to perform a read or write operation to the drive. | 
|  | * Write obviously fail.  Reads to a read ahead of sony_buffer_size | 
|  | * bytes to help speed operations.  This especially helps since the OS | 
|  | * may use 1024 byte blocks and the drive uses 2048 byte blocks.  Since most | 
|  | * data access on a CD is done sequentially, this saves a lot of operations. | 
|  | */ | 
|  | static void | 
|  | do_cdu535_request(request_queue_t * q) | 
|  | { | 
|  | struct request *req; | 
|  | unsigned int read_size; | 
|  | int  block; | 
|  | int  nsect; | 
|  | int  copyoff; | 
|  | int  spin_up_retry; | 
|  | Byte params[10]; | 
|  | Byte status[2]; | 
|  | Byte cmd[2]; | 
|  |  | 
|  | while (1) { | 
|  | req = elv_next_request(q); | 
|  | if (!req) | 
|  | return; | 
|  |  | 
|  | block = req->sector; | 
|  | nsect = req->nr_sectors; | 
|  | if (!blk_fs_request(req)) { | 
|  | end_request(req, 0); | 
|  | continue; | 
|  | } | 
|  | if (rq_data_dir(req) == WRITE) { | 
|  | end_request(req, 0); | 
|  | continue; | 
|  | } | 
|  | /* | 
|  | * If the block address is invalid or the request goes beyond | 
|  | * the end of the media, return an error. | 
|  | */ | 
|  | if (sony_toc->lead_out_start_lba <= (block/4)) { | 
|  | end_request(req, 0); | 
|  | return; | 
|  | } | 
|  | if (sony_toc->lead_out_start_lba <= ((block + nsect) / 4)) { | 
|  | end_request(req, 0); | 
|  | return; | 
|  | } | 
|  | while (0 < nsect) { | 
|  | /* | 
|  | * If the requested sector is not currently in | 
|  | * the read-ahead buffer, it must be read in. | 
|  | */ | 
|  | if ((block < sony_first_block) || (sony_last_block < block)) { | 
|  | sony_first_block = (block / 4) * 4; | 
|  | log_to_msf(block / 4, params); | 
|  |  | 
|  | /* | 
|  | * If the full read-ahead would go beyond the end of the media, trim | 
|  | * it back to read just till the end of the media. | 
|  | */ | 
|  | if (sony_toc->lead_out_start_lba <= ((block / 4) + sony_buffer_sectors)) { | 
|  | sony_last_block = (sony_toc->lead_out_start_lba * 4) - 1; | 
|  | read_size = sony_toc->lead_out_start_lba - (block / 4); | 
|  | } else { | 
|  | sony_last_block = sony_first_block + (sony_buffer_sectors * 4) - 1; | 
|  | read_size = sony_buffer_sectors; | 
|  | } | 
|  | size_to_buf(read_size, ¶ms[3]); | 
|  |  | 
|  | /* | 
|  | * Read the data.  If the drive was not spinning, | 
|  | * spin it up and try some more. | 
|  | */ | 
|  | for (spin_up_retry=0 ;; ++spin_up_retry) { | 
|  | /* This loop has been modified to support the Sony | 
|  | * CDU-510/515 series, thanks to Claudio Porfiri | 
|  | * <C.Porfiri@nisms.tei.ericsson.se>. | 
|  | */ | 
|  | /* | 
|  | * This part is to deal with very slow hardware.  We | 
|  | * try at most MAX_SPINUP_RETRY times to read the same | 
|  | * block.  A check for seek_and_read_N_blocks' result is | 
|  | * performed; if the result is wrong, the CDROM's engine | 
|  | * is restarted and the operation is tried again. | 
|  | */ | 
|  | /* | 
|  | * 1995-06-01: The system got problems when downloading | 
|  | * from Slackware CDROM, the problem seems to be: | 
|  | * seek_and_read_N_blocks returns BAD_STATUS and we | 
|  | * should wait for a while before retrying, so a new | 
|  | * part was added to discriminate the return value from | 
|  | * seek_and_read_N_blocks for the various cases. | 
|  | */ | 
|  | int readStatus = seek_and_read_N_blocks(params, read_size, | 
|  | status, sony_buffer, (read_size * CDU535_BLOCK_SIZE)); | 
|  | if (0 <= readStatus)	/* Good data; common case, placed first */ | 
|  | break; | 
|  | if (readStatus == NO_ROOM || spin_up_retry == MAX_SPINUP_RETRY) { | 
|  | /* give up */ | 
|  | if (readStatus == NO_ROOM) | 
|  | printk(CDU535_MESSAGE_NAME " No room to read from CD\n"); | 
|  | else | 
|  | printk(CDU535_MESSAGE_NAME " Read error: 0x%.2x\n", | 
|  | status[0]); | 
|  | sony_first_block = -1; | 
|  | sony_last_block = -1; | 
|  | end_request(req, 0); | 
|  | return; | 
|  | } | 
|  | if (readStatus == BAD_STATUS) { | 
|  | /* Sleep for a while, then retry */ | 
|  | set_current_state(TASK_INTERRUPTIBLE); | 
|  | spin_unlock_irq(&sonycd535_lock); | 
|  | schedule_timeout(RETRY_FOR_BAD_STATUS*HZ/10); | 
|  | spin_lock_irq(&sonycd535_lock); | 
|  | } | 
|  | #if DEBUG > 0 | 
|  | printk(CDU535_MESSAGE_NAME | 
|  | " debug: calling spin up when reading data!\n"); | 
|  | #endif | 
|  | cmd[0] = SONY535_SPIN_UP; | 
|  | do_sony_cmd(cmd, 1, status, NULL, 0, 0); | 
|  | } | 
|  | } | 
|  | /* | 
|  | * The data is in memory now, copy it to the buffer and advance to the | 
|  | * next block to read. | 
|  | */ | 
|  | copyoff = block - sony_first_block; | 
|  | memcpy(req->buffer, | 
|  | sony_buffer[copyoff / 4] + 512 * (copyoff % 4), 512); | 
|  |  | 
|  | block += 1; | 
|  | nsect -= 1; | 
|  | req->buffer += 512; | 
|  | } | 
|  |  | 
|  | end_request(req, 1); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Read the table of contents from the drive and set sony_toc_read if | 
|  | * successful. | 
|  | */ | 
|  | static void | 
|  | sony_get_toc(void) | 
|  | { | 
|  | Byte status[2]; | 
|  | if (!sony_toc_read) { | 
|  | /* do not call check_drive_status() from here since it can call this routine */ | 
|  | if (request_toc_data(status, sony_toc) < 0) | 
|  | return; | 
|  | sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf); | 
|  | sony_toc_read = 1; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Search for a specific track in the table of contents.  track is | 
|  | * passed in bcd format | 
|  | */ | 
|  | static int | 
|  | find_track(int track) | 
|  | { | 
|  | int i; | 
|  | int num_tracks; | 
|  |  | 
|  |  | 
|  | num_tracks = bcd_to_int(sony_toc->last_track_num) - | 
|  | bcd_to_int(sony_toc->first_track_num) + 1; | 
|  | for (i = 0; i < num_tracks; i++) { | 
|  | if (sony_toc->tracks[i].track == track) { | 
|  | return i; | 
|  | } | 
|  | } | 
|  |  | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Read the subcode and put it int last_sony_subcode for future use. | 
|  | */ | 
|  | static int | 
|  | read_subcode(void) | 
|  | { | 
|  | Byte cmd = SONY535_REQUEST_SUB_Q_DATA; | 
|  | Byte status[2]; | 
|  | int  dsc_status; | 
|  |  | 
|  | if (check_drive_status() != 0) | 
|  | return -EIO; | 
|  |  | 
|  | if ((dsc_status = do_sony_cmd(&cmd, 1, status, (Byte *) last_sony_subcode, | 
|  | sizeof(struct s535_sony_subcode), 1)) != 0) { | 
|  | printk(CDU535_MESSAGE_NAME " error 0x%.2x, %d (read_subcode)\n", | 
|  | status[0], dsc_status); | 
|  | return -EIO; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Get the subchannel info like the CDROMSUBCHNL command wants to see it.  If | 
|  | * the drive is playing, the subchannel needs to be read (since it would be | 
|  | * changing).  If the drive is paused or completed, the subcode information has | 
|  | * already been stored, just use that.  The ioctl call wants things in decimal | 
|  | * (not BCD), so all the conversions are done. | 
|  | */ | 
|  | static int | 
|  | sony_get_subchnl_info(void __user *arg) | 
|  | { | 
|  | struct cdrom_subchnl schi; | 
|  |  | 
|  | /* Get attention stuff */ | 
|  | if (check_drive_status() != 0) | 
|  | return -EIO; | 
|  |  | 
|  | sony_get_toc(); | 
|  | if (!sony_toc_read) { | 
|  | return -EIO; | 
|  | } | 
|  | if (copy_from_user(&schi, arg, sizeof schi)) | 
|  | return -EFAULT; | 
|  |  | 
|  | switch (sony_audio_status) { | 
|  | case CDROM_AUDIO_PLAY: | 
|  | if (read_subcode() < 0) { | 
|  | return -EIO; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case CDROM_AUDIO_PAUSED: | 
|  | case CDROM_AUDIO_COMPLETED: | 
|  | break; | 
|  |  | 
|  | case CDROM_AUDIO_NO_STATUS: | 
|  | schi.cdsc_audiostatus = sony_audio_status; | 
|  | if (copy_to_user(arg, &schi, sizeof schi)) | 
|  | return -EFAULT; | 
|  | return 0; | 
|  | break; | 
|  |  | 
|  | case CDROM_AUDIO_INVALID: | 
|  | case CDROM_AUDIO_ERROR: | 
|  | default: | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | schi.cdsc_audiostatus = sony_audio_status; | 
|  | schi.cdsc_adr = last_sony_subcode->address; | 
|  | schi.cdsc_ctrl = last_sony_subcode->control; | 
|  | schi.cdsc_trk = bcd_to_int(last_sony_subcode->track_num); | 
|  | schi.cdsc_ind = bcd_to_int(last_sony_subcode->index_num); | 
|  | if (schi.cdsc_format == CDROM_MSF) { | 
|  | schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode->abs_msf[0]); | 
|  | schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode->abs_msf[1]); | 
|  | schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode->abs_msf[2]); | 
|  |  | 
|  | schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode->rel_msf[0]); | 
|  | schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode->rel_msf[1]); | 
|  | schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode->rel_msf[2]); | 
|  | } else if (schi.cdsc_format == CDROM_LBA) { | 
|  | schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode->abs_msf); | 
|  | schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode->rel_msf); | 
|  | } | 
|  | return copy_to_user(arg, &schi, sizeof schi) ? -EFAULT : 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * The big ugly ioctl handler. | 
|  | */ | 
|  | static int | 
|  | cdu_ioctl(struct inode *inode, | 
|  | struct file *file, | 
|  | unsigned int cmd, | 
|  | unsigned long arg) | 
|  | { | 
|  | Byte status[2]; | 
|  | Byte cmd_buff[10], params[10]; | 
|  | int  i; | 
|  | int  dsc_status; | 
|  | void __user *argp = (void __user *)arg; | 
|  |  | 
|  | if (check_drive_status() != 0) | 
|  | return -EIO; | 
|  |  | 
|  | switch (cmd) { | 
|  | case CDROMSTART:			/* Spin up the drive */ | 
|  | if (spin_up_drive(status) < 0) { | 
|  | printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMSTART)\n", | 
|  | status[0]); | 
|  | return -EIO; | 
|  | } | 
|  | return 0; | 
|  | break; | 
|  |  | 
|  | case CDROMSTOP:			/* Spin down the drive */ | 
|  | cmd_buff[0] = SONY535_HOLD; | 
|  | do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); | 
|  |  | 
|  | /* | 
|  | * Spin the drive down, ignoring the error if the disk was | 
|  | * already not spinning. | 
|  | */ | 
|  | sony_audio_status = CDROM_AUDIO_NO_STATUS; | 
|  | cmd_buff[0] = SONY535_SPIN_DOWN; | 
|  | dsc_status = do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); | 
|  | if (((dsc_status < 0) && (dsc_status != BAD_STATUS)) || | 
|  | ((status[0] & ~(SONY535_STATUS1_NOT_SPINNING)) != 0)) { | 
|  | printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMSTOP)\n", | 
|  | status[0]); | 
|  | return -EIO; | 
|  | } | 
|  | return 0; | 
|  | break; | 
|  |  | 
|  | case CDROMPAUSE:			/* Pause the drive */ | 
|  | cmd_buff[0] = SONY535_HOLD;		/* CDU-31 driver uses AUDIO_STOP, not pause */ | 
|  | if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0) { | 
|  | printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPAUSE)\n", | 
|  | status[0]); | 
|  | return -EIO; | 
|  | } | 
|  | /* Get the current position and save it for resuming */ | 
|  | if (read_subcode() < 0) { | 
|  | return -EIO; | 
|  | } | 
|  | cur_pos_msf[0] = last_sony_subcode->abs_msf[0]; | 
|  | cur_pos_msf[1] = last_sony_subcode->abs_msf[1]; | 
|  | cur_pos_msf[2] = last_sony_subcode->abs_msf[2]; | 
|  | sony_audio_status = CDROM_AUDIO_PAUSED; | 
|  | return 0; | 
|  | break; | 
|  |  | 
|  | case CDROMRESUME:			/* Start the drive after being paused */ | 
|  | set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status); | 
|  |  | 
|  | if (sony_audio_status != CDROM_AUDIO_PAUSED) { | 
|  | return -EINVAL; | 
|  | } | 
|  | spin_up_drive(status); | 
|  |  | 
|  | /* Start the drive at the saved position. */ | 
|  | cmd_buff[0] = SONY535_PLAY_AUDIO; | 
|  | cmd_buff[1] = 0;		/* play back starting at this address */ | 
|  | cmd_buff[2] = cur_pos_msf[0]; | 
|  | cmd_buff[3] = cur_pos_msf[1]; | 
|  | cmd_buff[4] = cur_pos_msf[2]; | 
|  | cmd_buff[5] = SONY535_PLAY_AUDIO; | 
|  | cmd_buff[6] = 2;		/* set ending address */ | 
|  | cmd_buff[7] = final_pos_msf[0]; | 
|  | cmd_buff[8] = final_pos_msf[1]; | 
|  | cmd_buff[9] = final_pos_msf[2]; | 
|  | if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) || | 
|  | (do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) { | 
|  | printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMRESUME)\n", | 
|  | status[0]); | 
|  | return -EIO; | 
|  | } | 
|  | sony_audio_status = CDROM_AUDIO_PLAY; | 
|  | return 0; | 
|  | break; | 
|  |  | 
|  | case CDROMPLAYMSF:			/* Play starting at the given MSF address. */ | 
|  | if (copy_from_user(params, argp, 6)) | 
|  | return -EFAULT; | 
|  | spin_up_drive(status); | 
|  | set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status); | 
|  |  | 
|  | /* The parameters are given in int, must be converted */ | 
|  | for (i = 0; i < 3; i++) { | 
|  | cmd_buff[2 + i] = int_to_bcd(params[i]); | 
|  | cmd_buff[7 + i] = int_to_bcd(params[i + 3]); | 
|  | } | 
|  | cmd_buff[0] = SONY535_PLAY_AUDIO; | 
|  | cmd_buff[1] = 0;		/* play back starting at this address */ | 
|  | /* cmd_buff[2-4] are filled in for loop above */ | 
|  | cmd_buff[5] = SONY535_PLAY_AUDIO; | 
|  | cmd_buff[6] = 2;		/* set ending address */ | 
|  | /* cmd_buff[7-9] are filled in for loop above */ | 
|  | if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) || | 
|  | (do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) { | 
|  | printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPLAYMSF)\n", | 
|  | status[0]); | 
|  | return -EIO; | 
|  | } | 
|  | /* Save the final position for pauses and resumes */ | 
|  | final_pos_msf[0] = cmd_buff[7]; | 
|  | final_pos_msf[1] = cmd_buff[8]; | 
|  | final_pos_msf[2] = cmd_buff[9]; | 
|  | sony_audio_status = CDROM_AUDIO_PLAY; | 
|  | return 0; | 
|  | break; | 
|  |  | 
|  | case CDROMREADTOCHDR:		/* Read the table of contents header */ | 
|  | { | 
|  | struct cdrom_tochdr __user *hdr = argp; | 
|  | struct cdrom_tochdr loc_hdr; | 
|  |  | 
|  | sony_get_toc(); | 
|  | if (!sony_toc_read) | 
|  | return -EIO; | 
|  | loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num); | 
|  | loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num); | 
|  | if (copy_to_user(hdr, &loc_hdr, sizeof *hdr)) | 
|  | return -EFAULT; | 
|  | } | 
|  | return 0; | 
|  | break; | 
|  |  | 
|  | case CDROMREADTOCENTRY:	/* Read a given table of contents entry */ | 
|  | { | 
|  | struct cdrom_tocentry __user *entry = argp; | 
|  | struct cdrom_tocentry loc_entry; | 
|  | int  track_idx; | 
|  | Byte *msf_val = NULL; | 
|  |  | 
|  | sony_get_toc(); | 
|  | if (!sony_toc_read) { | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | if (copy_from_user(&loc_entry, entry, sizeof loc_entry)) | 
|  | return -EFAULT; | 
|  |  | 
|  | /* Lead out is handled separately since it is special. */ | 
|  | if (loc_entry.cdte_track == CDROM_LEADOUT) { | 
|  | loc_entry.cdte_adr = 0 /*sony_toc->address2 */ ; | 
|  | loc_entry.cdte_ctrl = sony_toc->control2; | 
|  | msf_val = sony_toc->lead_out_start_msf; | 
|  | } else { | 
|  | track_idx = find_track(int_to_bcd(loc_entry.cdte_track)); | 
|  | if (track_idx < 0) | 
|  | return -EINVAL; | 
|  | loc_entry.cdte_adr = 0 /*sony_toc->tracks[track_idx].address */ ; | 
|  | loc_entry.cdte_ctrl = sony_toc->tracks[track_idx].control; | 
|  | msf_val = sony_toc->tracks[track_idx].track_start_msf; | 
|  | } | 
|  |  | 
|  | /* Logical buffer address or MSF format requested? */ | 
|  | if (loc_entry.cdte_format == CDROM_LBA) { | 
|  | loc_entry.cdte_addr.lba = msf_to_log(msf_val); | 
|  | } else if (loc_entry.cdte_format == CDROM_MSF) { | 
|  | loc_entry.cdte_addr.msf.minute = bcd_to_int(*msf_val); | 
|  | loc_entry.cdte_addr.msf.second = bcd_to_int(*(msf_val + 1)); | 
|  | loc_entry.cdte_addr.msf.frame = bcd_to_int(*(msf_val + 2)); | 
|  | } | 
|  | if (copy_to_user(entry, &loc_entry, sizeof *entry)) | 
|  | return -EFAULT; | 
|  | } | 
|  | return 0; | 
|  | break; | 
|  |  | 
|  | case CDROMPLAYTRKIND:		/* Play a track.  This currently ignores index. */ | 
|  | { | 
|  | struct cdrom_ti ti; | 
|  | int track_idx; | 
|  |  | 
|  | sony_get_toc(); | 
|  | if (!sony_toc_read) | 
|  | return -EIO; | 
|  |  | 
|  | if (copy_from_user(&ti, argp, sizeof ti)) | 
|  | return -EFAULT; | 
|  | if ((ti.cdti_trk0 < sony_toc->first_track_num) | 
|  | || (sony_toc->last_track_num < ti.cdti_trk0) | 
|  | || (ti.cdti_trk1 < ti.cdti_trk0)) { | 
|  | return -EINVAL; | 
|  | } | 
|  | track_idx = find_track(int_to_bcd(ti.cdti_trk0)); | 
|  | if (track_idx < 0) | 
|  | return -EINVAL; | 
|  | params[1] = sony_toc->tracks[track_idx].track_start_msf[0]; | 
|  | params[2] = sony_toc->tracks[track_idx].track_start_msf[1]; | 
|  | params[3] = sony_toc->tracks[track_idx].track_start_msf[2]; | 
|  | /* | 
|  | * If we want to stop after the last track, use the lead-out | 
|  | * MSF to do that. | 
|  | */ | 
|  | if (bcd_to_int(sony_toc->last_track_num) <= ti.cdti_trk1) { | 
|  | log_to_msf(msf_to_log(sony_toc->lead_out_start_msf) - 1, | 
|  | &(params[4])); | 
|  | } else { | 
|  | track_idx = find_track(int_to_bcd(ti.cdti_trk1 + 1)); | 
|  | if (track_idx < 0) | 
|  | return -EINVAL; | 
|  | log_to_msf(msf_to_log(sony_toc->tracks[track_idx].track_start_msf) - 1, | 
|  | &(params[4])); | 
|  | } | 
|  | params[0] = 0x03; | 
|  |  | 
|  | spin_up_drive(status); | 
|  |  | 
|  | set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status); | 
|  |  | 
|  | /* Start the drive at the saved position. */ | 
|  | cmd_buff[0] = SONY535_PLAY_AUDIO; | 
|  | cmd_buff[1] = 0;	/* play back starting at this address */ | 
|  | cmd_buff[2] = params[1]; | 
|  | cmd_buff[3] = params[2]; | 
|  | cmd_buff[4] = params[3]; | 
|  | cmd_buff[5] = SONY535_PLAY_AUDIO; | 
|  | cmd_buff[6] = 2;	/* set ending address */ | 
|  | cmd_buff[7] = params[4]; | 
|  | cmd_buff[8] = params[5]; | 
|  | cmd_buff[9] = params[6]; | 
|  | if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) || | 
|  | (do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) { | 
|  | printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPLAYTRKIND)\n", | 
|  | status[0]); | 
|  | printk("... Params: %x %x %x %x %x %x %x\n", | 
|  | params[0], params[1], params[2], | 
|  | params[3], params[4], params[5], params[6]); | 
|  | return -EIO; | 
|  | } | 
|  | /* Save the final position for pauses and resumes */ | 
|  | final_pos_msf[0] = params[4]; | 
|  | final_pos_msf[1] = params[5]; | 
|  | final_pos_msf[2] = params[6]; | 
|  | sony_audio_status = CDROM_AUDIO_PLAY; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | case CDROMSUBCHNL:			/* Get subchannel info */ | 
|  | return sony_get_subchnl_info(argp); | 
|  |  | 
|  | case CDROMVOLCTRL:			/* Volume control.  What volume does this change, anyway? */ | 
|  | { | 
|  | struct cdrom_volctrl volctrl; | 
|  |  | 
|  | if (copy_from_user(&volctrl, argp, sizeof volctrl)) | 
|  | return -EFAULT; | 
|  | cmd_buff[0] = SONY535_SET_VOLUME; | 
|  | cmd_buff[1] = volctrl.channel0; | 
|  | cmd_buff[2] = volctrl.channel1; | 
|  | if (do_sony_cmd(cmd_buff, 3, status, NULL, 0, 0) != 0) { | 
|  | printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMVOLCTRL)\n", | 
|  | status[0]); | 
|  | return -EIO; | 
|  | } | 
|  | } | 
|  | return 0; | 
|  |  | 
|  | case CDROMEJECT:			/* Eject the drive */ | 
|  | cmd_buff[0] = SONY535_STOP; | 
|  | do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); | 
|  | cmd_buff[0] = SONY535_SPIN_DOWN; | 
|  | do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); | 
|  |  | 
|  | sony_audio_status = CDROM_AUDIO_INVALID; | 
|  | cmd_buff[0] = SONY535_EJECT_CADDY; | 
|  | if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0) { | 
|  | printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMEJECT)\n", | 
|  | status[0]); | 
|  | return -EIO; | 
|  | } | 
|  | return 0; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | return -EINVAL; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Open the drive for operations.  Spin the drive up and read the table of | 
|  | * contents if these have not already been done. | 
|  | */ | 
|  | static int | 
|  | cdu_open(struct inode *inode, | 
|  | struct file *filp) | 
|  | { | 
|  | Byte status[2], cmd_buff[2]; | 
|  |  | 
|  | if (sony_inuse) | 
|  | return -EBUSY; | 
|  | if (check_drive_status() != 0) | 
|  | return -EIO; | 
|  | sony_inuse = 1; | 
|  |  | 
|  | if (spin_up_drive(status) != 0) { | 
|  | printk(CDU535_MESSAGE_NAME " error 0x%.2x (cdu_open, spin up)\n", | 
|  | status[0]); | 
|  | sony_inuse = 0; | 
|  | return -EIO; | 
|  | } | 
|  | sony_get_toc(); | 
|  | if (!sony_toc_read) { | 
|  | cmd_buff[0] = SONY535_SPIN_DOWN; | 
|  | do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); | 
|  | sony_inuse = 0; | 
|  | return -EIO; | 
|  | } | 
|  | check_disk_change(inode->i_bdev); | 
|  | sony_usage++; | 
|  |  | 
|  | #ifdef LOCK_DOORS | 
|  | /* disable the eject button while mounted */ | 
|  | cmd_buff[0] = SONY535_DISABLE_EJECT_BUTTON; | 
|  | do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); | 
|  | #endif | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Close the drive.  Spin it down if no task is using it.  The spin | 
|  | * down will fail if playing audio, so audio play is OK. | 
|  | */ | 
|  | static int | 
|  | cdu_release(struct inode *inode, | 
|  | struct file *filp) | 
|  | { | 
|  | Byte status[2], cmd_no; | 
|  |  | 
|  | sony_inuse = 0; | 
|  |  | 
|  | if (0 < sony_usage) { | 
|  | sony_usage--; | 
|  | } | 
|  | if (sony_usage == 0) { | 
|  | check_drive_status(); | 
|  |  | 
|  | if (sony_audio_status != CDROM_AUDIO_PLAY) { | 
|  | cmd_no = SONY535_SPIN_DOWN; | 
|  | do_sony_cmd(&cmd_no, 1, status, NULL, 0, 0); | 
|  | } | 
|  | #ifdef LOCK_DOORS | 
|  | /* enable the eject button after umount */ | 
|  | cmd_no = SONY535_ENABLE_EJECT_BUTTON; | 
|  | do_sony_cmd(&cmd_no, 1, status, NULL, 0, 0); | 
|  | #endif | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static struct block_device_operations cdu_fops = | 
|  | { | 
|  | .owner		= THIS_MODULE, | 
|  | .open		= cdu_open, | 
|  | .release	= cdu_release, | 
|  | .ioctl		= cdu_ioctl, | 
|  | .media_changed	= cdu535_check_media_change, | 
|  | }; | 
|  |  | 
|  | static struct gendisk *cdu_disk; | 
|  |  | 
|  | /* | 
|  | * Initialize the driver. | 
|  | */ | 
|  | static int __init sony535_init(void) | 
|  | { | 
|  | struct s535_sony_drive_config drive_config; | 
|  | Byte cmd_buff[3]; | 
|  | Byte ret_buff[2]; | 
|  | Byte status[2]; | 
|  | unsigned long snap; | 
|  | int  got_result = 0; | 
|  | int  tmp_irq; | 
|  | int  i; | 
|  | int err; | 
|  |  | 
|  | /* Setting the base I/O address to 0 will disable it. */ | 
|  | if ((sony535_cd_base_io == 0xffff)||(sony535_cd_base_io == 0)) | 
|  | return 0; | 
|  |  | 
|  | /* Set up all the register locations */ | 
|  | result_reg = sony535_cd_base_io; | 
|  | command_reg = sony535_cd_base_io; | 
|  | data_reg = sony535_cd_base_io + 1; | 
|  | read_status_reg = sony535_cd_base_io + 2; | 
|  | select_unit_reg = sony535_cd_base_io + 3; | 
|  |  | 
|  | #ifndef USE_IRQ | 
|  | sony535_irq_used = 0;	/* polling only until this is ready... */ | 
|  | #endif | 
|  | /* we need to poll until things get initialized */ | 
|  | tmp_irq = sony535_irq_used; | 
|  | sony535_irq_used = 0; | 
|  |  | 
|  | #if DEBUG > 0 | 
|  | printk(KERN_INFO CDU535_MESSAGE_NAME ": probing base address %03X\n", | 
|  | sony535_cd_base_io); | 
|  | #endif | 
|  | /* look for the CD-ROM, follows the procedure in the DOS driver */ | 
|  | inb(select_unit_reg); | 
|  | /* wait for 40 18 Hz ticks (reverse-engineered from DOS driver) */ | 
|  | schedule_timeout_interruptible((HZ+17)*40/18); | 
|  | inb(result_reg); | 
|  |  | 
|  | outb(0, read_status_reg);	/* does a reset? */ | 
|  | snap = jiffies; | 
|  | while (jiffies-snap < SONY_JIFFIES_TIMEOUT) { | 
|  | select_unit(0); | 
|  | if (inb(result_reg) != 0xff) { | 
|  | got_result = 1; | 
|  | break; | 
|  | } | 
|  | sony_sleep(); | 
|  | } | 
|  |  | 
|  | if (!got_result || check_drive_status() == TIME_OUT) | 
|  | goto Enodev; | 
|  |  | 
|  | /* CD-ROM drive responded --  get the drive configuration */ | 
|  | cmd_buff[0] = SONY535_INQUIRY; | 
|  | if (do_sony_cmd(cmd_buff, 1, status, (Byte *)&drive_config, 28, 1) != 0) | 
|  | goto Enodev; | 
|  |  | 
|  | /* was able to get the configuration, | 
|  | * set drive mode as rest of init | 
|  | */ | 
|  | #if DEBUG > 0 | 
|  | /* 0x50 == CADDY_NOT_INSERTED | NOT_SPINNING */ | 
|  | if ( (status[0] & 0x7f) != 0 && (status[0] & 0x7f) != 0x50 ) | 
|  | printk(CDU535_MESSAGE_NAME | 
|  | "Inquiry command returned status = 0x%x\n", status[0]); | 
|  | #endif | 
|  | /* now ready to use interrupts, if available */ | 
|  | sony535_irq_used = tmp_irq; | 
|  |  | 
|  | /* A negative sony535_irq_used will attempt an autoirq. */ | 
|  | if (sony535_irq_used < 0) { | 
|  | unsigned long irq_mask, delay; | 
|  |  | 
|  | irq_mask = probe_irq_on(); | 
|  | enable_interrupts(); | 
|  | outb(0, read_status_reg);	/* does a reset? */ | 
|  | delay = jiffies + HZ/10; | 
|  | while (time_before(jiffies, delay)) ; | 
|  |  | 
|  | sony535_irq_used = probe_irq_off(irq_mask); | 
|  | disable_interrupts(); | 
|  | } | 
|  | if (sony535_irq_used > 0) { | 
|  | if (request_irq(sony535_irq_used, cdu535_interrupt, | 
|  | IRQF_DISABLED, CDU535_HANDLE, NULL)) { | 
|  | printk("Unable to grab IRQ%d for the " CDU535_MESSAGE_NAME | 
|  | " driver; polling instead.\n", sony535_irq_used); | 
|  | sony535_irq_used = 0; | 
|  | } | 
|  | } | 
|  | cmd_buff[0] = SONY535_SET_DRIVE_MODE; | 
|  | cmd_buff[1] = 0x0;	/* default audio */ | 
|  | if (do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1) != 0) | 
|  | goto Enodev_irq; | 
|  |  | 
|  | /* set the drive mode successful, we are set! */ | 
|  | sony_buffer_size = SONY535_BUFFER_SIZE; | 
|  | sony_buffer_sectors = sony_buffer_size / CDU535_BLOCK_SIZE; | 
|  |  | 
|  | printk(KERN_INFO CDU535_MESSAGE_NAME " I/F CDROM : %8.8s %16.16s %4.4s", | 
|  | drive_config.vendor_id, | 
|  | drive_config.product_id, | 
|  | drive_config.product_rev_level); | 
|  | printk("  base address %03X, ", sony535_cd_base_io); | 
|  | if (tmp_irq > 0) | 
|  | printk("IRQ%d, ", tmp_irq); | 
|  | printk("using %d byte buffer\n", sony_buffer_size); | 
|  |  | 
|  | if (register_blkdev(MAJOR_NR, CDU535_HANDLE)) { | 
|  | err = -EIO; | 
|  | goto out1; | 
|  | } | 
|  | sonycd535_queue = blk_init_queue(do_cdu535_request, &sonycd535_lock); | 
|  | if (!sonycd535_queue) { | 
|  | err = -ENOMEM; | 
|  | goto out1a; | 
|  | } | 
|  |  | 
|  | blk_queue_hardsect_size(sonycd535_queue, CDU535_BLOCK_SIZE); | 
|  | sony_toc = kmalloc(sizeof(struct s535_sony_toc), GFP_KERNEL); | 
|  | err = -ENOMEM; | 
|  | if (!sony_toc) | 
|  | goto out2; | 
|  | last_sony_subcode = kmalloc(sizeof(struct s535_sony_subcode), GFP_KERNEL); | 
|  | if (!last_sony_subcode) | 
|  | goto out3; | 
|  | sony_buffer = kmalloc(sizeof(Byte *) * sony_buffer_sectors, GFP_KERNEL); | 
|  | if (!sony_buffer) | 
|  | goto out4; | 
|  | for (i = 0; i < sony_buffer_sectors; i++) { | 
|  | sony_buffer[i] = kmalloc(CDU535_BLOCK_SIZE, GFP_KERNEL); | 
|  | if (!sony_buffer[i]) { | 
|  | while (--i>=0) | 
|  | kfree(sony_buffer[i]); | 
|  | goto out5; | 
|  | } | 
|  | } | 
|  | initialized = 1; | 
|  |  | 
|  | cdu_disk = alloc_disk(1); | 
|  | if (!cdu_disk) | 
|  | goto out6; | 
|  | cdu_disk->major = MAJOR_NR; | 
|  | cdu_disk->first_minor = 0; | 
|  | cdu_disk->fops = &cdu_fops; | 
|  | sprintf(cdu_disk->disk_name, "cdu"); | 
|  |  | 
|  | if (!request_region(sony535_cd_base_io, 4, CDU535_HANDLE)) { | 
|  | printk(KERN_WARNING"sonycd535: Unable to request region 0x%x\n", | 
|  | sony535_cd_base_io); | 
|  | goto out7; | 
|  | } | 
|  | cdu_disk->queue = sonycd535_queue; | 
|  | add_disk(cdu_disk); | 
|  | return 0; | 
|  |  | 
|  | out7: | 
|  | put_disk(cdu_disk); | 
|  | out6: | 
|  | for (i = 0; i < sony_buffer_sectors; i++) | 
|  | kfree(sony_buffer[i]); | 
|  | out5: | 
|  | kfree(sony_buffer); | 
|  | out4: | 
|  | kfree(last_sony_subcode); | 
|  | out3: | 
|  | kfree(sony_toc); | 
|  | out2: | 
|  | blk_cleanup_queue(sonycd535_queue); | 
|  | out1a: | 
|  | unregister_blkdev(MAJOR_NR, CDU535_HANDLE); | 
|  | out1: | 
|  | if (sony535_irq_used) | 
|  | free_irq(sony535_irq_used, NULL); | 
|  | return err; | 
|  | Enodev_irq: | 
|  | if (sony535_irq_used) | 
|  | free_irq(sony535_irq_used, NULL); | 
|  | Enodev: | 
|  | printk("Did not find a " CDU535_MESSAGE_NAME " drive\n"); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | #ifndef MODULE | 
|  |  | 
|  | /* | 
|  | * accept "kernel command line" parameters | 
|  | * (added by emoenke@gwdg.de) | 
|  | * | 
|  | * use: tell LILO: | 
|  | *                 sonycd535=0x320 | 
|  | * | 
|  | * the address value has to be the existing CDROM port address. | 
|  | */ | 
|  | static int __init | 
|  | sonycd535_setup(char *strings) | 
|  | { | 
|  | int ints[3]; | 
|  | (void)get_options(strings, ARRAY_SIZE(ints), ints); | 
|  | /* if IRQ change and default io base desired, | 
|  | * then call with io base of 0 | 
|  | */ | 
|  | if (ints[0] > 0) | 
|  | if (ints[1] != 0) | 
|  | sony535_cd_base_io = ints[1]; | 
|  | if (ints[0] > 1) | 
|  | sony535_irq_used = ints[2]; | 
|  | if ((strings != NULL) && (*strings != '\0')) | 
|  | printk(CDU535_MESSAGE_NAME | 
|  | ": Warning: Unknown interface type: %s\n", strings); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | __setup("sonycd535=", sonycd535_setup); | 
|  |  | 
|  | #endif /* MODULE */ | 
|  |  | 
|  | static void __exit | 
|  | sony535_exit(void) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | release_region(sony535_cd_base_io, 4); | 
|  | for (i = 0; i < sony_buffer_sectors; i++) | 
|  | kfree(sony_buffer[i]); | 
|  | kfree(sony_buffer); | 
|  | kfree(last_sony_subcode); | 
|  | kfree(sony_toc); | 
|  | del_gendisk(cdu_disk); | 
|  | put_disk(cdu_disk); | 
|  | blk_cleanup_queue(sonycd535_queue); | 
|  | if (unregister_blkdev(MAJOR_NR, CDU535_HANDLE) == -EINVAL) | 
|  | printk("Uh oh, couldn't unregister " CDU535_HANDLE "\n"); | 
|  | else | 
|  | printk(KERN_INFO CDU535_HANDLE " module released\n"); | 
|  | } | 
|  |  | 
|  | module_init(sony535_init); | 
|  | module_exit(sony535_exit); | 
|  |  | 
|  |  | 
|  | MODULE_LICENSE("GPL"); | 
|  | MODULE_ALIAS_BLOCKDEV_MAJOR(CDU535_CDROM_MAJOR); |