Beispiel #1
0
static int __init
ps2mcfs_init()
{
	TRACE("ps2mcfs_init()\n");
	printk("PlayStation 2 Memory Card file system\n");
	init_completion(&thread_comp);

	if ((ps2mcfs_lock = ps2sif_getlock(PS2LOCK_MC)) == NULL) {
		printk(KERN_ERR "ps2mcfs: Can't get lock\n");
		return (-1);
	}
#ifdef PS2MCFS_DEBUG
	if (ps2mcfs_debug & DBG_LOCK) {
		oldflags = ps2sif_getlockflags(ps2mcfs_lock);
		ps2sif_setlockflags(ps2mcfs_lock,
				    (oldflags | PS2LOCK_FLAG_DEBUG));
	}
#endif

	if (ps2sif_lock_interruptible(ps2mcfs_lock, "mcfs init") < 0)
		return (-1);

	if (ps2mcfs_init_filebuf() < 0 ||
	    ps2mcfs_init_pathcache() < 0 ||
	    ps2mcfs_init_fdcache() < 0 ||
	    ps2mcfs_init_dirent() < 0 ||
	    ps2mcfs_init_root() < 0)
		goto error;

	/*
	 * hook block device read/write routine
	 */
	if (ps2mc_blkrw_hook == NULL)
		ps2mc_blkrw_hook = ps2mcfs_blkrw;

	/*
	 * create and start thread
	 */
	kernel_thread(ps2mcfs_thread, NULL, 0);
	wait_for_completion(&thread_comp);	/* wait the thread ready */
                
	if (register_filesystem(&ps2mcfs_fs_type) < 0)
		goto error;

	ps2sif_unlock(ps2mcfs_lock);

	return (0);

 error:
	ps2mcfs_cleanup();
	ps2sif_unlock(ps2mcfs_lock);

	return (-1);
}
Beispiel #2
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;
}