mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-16 08:44:21 +08:00
cd354f1ae7
After Al Viro (finally) succeeded in removing the sched.h #include in module.h recently, it makes sense again to remove other superfluous sched.h includes. There are quite a lot of files which include it but don't actually need anything defined in there. Presumably these includes were once needed for macros that used to live in sched.h, but moved to other header files in the course of cleaning it up. To ease the pain, this time I did not fiddle with any header files and only removed #includes from .c-files, which tend to cause less trouble. Compile tested against 2.6.20-rc2 and 2.6.20-rc2-mm2 (with offsets) on alpha, arm, i386, ia64, mips, powerpc, and x86_64 with allnoconfig, defconfig, allmodconfig, and allyesconfig as well as a few randconfigs on x86_64 and all configs in arch/arm/configs on arm. I also checked that no new warnings were introduced by the patch (actually, some warnings are removed that were emitted by unnecessarily included header files). Signed-off-by: Tim Schmielau <tim@physik3.uni-rostock.de> Acked-by: Russell King <rmk+kernel@arm.linux.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1816 lines
42 KiB
C
1816 lines
42 KiB
C
/* -- sjcd.c
|
|
*
|
|
* Sanyo CD-ROM device driver implementation, Version 1.6
|
|
* Copyright (C) 1995 Vadim V. Model
|
|
*
|
|
* model@cecmow.enet.dec.com
|
|
* vadim@rbrf.ru
|
|
* vadim@ipsun.ras.ru
|
|
*
|
|
*
|
|
* This driver is based on pre-works by Eberhard Moenkeberg (emoenke@gwdg.de);
|
|
* it was developed under use of mcd.c from Martin Harriss, with help of
|
|
* Eric van der Maarel (H.T.M.v.d.Maarel@marin.nl).
|
|
*
|
|
* It is planned to include these routines into sbpcd.c later - to make
|
|
* a "mixed use" on one cable possible for all kinds of drives which use
|
|
* the SoundBlaster/Panasonic style CDROM interface. But today, the
|
|
* ability to install directly from CDROM is more important than flexibility.
|
|
*
|
|
* 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.
|
|
*
|
|
* History:
|
|
* 1.1 First public release with kernel version 1.3.7.
|
|
* Written by Vadim Model.
|
|
* 1.2 Added detection and configuration of cdrom interface
|
|
* on ISP16 soundcard.
|
|
* Allow for command line options: sjcd=<io_base>,<irq>,<dma>
|
|
* 1.3 Some minor changes to README.sjcd.
|
|
* 1.4 MSS Sound support!! Listen to a CD through the speakers.
|
|
* 1.5 Module support and bugfixes.
|
|
* Tray locking.
|
|
* 1.6 Removed ISP16 code from this driver.
|
|
* Allow only to set io base address on command line: sjcd=<io_base>
|
|
* Changes to Documentation/cdrom/sjcd
|
|
* Added cleanup after any error in the initialisation.
|
|
* 1.7 Added code to set the sector size tables to prevent the bug present in
|
|
* the previous version of this driver. Coded added by Anthony Barbachan
|
|
* from bugfix tip originally suggested by Alan Cox.
|
|
*
|
|
* 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>
|
|
*/
|
|
|
|
#define SJCD_VERSION_MAJOR 1
|
|
#define SJCD_VERSION_MINOR 7
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/cdrom.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/string.h>
|
|
#include <linux/major.h>
|
|
#include <linux/init.h>
|
|
|
|
#include <asm/system.h>
|
|
#include <asm/io.h>
|
|
#include <asm/uaccess.h>
|
|
#include <linux/blkdev.h>
|
|
#include "sjcd.h"
|
|
|
|
static int sjcd_present = 0;
|
|
static struct request_queue *sjcd_queue;
|
|
|
|
#define MAJOR_NR SANYO_CDROM_MAJOR
|
|
#define QUEUE (sjcd_queue)
|
|
#define CURRENT elv_next_request(sjcd_queue)
|
|
|
|
#define SJCD_BUF_SIZ 32 /* cdr-h94a has internal 64K buffer */
|
|
|
|
/*
|
|
* buffer for block size conversion
|
|
*/
|
|
static char sjcd_buf[2048 * SJCD_BUF_SIZ];
|
|
static volatile int sjcd_buf_bn[SJCD_BUF_SIZ], sjcd_next_bn;
|
|
static volatile int sjcd_buf_in, sjcd_buf_out = -1;
|
|
|
|
/*
|
|
* Status.
|
|
*/
|
|
static unsigned short sjcd_status_valid = 0;
|
|
static unsigned short sjcd_door_closed;
|
|
static unsigned short sjcd_door_was_open;
|
|
static unsigned short sjcd_media_is_available;
|
|
static unsigned short sjcd_media_is_changed;
|
|
static unsigned short sjcd_toc_uptodate = 0;
|
|
static unsigned short sjcd_command_failed;
|
|
static volatile unsigned char sjcd_completion_status = 0;
|
|
static volatile unsigned char sjcd_completion_error = 0;
|
|
static unsigned short sjcd_command_is_in_progress = 0;
|
|
static unsigned short sjcd_error_reported = 0;
|
|
static DEFINE_SPINLOCK(sjcd_lock);
|
|
|
|
static int sjcd_open_count;
|
|
|
|
static int sjcd_audio_status;
|
|
static struct sjcd_play_msf sjcd_playing;
|
|
|
|
static int sjcd_base = SJCD_BASE_ADDR;
|
|
|
|
module_param(sjcd_base, int, 0);
|
|
|
|
static DECLARE_WAIT_QUEUE_HEAD(sjcd_waitq);
|
|
|
|
/*
|
|
* Data transfer.
|
|
*/
|
|
static volatile unsigned short sjcd_transfer_is_active = 0;
|
|
|
|
enum sjcd_transfer_state {
|
|
SJCD_S_IDLE = 0,
|
|
SJCD_S_START = 1,
|
|
SJCD_S_MODE = 2,
|
|
SJCD_S_READ = 3,
|
|
SJCD_S_DATA = 4,
|
|
SJCD_S_STOP = 5,
|
|
SJCD_S_STOPPING = 6
|
|
};
|
|
static enum sjcd_transfer_state sjcd_transfer_state = SJCD_S_IDLE;
|
|
static long sjcd_transfer_timeout = 0;
|
|
static int sjcd_read_count = 0;
|
|
static unsigned char sjcd_mode = 0;
|
|
|
|
#define SJCD_READ_TIMEOUT 5000
|
|
|
|
#if defined( SJCD_GATHER_STAT )
|
|
/*
|
|
* Statistic.
|
|
*/
|
|
static struct sjcd_stat statistic;
|
|
#endif
|
|
|
|
/*
|
|
* Timer.
|
|
*/
|
|
static DEFINE_TIMER(sjcd_delay_timer, NULL, 0, 0);
|
|
|
|
#define SJCD_SET_TIMER( func, tmout ) \
|
|
( sjcd_delay_timer.expires = jiffies+tmout, \
|
|
sjcd_delay_timer.function = ( void * )func, \
|
|
add_timer( &sjcd_delay_timer ) )
|
|
|
|
#define CLEAR_TIMER del_timer( &sjcd_delay_timer )
|
|
|
|
/*
|
|
* Set up device, i.e., use command line data to set
|
|
* base address.
|
|
*/
|
|
#ifndef MODULE
|
|
static int __init sjcd_setup(char *str)
|
|
{
|
|
int ints[2];
|
|
(void) get_options(str, ARRAY_SIZE(ints), ints);
|
|
if (ints[0] > 0)
|
|
sjcd_base = ints[1];
|
|
|
|
return 1;
|
|
}
|
|
|
|
__setup("sjcd=", sjcd_setup);
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Special converters.
|
|
*/
|
|
static unsigned char bin2bcd(int bin)
|
|
{
|
|
int u, v;
|
|
|
|
u = bin % 10;
|
|
v = bin / 10;
|
|
return (u | (v << 4));
|
|
}
|
|
|
|
static int bcd2bin(unsigned char bcd)
|
|
{
|
|
return ((bcd >> 4) * 10 + (bcd & 0x0F));
|
|
}
|
|
|
|
static long msf2hsg(struct msf *mp)
|
|
{
|
|
return (bcd2bin(mp->frame) + bcd2bin(mp->sec) * 75
|
|
+ bcd2bin(mp->min) * 4500 - 150);
|
|
}
|
|
|
|
static void hsg2msf(long hsg, struct msf *msf)
|
|
{
|
|
hsg += 150;
|
|
msf->min = hsg / 4500;
|
|
hsg %= 4500;
|
|
msf->sec = hsg / 75;
|
|
msf->frame = hsg % 75;
|
|
msf->min = bin2bcd(msf->min); /* convert to BCD */
|
|
msf->sec = bin2bcd(msf->sec);
|
|
msf->frame = bin2bcd(msf->frame);
|
|
}
|
|
|
|
/*
|
|
* Send a command to cdrom. Invalidate status.
|
|
*/
|
|
static void sjcd_send_cmd(unsigned char cmd)
|
|
{
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: send_cmd( 0x%x )\n", cmd);
|
|
#endif
|
|
outb(cmd, SJCDPORT(0));
|
|
sjcd_command_is_in_progress = 1;
|
|
sjcd_status_valid = 0;
|
|
sjcd_command_failed = 0;
|
|
}
|
|
|
|
/*
|
|
* Send a command with one arg to cdrom. Invalidate status.
|
|
*/
|
|
static void sjcd_send_1_cmd(unsigned char cmd, unsigned char a)
|
|
{
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: send_1_cmd( 0x%x, 0x%x )\n", cmd, a);
|
|
#endif
|
|
outb(cmd, SJCDPORT(0));
|
|
outb(a, SJCDPORT(0));
|
|
sjcd_command_is_in_progress = 1;
|
|
sjcd_status_valid = 0;
|
|
sjcd_command_failed = 0;
|
|
}
|
|
|
|
/*
|
|
* Send a command with four args to cdrom. Invalidate status.
|
|
*/
|
|
static void sjcd_send_4_cmd(unsigned char cmd, unsigned char a,
|
|
unsigned char b, unsigned char c,
|
|
unsigned char d)
|
|
{
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: send_4_cmd( 0x%x )\n", cmd);
|
|
#endif
|
|
outb(cmd, SJCDPORT(0));
|
|
outb(a, SJCDPORT(0));
|
|
outb(b, SJCDPORT(0));
|
|
outb(c, SJCDPORT(0));
|
|
outb(d, SJCDPORT(0));
|
|
sjcd_command_is_in_progress = 1;
|
|
sjcd_status_valid = 0;
|
|
sjcd_command_failed = 0;
|
|
}
|
|
|
|
/*
|
|
* Send a play or read command to cdrom. Invalidate Status.
|
|
*/
|
|
static void sjcd_send_6_cmd(unsigned char cmd, struct sjcd_play_msf *pms)
|
|
{
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: send_long_cmd( 0x%x )\n", cmd);
|
|
#endif
|
|
outb(cmd, SJCDPORT(0));
|
|
outb(pms->start.min, SJCDPORT(0));
|
|
outb(pms->start.sec, SJCDPORT(0));
|
|
outb(pms->start.frame, SJCDPORT(0));
|
|
outb(pms->end.min, SJCDPORT(0));
|
|
outb(pms->end.sec, SJCDPORT(0));
|
|
outb(pms->end.frame, SJCDPORT(0));
|
|
sjcd_command_is_in_progress = 1;
|
|
sjcd_status_valid = 0;
|
|
sjcd_command_failed = 0;
|
|
}
|
|
|
|
/*
|
|
* Get a value from the data port. Should not block, so we use a little
|
|
* wait for a while. Returns 0 if OK.
|
|
*/
|
|
static int sjcd_load_response(void *buf, int len)
|
|
{
|
|
unsigned char *resp = (unsigned char *) buf;
|
|
|
|
for (; len; --len) {
|
|
int i;
|
|
for (i = 200;
|
|
i-- && !SJCD_STATUS_AVAILABLE(inb(SJCDPORT(1))););
|
|
if (i > 0)
|
|
*resp++ = (unsigned char) inb(SJCDPORT(0));
|
|
else
|
|
break;
|
|
}
|
|
return (len);
|
|
}
|
|
|
|
/*
|
|
* Load and parse command completion status (drive info byte and maybe error).
|
|
* Sorry, no error classification yet.
|
|
*/
|
|
static void sjcd_load_status(void)
|
|
{
|
|
sjcd_media_is_changed = 0;
|
|
sjcd_completion_error = 0;
|
|
sjcd_completion_status = inb(SJCDPORT(0));
|
|
if (sjcd_completion_status & SST_DOOR_OPENED) {
|
|
sjcd_door_closed = sjcd_media_is_available = 0;
|
|
} else {
|
|
sjcd_door_closed = 1;
|
|
if (sjcd_completion_status & SST_MEDIA_CHANGED)
|
|
sjcd_media_is_available = sjcd_media_is_changed =
|
|
1;
|
|
else if (sjcd_completion_status & 0x0F) {
|
|
/*
|
|
* OK, we seem to catch an error ...
|
|
*/
|
|
while (!SJCD_STATUS_AVAILABLE(inb(SJCDPORT(1))));
|
|
sjcd_completion_error = inb(SJCDPORT(0));
|
|
if ((sjcd_completion_status & 0x08) &&
|
|
(sjcd_completion_error & 0x40))
|
|
sjcd_media_is_available = 0;
|
|
else
|
|
sjcd_command_failed = 1;
|
|
} else
|
|
sjcd_media_is_available = 1;
|
|
}
|
|
/*
|
|
* Ok, status loaded successfully.
|
|
*/
|
|
sjcd_status_valid = 1, sjcd_error_reported = 0;
|
|
sjcd_command_is_in_progress = 0;
|
|
|
|
/*
|
|
* If the disk is changed, the TOC is not valid.
|
|
*/
|
|
if (sjcd_media_is_changed)
|
|
sjcd_toc_uptodate = 0;
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: status %02x.%02x loaded.\n",
|
|
(int) sjcd_completion_status, (int) sjcd_completion_error);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Read status from cdrom. Check to see if the status is available.
|
|
*/
|
|
static int sjcd_check_status(void)
|
|
{
|
|
/*
|
|
* Try to load the response from cdrom into buffer.
|
|
*/
|
|
if (SJCD_STATUS_AVAILABLE(inb(SJCDPORT(1)))) {
|
|
sjcd_load_status();
|
|
return (1);
|
|
} else {
|
|
/*
|
|
* No status is available.
|
|
*/
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This is just timeout counter, and nothing more. Surprised ? :-)
|
|
*/
|
|
static volatile long sjcd_status_timeout;
|
|
|
|
/*
|
|
* We need about 10 seconds to wait. The longest command takes about 5 seconds
|
|
* to probe the disk (usually after tray closed or drive reset). Other values
|
|
* should be thought of for other commands.
|
|
*/
|
|
#define SJCD_WAIT_FOR_STATUS_TIMEOUT 1000
|
|
|
|
static void sjcd_status_timer(void)
|
|
{
|
|
if (sjcd_check_status()) {
|
|
/*
|
|
* The command completed and status is loaded, stop waiting.
|
|
*/
|
|
wake_up(&sjcd_waitq);
|
|
} else if (--sjcd_status_timeout <= 0) {
|
|
/*
|
|
* We are timed out.
|
|
*/
|
|
wake_up(&sjcd_waitq);
|
|
} else {
|
|
/*
|
|
* We have still some time to wait. Try again.
|
|
*/
|
|
SJCD_SET_TIMER(sjcd_status_timer, 1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Wait for status for 10 sec approx. Returns non-positive when timed out.
|
|
* Should not be used while reading data CDs.
|
|
*/
|
|
static int sjcd_wait_for_status(void)
|
|
{
|
|
sjcd_status_timeout = SJCD_WAIT_FOR_STATUS_TIMEOUT;
|
|
SJCD_SET_TIMER(sjcd_status_timer, 1);
|
|
sleep_on(&sjcd_waitq);
|
|
#if defined( SJCD_DIAGNOSTIC ) || defined ( SJCD_TRACE )
|
|
if (sjcd_status_timeout <= 0)
|
|
printk("SJCD: Error Wait For Status.\n");
|
|
#endif
|
|
return (sjcd_status_timeout);
|
|
}
|
|
|
|
static int sjcd_receive_status(void)
|
|
{
|
|
int i;
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: receive_status\n");
|
|
#endif
|
|
/*
|
|
* Wait a bit for status available.
|
|
*/
|
|
for (i = 200; i-- && (sjcd_check_status() == 0););
|
|
if (i < 0) {
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: long wait for status\n");
|
|
#endif
|
|
if (sjcd_wait_for_status() <= 0)
|
|
printk("SJCD: Timeout when read status.\n");
|
|
else
|
|
i = 0;
|
|
}
|
|
return (i);
|
|
}
|
|
|
|
/*
|
|
* Load the status. Issue get status command and wait for status available.
|
|
*/
|
|
static void sjcd_get_status(void)
|
|
{
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: get_status\n");
|
|
#endif
|
|
sjcd_send_cmd(SCMD_GET_STATUS);
|
|
sjcd_receive_status();
|
|
}
|
|
|
|
/*
|
|
* Check the drive if the disk is changed. Should be revised.
|
|
*/
|
|
static int sjcd_disk_change(struct gendisk *disk)
|
|
{
|
|
#if 0
|
|
printk("SJCD: sjcd_disk_change(%s)\n", disk->disk_name);
|
|
#endif
|
|
if (!sjcd_command_is_in_progress)
|
|
sjcd_get_status();
|
|
return (sjcd_status_valid ? sjcd_media_is_changed : 0);
|
|
}
|
|
|
|
/*
|
|
* Read the table of contents (TOC) and TOC header if necessary.
|
|
* We assume that the drive contains no more than 99 toc entries.
|
|
*/
|
|
static struct sjcd_hw_disk_info sjcd_table_of_contents[SJCD_MAX_TRACKS];
|
|
static unsigned char sjcd_first_track_no, sjcd_last_track_no;
|
|
#define sjcd_disk_length sjcd_table_of_contents[0].un.track_msf
|
|
|
|
static int sjcd_update_toc(void)
|
|
{
|
|
struct sjcd_hw_disk_info info;
|
|
int i;
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: update toc:\n");
|
|
#endif
|
|
/*
|
|
* check to see if we need to do anything
|
|
*/
|
|
if (sjcd_toc_uptodate)
|
|
return (0);
|
|
|
|
/*
|
|
* Get the TOC start information.
|
|
*/
|
|
sjcd_send_1_cmd(SCMD_GET_DISK_INFO, SCMD_GET_1_TRACK);
|
|
sjcd_receive_status();
|
|
|
|
if (!sjcd_status_valid) {
|
|
printk("SJCD: cannot load status.\n");
|
|
return (-1);
|
|
}
|
|
|
|
if (!sjcd_media_is_available) {
|
|
printk("SJCD: no disk in drive\n");
|
|
return (-1);
|
|
}
|
|
|
|
if (!sjcd_command_failed) {
|
|
if (sjcd_load_response(&info, sizeof(info)) != 0) {
|
|
printk
|
|
("SJCD: cannot load response about TOC start.\n");
|
|
return (-1);
|
|
}
|
|
sjcd_first_track_no = bcd2bin(info.un.track_no);
|
|
} else {
|
|
printk("SJCD: get first failed\n");
|
|
return (-1);
|
|
}
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: TOC start 0x%02x ", sjcd_first_track_no);
|
|
#endif
|
|
/*
|
|
* Get the TOC finish information.
|
|
*/
|
|
sjcd_send_1_cmd(SCMD_GET_DISK_INFO, SCMD_GET_L_TRACK);
|
|
sjcd_receive_status();
|
|
|
|
if (!sjcd_status_valid) {
|
|
printk("SJCD: cannot load status.\n");
|
|
return (-1);
|
|
}
|
|
|
|
if (!sjcd_media_is_available) {
|
|
printk("SJCD: no disk in drive\n");
|
|
return (-1);
|
|
}
|
|
|
|
if (!sjcd_command_failed) {
|
|
if (sjcd_load_response(&info, sizeof(info)) != 0) {
|
|
printk
|
|
("SJCD: cannot load response about TOC finish.\n");
|
|
return (-1);
|
|
}
|
|
sjcd_last_track_no = bcd2bin(info.un.track_no);
|
|
} else {
|
|
printk("SJCD: get last failed\n");
|
|
return (-1);
|
|
}
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: TOC finish 0x%02x ", sjcd_last_track_no);
|
|
#endif
|
|
for (i = sjcd_first_track_no; i <= sjcd_last_track_no; i++) {
|
|
/*
|
|
* Get the first track information.
|
|
*/
|
|
sjcd_send_1_cmd(SCMD_GET_DISK_INFO, bin2bcd(i));
|
|
sjcd_receive_status();
|
|
|
|
if (!sjcd_status_valid) {
|
|
printk("SJCD: cannot load status.\n");
|
|
return (-1);
|
|
}
|
|
|
|
if (!sjcd_media_is_available) {
|
|
printk("SJCD: no disk in drive\n");
|
|
return (-1);
|
|
}
|
|
|
|
if (!sjcd_command_failed) {
|
|
if (sjcd_load_response(&sjcd_table_of_contents[i],
|
|
sizeof(struct
|
|
sjcd_hw_disk_info))
|
|
!= 0) {
|
|
printk
|
|
("SJCD: cannot load info for %d track\n",
|
|
i);
|
|
return (-1);
|
|
}
|
|
} else {
|
|
printk("SJCD: get info %d failed\n", i);
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get the disk length info.
|
|
*/
|
|
sjcd_send_1_cmd(SCMD_GET_DISK_INFO, SCMD_GET_D_SIZE);
|
|
sjcd_receive_status();
|
|
|
|
if (!sjcd_status_valid) {
|
|
printk("SJCD: cannot load status.\n");
|
|
return (-1);
|
|
}
|
|
|
|
if (!sjcd_media_is_available) {
|
|
printk("SJCD: no disk in drive\n");
|
|
return (-1);
|
|
}
|
|
|
|
if (!sjcd_command_failed) {
|
|
if (sjcd_load_response(&info, sizeof(info)) != 0) {
|
|
printk
|
|
("SJCD: cannot load response about disk size.\n");
|
|
return (-1);
|
|
}
|
|
sjcd_disk_length.min = info.un.track_msf.min;
|
|
sjcd_disk_length.sec = info.un.track_msf.sec;
|
|
sjcd_disk_length.frame = info.un.track_msf.frame;
|
|
} else {
|
|
printk("SJCD: get size failed\n");
|
|
return (1);
|
|
}
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: (%02x:%02x.%02x)\n", sjcd_disk_length.min,
|
|
sjcd_disk_length.sec, sjcd_disk_length.frame);
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Load subchannel information.
|
|
*/
|
|
static int sjcd_get_q_info(struct sjcd_hw_qinfo *qp)
|
|
{
|
|
int s;
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: load sub q\n");
|
|
#endif
|
|
sjcd_send_cmd(SCMD_GET_QINFO);
|
|
s = sjcd_receive_status();
|
|
if (s < 0 || sjcd_command_failed || !sjcd_status_valid) {
|
|
sjcd_send_cmd(0xF2);
|
|
s = sjcd_receive_status();
|
|
if (s < 0 || sjcd_command_failed || !sjcd_status_valid)
|
|
return (-1);
|
|
sjcd_send_cmd(SCMD_GET_QINFO);
|
|
s = sjcd_receive_status();
|
|
if (s < 0 || sjcd_command_failed || !sjcd_status_valid)
|
|
return (-1);
|
|
}
|
|
if (sjcd_media_is_available)
|
|
if (sjcd_load_response(qp, sizeof(*qp)) == 0)
|
|
return (0);
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Start playing from the specified position.
|
|
*/
|
|
static int sjcd_play(struct sjcd_play_msf *mp)
|
|
{
|
|
struct sjcd_play_msf msf;
|
|
|
|
/*
|
|
* Turn the device to play mode.
|
|
*/
|
|
sjcd_send_1_cmd(SCMD_SET_MODE, SCMD_MODE_PLAY);
|
|
if (sjcd_receive_status() < 0)
|
|
return (-1);
|
|
|
|
/*
|
|
* Seek to the starting point.
|
|
*/
|
|
msf.start = mp->start;
|
|
msf.end.min = msf.end.sec = msf.end.frame = 0x00;
|
|
sjcd_send_6_cmd(SCMD_SEEK, &msf);
|
|
if (sjcd_receive_status() < 0)
|
|
return (-1);
|
|
|
|
/*
|
|
* Start playing.
|
|
*/
|
|
sjcd_send_6_cmd(SCMD_PLAY, mp);
|
|
return (sjcd_receive_status());
|
|
}
|
|
|
|
/*
|
|
* Tray control functions.
|
|
*/
|
|
static int sjcd_tray_close(void)
|
|
{
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: tray_close\n");
|
|
#endif
|
|
sjcd_send_cmd(SCMD_CLOSE_TRAY);
|
|
return (sjcd_receive_status());
|
|
}
|
|
|
|
static int sjcd_tray_lock(void)
|
|
{
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: tray_lock\n");
|
|
#endif
|
|
sjcd_send_cmd(SCMD_LOCK_TRAY);
|
|
return (sjcd_receive_status());
|
|
}
|
|
|
|
static int sjcd_tray_unlock(void)
|
|
{
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: tray_unlock\n");
|
|
#endif
|
|
sjcd_send_cmd(SCMD_UNLOCK_TRAY);
|
|
return (sjcd_receive_status());
|
|
}
|
|
|
|
static int sjcd_tray_open(void)
|
|
{
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: tray_open\n");
|
|
#endif
|
|
sjcd_send_cmd(SCMD_EJECT_TRAY);
|
|
return (sjcd_receive_status());
|
|
}
|
|
|
|
/*
|
|
* Do some user commands.
|
|
*/
|
|
static int sjcd_ioctl(struct inode *ip, struct file *fp,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
void __user *argp = (void __user *)arg;
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD:ioctl\n");
|
|
#endif
|
|
|
|
sjcd_get_status();
|
|
if (!sjcd_status_valid)
|
|
return (-EIO);
|
|
if (sjcd_update_toc() < 0)
|
|
return (-EIO);
|
|
|
|
switch (cmd) {
|
|
case CDROMSTART:{
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: ioctl: start\n");
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
case CDROMSTOP:{
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: ioctl: stop\n");
|
|
#endif
|
|
sjcd_send_cmd(SCMD_PAUSE);
|
|
(void) sjcd_receive_status();
|
|
sjcd_audio_status = CDROM_AUDIO_NO_STATUS;
|
|
return (0);
|
|
}
|
|
|
|
case CDROMPAUSE:{
|
|
struct sjcd_hw_qinfo q_info;
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: ioctl: pause\n");
|
|
#endif
|
|
if (sjcd_audio_status == CDROM_AUDIO_PLAY) {
|
|
sjcd_send_cmd(SCMD_PAUSE);
|
|
(void) sjcd_receive_status();
|
|
if (sjcd_get_q_info(&q_info) < 0) {
|
|
sjcd_audio_status =
|
|
CDROM_AUDIO_NO_STATUS;
|
|
} else {
|
|
sjcd_audio_status =
|
|
CDROM_AUDIO_PAUSED;
|
|
sjcd_playing.start = q_info.abs;
|
|
}
|
|
return (0);
|
|
} else
|
|
return (-EINVAL);
|
|
}
|
|
|
|
case CDROMRESUME:{
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: ioctl: resume\n");
|
|
#endif
|
|
if (sjcd_audio_status == CDROM_AUDIO_PAUSED) {
|
|
/*
|
|
* continue play starting at saved location
|
|
*/
|
|
if (sjcd_play(&sjcd_playing) < 0) {
|
|
sjcd_audio_status =
|
|
CDROM_AUDIO_ERROR;
|
|
return (-EIO);
|
|
} else {
|
|
sjcd_audio_status =
|
|
CDROM_AUDIO_PLAY;
|
|
return (0);
|
|
}
|
|
} else
|
|
return (-EINVAL);
|
|
}
|
|
|
|
case CDROMPLAYTRKIND:{
|
|
struct cdrom_ti ti;
|
|
int s = -EFAULT;
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: ioctl: playtrkind\n");
|
|
#endif
|
|
if (!copy_from_user(&ti, argp, sizeof(ti))) {
|
|
s = 0;
|
|
if (ti.cdti_trk0 < sjcd_first_track_no)
|
|
return (-EINVAL);
|
|
if (ti.cdti_trk1 > sjcd_last_track_no)
|
|
ti.cdti_trk1 = sjcd_last_track_no;
|
|
if (ti.cdti_trk0 > ti.cdti_trk1)
|
|
return (-EINVAL);
|
|
|
|
sjcd_playing.start =
|
|
sjcd_table_of_contents[ti.cdti_trk0].
|
|
un.track_msf;
|
|
sjcd_playing.end =
|
|
(ti.cdti_trk1 <
|
|
sjcd_last_track_no) ?
|
|
sjcd_table_of_contents[ti.cdti_trk1 +
|
|
1].un.
|
|
track_msf : sjcd_table_of_contents[0].
|
|
un.track_msf;
|
|
|
|
if (sjcd_play(&sjcd_playing) < 0) {
|
|
sjcd_audio_status =
|
|
CDROM_AUDIO_ERROR;
|
|
return (-EIO);
|
|
} else
|
|
sjcd_audio_status =
|
|
CDROM_AUDIO_PLAY;
|
|
}
|
|
return (s);
|
|
}
|
|
|
|
case CDROMPLAYMSF:{
|
|
struct cdrom_msf sjcd_msf;
|
|
int s;
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: ioctl: playmsf\n");
|
|
#endif
|
|
if ((s =
|
|
access_ok(VERIFY_READ, argp, sizeof(sjcd_msf))
|
|
? 0 : -EFAULT) == 0) {
|
|
if (sjcd_audio_status == CDROM_AUDIO_PLAY) {
|
|
sjcd_send_cmd(SCMD_PAUSE);
|
|
(void) sjcd_receive_status();
|
|
sjcd_audio_status =
|
|
CDROM_AUDIO_NO_STATUS;
|
|
}
|
|
|
|
if (copy_from_user(&sjcd_msf, argp,
|
|
sizeof(sjcd_msf)))
|
|
return (-EFAULT);
|
|
|
|
sjcd_playing.start.min =
|
|
bin2bcd(sjcd_msf.cdmsf_min0);
|
|
sjcd_playing.start.sec =
|
|
bin2bcd(sjcd_msf.cdmsf_sec0);
|
|
sjcd_playing.start.frame =
|
|
bin2bcd(sjcd_msf.cdmsf_frame0);
|
|
sjcd_playing.end.min =
|
|
bin2bcd(sjcd_msf.cdmsf_min1);
|
|
sjcd_playing.end.sec =
|
|
bin2bcd(sjcd_msf.cdmsf_sec1);
|
|
sjcd_playing.end.frame =
|
|
bin2bcd(sjcd_msf.cdmsf_frame1);
|
|
|
|
if (sjcd_play(&sjcd_playing) < 0) {
|
|
sjcd_audio_status =
|
|
CDROM_AUDIO_ERROR;
|
|
return (-EIO);
|
|
} else
|
|
sjcd_audio_status =
|
|
CDROM_AUDIO_PLAY;
|
|
}
|
|
return (s);
|
|
}
|
|
|
|
case CDROMREADTOCHDR:{
|
|
struct cdrom_tochdr toc_header;
|
|
#if defined (SJCD_TRACE )
|
|
printk("SJCD: ioctl: readtocheader\n");
|
|
#endif
|
|
toc_header.cdth_trk0 = sjcd_first_track_no;
|
|
toc_header.cdth_trk1 = sjcd_last_track_no;
|
|
if (copy_to_user(argp, &toc_header,
|
|
sizeof(toc_header)))
|
|
return -EFAULT;
|
|
return 0;
|
|
}
|
|
|
|
case CDROMREADTOCENTRY:{
|
|
struct cdrom_tocentry toc_entry;
|
|
int s;
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: ioctl: readtocentry\n");
|
|
#endif
|
|
if ((s =
|
|
access_ok(VERIFY_WRITE, argp, sizeof(toc_entry))
|
|
? 0 : -EFAULT) == 0) {
|
|
struct sjcd_hw_disk_info *tp;
|
|
|
|
if (copy_from_user(&toc_entry, argp,
|
|
sizeof(toc_entry)))
|
|
return (-EFAULT);
|
|
if (toc_entry.cdte_track == CDROM_LEADOUT)
|
|
tp = &sjcd_table_of_contents[0];
|
|
else if (toc_entry.cdte_track <
|
|
sjcd_first_track_no)
|
|
return (-EINVAL);
|
|
else if (toc_entry.cdte_track >
|
|
sjcd_last_track_no)
|
|
return (-EINVAL);
|
|
else
|
|
tp = &sjcd_table_of_contents
|
|
[toc_entry.cdte_track];
|
|
|
|
toc_entry.cdte_adr =
|
|
tp->track_control & 0x0F;
|
|
toc_entry.cdte_ctrl =
|
|
tp->track_control >> 4;
|
|
|
|
switch (toc_entry.cdte_format) {
|
|
case CDROM_LBA:
|
|
toc_entry.cdte_addr.lba =
|
|
msf2hsg(&(tp->un.track_msf));
|
|
break;
|
|
case CDROM_MSF:
|
|
toc_entry.cdte_addr.msf.minute =
|
|
bcd2bin(tp->un.track_msf.min);
|
|
toc_entry.cdte_addr.msf.second =
|
|
bcd2bin(tp->un.track_msf.sec);
|
|
toc_entry.cdte_addr.msf.frame =
|
|
bcd2bin(tp->un.track_msf.
|
|
frame);
|
|
break;
|
|
default:
|
|
return (-EINVAL);
|
|
}
|
|
if (copy_to_user(argp, &toc_entry,
|
|
sizeof(toc_entry)))
|
|
s = -EFAULT;
|
|
}
|
|
return (s);
|
|
}
|
|
|
|
case CDROMSUBCHNL:{
|
|
struct cdrom_subchnl subchnl;
|
|
int s;
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: ioctl: subchnl\n");
|
|
#endif
|
|
if ((s =
|
|
access_ok(VERIFY_WRITE, argp, sizeof(subchnl))
|
|
? 0 : -EFAULT) == 0) {
|
|
struct sjcd_hw_qinfo q_info;
|
|
|
|
if (copy_from_user(&subchnl, argp,
|
|
sizeof(subchnl)))
|
|
return (-EFAULT);
|
|
|
|
if (sjcd_get_q_info(&q_info) < 0)
|
|
return (-EIO);
|
|
|
|
subchnl.cdsc_audiostatus =
|
|
sjcd_audio_status;
|
|
subchnl.cdsc_adr =
|
|
q_info.track_control & 0x0F;
|
|
subchnl.cdsc_ctrl =
|
|
q_info.track_control >> 4;
|
|
subchnl.cdsc_trk =
|
|
bcd2bin(q_info.track_no);
|
|
subchnl.cdsc_ind = bcd2bin(q_info.x);
|
|
|
|
switch (subchnl.cdsc_format) {
|
|
case CDROM_LBA:
|
|
subchnl.cdsc_absaddr.lba =
|
|
msf2hsg(&(q_info.abs));
|
|
subchnl.cdsc_reladdr.lba =
|
|
msf2hsg(&(q_info.rel));
|
|
break;
|
|
case CDROM_MSF:
|
|
subchnl.cdsc_absaddr.msf.minute =
|
|
bcd2bin(q_info.abs.min);
|
|
subchnl.cdsc_absaddr.msf.second =
|
|
bcd2bin(q_info.abs.sec);
|
|
subchnl.cdsc_absaddr.msf.frame =
|
|
bcd2bin(q_info.abs.frame);
|
|
subchnl.cdsc_reladdr.msf.minute =
|
|
bcd2bin(q_info.rel.min);
|
|
subchnl.cdsc_reladdr.msf.second =
|
|
bcd2bin(q_info.rel.sec);
|
|
subchnl.cdsc_reladdr.msf.frame =
|
|
bcd2bin(q_info.rel.frame);
|
|
break;
|
|
default:
|
|
return (-EINVAL);
|
|
}
|
|
if (copy_to_user(argp, &subchnl,
|
|
sizeof(subchnl)))
|
|
s = -EFAULT;
|
|
}
|
|
return (s);
|
|
}
|
|
|
|
case CDROMVOLCTRL:{
|
|
struct cdrom_volctrl vol_ctrl;
|
|
int s;
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: ioctl: volctrl\n");
|
|
#endif
|
|
if ((s =
|
|
access_ok(VERIFY_READ, argp, sizeof(vol_ctrl))
|
|
? 0 : -EFAULT) == 0) {
|
|
unsigned char dummy[4];
|
|
|
|
if (copy_from_user(&vol_ctrl, argp,
|
|
sizeof(vol_ctrl)))
|
|
return (-EFAULT);
|
|
sjcd_send_4_cmd(SCMD_SET_VOLUME,
|
|
vol_ctrl.channel0, 0xFF,
|
|
vol_ctrl.channel1, 0xFF);
|
|
if (sjcd_receive_status() < 0)
|
|
return (-EIO);
|
|
(void) sjcd_load_response(dummy, 4);
|
|
}
|
|
return (s);
|
|
}
|
|
|
|
case CDROMEJECT:{
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: ioctl: eject\n");
|
|
#endif
|
|
if (!sjcd_command_is_in_progress) {
|
|
sjcd_tray_unlock();
|
|
sjcd_send_cmd(SCMD_EJECT_TRAY);
|
|
(void) sjcd_receive_status();
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
#if defined( SJCD_GATHER_STAT )
|
|
case 0xABCD:{
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: ioctl: statistic\n");
|
|
#endif
|
|
if (copy_to_user(argp, &statistic, sizeof(statistic)))
|
|
return -EFAULT;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
default:
|
|
return (-EINVAL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Invalidate internal buffers of the driver.
|
|
*/
|
|
static void sjcd_invalidate_buffers(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < SJCD_BUF_SIZ; sjcd_buf_bn[i++] = -1);
|
|
sjcd_buf_out = -1;
|
|
}
|
|
|
|
/*
|
|
* Take care of the different block sizes between cdrom and Linux.
|
|
* When Linux gets variable block sizes this will probably go away.
|
|
*/
|
|
|
|
static int current_valid(void)
|
|
{
|
|
return CURRENT &&
|
|
CURRENT->cmd == READ &&
|
|
CURRENT->sector != -1;
|
|
}
|
|
|
|
static void sjcd_transfer(void)
|
|
{
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: transfer:\n");
|
|
#endif
|
|
if (current_valid()) {
|
|
while (CURRENT->nr_sectors) {
|
|
int i, bn = CURRENT->sector / 4;
|
|
for (i = 0;
|
|
i < SJCD_BUF_SIZ && sjcd_buf_bn[i] != bn;
|
|
i++);
|
|
if (i < SJCD_BUF_SIZ) {
|
|
int offs =
|
|
(i * 4 + (CURRENT->sector & 3)) * 512;
|
|
int nr_sectors = 4 - (CURRENT->sector & 3);
|
|
if (sjcd_buf_out != i) {
|
|
sjcd_buf_out = i;
|
|
if (sjcd_buf_bn[i] != bn) {
|
|
sjcd_buf_out = -1;
|
|
continue;
|
|
}
|
|
}
|
|
if (nr_sectors > CURRENT->nr_sectors)
|
|
nr_sectors = CURRENT->nr_sectors;
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: copy out\n");
|
|
#endif
|
|
memcpy(CURRENT->buffer, sjcd_buf + offs,
|
|
nr_sectors * 512);
|
|
CURRENT->nr_sectors -= nr_sectors;
|
|
CURRENT->sector += nr_sectors;
|
|
CURRENT->buffer += nr_sectors * 512;
|
|
} else {
|
|
sjcd_buf_out = -1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: transfer: done\n");
|
|
#endif
|
|
}
|
|
|
|
static void sjcd_poll(void)
|
|
{
|
|
#if defined( SJCD_GATHER_STAT )
|
|
/*
|
|
* Update total number of ticks.
|
|
*/
|
|
statistic.ticks++;
|
|
statistic.tticks[sjcd_transfer_state]++;
|
|
#endif
|
|
|
|
ReSwitch:switch (sjcd_transfer_state) {
|
|
|
|
case SJCD_S_IDLE:{
|
|
#if defined( SJCD_GATHER_STAT )
|
|
statistic.idle_ticks++;
|
|
#endif
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD_S_IDLE\n");
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
case SJCD_S_START:{
|
|
#if defined( SJCD_GATHER_STAT )
|
|
statistic.start_ticks++;
|
|
#endif
|
|
sjcd_send_cmd(SCMD_GET_STATUS);
|
|
sjcd_transfer_state =
|
|
sjcd_mode ==
|
|
SCMD_MODE_COOKED ? SJCD_S_READ : SJCD_S_MODE;
|
|
sjcd_transfer_timeout = 500;
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD_S_START: goto SJCD_S_%s mode\n",
|
|
sjcd_transfer_state ==
|
|
SJCD_S_READ ? "READ" : "MODE");
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
case SJCD_S_MODE:{
|
|
if (sjcd_check_status()) {
|
|
/*
|
|
* Previous command is completed.
|
|
*/
|
|
if (!sjcd_status_valid
|
|
|| sjcd_command_failed) {
|
|
#if defined( SJCD_TRACE )
|
|
printk
|
|
("SJCD_S_MODE: pre-cmd failed: goto to SJCD_S_STOP mode\n");
|
|
#endif
|
|
sjcd_transfer_state = SJCD_S_STOP;
|
|
goto ReSwitch;
|
|
}
|
|
|
|
sjcd_mode = 0; /* unknown mode; should not be valid when failed */
|
|
sjcd_send_1_cmd(SCMD_SET_MODE,
|
|
SCMD_MODE_COOKED);
|
|
sjcd_transfer_state = SJCD_S_READ;
|
|
sjcd_transfer_timeout = 1000;
|
|
#if defined( SJCD_TRACE )
|
|
printk
|
|
("SJCD_S_MODE: goto SJCD_S_READ mode\n");
|
|
#endif
|
|
}
|
|
#if defined( SJCD_GATHER_STAT )
|
|
else
|
|
statistic.mode_ticks++;
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
case SJCD_S_READ:{
|
|
if (sjcd_status_valid ? 1 : sjcd_check_status()) {
|
|
/*
|
|
* Previous command is completed.
|
|
*/
|
|
if (!sjcd_status_valid
|
|
|| sjcd_command_failed) {
|
|
#if defined( SJCD_TRACE )
|
|
printk
|
|
("SJCD_S_READ: pre-cmd failed: goto to SJCD_S_STOP mode\n");
|
|
#endif
|
|
sjcd_transfer_state = SJCD_S_STOP;
|
|
goto ReSwitch;
|
|
}
|
|
if (!sjcd_media_is_available) {
|
|
#if defined( SJCD_TRACE )
|
|
printk
|
|
("SJCD_S_READ: no disk: goto to SJCD_S_STOP mode\n");
|
|
#endif
|
|
sjcd_transfer_state = SJCD_S_STOP;
|
|
goto ReSwitch;
|
|
}
|
|
if (sjcd_mode != SCMD_MODE_COOKED) {
|
|
/*
|
|
* We seem to come from set mode. So discard one byte of result.
|
|
*/
|
|
if (sjcd_load_response
|
|
(&sjcd_mode, 1) != 0) {
|
|
#if defined( SJCD_TRACE )
|
|
printk
|
|
("SJCD_S_READ: load failed: goto to SJCD_S_STOP mode\n");
|
|
#endif
|
|
sjcd_transfer_state =
|
|
SJCD_S_STOP;
|
|
goto ReSwitch;
|
|
}
|
|
if (sjcd_mode != SCMD_MODE_COOKED) {
|
|
#if defined( SJCD_TRACE )
|
|
printk
|
|
("SJCD_S_READ: mode failed: goto to SJCD_S_STOP mode\n");
|
|
#endif
|
|
sjcd_transfer_state =
|
|
SJCD_S_STOP;
|
|
goto ReSwitch;
|
|
}
|
|
}
|
|
|
|
if (current_valid()) {
|
|
struct sjcd_play_msf msf;
|
|
|
|
sjcd_next_bn = CURRENT->sector / 4;
|
|
hsg2msf(sjcd_next_bn, &msf.start);
|
|
msf.end.min = 0;
|
|
msf.end.sec = 0;
|
|
msf.end.frame = sjcd_read_count =
|
|
SJCD_BUF_SIZ;
|
|
#if defined( SJCD_TRACE )
|
|
printk
|
|
("SJCD: ---reading msf-address %x:%x:%x %x:%x:%x\n",
|
|
msf.start.min, msf.start.sec,
|
|
msf.start.frame, msf.end.min,
|
|
msf.end.sec, msf.end.frame);
|
|
printk
|
|
("sjcd_next_bn:%x buf_in:%x buf_out:%x buf_bn:%x\n",
|
|
sjcd_next_bn, sjcd_buf_in,
|
|
sjcd_buf_out,
|
|
sjcd_buf_bn[sjcd_buf_in]);
|
|
#endif
|
|
sjcd_send_6_cmd(SCMD_DATA_READ,
|
|
&msf);
|
|
sjcd_transfer_state = SJCD_S_DATA;
|
|
sjcd_transfer_timeout = 500;
|
|
#if defined( SJCD_TRACE )
|
|
printk
|
|
("SJCD_S_READ: go to SJCD_S_DATA mode\n");
|
|
#endif
|
|
} else {
|
|
#if defined( SJCD_TRACE )
|
|
printk
|
|
("SJCD_S_READ: nothing to read: go to SJCD_S_STOP mode\n");
|
|
#endif
|
|
sjcd_transfer_state = SJCD_S_STOP;
|
|
goto ReSwitch;
|
|
}
|
|
}
|
|
#if defined( SJCD_GATHER_STAT )
|
|
else
|
|
statistic.read_ticks++;
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
case SJCD_S_DATA:{
|
|
unsigned char stat;
|
|
|
|
sjcd_s_data:stat =
|
|
inb(SJCDPORT
|
|
(1));
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD_S_DATA: status = 0x%02x\n", stat);
|
|
#endif
|
|
if (SJCD_STATUS_AVAILABLE(stat)) {
|
|
/*
|
|
* No data is waiting for us in the drive buffer. Status of operation
|
|
* completion is available. Read and parse it.
|
|
*/
|
|
sjcd_load_status();
|
|
|
|
if (!sjcd_status_valid
|
|
|| sjcd_command_failed) {
|
|
#if defined( SJCD_TRACE )
|
|
printk
|
|
("SJCD: read block %d failed, maybe audio disk? Giving up\n",
|
|
sjcd_next_bn);
|
|
#endif
|
|
if (current_valid())
|
|
end_request(CURRENT, 0);
|
|
#if defined( SJCD_TRACE )
|
|
printk
|
|
("SJCD_S_DATA: pre-cmd failed: go to SJCD_S_STOP mode\n");
|
|
#endif
|
|
sjcd_transfer_state = SJCD_S_STOP;
|
|
goto ReSwitch;
|
|
}
|
|
|
|
if (!sjcd_media_is_available) {
|
|
printk
|
|
("SJCD_S_DATA: no disk: go to SJCD_S_STOP mode\n");
|
|
sjcd_transfer_state = SJCD_S_STOP;
|
|
goto ReSwitch;
|
|
}
|
|
|
|
sjcd_transfer_state = SJCD_S_READ;
|
|
goto ReSwitch;
|
|
} else if (SJCD_DATA_AVAILABLE(stat)) {
|
|
/*
|
|
* One frame is read into device buffer. We must copy it to our memory.
|
|
* Otherwise cdrom hangs up. Check to see if we have something to copy
|
|
* to.
|
|
*/
|
|
if (!current_valid()
|
|
&& sjcd_buf_in == sjcd_buf_out) {
|
|
#if defined( SJCD_TRACE )
|
|
printk
|
|
("SJCD_S_DATA: nothing to read: go to SJCD_S_STOP mode\n");
|
|
printk
|
|
(" ... all the date would be discarded\n");
|
|
#endif
|
|
sjcd_transfer_state = SJCD_S_STOP;
|
|
goto ReSwitch;
|
|
}
|
|
|
|
/*
|
|
* Everything seems to be OK. Just read the frame and recalculate
|
|
* indices.
|
|
*/
|
|
sjcd_buf_bn[sjcd_buf_in] = -1; /* ??? */
|
|
insb(SJCDPORT(2),
|
|
sjcd_buf + 2048 * sjcd_buf_in, 2048);
|
|
#if defined( SJCD_TRACE )
|
|
printk
|
|
("SJCD_S_DATA: next_bn=%d, buf_in=%d, buf_out=%d, buf_bn=%d\n",
|
|
sjcd_next_bn, sjcd_buf_in,
|
|
sjcd_buf_out,
|
|
sjcd_buf_bn[sjcd_buf_in]);
|
|
#endif
|
|
sjcd_buf_bn[sjcd_buf_in] = sjcd_next_bn++;
|
|
if (sjcd_buf_out == -1)
|
|
sjcd_buf_out = sjcd_buf_in;
|
|
if (++sjcd_buf_in == SJCD_BUF_SIZ)
|
|
sjcd_buf_in = 0;
|
|
|
|
/*
|
|
* Only one frame is ready at time. So we should turn over to wait for
|
|
* another frame. If we need that, of course.
|
|
*/
|
|
if (--sjcd_read_count == 0) {
|
|
/*
|
|
* OK, request seems to be precessed. Continue transferring...
|
|
*/
|
|
if (!sjcd_transfer_is_active) {
|
|
while (current_valid()) {
|
|
/*
|
|
* Continue transferring.
|
|
*/
|
|
sjcd_transfer();
|
|
if (CURRENT->
|
|
nr_sectors ==
|
|
0)
|
|
end_request
|
|
(CURRENT, 1);
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
if (current_valid() &&
|
|
(CURRENT->sector / 4 <
|
|
sjcd_next_bn
|
|
|| CURRENT->sector / 4 >
|
|
sjcd_next_bn +
|
|
SJCD_BUF_SIZ)) {
|
|
#if defined( SJCD_TRACE )
|
|
printk
|
|
("SJCD_S_DATA: can't read: go to SJCD_S_STOP mode\n");
|
|
#endif
|
|
sjcd_transfer_state =
|
|
SJCD_S_STOP;
|
|
goto ReSwitch;
|
|
}
|
|
}
|
|
/*
|
|
* Now we should turn around rather than wait for while.
|
|
*/
|
|
goto sjcd_s_data;
|
|
}
|
|
#if defined( SJCD_GATHER_STAT )
|
|
else
|
|
statistic.data_ticks++;
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
case SJCD_S_STOP:{
|
|
sjcd_read_count = 0;
|
|
sjcd_send_cmd(SCMD_STOP);
|
|
sjcd_transfer_state = SJCD_S_STOPPING;
|
|
sjcd_transfer_timeout = 500;
|
|
#if defined( SJCD_GATHER_STAT )
|
|
statistic.stop_ticks++;
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
case SJCD_S_STOPPING:{
|
|
unsigned char stat;
|
|
|
|
stat = inb(SJCDPORT(1));
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD_S_STOP: status = 0x%02x\n", stat);
|
|
#endif
|
|
if (SJCD_DATA_AVAILABLE(stat)) {
|
|
int i;
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD_S_STOP: discard data\n");
|
|
#endif
|
|
/*
|
|
* Discard all the data from the pipe. Foolish method.
|
|
*/
|
|
for (i = 2048; i--;
|
|
(void) inb(SJCDPORT(2)));
|
|
sjcd_transfer_timeout = 500;
|
|
} else if (SJCD_STATUS_AVAILABLE(stat)) {
|
|
sjcd_load_status();
|
|
if (sjcd_status_valid
|
|
&& sjcd_media_is_changed) {
|
|
sjcd_toc_uptodate = 0;
|
|
sjcd_invalidate_buffers();
|
|
}
|
|
if (current_valid()) {
|
|
if (sjcd_status_valid)
|
|
sjcd_transfer_state =
|
|
SJCD_S_READ;
|
|
else
|
|
sjcd_transfer_state =
|
|
SJCD_S_START;
|
|
} else
|
|
sjcd_transfer_state = SJCD_S_IDLE;
|
|
goto ReSwitch;
|
|
}
|
|
#if defined( SJCD_GATHER_STAT )
|
|
else
|
|
statistic.stopping_ticks++;
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
default:
|
|
printk("SJCD: poll: invalid state %d\n",
|
|
sjcd_transfer_state);
|
|
return;
|
|
}
|
|
|
|
if (--sjcd_transfer_timeout == 0) {
|
|
printk("SJCD: timeout in state %d\n", sjcd_transfer_state);
|
|
while (current_valid())
|
|
end_request(CURRENT, 0);
|
|
sjcd_send_cmd(SCMD_STOP);
|
|
sjcd_transfer_state = SJCD_S_IDLE;
|
|
goto ReSwitch;
|
|
}
|
|
|
|
/*
|
|
* Get back in some time. 1 should be replaced with count variable to
|
|
* avoid unnecessary testings.
|
|
*/
|
|
SJCD_SET_TIMER(sjcd_poll, 1);
|
|
}
|
|
|
|
static void do_sjcd_request(request_queue_t * q)
|
|
{
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: do_sjcd_request(%ld+%ld)\n",
|
|
CURRENT->sector, CURRENT->nr_sectors);
|
|
#endif
|
|
sjcd_transfer_is_active = 1;
|
|
while (current_valid()) {
|
|
sjcd_transfer();
|
|
if (CURRENT->nr_sectors == 0)
|
|
end_request(CURRENT, 1);
|
|
else {
|
|
sjcd_buf_out = -1; /* Want to read a block not in buffer */
|
|
if (sjcd_transfer_state == SJCD_S_IDLE) {
|
|
if (!sjcd_toc_uptodate) {
|
|
if (sjcd_update_toc() < 0) {
|
|
printk
|
|
("SJCD: transfer: discard\n");
|
|
while (current_valid())
|
|
end_request(CURRENT, 0);
|
|
break;
|
|
}
|
|
}
|
|
sjcd_transfer_state = SJCD_S_START;
|
|
SJCD_SET_TIMER(sjcd_poll, HZ / 100);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
sjcd_transfer_is_active = 0;
|
|
#if defined( SJCD_TRACE )
|
|
printk
|
|
("sjcd_next_bn:%x sjcd_buf_in:%x sjcd_buf_out:%x sjcd_buf_bn:%x\n",
|
|
sjcd_next_bn, sjcd_buf_in, sjcd_buf_out,
|
|
sjcd_buf_bn[sjcd_buf_in]);
|
|
printk("do_sjcd_request ends\n");
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Open the device special file. Check disk is in.
|
|
*/
|
|
static int sjcd_open(struct inode *ip, struct file *fp)
|
|
{
|
|
/*
|
|
* Check the presence of device.
|
|
*/
|
|
if (!sjcd_present)
|
|
return (-ENXIO);
|
|
|
|
/*
|
|
* Only read operations are allowed. Really? (:-)
|
|
*/
|
|
if (fp->f_mode & 2)
|
|
return (-EROFS);
|
|
|
|
if (sjcd_open_count == 0) {
|
|
int s, sjcd_open_tries;
|
|
/* We don't know that, do we? */
|
|
/*
|
|
sjcd_audio_status = CDROM_AUDIO_NO_STATUS;
|
|
*/
|
|
sjcd_mode = 0;
|
|
sjcd_door_was_open = 0;
|
|
sjcd_transfer_state = SJCD_S_IDLE;
|
|
sjcd_invalidate_buffers();
|
|
sjcd_status_valid = 0;
|
|
|
|
/*
|
|
* Strict status checking.
|
|
*/
|
|
for (sjcd_open_tries = 4; --sjcd_open_tries;) {
|
|
if (!sjcd_status_valid)
|
|
sjcd_get_status();
|
|
if (!sjcd_status_valid) {
|
|
#if defined( SJCD_DIAGNOSTIC )
|
|
printk
|
|
("SJCD: open: timed out when check status.\n");
|
|
#endif
|
|
goto err_out;
|
|
} else if (!sjcd_media_is_available) {
|
|
#if defined( SJCD_DIAGNOSTIC )
|
|
printk("SJCD: open: no disk in drive\n");
|
|
#endif
|
|
if (!sjcd_door_closed) {
|
|
sjcd_door_was_open = 1;
|
|
#if defined( SJCD_TRACE )
|
|
printk
|
|
("SJCD: open: close the tray\n");
|
|
#endif
|
|
s = sjcd_tray_close();
|
|
if (s < 0 || !sjcd_status_valid
|
|
|| sjcd_command_failed) {
|
|
#if defined( SJCD_DIAGNOSTIC )
|
|
printk
|
|
("SJCD: open: tray close attempt failed\n");
|
|
#endif
|
|
goto err_out;
|
|
}
|
|
continue;
|
|
} else
|
|
goto err_out;
|
|
}
|
|
break;
|
|
}
|
|
s = sjcd_tray_lock();
|
|
if (s < 0 || !sjcd_status_valid || sjcd_command_failed) {
|
|
#if defined( SJCD_DIAGNOSTIC )
|
|
printk("SJCD: open: tray lock attempt failed\n");
|
|
#endif
|
|
goto err_out;
|
|
}
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: open: done\n");
|
|
#endif
|
|
}
|
|
|
|
++sjcd_open_count;
|
|
return (0);
|
|
|
|
err_out:
|
|
return (-EIO);
|
|
}
|
|
|
|
/*
|
|
* On close, we flush all sjcd blocks from the buffer cache.
|
|
*/
|
|
static int sjcd_release(struct inode *inode, struct file *file)
|
|
{
|
|
int s;
|
|
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: release\n");
|
|
#endif
|
|
if (--sjcd_open_count == 0) {
|
|
sjcd_invalidate_buffers();
|
|
s = sjcd_tray_unlock();
|
|
if (s < 0 || !sjcd_status_valid || sjcd_command_failed) {
|
|
#if defined( SJCD_DIAGNOSTIC )
|
|
printk
|
|
("SJCD: release: tray unlock attempt failed.\n");
|
|
#endif
|
|
}
|
|
if (sjcd_door_was_open) {
|
|
s = sjcd_tray_open();
|
|
if (s < 0 || !sjcd_status_valid
|
|
|| sjcd_command_failed) {
|
|
#if defined( SJCD_DIAGNOSTIC )
|
|
printk
|
|
("SJCD: release: tray unload attempt failed.\n");
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* A list of file operations allowed for this cdrom.
|
|
*/
|
|
static struct block_device_operations sjcd_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = sjcd_open,
|
|
.release = sjcd_release,
|
|
.ioctl = sjcd_ioctl,
|
|
.media_changed = sjcd_disk_change,
|
|
};
|
|
|
|
/*
|
|
* Following stuff is intended for initialization of the cdrom. It
|
|
* first looks for presence of device. If the device is present, it
|
|
* will be reset. Then read the version of the drive and load status.
|
|
* The version is two BCD-coded bytes.
|
|
*/
|
|
static struct {
|
|
unsigned char major, minor;
|
|
} sjcd_version;
|
|
|
|
static struct gendisk *sjcd_disk;
|
|
|
|
/*
|
|
* Test for presence of drive and initialize it. Called at boot time.
|
|
* Probe cdrom, find out version and status.
|
|
*/
|
|
static int __init sjcd_init(void)
|
|
{
|
|
int i;
|
|
|
|
printk(KERN_INFO
|
|
"SJCD: Sanyo CDR-H94A cdrom driver version %d.%d.\n",
|
|
SJCD_VERSION_MAJOR, SJCD_VERSION_MINOR);
|
|
|
|
#if defined( SJCD_TRACE )
|
|
printk("SJCD: sjcd=0x%x: ", sjcd_base);
|
|
#endif
|
|
|
|
if (register_blkdev(MAJOR_NR, "sjcd"))
|
|
return -EIO;
|
|
|
|
sjcd_queue = blk_init_queue(do_sjcd_request, &sjcd_lock);
|
|
if (!sjcd_queue)
|
|
goto out0;
|
|
|
|
blk_queue_hardsect_size(sjcd_queue, 2048);
|
|
|
|
sjcd_disk = alloc_disk(1);
|
|
if (!sjcd_disk) {
|
|
printk(KERN_ERR "SJCD: can't allocate disk");
|
|
goto out1;
|
|
}
|
|
sjcd_disk->major = MAJOR_NR,
|
|
sjcd_disk->first_minor = 0,
|
|
sjcd_disk->fops = &sjcd_fops,
|
|
sprintf(sjcd_disk->disk_name, "sjcd");
|
|
|
|
if (!request_region(sjcd_base, 4,"sjcd")) {
|
|
printk
|
|
("SJCD: Init failed, I/O port (%X) is already in use\n",
|
|
sjcd_base);
|
|
goto out2;
|
|
}
|
|
|
|
/*
|
|
* Check for card. Since we are booting now, we can't use standard
|
|
* wait algorithm.
|
|
*/
|
|
printk(KERN_INFO "SJCD: Resetting: ");
|
|
sjcd_send_cmd(SCMD_RESET);
|
|
for (i = 1000; i > 0 && !sjcd_status_valid; --i) {
|
|
unsigned long timer;
|
|
|
|
/*
|
|
* Wait 10ms approx.
|
|
*/
|
|
for (timer = jiffies; time_before_eq(jiffies, timer););
|
|
if ((i % 100) == 0)
|
|
printk(".");
|
|
(void) sjcd_check_status();
|
|
}
|
|
if (i == 0 || sjcd_command_failed) {
|
|
printk(" reset failed, no drive found.\n");
|
|
goto out3;
|
|
} else
|
|
printk("\n");
|
|
|
|
/*
|
|
* Get and print out cdrom version.
|
|
*/
|
|
printk(KERN_INFO "SJCD: Getting version: ");
|
|
sjcd_send_cmd(SCMD_GET_VERSION);
|
|
for (i = 1000; i > 0 && !sjcd_status_valid; --i) {
|
|
unsigned long timer;
|
|
|
|
/*
|
|
* Wait 10ms approx.
|
|
*/
|
|
for (timer = jiffies; time_before_eq(jiffies, timer););
|
|
if ((i % 100) == 0)
|
|
printk(".");
|
|
(void) sjcd_check_status();
|
|
}
|
|
if (i == 0 || sjcd_command_failed) {
|
|
printk(" get version failed, no drive found.\n");
|
|
goto out3;
|
|
}
|
|
|
|
if (sjcd_load_response(&sjcd_version, sizeof(sjcd_version)) == 0) {
|
|
printk(" %1x.%02x\n", (int) sjcd_version.major,
|
|
(int) sjcd_version.minor);
|
|
} else {
|
|
printk(" read version failed, no drive found.\n");
|
|
goto out3;
|
|
}
|
|
|
|
/*
|
|
* Check and print out the tray state. (if it is needed?).
|
|
*/
|
|
if (!sjcd_status_valid) {
|
|
printk(KERN_INFO "SJCD: Getting status: ");
|
|
sjcd_send_cmd(SCMD_GET_STATUS);
|
|
for (i = 1000; i > 0 && !sjcd_status_valid; --i) {
|
|
unsigned long timer;
|
|
|
|
/*
|
|
* Wait 10ms approx.
|
|
*/
|
|
for (timer = jiffies;
|
|
time_before_eq(jiffies, timer););
|
|
if ((i % 100) == 0)
|
|
printk(".");
|
|
(void) sjcd_check_status();
|
|
}
|
|
if (i == 0 || sjcd_command_failed) {
|
|
printk(" get status failed, no drive found.\n");
|
|
goto out3;
|
|
} else
|
|
printk("\n");
|
|
}
|
|
|
|
printk(KERN_INFO "SJCD: Status: port=0x%x.\n", sjcd_base);
|
|
sjcd_disk->queue = sjcd_queue;
|
|
add_disk(sjcd_disk);
|
|
|
|
sjcd_present++;
|
|
return (0);
|
|
out3:
|
|
release_region(sjcd_base, 4);
|
|
out2:
|
|
put_disk(sjcd_disk);
|
|
out1:
|
|
blk_cleanup_queue(sjcd_queue);
|
|
out0:
|
|
if ((unregister_blkdev(MAJOR_NR, "sjcd") == -EINVAL))
|
|
printk("SJCD: cannot unregister device.\n");
|
|
return (-EIO);
|
|
}
|
|
|
|
static void __exit sjcd_exit(void)
|
|
{
|
|
del_gendisk(sjcd_disk);
|
|
put_disk(sjcd_disk);
|
|
release_region(sjcd_base, 4);
|
|
blk_cleanup_queue(sjcd_queue);
|
|
if ((unregister_blkdev(MAJOR_NR, "sjcd") == -EINVAL))
|
|
printk("SJCD: cannot unregister device.\n");
|
|
printk(KERN_INFO "SJCD: module: removed.\n");
|
|
}
|
|
|
|
module_init(sjcd_init);
|
|
module_exit(sjcd_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_ALIAS_BLOCKDEV_MAJOR(SANYO_CDROM_MAJOR);
|