コード例 #1
0
ファイル: basic_protocol.c プロジェクト: AmirAbrams/haiku
bool
send_command(ide_device_info *device, ide_qrequest *qrequest,
	bool need_drdy, uint32 timeout, ide_bus_state new_state)
{
	ide_bus_info *bus = device->bus;
	bigtime_t irq_disabled_at = 0; // make compiler happy
	uint8 num_retries = 0;
	bool irq_guard;

retry:
	irq_guard = bus->num_running_reqs > 1;

	SHOW_FLOW(3, "qrequest=%p, request=%p", qrequest,
		qrequest ? qrequest->request : NULL);

	// if there are pending requests, IRQs must be disabled to
	// not mix up IRQ reasons
	// XXX can we avoid that with the IDE_LOCK trick? It would
	//     save some work and the bug workaround!
	if (irq_guard) {
		if (bus->controller->write_device_control(bus->channel_cookie,
				ide_devctrl_nien | ide_devctrl_bit3) != B_OK)
			goto err;

		irq_disabled_at = system_time();
	}

	// select device
	if (bus->controller->write_command_block_regs(bus->channel_cookie, &device->tf,
			ide_mask_device_head) != B_OK)
		goto err;

	bus->active_device = device;

	if (!ide_wait(device, 0, ide_status_bsy | ide_status_drq, false, 50000)) {
		uint8 status;

		SHOW_FLOW0(1, "device is not ready");

		status = bus->controller->get_altstatus(bus->channel_cookie);
		if (status == 0xff) {
			// there is no device (should happen during detection only)
			SHOW_FLOW0(1, "there is no device");

			// device detection recognizes this code as "all hope lost", so
			// neither replace it nor use it anywhere else
			device->subsys_status = SCSI_TID_INVALID;
			return false;
		}

		// reset device and retry
		if (reset_device(device, qrequest) && ++num_retries <= MAX_FAILED_SEND) {
			SHOW_FLOW0(1, "retrying");
			goto retry;
		}

		SHOW_FLOW0(1, "giving up");

		// reset to often - abort request
		device->subsys_status = SCSI_SEL_TIMEOUT;
		return false;
	}

	if (need_drdy
		&& (bus->controller->get_altstatus(bus->channel_cookie) & ide_status_drdy) == 0) {
		SHOW_FLOW0(3, "drdy not set");
		device->subsys_status = SCSI_SEQUENCE_FAIL;
		return false;
	}

	// write parameters
	if (bus->controller->write_command_block_regs(bus->channel_cookie, &device->tf,
			device->tf_param_mask) != B_OK)
		goto err;

	if (irq_guard) {
		// IRQ may be fired by service requests and by the process of disabling(!)
		// them (I heard this is caused by edge triggered PCI IRQs)

		// wait at least 50 µs to catch all pending irq's
		// (at my system, up to 30 µs elapsed)

		// additionally, old drives (at least my IBM-DTTA-351010) loose
		// sync if they are pushed too hard - on heavy overlapped write
		// stress this drive tends to forget outstanding requests,
		// waiting at least 50 µs seems(!) to solve this
		while (system_time() - irq_disabled_at < MAX_IRQ_DELAY)
			spin(1);
	}

	// if we will start waiting once the command is sent, we have to
	// lock the bus before sending; this way, IRQs that are fired
	// shortly before/after sending of command are delayed until the
	// command is really sent (start_waiting unlocks the bus) and then
	// the IRQ handler can check savely whether the IRQ really signals
	// finishing of command or not by testing the busy-signal of the device
	if (new_state != ide_state_accessing) {
		IDE_LOCK(bus);
	}

	if (irq_guard) {
		// now it's clear why IRQs gets fired, so we can enable them again
		if (bus->controller->write_device_control(bus->channel_cookie,
				ide_devctrl_bit3) != B_OK)
			goto err1;
	}

	// write command code - this will start the actual command
	SHOW_FLOW(3, "Writing command 0x%02x", (int)device->tf.write.command);
	if (bus->controller->write_command_block_regs(bus->channel_cookie,
			&device->tf, ide_mask_command) != B_OK)
		goto err1;

	// start waiting now; also un-blocks IRQ handler (see above)
	if (new_state != ide_state_accessing)
		start_waiting(bus, timeout, new_state);

	return true;

err1:
	if (timeout > 0) {
		bus->state = ide_state_accessing;
		IDE_UNLOCK(bus);
	}

err:
	device->subsys_status = SCSI_HBA_ERR;
	return false;
}
コード例 #2
0
ファイル: ide_sim.c プロジェクト: HTshandou/newos
static inline void scsi_io( ide_bus_info *bus, CCB_HEADER *ccb )
{
	CCB_SCSIIO *request = (CCB_SCSIIO *)ccb;
	ide_device_info *device;
	bool queuable;
	ide_qrequest *qrequest;
	//ide_request_priv *priv;

	if( ccb->cam_target_id >= 2 || ccb->cam_target_lun != 0 )
		goto err_inv_device;

	device = bus->devices[ccb->cam_target_id];		
	if( device == NULL )
		goto err_inv_device;
	
	queuable = is_queuable( device, request );
	
	IDE_LOCK( bus );
	
	if( bus->state != ide_state_idle ) 
		goto err_bus_busy;

	if( device->free_qrequests == NULL ||
		(device->num_running_reqs > 0 && !queuable )) 
		goto err_device_busy;
	
	bus->state = ide_state_accessing;

	IDE_UNLOCK( bus );
	
	qrequest = device->free_qrequests;
	device->free_qrequests = qrequest->next;
	
	qrequest->request = request;
	qrequest->queuable = queuable;
	qrequest->running = true;

	/*priv = get_request_priv( request );
	priv->qrequest = qrequest;
	priv->device = device;*/
	
	++device->num_running_reqs;
	++bus->num_running_reqs;
	bus->active_qrequest = qrequest;

	device->exec_io( device, qrequest );
	
	return;
	
err_inv_device:
	ccb->cam_status = CAM_SEL_TIMEOUT;
	xpt->done( ccb );
	return;
	
err_bus_busy:
	//xpt->block_bus( bus->xpt_cookie );
	
	ccb->cam_status = CAM_BUS_QUEUE_FULL;
	xpt->done( ccb );
	IDE_UNLOCK( bus );
	return;
	
err_device_busy:
	//xpt->block_device( bus->xpt_cookie, ccb->cam_target_id, 0 );
		
	ccb->cam_status = CAM_DEV_QUEUE_FULL;
	xpt->done( ccb );
	IDE_UNLOCK( bus );
	return;
}
コード例 #3
0
ファイル: devices.c プロジェクト: mmanley/Antares
static bool
scan_device_int(ide_device_info *device, bool atapi)
{
	ide_bus_info *bus = device->bus;
	int status;

	TRACE("scan_device_int: device %p, atapi %d\n", device, atapi);

	device->tf_param_mask = 0;
	device->tf.write.command = atapi ? IDE_CMD_IDENTIFY_PACKET_DEVICE
		: IDE_CMD_IDENTIFY_DEVICE;

	// initialize device selection flags,
	// this is the only place where this bit gets initialized in the task file
	if (bus->controller->read_command_block_regs(bus->channel_cookie, &device->tf,
			ide_mask_device_head) != B_OK) {
		TRACE("scan_device_int: read_command_block_regs failed\n");
		return false;
	}

	device->tf.lba.device = device->is_device1;

	if (!send_command(device, NULL, atapi ? false : true, 20, ide_state_sync_waiting)) {
		TRACE("scan_device_int: send_command failed\n");
		return false;
	}

	// do a short wait first - if there's no device at all we could wait forever
	// ToDo: have a look at this; if it times out (when the time is too short),
	//		the kernel seems to crash a little later)!
	TRACE("scan_device_int: waiting 100ms...\n");
	if (acquire_sem_etc(bus->sync_wait_sem, 1, B_RELATIVE_TIMEOUT, 100000) == B_TIMED_OUT) {
		bool cont;

		TRACE("scan_device_int: no fast response to inquiry\n");

		// check the busy flag - if it's still set, there's probably no device	
		IDE_LOCK(bus);

		status = bus->controller->get_altstatus(bus->channel_cookie);
		cont = (status & ide_status_bsy) == ide_status_bsy;

		IDE_UNLOCK(bus);

		TRACE("scan_device_int: status %#04x\n", status);

		if (!cont) {
			TRACE("scan_device_int: busy bit not set after 100ms - probably noone there\n");
			// no reaction -> abort waiting
			cancel_irq_timeout(bus);

			// timeout or irq may have been fired, reset semaphore just is case
			acquire_sem_etc(bus->sync_wait_sem, 1, B_RELATIVE_TIMEOUT, 0);

			TRACE("scan_device_int: aborting because busy bit not set\n");
			return false;
		}

		TRACE("scan_device_int: busy bit set, give device more time\n");

		// there is something, so wait for it
		acquire_sem(bus->sync_wait_sem);
	}
	TRACE("scan_device_int: got a fast response\n");

	// cancel the timeout manually; usually this is done by wait_for_sync(), but
	// we've used the wait semaphore directly	
	cancel_irq_timeout(bus);

	if (bus->sync_wait_timeout) {
		TRACE("scan_device_int: aborting on sync_wait_timeout\n");
		return false;
	}

	ide_wait(device, ide_status_drq, ide_status_bsy, true, 1000);

	status = bus->controller->get_altstatus(bus->channel_cookie);

	if ((status & ide_status_err) != 0) {
		// if there's no device, all bits including the error bit are set
		TRACE("scan_device_int: error bit set - no device or wrong type (status: %#04x)\n", status);
		return false;
	}

	// get the infoblock		
	bus->controller->read_pio(bus->channel_cookie, (uint16 *)&device->infoblock, 
		sizeof(device->infoblock) / sizeof(uint16), false);

	if (!wait_for_drqdown(device)) {
		TRACE("scan_device_int: wait_for_drqdown failed\n");
		return false;
	}

	TRACE("scan_device_int: device found\n");

	prep_infoblock(device);
	return true;	
}
コード例 #4
0
ファイル: basic_prot.c プロジェクト: HTshandou/newos
// new_state must be either accessing, async_waiting or sync_waiting
// param_mask must not include command register
bool send_command( ide_device_info *device,
	bool need_drdy, bigtime_t timeout, ide_bus_state new_state )
{
	ide_bus_info *bus = device->bus;
	bigtime_t irq_disabled_at = 0; // make compiler happy

	bool irq_guard = bus->num_running_reqs > 1;

	SHOW_FLOW0( 3, "" );

	reset_timeouts( device );

	if( irq_guard ) {
		if( bus->controller->write_device_control( bus->channel,
			ide_devctrl_nien | ide_devctrl_bit3 ) != NO_ERROR )
			goto err;

		irq_disabled_at = system_time();
	}

	if( bus->controller->write_command_block_regs( bus->channel, &device->tf,
		ide_mask_device_head ) != NO_ERROR )
		goto err;

	SHOW_FLOW0( 3, "1" );

	bus->active_device = device;

	if( !ide_wait( device, 0, ide_status_bsy | ide_status_drq, false, 50000 )) {
		uint8 status;

		SHOW_FLOW0( 1, "device is not ready" );

		status = bus->controller->get_altstatus( bus->channel );
		if( status == 0xff ) {
			// this shouldn't happen unless the device has died
			// as we only submit commands to existing devices
			// (only detection routines shoot at will)
			set_sense( device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_INTERNAL_FAILURE );
			return false;
		}

		if( !reset_bus( device ))
			return false;

		SHOW_FLOW0( 1, "retrying" );

		if( bus->controller->write_command_block_regs( bus->channel,
			 &device->tf, ide_mask_device_head ) != NO_ERROR )
			goto err;

		bus->active_device = device;

		if( !ide_wait( device, 0, ide_status_bsy | ide_status_drq, false, 50000 )) {
			// XXX this is not a device but a bus error, we should return
			// CAM_SEL_TIMEOUT instead
			SHOW_FLOW0( 1, "device is dead" );
			set_sense( device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_LUN_SEL_FAILED );
			return false;
		}
	}

	SHOW_FLOW0( 3, "3" );

	if( need_drdy &&
		(bus->controller->get_altstatus( bus->channel ) & ide_status_drdy) == 0 )
	{
		SHOW_FLOW0( 3, "drdy not set" );
		set_sense( device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_LUN_TIMEOUT );
		return false;
	}

	SHOW_FLOW0( 3, "4" );

	if( bus->controller->write_command_block_regs( bus->channel, &device->tf,
		device->tf_param_mask ) != NO_ERROR )
		goto err;

	SHOW_FLOW0( 3, "5" );

	if( irq_guard ) {
		// enable IRQs now
		// IRQ may be fired by service requests and by the process of disabling(!)
		// them (I heard this is caused by edge triggered PCI IRQs)

		// wait at least 50 µs to catch all pending irq's
		// (at my system, up to 30 µs elapsed)

		// additionally, old drives (at least my IBM-DTTA-351010) loose
		// sync if they are pushed too hard - on heavy overlapped write
		// stress this drive tends to forget outstanding requests,
		// waiting at least 50 µs seems(!) to solve this
		while( system_time() - irq_disabled_at < MAX_IRQ_DELAY )
			cpu_spin( 1 );

	}

	SHOW_FLOW0( 3, "6" );

	if( new_state != ide_state_accessing ) {
		IDE_LOCK( bus );
	}

	SHOW_FLOW( 3, "Writing command %x", (int)device->tf.write.command );
	if( bus->controller->write_command_block_regs( bus->channel,
		&device->tf, ide_mask_command ) != NO_ERROR )
		goto err2;

	SHOW_FLOW0( 3, "7" );

	if( irq_guard ) {
		if( bus->controller->write_device_control( bus->channel,
			ide_devctrl_bit3 ) != NO_ERROR )
			goto err1;
	}

	SHOW_FLOW0( 3, "8" );

	if( new_state != ide_state_accessing ) {
		start_waiting( bus, timeout, new_state );
	}

	SHOW_FLOW0( 3, "9" );

	return true;

err2:
	if( irq_guard )
		bus->controller->write_device_control( bus->channel,
			ide_devctrl_bit3 );

err1:
	if( timeout > 0 ) {
		bus->state = ide_state_accessing;
		IDE_UNLOCK( bus );
	}

err:
	set_sense( device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_INTERNAL_FAILURE );
	return false;
}