Beispiel #1
0
__initfunc(int mcd_init(void))
{
	int count;
	unsigned char result[3];
	char msg[80];

	if (mcd_port <= 0 || mcd_irq <= 0) {
	  printk("skip mcd_init\n");
          return -EIO;
	}

	if (register_blkdev(MAJOR_NR, "mcd", &cdrom_fops) != 0)
	{
		printk("Unable to get major %d for Mitsumi CD-ROM\n",
		       MAJOR_NR);
                return -EIO;
	}
        if (check_region(mcd_port, 4)) {
	  cleanup(1);
	  printk("Init failed, I/O port (%X) already in use\n",
		 mcd_port);
          return -EIO;
	}

	blksize_size[MAJOR_NR] = mcd_blocksizes;
	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
	read_ahead[MAJOR_NR] = 4;

	/* check for card */

	outb(0, MCDPORT(1));			/* send reset */
	for (count = 0; count < 2000000; count++)
		(void) inb(MCDPORT(1));		/* delay a bit */

	outb(0x40, MCDPORT(0));	                /* send get-stat cmd */
	for (count = 0; count < 2000000; count++)
		if (!(inb(MCDPORT(1)) & MFL_STATUS))
			break;

	if (count >= 2000000) {
		printk("Init failed. No mcd device at 0x%x irq %d\n",
		     mcd_port, mcd_irq);
		cleanup(1);
                return -EIO;
	}
	count = inb(MCDPORT(0));		/* pick up the status */
	
	outb(MCMD_GET_VERSION,MCDPORT(0));
	for(count=0;count<3;count++)
		if(getValue(result+count)) {
			printk("mitsumi get version failed at 0x%d\n",
			       mcd_port);
                        cleanup(1);
                        return -EIO;
		}	

	if (result[0] == result[1] && result[1] == result[2]) {
		cleanup(1);
                return -EIO;
	}

	mcdVersion=result[2];

	if (mcdVersion >=4)
		outb(4,MCDPORT(2)); 	/* magic happens */

	/* don't get the IRQ until we know for sure the drive is there */

	if (request_irq(mcd_irq, mcd_interrupt, SA_INTERRUPT, "Mitsumi CD", NULL))
	{
		printk("Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq);
		cleanup(1);
                return -EIO;
	}

        if (result[1] == 'D') 
	{
		sprintf(msg, " mcd: Mitsumi Double Speed CD-ROM at port=0x%x,"
			     " irq=%d\n", mcd_port, mcd_irq);
		MCMD_DATA_READ = MCMD_2X_READ;

		mcd_info.speed = 2;
		/* Added flag to drop to 1x speed if too many errors */
		mcdDouble = 1;
        } else {
		sprintf(msg, " mcd: Mitsumi Single Speed CD-ROM at port=0x%x,"
			     " irq=%d\n", mcd_port, mcd_irq);
		mcd_info.speed = 2;
	}

	request_region(mcd_port, 4, "mcd");

	outb(MCMD_CONFIG_DRIVE, MCDPORT(0));
	outb(0x02,MCDPORT(0));
	outb(0x00,MCDPORT(0));
	getValue(result);

	outb(MCMD_CONFIG_DRIVE, MCDPORT(0));
	outb(0x10,MCDPORT(0));
	outb(0x04,MCDPORT(0));
	getValue(result);

	mcd_invalidate_buffers();
	mcdPresent = 1;

	mcd_info.dev = MKDEV(MAJOR_NR,0);

        if (register_cdrom(&mcd_info) != 0) {
              printk("Cannot register Mitsumi CD-ROM!\n");
              cleanup(3);
              return -EIO;
        }
        printk(msg);

	return 0;
}
Beispiel #2
0
int __init mcd_init(void)
{
	struct gendisk *disk = alloc_disk(1);
	int count;
	unsigned char result[3];
	char msg[80];

	if (!disk) {
		printk(KERN_INFO "mcd: can't allocated disk.\n");
		return -ENOMEM;
	}
	if (mcd_port <= 0 || mcd_irq <= 0) {
		printk(KERN_INFO "mcd: not probing.\n");
		put_disk(disk);
		return -EIO;
	}
	if (register_blkdev(MAJOR_NR, "mcd")) {
		put_disk(disk);
		return -EIO;
	}
	if (!request_region(mcd_port, 4, "mcd")) {
		printk(KERN_ERR "mcd: Initialization failed, I/O port (%X) already in use\n", mcd_port);
		goto out_region;
	}

	mcd_queue = blk_init_queue(do_mcd_request, &mcd_spinlock);
	if (!mcd_queue)
		goto out_queue;

	/* check for card */

	outb(0, MCDPORT(1));	/* send reset */
	for (count = 0; count < 2000000; count++)
		(void) inb(MCDPORT(1));	/* delay a bit */

	outb(0x40, MCDPORT(0));	/* send get-stat cmd */
	for (count = 0; count < 2000000; count++)
		if (!(inb(MCDPORT(1)) & MFL_STATUS))
			break;

	if (count >= 2000000) {
		printk(KERN_INFO "mcd: initialisation failed - No mcd device at 0x%x irq %d\n",
		       mcd_port, mcd_irq);
		goto out_probe;
	}
	count = inb(MCDPORT(0));	/* pick up the status */

	outb(MCMD_GET_VERSION, MCDPORT(0));
	for (count = 0; count < 3; count++)
		if (getValue(result + count)) {
			printk(KERN_ERR "mcd: mitsumi get version failed at 0x%x\n",
			       mcd_port);
			goto out_probe;
		}

	if (result[0] == result[1] && result[1] == result[2])
		goto out_probe;

	mcdVersion = result[2];

	if (mcdVersion >= 4)
		outb(4, MCDPORT(2));	/* magic happens */

	/* don't get the IRQ until we know for sure the drive is there */

	if (request_irq(mcd_irq, mcd_interrupt, SA_INTERRUPT, "Mitsumi CD", NULL)) {
		printk(KERN_ERR "mcd: Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq);
		goto out_probe;
	}

	if (result[1] == 'D') {
		MCMD_DATA_READ = MCMD_2X_READ;
		/* Added flag to drop to 1x speed if too many errors */
		mcdDouble = 1;
	} else
		mcd_info.speed = 1;
	sprintf(msg, " mcd: Mitsumi %s Speed CD-ROM at port=0x%x,"
		" irq=%d\n", mcd_info.speed == 1 ? "Single" : "Double",
		mcd_port, mcd_irq);

	outb(MCMD_CONFIG_DRIVE, MCDPORT(0));
	outb(0x02, MCDPORT(0));
	outb(0x00, MCDPORT(0));
	getValue(result);

	outb(MCMD_CONFIG_DRIVE, MCDPORT(0));
	outb(0x10, MCDPORT(0));
	outb(0x04, MCDPORT(0));
	getValue(result);

	mcd_invalidate_buffers();
	mcdPresent = 1;

	disk->major = MAJOR_NR;
	disk->first_minor = 0;
	sprintf(disk->disk_name, "mcd");
	disk->fops = &mcd_bdops;
	disk->flags = GENHD_FL_CD;
	mcd_gendisk = disk;

	if (register_cdrom(&mcd_info) != 0) {
		printk(KERN_ERR "mcd: Unable to register Mitsumi CD-ROM.\n");
		goto out_cdrom;
	}
	disk->queue = mcd_queue;
	add_disk(disk);
	printk(msg);
	return 0;

out_cdrom:
	free_irq(mcd_irq, NULL);
out_queue:
	release_region(mcd_port, 4);
out_probe:
	blk_cleanup_queue(mcd_queue);
out_region:
	unregister_blkdev(MAJOR_NR, "mcd");
	put_disk(disk);
	return -EIO;
}
Beispiel #3
0
int __init mcd_init(void)
{
	int count;
	unsigned char result[3];
	char msg[80];

	if (mcd_port <= 0 || mcd_irq <= 0) {
		printk(KERN_INFO "mcd: not probing.\n");
		return -EIO;
	}

	if (devfs_register_blkdev(MAJOR_NR, "mcd", &mcd_bdops) != 0) {
		printk(KERN_ERR "mcd: Unable to get major %d for Mitsumi CD-ROM\n", MAJOR_NR);
		return -EIO;
	}
	if (check_region(mcd_port, 4)) {
		cleanup(1);
		printk(KERN_ERR "mcd: Initialization failed, I/O port (%X) already in use\n", mcd_port);
		return -EIO;
	}

	blksize_size[MAJOR_NR] = mcd_blocksizes;
	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST,
		       &mcd_spinlock);
	read_ahead[MAJOR_NR] = 4;

	/* check for card */

	outb(0, MCDPORT(1));	/* send reset */
	for (count = 0; count < 2000000; count++)
		(void) inb(MCDPORT(1));	/* delay a bit */

	outb(0x40, MCDPORT(0));	/* send get-stat cmd */
	for (count = 0; count < 2000000; count++)
		if (!(inb(MCDPORT(1)) & MFL_STATUS))
			break;

	if (count >= 2000000) {
		printk(KERN_INFO "mcd: initialisation failed - No mcd device at 0x%x irq %d\n",
		       mcd_port, mcd_irq);
		cleanup(1);
		return -EIO;
	}
	count = inb(MCDPORT(0));	/* pick up the status */

	outb(MCMD_GET_VERSION, MCDPORT(0));
	for (count = 0; count < 3; count++)
		if (getValue(result + count)) {
			printk(KERN_ERR "mcd: mitsumi get version failed at 0x%x\n",
			       mcd_port);
			cleanup(1);
			return -EIO;
		}

	if (result[0] == result[1] && result[1] == result[2]) {
		cleanup(1);
		return -EIO;
	}

	mcdVersion = result[2];

	if (mcdVersion >= 4)
		outb(4, MCDPORT(2));	/* magic happens */

	/* don't get the IRQ until we know for sure the drive is there */

	if (request_irq(mcd_irq, mcd_interrupt, SA_INTERRUPT, "Mitsumi CD", NULL)) {
		printk(KERN_ERR "mcd: Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq);
		cleanup(1);
		return -EIO;
	}

	if (result[1] == 'D') {
		MCMD_DATA_READ = MCMD_2X_READ;
		/* Added flag to drop to 1x speed if too many errors */
		mcdDouble = 1;
	} else
		mcd_info.speed = 1;
	sprintf(msg, " mcd: Mitsumi %s Speed CD-ROM at port=0x%x,"
		" irq=%d\n", mcd_info.speed == 1 ? "Single" : "Double",
		mcd_port, mcd_irq);

	request_region(mcd_port, 4, "mcd");

	outb(MCMD_CONFIG_DRIVE, MCDPORT(0));
	outb(0x02, MCDPORT(0));
	outb(0x00, MCDPORT(0));
	getValue(result);

	outb(MCMD_CONFIG_DRIVE, MCDPORT(0));
	outb(0x10, MCDPORT(0));
	outb(0x04, MCDPORT(0));
	getValue(result);

	mcd_invalidate_buffers();
	mcdPresent = 1;

	mcd_info.dev = mk_kdev(MAJOR_NR, 0);

	if (register_cdrom(&mcd_info) != 0) {
		printk(KERN_ERR "mcd: Unable to register Mitsumi CD-ROM.\n");
		cleanup(3);
		return -EIO;
	}
	devfs_plain_cdrom(&mcd_info, &mcd_bdops);
	printk(msg);

	return 0;
}
Beispiel #4
0
static int __init mcdx_init_drive(int drive)
{
	struct s_version version;
	struct gendisk *disk;
	struct s_drive_stuff *stuffp;
	int size = sizeof(*stuffp);
	char msg[80];

	xtrace(INIT, "init() try drive %d\n", drive);

	xtrace(INIT, "kmalloc space for stuffpt's\n");
	xtrace(MALLOC, "init() malloc %d bytes\n", size);
	if (!(stuffp = kzalloc(size, GFP_KERNEL))) {
		xwarn("init() malloc failed\n");
		return 1;
	}

	disk = alloc_disk(1);
	if (!disk) {
		xwarn("init() malloc failed\n");
		kfree(stuffp);
		return 1;
	}

	xtrace(INIT, "init() got %d bytes for drive stuff @ %p\n",
	       sizeof(*stuffp), stuffp);

	/* set default values */
	stuffp->present = 0;	/* this should be 0 already */
	stuffp->toc = NULL;	/* this should be NULL already */

	/* setup our irq and i/o addresses */
	stuffp->irq = irq(mcdx_drive_map[drive]);
	stuffp->wreg_data = stuffp->rreg_data = port(mcdx_drive_map[drive]);
	stuffp->wreg_reset = stuffp->rreg_status = stuffp->wreg_data + 1;
	stuffp->wreg_hcon = stuffp->wreg_reset + 1;
	stuffp->wreg_chn = stuffp->wreg_hcon + 1;

	init_waitqueue_head(&stuffp->busyq);
	init_waitqueue_head(&stuffp->lockq);
	init_waitqueue_head(&stuffp->sleepq);

	/* check if i/o addresses are available */
	if (!request_region(stuffp->wreg_data, MCDX_IO_SIZE, "mcdx")) {
		xwarn("0x%03x,%d: Init failed. "
		      "I/O ports (0x%03x..0x%03x) already in use.\n",
		      stuffp->wreg_data, stuffp->irq,
		      stuffp->wreg_data,
		      stuffp->wreg_data + MCDX_IO_SIZE - 1);
		xtrace(MALLOC, "init() free stuffp @ %p\n", stuffp);
		kfree(stuffp);
		put_disk(disk);
		xtrace(INIT, "init() continue at next drive\n");
		return 0;	/* next drive */
	}

	xtrace(INIT, "init() i/o port is available at 0x%03x\n"
	       stuffp->wreg_data);
	xtrace(INIT, "init() hardware reset\n");
	mcdx_reset(stuffp, HARD, 1);

	xtrace(INIT, "init() get version\n");
	if (-1 == mcdx_requestversion(stuffp, &version, 4)) {
		/* failed, next drive */
		release_region(stuffp->wreg_data, MCDX_IO_SIZE);
		xwarn("%s=0x%03x,%d: Init failed. Can't get version.\n",
		      MCDX, stuffp->wreg_data, stuffp->irq);
		xtrace(MALLOC, "init() free stuffp @ %p\n", stuffp);
		kfree(stuffp);
		put_disk(disk);
		xtrace(INIT, "init() continue at next drive\n");
		return 0;
	}

	switch (version.code) {
	case 'D':
		stuffp->readcmd = READ2X;
		stuffp->present = DOUBLE | DOOR | MULTI;
		break;
	case 'F':
		stuffp->readcmd = READ1X;
		stuffp->present = SINGLE | DOOR | MULTI;
		break;
	case 'M':
		stuffp->readcmd = READ1X;
		stuffp->present = SINGLE;
		break;
	default:
		stuffp->present = 0;
		break;
	}

	stuffp->playcmd = READ1X;

	if (!stuffp->present) {
		release_region(stuffp->wreg_data, MCDX_IO_SIZE);
		xwarn("%s=0x%03x,%d: Init failed. No Mitsumi CD-ROM?.\n",
		      MCDX, stuffp->wreg_data, stuffp->irq);
		kfree(stuffp);
		put_disk(disk);
		return 0;	/* next drive */
	}

	xtrace(INIT, "init() register blkdev\n");
	if (register_blkdev(MAJOR_NR, "mcdx")) {
		release_region(stuffp->wreg_data, MCDX_IO_SIZE);
		kfree(stuffp);
		put_disk(disk);
		return 1;
	}

	mcdx_queue = blk_init_queue(do_mcdx_request, &mcdx_lock);
	if (!mcdx_queue) {
		unregister_blkdev(MAJOR_NR, "mcdx");
		release_region(stuffp->wreg_data, MCDX_IO_SIZE);
		kfree(stuffp);
		put_disk(disk);
		return 1;
	}

	xtrace(INIT, "init() subscribe irq and i/o\n");
	if (request_irq(stuffp->irq, mcdx_intr, IRQF_DISABLED, "mcdx", stuffp)) {
		release_region(stuffp->wreg_data, MCDX_IO_SIZE);
		xwarn("%s=0x%03x,%d: Init failed. Can't get irq (%d).\n",
		      MCDX, stuffp->wreg_data, stuffp->irq, stuffp->irq);
		stuffp->irq = 0;
		blk_cleanup_queue(mcdx_queue);
		kfree(stuffp);
		put_disk(disk);
		return 0;
	}

	xtrace(INIT, "init() get garbage\n");
	{
		int i;
		mcdx_delay(stuffp, HZ / 2);
		for (i = 100; i; i--)
			(void) inb(stuffp->rreg_status);
	}


#ifdef WE_KNOW_WHY
	/* irq 11 -> channel register */
	outb(0x50, stuffp->wreg_chn);
#endif

	xtrace(INIT, "init() set non dma but irq mode\n");
	mcdx_config(stuffp, 1);

	stuffp->info.ops = &mcdx_dops;
	stuffp->info.speed = 2;
	stuffp->info.capacity = 1;
	stuffp->info.handle = stuffp;
	sprintf(stuffp->info.name, "mcdx%d", drive);
	disk->major = MAJOR_NR;
	disk->first_minor = drive;
	strcpy(disk->disk_name, stuffp->info.name);
	disk->fops = &mcdx_bdops;
	disk->flags = GENHD_FL_CD;
	stuffp->disk = disk;

	sprintf(msg, " mcdx: Mitsumi CD-ROM installed at 0x%03x, irq %d."
		" (Firmware version %c %x)\n",
		stuffp->wreg_data, stuffp->irq, version.code, version.ver);
	mcdx_stuffp[drive] = stuffp;
	xtrace(INIT, "init() mcdx_stuffp[%d] = %p\n", drive, stuffp);
	if (register_cdrom(&stuffp->info) != 0) {
		printk("Cannot register Mitsumi CD-ROM!\n");
		free_irq(stuffp->irq, NULL);
		release_region(stuffp->wreg_data, MCDX_IO_SIZE);
		kfree(stuffp);
		put_disk(disk);
		if (unregister_blkdev(MAJOR_NR, "mcdx") != 0)
			xwarn("cleanup() unregister_blkdev() failed\n");
		blk_cleanup_queue(mcdx_queue);
		return 2;
	}
	disk->private_data = stuffp;
	disk->queue = mcdx_queue;
	add_disk(disk);
	printk(msg);
	return 0;
}
Beispiel #5
0
static void
ps2cdvd_state_machine(struct ps2cdvd_event* ev)
{
	unsigned long flags;
	int old_state = ps2cdvd_state;
	int new_state = ps2cdvd_state;
	struct sbr_common_arg *carg = ev->arg;

	DPRINT(DBG_STATE, "state: %s event: %s\n",
	       ps2cdvd_getstatestr(old_state),
	       ps2cdvd_geteventstr(ev->type));

	save_flags(flags);
	cli();

	switch (ps2cdvd_state) {
	case STAT_WAIT_DISC:
	  switch (ev->type) {
	  case EV_START:
	  case EV_TIMEOUT:
	    if (ps2cdvd_lowlevel_lock() < 0) {
	      /* waiting for unlock... */
	      RESET_TIMER();
	      SET_TIMER(HZ * ps2cdvd_check_interval);
	    } else {
	      new_state = STAT_INIT_TRAYSTAT;
	    }
	    break;
	  case EV_INTR:
	  }
	  break;
	case STAT_INIT_TRAYSTAT:
	  switch (ev->type) {
	  case EV_START:
	  case EV_TIMEOUT:
	    break;
	  case EV_INTR:
	    {
	      if (carg->result != 0) {
		new_state = STAT_WAIT_DISC;
	      } else {
		new_state = STAT_CHECK_DISCTYPE;
	      }
	    }
	    break;
	  }
	  break;
	case STAT_CHECK_DISCTYPE:
	  switch (ev->type) {
	  case EV_START:
	  case EV_TIMEOUT:
	    break;
	  case EV_INTR:
	    disc_type = carg->result;
	    DPRINT(DBG_INFO, "ps2cdvd_getdisctype()='%s', %d\n",
		   ps2cdvd_getdisctypestr(disc_type), disc_type);
	    switch (disc_type) {
	    case SCECdPS2CDDA:		/* PS2 CD DA */
	    case SCECdPS2CD:		/* PS2 CD */
	    case SCECdPSCDDA:		/* PS CD DA */
	    case SCECdPSCD:		/* PS CD */
	    case SCECdCDDA:		/* CD DA */
	    case SCECdPS2DVD:		/* PS2 DVD */
	    case SCECdDVDV:		/* DVD video */
	      new_state = STAT_INIT_CHECK_READY;
	      break;
	    case SCECdDETCTDVDD:	/* DVD-dual detecting */
	    case SCECdDETCTDVDS:	/* DVD-single detecting */
	    case SCECdDETCTCD:		/* CD detecting */
	    case SCECdDETCT:		/* detecting */
	    case SCECdNODISC:		/* no disc */
	      new_state = disc_locked ? STAT_INVALID_DISC : STAT_WAIT_DISC;
	      break;
	    case SCECdIllgalMedia:	/* illegal media */
	    case SCECdUNKNOWN:		/* unknown */
	      printk(KERN_CRIT "ps2cdvd: illegal media\n");
	      new_state = disc_locked ? STAT_INVALID_DISC : STAT_WAIT_DISC;
	      break;
	    }
	    break;
	  }
	  break;
	case STAT_INIT_CHECK_READY:
	  switch (ev->type) {
	  case EV_START:
	  case EV_TIMEOUT:
	    break;
	  case EV_INTR:
	    prev_read = jiffies;
	    if (carg->result == SCECdComplete) {
	      switch (disc_type) {
	      case SCECdPS2CDDA:	/* PS2 CD DA */
	      case SCECdPS2CD:		/* PS2 CD */
	      case SCECdPSCDDA:		/* PS CD DA */
	      case SCECdPSCD:		/* PS CD */
	      case SCECdCDDA:		/* CD DA */
		new_state = STAT_TOC_READ;
		media_mode = SCECdCD;
		break;
	      case SCECdPS2DVD:		/* PS2 DVD */
	      case SCECdDVDV:		/* DVD video */
		new_state = STAT_SET_MMODE;
		media_mode = SCECdDVD;
		break;
	      default:
		printk(KERN_CRIT "ps2cdvd: internal error at %s(%d)\n",
		       __FILE__, __LINE__);
		new_state = disc_locked ? STAT_INVALID_DISC : STAT_WAIT_DISC;
		break;
	      }
	    } else {
	      new_state = STAT_WAIT_DISC;
	    }
	    break;
	  }
	  break;
	case STAT_TOC_READ:
	  switch (ev->type) {
	  case EV_START:
	  case EV_TIMEOUT:
	    break;
	  case EV_INTR:
	    if (carg->result < 0) {
	      DPRINT(DBG_DIAG, "gettoc() failed\n");
	      new_state = disc_locked ? STAT_INVALID_DISC : STAT_WAIT_DISC;
	    } else {
	      struct ps2cdvd_tocentry *toc;

	      toc_valid = 1;
	      toc = (struct ps2cdvd_tocentry *)tocbuf;
	      leadout_start = msftolba(decode_bcd(toc[2].abs_msf[0]),
				       decode_bcd(toc[2].abs_msf[1]),
				       decode_bcd(toc[2].abs_msf[2]));
#ifdef PS2CDVD_DEBUG
	      if (ps2cdvd_debug & DBG_INFO) {
		struct sbr_cdvd_gettoc_arg *arg = carg->arg;
		if (arg->media == 0) {
		  ps2cdvd_tocdump(DBG_LOG_LEVEL "ps2cdvd: ",
				  (struct ps2cdvd_tocentry *)tocbuf);
		} else {
		  /*
		   * we have no interrest in DVD Physical format information
		   ps2cdvd_hexdump(tocbuf, len);
		  */
		}
	      }
#endif
	      new_state = STAT_SET_MMODE;
	    }
	    break;
	  }
	  break;
	case STAT_SET_MMODE:
	  switch (ev->type) {
	  case EV_START:
	  case EV_TIMEOUT:
	    break;
	  case EV_INTR:
	    if (carg->result == 0) {
	      DPRINT(DBG_DIAG, "set_mmode() failed\n");
	      new_state = disc_locked ? STAT_INVALID_DISC : STAT_WAIT_DISC;
	    } else {
	      switch (disc_type) {
	      case SCECdPS2DVD:		/* PS2 DVD */
	      case SCECdPS2CDDA:		/* PS2 CD DA */
	      case SCECdPS2CD:		/* PS2 CD */
	      case SCECdPSCDDA:		/* PS CD DA */
	      case SCECdPSCD:		/* PS CD */
		new_state = STAT_LABEL_READ;
		break;
	      case SCECdDVDV:		/* DVD video */
	      case SCECdCDDA:		/* CD DA */
		if (disc_locked && disc_lock_key_valid) {
		  new_state = STAT_LABEL_READ;
		} else {
		  disc_changed++;
		  new_state = STAT_READY;
		}
		break;
	      default:
		printk(KERN_CRIT "ps2cdvd: internal error at %s(%d)\n",
		       __FILE__, __LINE__);
		new_state = disc_locked ? STAT_INVALID_DISC : STAT_WAIT_DISC;
		break;
	      }
	      break;
	    }
	  }
	  break;
	case STAT_LABEL_READ:
	  switch (ev->type) {
	  case EV_START:
	  case EV_TIMEOUT:
	    break;
	  case EV_INTR:
	    new_state = STAT_LABEL_READ_ERROR_CHECK;
	    break;
	  }
	  break;
	case STAT_LABEL_READ_ERROR_CHECK:
	  switch (ev->type) {
	  case EV_START:
	  case EV_TIMEOUT:
	    break;
	  case EV_INTR:
	    if (carg->result != SCECdErNO) {
	      DPRINT(DBG_DIAG, "error: %s, code=0x%02x\n",
		     ps2cdvd_geterrorstr(carg->result),
		     carg->result);
	      if (disc_locked && disc_lock_key_valid) {
		printk(KERN_CRIT "ps2cdvd: =============================\n");
		printk(KERN_CRIT "ps2cdvd:          wrong disc.         \n");
		printk(KERN_CRIT "ps2cdvd: =============================\n");
		if (!ps2cdvd_wrong_disc_retry) {
		  INIT_REQUEST;
		  while (CURRENT)
		    end_request(0);
		  disc_changed++;
		}
	      }
	      new_state = disc_locked ? STAT_INVALID_DISC : STAT_WAIT_DISC;
	    } else {
	      unsigned long sum;
#ifdef PS2CDVD_DEBUG
	      struct iso_primary_descriptor *label;
	      label = (struct iso_primary_descriptor*)labelbuf;

	      if (ps2cdvd_debug & DBG_INFO) {
		printk(DBG_LOG_LEVEL "ps2cdvd: ");
		print_isofsstr(label->system_id, sizeof(label->system_id));
		print_isofsstr(label->volume_id, sizeof(label->volume_id));
		print_isofsstr(label->volume_set_id,
			       sizeof(label->volume_set_id));
		print_isofsstr(label->publisher_id,
			       sizeof(label->publisher_id));
		print_isofsstr(label->application_id,
			       sizeof(label->application_id));
		printk("\n");

		/* ps2cdvd_hexdump(DBG_LOG_LEVEL "ps2cdvd: ", labelbuf, 2048);
		 */
	      }
#endif
	      label_valid = 1;
	      DPRINT(DBG_DLOCK, "label is valid\n");
	      sum = checksum((u_long*)labelbuf, 2048/sizeof(u_long));
	      if (disc_lock_key_valid &&
		  disc_locked &&
		  disc_lock_key != sum) {
		printk(KERN_CRIT "ps2cdvd: =============================\n");
		printk(KERN_CRIT "ps2cdvd:          wrong disc.         \n");
		printk(KERN_CRIT "ps2cdvd: =============================\n");
		if (!ps2cdvd_wrong_disc_retry) {
		  INIT_REQUEST;
		  while (CURRENT)
		    end_request(0);
		  disc_changed++;
		}
		new_state = STAT_INVALID_DISC;
	      } else {
		disc_lock_key = sum;
		if (!disc_lock_key_valid && disc_locked) {
		  DPRINT(DBG_DLOCK, "disc lock key=%lX\n", sum);
		}
		disc_lock_key_valid = 1;
		new_state = STAT_READY;
	      }
	    }
	    break;
	  }
	  break;
	case STAT_READY:
	  switch (ev->type) {
	  case EV_TICK:
	    if (CURRENT == NULL || ps2sif_iswaiting(ps2cdvd_lock)) {
	      break;
	    }
	    /* fall through */
	  case EV_START:
	  case EV_TIMEOUT:
	    if (ps2cdvd_lowlevel_lock() < 0) {
	      /* waiting for unlock... */
	      RESET_TIMER();
	      SET_TIMER(HZ * ps2cdvd_check_interval);
	      break;
	    }
	    if (CURRENT == NULL) {
	      if (ps2cdvd_spindown * HZ < jiffies - prev_read) {
		new_state = STAT_SPINDOWN;
	      } else {
		/* nothing to do */
		ps2cdvd_lowlevel_unlock();
		RESET_TIMER();
		SET_TIMER(HZ * ps2cdvd_check_interval);
	      }
	    } else {
	      prev_read = jiffies;
	      if (jiffies - prev_tray_check < HZ/2) {
		new_state = STAT_READ;
	      } else {
		prev_tray_check = jiffies;
		new_state = STAT_CHECK_TRAY;
	      }
	    }
	    break;
	  case EV_INTR:
	    break;
	  }
	  break;
	case STAT_CHECK_TRAY:
	  switch (ev->type) {
	  case EV_START:
	  case EV_TIMEOUT:
	    break;
	  case EV_INTR:
	    if (carg->result < 0) {
	      new_state = STAT_ERROR;
	    } else {
	      struct sbr_cdvd_trayreq_arg *arg = carg->arg;
	      if (arg->traycount != 0) {
		if (disc_locked) {
		  printk(KERN_CRIT"ps2cdvd: =============================\n");
		  printk(KERN_CRIT"ps2cdvd: the disc is currently locked.\n");
		  printk(KERN_CRIT"ps2cdvd: please don't take it away!\n");
		  printk(KERN_CRIT"ps2cdvd: =============================\n");
		}

		invalidate_discinfo();

		new_state = STAT_CHECK_DISCTYPE;
	      } else {
		if (CURRENT) {
		  new_state = STAT_READ;
		} else {
		  new_state = STAT_READY;
		}
	      }
	    }
	    break;
	  }
	  break;
	case STAT_READ:
	  switch (ev->type) {
	  case EV_START:
	  case EV_TIMEOUT:
	    break;
	  case EV_INTR:
	    new_state = STAT_READ_ERROR_CHECK;
	    break;
	  }
	  break;
	case STAT_READ_EOM_RETRY:
	  switch (ev->type) {
	  case EV_START:
	  case EV_TIMEOUT:
	    break;
	  case EV_INTR:
	    new_state = STAT_READ_ERROR_CHECK;
	    break;
	  }
	  break;
	case STAT_READ_ERROR_CHECK:
	  switch (ev->type) {
	  case EV_START:
	  case EV_TIMEOUT:
	    break;
	  case EV_INTR:
	    if (carg->result == SCECdErNO) {
	      /*
	       * succeeded
	       */
	      while (CURRENT != NULL &&
		     ps2cdvd_databuf_addr <= CURRENT->sector/4 &&
		     CURRENT->sector/4 < ps2cdvd_databuf_addr + ps2cdvd_databuf_size) {
		memcpy(CURRENT->buffer,
		       ps2cdvd_databuf + DATA_SECT_SIZE * (CURRENT->sector/4 - ps2cdvd_databuf_addr),
		       DATA_SECT_SIZE);
		end_request(1);
	      }
	      if (!ps2sif_iswaiting(ps2cdvd_lock) && CURRENT != NULL) {
		/* tiny acceleration */
		new_state = STAT_READ;
	      } else {
		new_state = STAT_READY;
	      }
	    } else
	    if (carg->result == 0x38) {
	      /*
	       * sector format error
	       */
	      DPRINT(DBG_DIAG,
		     "error: sector format error, code=0x38 (ignored)\n");
	      memset(CURRENT->buffer, 0, DATA_SECT_SIZE);
	      end_request(1);
	      if (!ps2sif_iswaiting(ps2cdvd_lock) && CURRENT != NULL) {
		/* tiny acceleration */
		new_state = STAT_READ;
	      } else {
		new_state = STAT_READY;
	      }
	    } else
	    if (carg->result == SCECdErEOM &&
		ps2cdvd_databuf_addr != 
			CURRENT->sector/4 - ps2cdvd_databuf_size + 1 &&
		ps2cdvd_databuf_size <= CURRENT->sector/4) {
	      /* you got End Of Media and you have not retried */
	      DPRINT(DBG_DIAG, "error: %s, code=0x%02x (retry...)\n",
		     ps2cdvd_geterrorstr(carg->result),
		     carg->result);
	      new_state = STAT_READ_EOM_RETRY;
	    } else {
	      DPRINT(DBG_DIAG, "error: %s, code=0x%02x\n",
		     ps2cdvd_geterrorstr(carg->result),
		     carg->result);
	      ps2cdvd_databuf_addr = -1;
	      end_request(0);		/* I/O error */
	      new_state = STAT_READY;
	    }
	    break;
	  }
	  break;
	case STAT_INVALID_DISC:
	  switch (ev->type) {
	  case EV_START:
	  case EV_TIMEOUT:
	    if (ps2cdvd_lowlevel_lock() < 0) {
	      /* waiting for unlock... */
	      RESET_TIMER();
	      SET_TIMER(HZ * ps2cdvd_check_interval);
	    } else {
	      new_state = STAT_CHECK_DISCTYPE;
	    }
	    break;
	  case EV_INTR:
	    break;
	  }
	  break;
	case STAT_SPINDOWN:
	  switch (ev->type) {
	  case EV_START:
	  case EV_TIMEOUT:
	    break;
	  case EV_INTR:
	    if (CURRENT == NULL) {
	      new_state = STAT_IDLE;
	    } else {
	      DPRINT(DBG_VERBOSE, "re-spinup...\n");
	      new_state = STAT_CHECK_DISCTYPE;
	    }
	    break;
	  }
	  break;
	case STAT_IDLE:
	  switch (ev->type) {
	  case EV_START:
	  case EV_TIMEOUT:
	    if (ps2cdvd_lowlevel_lock() < 0) {
	      /* waiting for unlock... */
	      RESET_TIMER();
	      SET_TIMER(HZ * ps2cdvd_check_interval);
	    } else {
	      new_state = STAT_CHECK_DISCTYPE;
	    }
	    break;
	  case EV_INTR:
	    break;
	  }
	  break;
	case STAT_ERROR:
	  break;
	default:
	  printk(KERN_ERR "ps2cdvd: invalid state");
	  ps2cdvd_state = STAT_WAIT_DISC;
	  break;
	}

	if (new_state != old_state) {
		struct ps2cdvd_event tick;
		tick.type = EV_TICK;
		ps2cdvd_leave(old_state);
		ps2cdvd_state = ps2cdvd_enter(new_state);
		DPRINT(DBG_STATE, "  -> new state: %s\n",
		       ps2cdvd_getstatestr(ps2cdvd_state));
		if (old_state != ps2cdvd_state &&
		    ps2cdvd_state == STAT_READY) {
			ps2cdvd_state_machine(&tick);
		}
		wake_up_interruptible(&statq);
	}

	restore_flags(flags);
}

__initfunc(int ps2cdvd_init(void))
{
	int res;

	DPRINT(DBG_VERBOSE, "init: get lock\n");
	if ((ps2cdvd_lock = ps2sif_getlock(PS2LOCK_CDVD)) == NULL) {
		printk(KERN_ERR "ps2cdvd: Can't get lock\n");
		ps2cdvd_cleanup();
		return -EINVAL;
	}
	ps2sif_lockqueueinit(&ps2cdvd_lock_qi);
	ps2cdvd_lock_qi.name = "ps2cdvd";

	DPRINT(DBG_VERBOSE, "init: initialize timer\n");
	init_timer(&io_timer);
	io_timer.function = (void(*)(u_long))ps2cdvd_timer;
	ps2cdvd_state = STAT_WAIT_DISC;

	DPRINT(DBG_VERBOSE, "init: allocate diaklabel buffer\n");
	labelbuf = kmalloc(2048, GFP_KERNEL);
	if (labelbuf == NULL) {
		printk(KERN_ERR "ps2cdvd: Can't allocate buffer\n");
		ps2cdvd_cleanup();
		return -ENOMEM;
	}
	initialized |= INIT_LABELBUF;

	DPRINT(DBG_VERBOSE, "allocate buffer\n");
	ps2cdvd_databufx = kmalloc(ps2cdvd_databuf_size * AUDIO_SECT_SIZE +
				   BUFFER_ALIGNMENT, GFP_KERNEL);
	if (ps2cdvd_databufx == NULL) {
		printk(KERN_ERR "ps2cdvd: Can't allocate buffer\n");
		ps2cdvd_cleanup();
		return -ENOMEM;
	}
	ps2cdvd_databuf = ALIGN(ps2cdvd_databufx, BUFFER_ALIGNMENT);
	initialized |= INIT_DATABUF;

	DPRINT(DBG_VERBOSE, "init: call sbios\n");
	if (ps2cdvdcall_init()) {
		printk(KERN_ERR "ps2cdvd: Can't initialize CD/DVD-ROM subsystem\n");
		ps2cdvd_cleanup();
		return -EIO;
	}
#ifdef CONFIG_PS2_SBIOS_VER_CHECK
	if (0x0201 <= sbios(SB_GETVER, NULL))
		ps2cdvdcall_reset();
#else
	ps2cdvdcall_reset();
#endif
	initialized |= INIT_IOPSIDE;

	DPRINT(DBG_VERBOSE, "init: register block device\n");
	if ((res = register_blkdev(MAJOR_NR, "ps2cdvd", &cdrom_fops)) < 0) {
		printk(KERN_ERR "ps2cdvd: Unable to get major %d for PS2 CD/DVD-ROM\n",
		       MAJOR_NR);
		ps2cdvd_cleanup();
                return -EIO;
	}
	if (MAJOR_NR == 0) MAJOR_NR = res;
	initialized |= INIT_BLKDEV;

	blksize_size[MAJOR_NR] = ps2cdvd_blocksizes;
	hardsect_size[MAJOR_NR] = ps2cdvd_hardsectsizes;
	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
	read_ahead[MAJOR_NR] = ps2cdvd_read_ahead;

	DPRINT(DBG_VERBOSE, "init: register cdrom\n");
	ps2cdvd_info.dev = MKDEV(MAJOR_NR, 0);
        if (register_cdrom(&ps2cdvd_info) != 0) {
		printk(KERN_ERR "ps2cdvd: Cannot register PS2 CD/DVD-ROM\n");
		ps2cdvd_cleanup();
		return -EIO;
        }
	initialized |= INIT_CDROM;

	printk(KERN_INFO "PS2 CD/DVD-ROM driver\n");

	if (ps2cdvd_lowlevel_lock() == 0)
	  ps2cdvd_state = ps2cdvd_enter(STAT_INIT_TRAYSTAT);
	else
	  ps2cdvd_state = ps2cdvd_enter(STAT_WAIT_DISC);

	return 0;
}