|  | #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/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 DEFINE_TIMER(gscd_timer, 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 %u\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"); | 
|  |  | 
|  | 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); |