| #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); |