aboutsummaryrefslogtreecommitdiff
path: root/drivers/cdrom/gscd.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/cdrom/gscd.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/cdrom/gscd.c')
-rw-r--r--drivers/cdrom/gscd.c1031
1 files changed, 1031 insertions, 0 deletions
diff --git a/drivers/cdrom/gscd.c b/drivers/cdrom/gscd.c
new file mode 100644
index 00000000000..7eac10e63b2
--- /dev/null
+++ b/drivers/cdrom/gscd.c
@@ -0,0 +1,1031 @@
+#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 <asm/system.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#define MAJOR_NR GOLDSTAR_CDROM_MAJOR
+#include <linux/blkdev.h>
+#include "gscd.h"
+
+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_param_named(gscd, gscd_port, short, 0);
+
+/* Kommt spaeter vielleicht noch mal dran ...
+ * static DECLARE_WAIT_QUEUE_HEAD(gscd_waitq);
+ */
+
+static void gscd_read_cmd(struct request *req);
+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(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(struct gendisk *disk);
+
+/* GoldStar Funktionen */
+
+static void cmd_out(int, char *, char *, int);
+static void cmd_status(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
+
+
+/* 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 = TIMER_INITIALIZER(NULL, 0, 0);
+static DEFINE_SPINLOCK(gscd_lock);
+static struct request_queue *gscd_queue;
+
+static struct block_device_operations gscd_fops = {
+ .owner = THIS_MODULE,
+ .open = gscd_open,
+ .release = gscd_release,
+ .ioctl = gscd_ioctl,
+ .media_changed = check_gscd_med_chg,
+};
+
+/*
+ * Checking if the media has been changed
+ * (not yet implemented)
+ */
+static int check_gscd_med_chg(struct gendisk *disk)
+{
+#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(struct request *req)
+{
+ while (req->nr_sectors > 0 && gscd_bn == req->sector / 4) {
+ long offs = (req->sector & 3) * 512;
+ memcpy(req->buffer, gscd_buf + offs, 512);
+ req->nr_sectors--;
+ req->sector++;
+ req->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)
+{
+ struct request *req;
+ unsigned int block;
+ unsigned int nsect;
+
+repeat:
+ req = elv_next_request(gscd_queue);
+ if (!req)
+ return;
+
+ block = req->sector;
+ nsect = req->nr_sectors;
+
+ if (req->sector == -1)
+ goto out;
+
+ if (req->cmd != READ) {
+ printk("GSCD: bad cmd %lu\n", rq_data_dir(req));
+ end_request(req, 0);
+ goto repeat;
+ }
+
+ gscd_transfer(req);
+
+ /* if we satisfied the request from the buffer, we're done. */
+
+ if (req->nr_sectors == 0) {
+ end_request(req, 1);
+ goto repeat;
+ }
+#ifdef GSCD_DEBUG
+ printk("GSCD: block %d, nsect %d\n", block, nsect);
+#endif
+ gscd_read_cmd(req);
+out:
+ return;
+}
+
+
+
+/*
+ * Check the result of the set-mode command. On success, send the
+ * read-data command.
+ */
+
+static void gscd_read_cmd(struct request *req)
+{
+ 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(req, 0);
+ } else {
+ if (disk_state & ST_INVALID) {
+ printk("GSCD: disk invalid\n");
+ end_request(req, 0);
+ } else {
+ gscd_bn = -1; /* purge our buffer */
+ block = req->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 = req->sector / 4;
+ gscd_transfer(req);
+ end_request(req, 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;
+}
+
+
+static 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;
+ }
+}
+
+
+static void cc_invalidate(void)
+{
+ drv_num_read = 0xFF;
+ f_dsk_valid = 0xFF;
+ current_drive = 0xFF;
+ f_drv_ok = 0xFF;
+
+ clear_Audio();
+
+}
+
+static void clear_Audio(void)
+{
+
+ f_AudioPlay = 0;
+ f_AudioPause = 0;
+ AudioStart_m = 0;
+ AudioStart_f = 0;
+ AudioEnd_m = 0;
+ AudioEnd_f = 0;
+
+}
+
+/*
+ * waiting ?
+ */
+
+static 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;
+}
+
+static void cc_Ident(char *respons)
+{
+ char to_do[] = { CMD_IDENT, 0, 0 };
+
+ cmd_out(TYPE_INFO, (char *) &to_do, (char *) respons, (int) 0x1E);
+
+}
+
+static 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);
+ }
+}
+
+static void cc_Reset(void)
+{
+ char to_do[] = { CMD_RESET, 0 };
+ char dummy;
+
+ cmd_out(TYPE_INFO, (char *) &to_do, (char *) &dummy, 0);
+}
+
+static 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
+
+}
+
+static 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) {
+ disk_state = ST_x08 | ST_x04 | ST_INVALID;
+ return;
+ }
+
+ /* ...and waiting */
+ for (i = 1, dummy = 1; i < 0xFFFF; i++) {
+ dummy *= i;
+ }
+ }
+
+ /* LOC_172 */
+ /* check the unit */
+ /* and wake it up */
+ if (cmd_unit_alive() != 0x08) {
+ /* LOC_174 */
+ /* game over for this unit */
+ disk_state = ST_x08 | ST_x04 | ST_INVALID;
+ return;
+ }
+
+ /* LOC_176 */
+#ifdef GSCD_DEBUG
+ printk("LOC_176 ");
+#endif
+ if (drv_mode == 0x09) {
+ /* magic... */
+ printk("GSCD: magic ...\n");
+ outb(result, GSCDPORT(2));
+ }
+
+ /* write the command to the drive */
+ cmd_write_cmd(cmd);
+
+ /* LOC_178 */
+ for (;;) {
+ result = wait_drv_ready();
+ if (result != drv_mode) {
+ /* LOC_179 */
+ if (result == 0x04) { /* Mode 4 */
+ /* LOC_205 */
+#ifdef GSCD_DEBUG
+ printk("LOC_205 ");
+#endif
+ disk_state = inb(GSCDPORT(2));
+
+ do {
+ result = wait_drv_ready();
+ } while (result != drv_mode);
+ return;
+
+ } else {
+ if (result == 0x06) { /* Mode 6 */
+ /* LOC_181 */
+#ifdef GSCD_DEBUG
+ printk("LOC_181 ");
+#endif
+
+ if (cmd_type == TYPE_DATA) {
+ /* read data */
+ /* LOC_184 */
+ if (drv_mode == 9) {
+ /* read the data to the buffer (word) */
+
+ /* (*(cmd+1))?(CD_FRAMESIZE/2):(CD_FRAMESIZE_RAW/2) */
+ cmd_read_w
+ (respo_buf,
+ respo_count,
+ CD_FRAMESIZE /
+ 2);
+ return;
+ } else {
+ /* read the data to the buffer (byte) */
+
+ /* (*(cmd+1))?(CD_FRAMESIZE):(CD_FRAMESIZE_RAW) */
+ cmd_read_b
+ (respo_buf,
+ respo_count,
+ CD_FRAMESIZE);
+ return;
+ }
+ } else {
+ /* read the info to the buffer */
+ cmd_info_in(respo_buf,
+ respo_count);
+ return;
+ }
+
+ return;
+ }
+ }
+
+ } else {
+ disk_state = ST_x08 | ST_x04 | ST_INVALID;
+ return;
+ }
+ } /* for (;;) */
+
+
+#ifdef GSCD_DEBUG
+ printk("\n");
+#endif
+}
+
+
+static void cmd_write_cmd(char *pstr)
+{
+ int i, j;
+
+ /* LOC_177 */
+#ifdef GSCD_DEBUG
+ printk("LOC_177 ");
+#endif
+
+ /* calculate the number of parameter */
+ j = *pstr & 0x0F;
+
+ /* shift it out */
+ for (i = 0; i < j; i++) {
+ outb(*pstr, GSCDPORT(2));
+ pstr++;
+ }
+}
+
+
+static int cmd_unit_alive(void)
+{
+ int result;
+ unsigned long max_test_loops;
+
+
+ /* LOC_172 */
+#ifdef GSCD_DEBUG
+ printk("LOC_172 ");
+#endif
+
+ outb(curr_drv_state, GSCDPORT(0));
+ max_test_loops = 0xFFFF;
+
+ do {
+ result = wait_drv_ready();
+ max_test_loops--;
+ } while ((result != 0x08) && (max_test_loops > 0));
+
+ return result;
+}
+
+
+static void cmd_info_in(char *pb, int count)
+{
+ int result;
+ char read;
+
+
+ /* read info */
+ /* LOC_182 */
+#ifdef GSCD_DEBUG
+ printk("LOC_182 ");
+#endif
+
+ do {
+ read = inb(GSCDPORT(2));
+ if (count > 0) {
+ *pb = read;
+ pb++;
+ count--;
+ }
+
+ /* LOC_183 */
+ do {
+ result = wait_drv_ready();
+ } while (result == 0x0E);
+ } while (result == 6);
+
+ cmd_end();
+ return;
+}
+
+
+static void cmd_read_b(char *pb, int count, int size)
+{
+ int result;
+ int i;
+
+
+ /* LOC_188 */
+ /* LOC_189 */
+#ifdef GSCD_DEBUG
+ printk("LOC_189 ");
+#endif
+
+ do {
+ do {
+ result = wait_drv_ready();
+ } while (result != 6 || result == 0x0E);
+
+ if (result != 6) {
+ cmd_end();
+ return;
+ }
+#ifdef GSCD_DEBUG
+ printk("LOC_191 ");
+#endif
+
+ for (i = 0; i < size; i++) {
+ *pb = inb(GSCDPORT(2));
+ pb++;
+ }
+ count--;
+ } while (count > 0);
+
+ cmd_end();
+ return;
+}
+
+
+static void cmd_end(void)
+{
+ int result;
+
+
+ /* LOC_204 */
+#ifdef GSCD_DEBUG
+ printk("LOC_204 ");
+#endif
+
+ do {
+ result = wait_drv_ready();
+ if (result == drv_mode) {
+ return;
+ }
+ } while (result != 4);
+
+ /* LOC_205 */
+#ifdef GSCD_DEBUG
+ printk("LOC_205 ");
+#endif
+
+ disk_state = inb(GSCDPORT(2));
+
+ do {
+ result = wait_drv_ready();
+ } while (result != drv_mode);
+ return;
+
+}
+
+
+static void cmd_read_w(char *pb, int count, int size)
+{
+ int result;
+ int i;
+
+
+#ifdef GSCD_DEBUG
+ printk("LOC_185 ");
+#endif
+
+ do {
+ /* LOC_185 */
+ do {
+ result = wait_drv_ready();
+ } while (result != 6 || result == 0x0E);
+
+ if (result != 6) {
+ cmd_end();
+ return;
+ }
+
+ for (i = 0; i < size; i++) {
+ /* na, hier muss ich noch mal drueber nachdenken */
+ *pb = inw(GSCDPORT(2));
+ pb++;
+ }
+ count--;
+ } while (count > 0);
+
+ cmd_end();
+ return;
+}
+
+static int __init find_drives(void)
+{
+ int *pdrv;
+ int drvnum;
+ int subdrv;
+ int i;
+
+ speed = 0;
+ pdrv = (int *) &drv_states;
+ curr_drv_state = 0xFE;
+ subdrv = 0;
+ drvnum = 0;
+
+ for (i = 0; i < 8; i++) {
+ subdrv++;
+ cmd_status();
+ disk_state &= ST_x08 | ST_x04 | ST_INVALID | ST_x01;
+ if (disk_state != (ST_x08 | ST_x04 | ST_INVALID)) {
+ /* LOC_240 */
+ *pdrv = curr_drv_state;
+ init_cd_drive(drvnum);
+ pdrv++;
+ drvnum++;
+ } else {
+ if (subdrv < 2) {
+ continue;
+ } else {
+ subdrv = 0;
+ }
+ }
+
+/* curr_drv_state<<1; <-- das geht irgendwie nicht */
+/* muss heissen: curr_drv_state <<= 1; (ist ja Wert-Zuweisung) */
+ curr_drv_state *= 2;
+ curr_drv_state |= 1;
+#ifdef GSCD_DEBUG
+ printk("DriveState: %d\n", curr_drv_state);
+#endif
+ }
+
+ ndrives = drvnum;
+ return drvnum;
+}
+
+static void __init init_cd_drive(int num)
+{
+ char resp[50];
+ int i;
+
+ printk("GSCD: init unit %d\n", num);
+ cc_Ident((char *) &resp);
+
+ printk("GSCD: identification: ");
+ for (i = 0; i < 0x1E; i++) {
+ printk("%c", resp[i]);
+ }
+ printk("\n");
+
+ cc_SetSpeed();
+
+}
+
+#ifdef FUTURE_WORK
+/* return_done */
+static void update_state(void)
+{
+ unsigned int AX;
+
+
+ if ((disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01)) == 0) {
+ if (disk_state == (ST_x08 | ST_x04 | ST_INVALID)) {
+ AX = ST_INVALID;
+ }
+
+ if ((disk_state & (ST_x08 | ST_x04 | ST_INVALID | ST_x01))
+ == 0) {
+ invalidate();
+ f_drv_ok = 0;
+ }
+
+ AX |= 0x8000;
+ }
+
+ if (disk_state & ST_PLAYING) {
+ AX |= 0x200;
+ }
+
+ AX |= 0x100;
+ /* pkt_esbx = AX; */
+
+ disk_state = 0;
+
+}
+#endif
+
+static struct gendisk *gscd_disk;
+
+static void __exit gscd_exit(void)
+{
+ CLEAR_TIMER;
+
+ del_gendisk(gscd_disk);
+ put_disk(gscd_disk);
+ if ((unregister_blkdev(MAJOR_NR, "gscd") == -EINVAL)) {
+ printk("What's that: can't unregister GoldStar-module\n");
+ return;
+ }
+ blk_cleanup_queue(gscd_queue);
+ release_region(gscd_port, GSCD_IO_EXTENT);
+ printk(KERN_INFO "GoldStar-module released.\n");
+}
+
+/* This is the common initialisation for the GoldStar drive. */
+/* It is called at boot time AND for module init. */
+static int __init gscd_init(void)
+{
+ int i;
+ int result;
+ int ret=0;
+
+ printk(KERN_INFO "GSCD: version %s\n", GSCD_VERSION);
+ printk(KERN_INFO
+ "GSCD: Trying to detect a Goldstar R420 CD-ROM drive at 0x%X.\n",
+ gscd_port);
+
+ if (!request_region(gscd_port, GSCD_IO_EXTENT, "gscd")) {
+ printk(KERN_WARNING "GSCD: Init failed, I/O port (%X) already"
+ " in use.\n", gscd_port);
+ return -EIO;
+ }
+
+
+ /* check for card */
+ result = wait_drv_ready();
+ if (result == 0x09) {
+ printk(KERN_WARNING "GSCD: DMA kann ich noch nicht!\n");
+ ret = -EIO;
+ goto err_out1;
+ }
+
+ if (result == 0x0b) {
+ drv_mode = result;
+ i = find_drives();
+ if (i == 0) {
+ printk(KERN_WARNING "GSCD: GoldStar CD-ROM Drive is"
+ " not found.\n");
+ ret = -EIO;
+ goto err_out1;
+ }
+ }
+
+ if ((result != 0x0b) && (result != 0x09)) {
+ printk(KERN_WARNING "GSCD: GoldStar Interface Adapter does not "
+ "exist or H/W error\n");
+ ret = -EIO;
+ goto err_out1;
+ }
+
+ /* reset all drives */
+ i = 0;
+ while (drv_states[i] != 0) {
+ curr_drv_state = drv_states[i];
+ printk(KERN_INFO "GSCD: Reset unit %d ... ", i);
+ cc_Reset();
+ printk("done\n");
+ i++;
+ }
+
+ gscd_disk = alloc_disk(1);
+ if (!gscd_disk)
+ goto err_out1;
+ gscd_disk->major = MAJOR_NR;
+ gscd_disk->first_minor = 0;
+ gscd_disk->fops = &gscd_fops;
+ sprintf(gscd_disk->disk_name, "gscd");
+ sprintf(gscd_disk->devfs_name, "gscd");
+
+ if (register_blkdev(MAJOR_NR, "gscd")) {
+ ret = -EIO;
+ goto err_out2;
+ }
+
+ gscd_queue = blk_init_queue(do_gscd_request, &gscd_lock);
+ if (!gscd_queue) {
+ ret = -ENOMEM;
+ goto err_out3;
+ }
+
+ disk_state = 0;
+ gscdPresent = 1;
+
+ gscd_disk->queue = gscd_queue;
+ add_disk(gscd_disk);
+
+ printk(KERN_INFO "GSCD: GoldStar CD-ROM Drive found.\n");
+ return 0;
+
+err_out3:
+ unregister_blkdev(MAJOR_NR, "gscd");
+err_out2:
+ put_disk(gscd_disk);
+err_out1:
+ release_region(gscd_port, GSCD_IO_EXTENT);
+ return ret;
+}
+
+static void gscd_hsg2msf(long hsg, struct msf *msf)
+{
+ hsg += CD_MSF_OFFSET;
+ msf->min = hsg / (CD_FRAMES * CD_SECS);
+ hsg %= CD_FRAMES * CD_SECS;
+ msf->sec = hsg / CD_FRAMES;
+ msf->frame = hsg % CD_FRAMES;
+
+ gscd_bin2bcd(&msf->min); /* convert to BCD */
+ gscd_bin2bcd(&msf->sec);
+ gscd_bin2bcd(&msf->frame);
+}
+
+
+static void gscd_bin2bcd(unsigned char *p)
+{
+ int u, t;
+
+ u = *p % 10;
+ t = *p / 10;
+ *p = u | (t << 4);
+}
+
+
+#ifdef FUTURE_WORK
+static long gscd_msf2hsg(struct msf *mp)
+{
+ return gscd_bcd2bin(mp->frame)
+ + gscd_bcd2bin(mp->sec) * CD_FRAMES
+ + gscd_bcd2bin(mp->min) * CD_FRAMES * CD_SECS - CD_MSF_OFFSET;
+}
+
+static int gscd_bcd2bin(unsigned char bcd)
+{
+ return (bcd >> 4) * 10 + (bcd & 0xF);
+}
+#endif
+
+MODULE_AUTHOR("Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de>");
+MODULE_LICENSE("GPL");
+module_init(gscd_init);
+module_exit(gscd_exit);
+MODULE_ALIAS_BLOCKDEV_MAJOR(GOLDSTAR_CDROM_MAJOR);