URL
https://opencores.org/ocsvn/or1k_old/or1k_old/trunk
Subversion Repositories or1k_old
[/] [or1k_old/] [trunk/] [uclinux/] [uClinux-2.0.x/] [drivers/] [cdrom/] [gscd.c] - Rev 199
Go to most recent revision | Compare with Previous | Blame | View Log
#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. */ /* 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/malloc.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 <asm/system.h> #include <asm/io.h> #include <asm/segment.h> #define MAJOR_NR GOLDSTAR_CDROM_MAJOR #include <linux/blk.h> #define gscd_port gscd /* for compatible parameter passing with "insmod" */ #include <linux/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; /* Kommt spaeter vielleicht noch mal dran ... * static struct wait_queue *gscd_waitq = NULL; */ 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 (void); static int gscd_ioctl (struct inode *, struct file *, unsigned int, unsigned long); static int gscd_open (struct inode *, struct file *); static void 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 file_operations gscd_fops = { NULL, /* lseek - default */ block_read, /* read - general block-dev read */ block_write, /* write - general block-dev write */ NULL, /* readdir - bad */ NULL, /* select */ gscd_ioctl, /* ioctl */ NULL, /* mmap */ gscd_open, /* open */ gscd_release, /* release */ NULL, /* fsync */ NULL, /* fasync*/ check_gscd_med_chg, /* media change */ NULL /* revalidate */ }; /* * 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; } void gscd_setup (char *str, int *ints) { if (ints[0] > 0) { gscd_port = ints[1]; } } 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 (void) { unsigned int block,dev; unsigned int nsect; repeat: if (!(CURRENT) || CURRENT->rq_status == RQ_INACTIVE) return; INIT_REQUEST; dev = MINOR(CURRENT->rq_dev); block = CURRENT->sector; nsect = CURRENT->nr_sectors; if (CURRENT == NULL || CURRENT -> sector == -1) return; 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 (); } /* * 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 */ MOD_INC_USE_COUNT; get_status (); st = disk_state & (ST_NO_DISK | ST_DOOR_OPEN); if ( st ) { printk ( "GSCD: no disk or door open\n" ); MOD_DEC_USE_COUNT; return -ENXIO; } /* if (updateToc() < 0) return -EIO; */ return 0; } /* * On close, we flush all gscd blocks from the buffer cache. */ static void gscd_release (struct inode * inode, struct file * file) { #ifdef GSCD_DEBUG printk ( "GSCD: release\n" ); #endif gscd_bn = -1; sync_dev(inode->i_rdev); invalidate_buffers(inode -> i_rdev); MOD_DEC_USE_COUNT; } 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 ) { 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; } int 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; } void 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 #ifdef MODULE /* Init for the Module-Version */ int init_module (void) { long err; /* call the GoldStar-init */ err = my_gscd_init ( ); if ( err < 0 ) { return err; } else { printk (KERN_INFO "Happy GoldStar !\n" ); return 0; } } void cleanup_module (void) { if ((unregister_blkdev(MAJOR_NR, "gscd" ) == -EINVAL)) { printk("What's that: can't unregister GoldStar-module\n" ); return; } release_region (gscd_port,4); printk(KERN_INFO "GoldStar-module released.\n" ); } #endif /* Test for presence of drive and initialize it. Called only at boot time. */ int gscd_init (void) { return my_gscd_init (); } /* This is the common initialisation for the GoldStar drive. */ /* It is called at boot time AND for module init. */ int my_gscd_init (void) { int i; int result; 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 (check_region(gscd_port, 4)) { printk("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 ("GSCD: DMA kann ich noch nicht!\n" ); return -EIO; } if ( result == 0x0b ) { drv_mode = result; i = find_drives (); if ( i == 0 ) { printk ( "GSCD: GoldStar CD-ROM Drive is not found.\n" ); return -EIO; } } if ( (result != 0x0b) && (result != 0x09) ) { printk ("GSCD: GoldStar Interface Adapter does not exist or H/W error\n" ); return -EIO; } /* 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++; } if (register_blkdev(MAJOR_NR, "gscd", &gscd_fops) != 0) { printk("GSCD: Unable to get major %d for GoldStar CD-ROM\n", MAJOR_NR); return -EIO; } blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; read_ahead[MAJOR_NR] = 4; disk_state = 0; gscdPresent = 1; request_region(gscd_port, 4, "gscd"); printk (KERN_INFO "GSCD: GoldStar CD-ROM Drive found.\n" ); return 0; } static void gscd_hsg2msf (long hsg, struct msf *msf) { hsg += CD_BLOCK_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_BLOCK_OFFSET; } static int gscd_bcd2bin (unsigned char bcd) { return (bcd >> 4) * 10 + (bcd & 0xF); } #endif
Go to most recent revision | Compare with Previous | Blame | View Log