📄 gscd.c
字号:
#define GSCD_VERSION "0.4a Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de>"
/*
linux/drivers/block/gscd.c - GoldStar R420 CDROM driver
Copyright (C) 1995 Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de>
based upon pre-works by Eberhard Moenkeberg <emoenke@gwdg.de>
For all kind of other information about the GoldStar CDROM
and this Linux device driver I installed a WWW-URL:
http://linux.rz.fh-hannover.de/~raupach
If you are the editor of a Linux CD, you should
enable gscd.c within your boot floppy kernel and
send me one of your CDs for free.
--------------------------------------------------------------------
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
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.
--------------------------------------------------------------------
9 November 1999 -- Make kernel-parameter implementation work with 2.3.x
Removed init_module & cleanup_module in favor of
module_init & module_exit.
Torben Mathiasen <tmm@image.dk>
*/
/* These settings are for various debug-level. Leave they untouched ... */
#define NO_GSCD_DEBUG
#define NO_IOCTL_DEBUG
#define NO_MODULE_DEBUG
#define NO_FUTURE_WORK
/*------------------------*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/kernel.h>
#include <linux/cdrom.h>
#include <linux/ioport.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/devfs_fs_kernel.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#define MAJOR_NR GOLDSTAR_CDROM_MAJOR
#include <linux/blk.h>
#define gscd_port gscd /* for compatible parameter passing with "insmod" */
#include "gscd.h"
static int gscd_blocksizes[1] = { 512 };
static int gscdPresent = 0;
static unsigned char gscd_buf[2048]; /* buffer for block size conversion */
static int gscd_bn = -1;
static short gscd_port = GSCD_BASE_ADDR;
MODULE_PARM(gscd, "h");
/* Kommt spaeter vielleicht noch mal dran ...
* static DECLARE_WAIT_QUEUE_HEAD(gscd_waitq);
*/
static void gscd_transfer(void);
static void gscd_read_cmd(void);
static void gscd_hsg2msf(long hsg, struct msf *msf);
static void gscd_bin2bcd(unsigned char *p);
/* Schnittstellen zum Kern/FS */
static void do_gscd_request(request_queue_t *);
static void __do_gscd_request(unsigned long dummy);
static int gscd_ioctl(struct inode *, struct file *, unsigned int,
unsigned long);
static int gscd_open(struct inode *, struct file *);
static int gscd_release(struct inode *, struct file *);
static int check_gscd_med_chg(kdev_t);
/* GoldStar Funktionen */
static void cc_Reset(void);
static int wait_drv_ready(void);
static int find_drives(void);
static void cmd_out(int, char *, char *, int);
static void cmd_status(void);
static void cc_Ident(char *);
static void cc_SetSpeed(void);
static void init_cd_drive(int);
static int get_status(void);
static void clear_Audio(void);
static void cc_invalidate(void);
/* some things for the next version */
#ifdef FUTURE_WORK
static void update_state(void);
static long gscd_msf2hsg(struct msf *mp);
static int gscd_bcd2bin(unsigned char bcd);
#endif
/* common GoldStar Initialization */
static int my_gscd_init(void);
/* lo-level cmd-Funktionen */
static void cmd_info_in(char *, int);
static void cmd_end(void);
static void cmd_read_b(char *, int, int);
static void cmd_read_w(char *, int, int);
static int cmd_unit_alive(void);
static void cmd_write_cmd(char *);
/* GoldStar Variablen */
static int curr_drv_state;
static int drv_states[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
static int drv_mode;
static int disk_state;
static int speed;
static int ndrives;
static unsigned char drv_num_read;
static unsigned char f_dsk_valid;
static unsigned char current_drive;
static unsigned char f_drv_ok;
static char f_AudioPlay;
static char f_AudioPause;
static int AudioStart_m;
static int AudioStart_f;
static int AudioEnd_m;
static int AudioEnd_f;
static struct timer_list gscd_timer;
static struct block_device_operations gscd_fops = {
owner:THIS_MODULE,
open:gscd_open,
release:gscd_release,
ioctl:gscd_ioctl,
check_media_change:check_gscd_med_chg,
};
/*
* Checking if the media has been changed
* (not yet implemented)
*/
static int check_gscd_med_chg(kdev_t full_dev)
{
int target;
target = MINOR(full_dev);
if (target > 0) {
printk
("GSCD: GoldStar CD-ROM request error: invalid device.\n");
return 0;
}
#ifdef GSCD_DEBUG
printk("gscd: check_med_change\n");
#endif
return 0;
}
#ifndef MODULE
/* Using new interface for kernel-parameters */
static int __init gscd_setup(char *str)
{
int ints[2];
(void) get_options(str, ARRAY_SIZE(ints), ints);
if (ints[0] > 0) {
gscd_port = ints[1];
}
return 1;
}
__setup("gscd=", gscd_setup);
#endif
static int gscd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd,
unsigned long arg)
{
unsigned char to_do[10];
unsigned char dummy;
switch (cmd) {
case CDROMSTART: /* Spin up the drive */
/* Don't think we can do this. Even if we could,
* I think the drive times out and stops after a while
* anyway. For now, ignore it.
*/
return 0;
case CDROMRESUME: /* keine Ahnung was das ist */
return 0;
case CDROMEJECT:
cmd_status();
to_do[0] = CMD_TRAY_CTL;
cmd_out(TYPE_INFO, (char *) &to_do, (char *) &dummy, 0);
return 0;
default:
return -EINVAL;
}
}
/*
* Take care of the different block sizes between cdrom and Linux.
* When Linux gets variable block sizes this will probably go away.
*/
static void gscd_transfer(void)
{
long offs;
while (CURRENT->nr_sectors > 0 && gscd_bn == CURRENT->sector / 4) {
offs = (CURRENT->sector & 3) * 512;
memcpy(CURRENT->buffer, gscd_buf + offs, 512);
CURRENT->nr_sectors--;
CURRENT->sector++;
CURRENT->buffer += 512;
}
}
/*
* I/O request routine called from Linux kernel.
*/
static void do_gscd_request(request_queue_t * q)
{
__do_gscd_request(0);
}
static void __do_gscd_request(unsigned long dummy)
{
unsigned int block, dev;
unsigned int nsect;
repeat:
if (QUEUE_EMPTY || CURRENT->rq_status == RQ_INACTIVE)
goto out;
INIT_REQUEST;
dev = MINOR(CURRENT->rq_dev);
block = CURRENT->sector;
nsect = CURRENT->nr_sectors;
if (QUEUE_EMPTY || CURRENT->sector == -1)
goto out;
if (CURRENT->cmd != READ) {
printk("GSCD: bad cmd %d\n", CURRENT->cmd);
end_request(0);
goto repeat;
}
if (MINOR(CURRENT->rq_dev) != 0) {
printk("GSCD: this version supports only one device\n");
end_request(0);
goto repeat;
}
gscd_transfer();
/* if we satisfied the request from the buffer, we're done. */
if (CURRENT->nr_sectors == 0) {
end_request(1);
goto repeat;
}
#ifdef GSCD_DEBUG
printk("GSCD: dev %d, block %d, nsect %d\n", dev, block, nsect);
#endif
gscd_read_cmd();
out:
return;
}
/*
* Check the result of the set-mode command. On success, send the
* read-data command.
*/
static void gscd_read_cmd(void)
{
long block;
struct gscd_Play_msf gscdcmd;
char cmd[] = { CMD_READ, 0x80, 0, 0, 0, 0, 1 }; /* cmd mode M-S-F secth sectl */
cmd_status();
if (disk_state & (ST_NO_DISK | ST_DOOR_OPEN)) {
printk("GSCD: no disk or door open\n");
end_request(0);
} else {
if (disk_state & ST_INVALID) {
printk("GSCD: disk invalid\n");
end_request(0);
} else {
gscd_bn = -1; /* purge our buffer */
block = CURRENT->sector / 4;
gscd_hsg2msf(block, &gscdcmd.start); /* cvt to msf format */
cmd[2] = gscdcmd.start.min;
cmd[3] = gscdcmd.start.sec;
cmd[4] = gscdcmd.start.frame;
#ifdef GSCD_DEBUG
printk("GSCD: read msf %d:%d:%d\n", cmd[2], cmd[3],
cmd[4]);
#endif
cmd_out(TYPE_DATA, (char *) &cmd,
(char *) &gscd_buf[0], 1);
gscd_bn = CURRENT->sector / 4;
gscd_transfer();
end_request(1);
}
}
SET_TIMER(__do_gscd_request, 1);
}
/*
* Open the device special file. Check that a disk is in.
*/
static int gscd_open(struct inode *ip, struct file *fp)
{
int st;
#ifdef GSCD_DEBUG
printk("GSCD: open\n");
#endif
if (gscdPresent == 0)
return -ENXIO; /* no hardware */
get_status();
st = disk_state & (ST_NO_DISK | ST_DOOR_OPEN);
if (st) {
printk("GSCD: no disk or door open\n");
return -ENXIO;
}
/* if (updateToc() < 0)
return -EIO;
*/
return 0;
}
/*
* On close, we flush all gscd blocks from the buffer cache.
*/
static int gscd_release(struct inode *inode, struct file *file)
{
#ifdef GSCD_DEBUG
printk("GSCD: release\n");
#endif
gscd_bn = -1;
return 0;
}
int get_status(void)
{
int status;
cmd_status();
status = disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01);
if (status == (ST_x08 | ST_x04 | ST_INVALID | ST_x01)) {
cc_invalidate();
return 1;
} else {
return 0;
}
}
void cc_invalidate(void)
{
drv_num_read = 0xFF;
f_dsk_valid = 0xFF;
current_drive = 0xFF;
f_drv_ok = 0xFF;
clear_Audio();
}
void clear_Audio(void)
{
f_AudioPlay = 0;
f_AudioPause = 0;
AudioStart_m = 0;
AudioStart_f = 0;
AudioEnd_m = 0;
AudioEnd_f = 0;
}
/*
* waiting ?
*/
int wait_drv_ready(void)
{
int found, read;
do {
found = inb(GSCDPORT(0));
found &= 0x0f;
read = inb(GSCDPORT(0));
read &= 0x0f;
} while (read != found);
#ifdef GSCD_DEBUG
printk("Wait for: %d\n", read);
#endif
return read;
}
void cc_Ident(char *respons)
{
char to_do[] = { CMD_IDENT, 0, 0 };
cmd_out(TYPE_INFO, (char *) &to_do, (char *) respons, (int) 0x1E);
}
void cc_SetSpeed(void)
{
char to_do[] = { CMD_SETSPEED, 0, 0 };
char dummy;
if (speed > 0) {
to_do[1] = speed & 0x0F;
cmd_out(TYPE_INFO, (char *) &to_do, (char *) &dummy, 0);
}
}
void cc_Reset(void)
{
char to_do[] = { CMD_RESET, 0 };
char dummy;
cmd_out(TYPE_INFO, (char *) &to_do, (char *) &dummy, 0);
}
void cmd_status(void)
{
char to_do[] = { CMD_STATUS, 0 };
char dummy;
cmd_out(TYPE_INFO, (char *) &to_do, (char *) &dummy, 0);
#ifdef GSCD_DEBUG
printk("GSCD: Status: %d\n", disk_state);
#endif
}
void cmd_out(int cmd_type, char *cmd, char *respo_buf, int respo_count)
{
int result;
result = wait_drv_ready();
if (result != drv_mode) {
unsigned long test_loops = 0xFFFF;
int i, dummy;
outb(curr_drv_state, GSCDPORT(0));
/* LOCLOOP_170 */
do {
result = wait_drv_ready();
test_loops--;
} while ((result != drv_mode) && (test_loops > 0));
if (result != drv_mode) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -