1/*
2 * Copyright 2004-2007, Haiku, Inc. All RightsReserved.
3 * Copyright 2002/03, Thomas Kurschel. All rights reserved.
4 *
5 * Distributed under the terms of the MIT License.
6 */
7
8/*
9	Part of Open IDE bus manager
10
11	Converts SCSI commands to ATA commands.
12*/
13
14
15#include "ide_internal.h"
16#include "ide_sim.h"
17#include "ide_cmds.h"
18
19#include <string.h>
20
21
22/** emulate MODE SENSE 10 command */
23
24static void
25ata_mode_sense_10(ide_device_info *device, ide_qrequest *qrequest)
26{
27	scsi_ccb *request = qrequest->request;
28	scsi_cmd_mode_sense_10 *cmd = (scsi_cmd_mode_sense_10 *)request->cdb;
29	scsi_mode_param_header_10 param_header;
30	scsi_modepage_control control;
31	scsi_mode_param_block_desc block_desc;
32	size_t totalLength = sizeof(scsi_mode_param_header_10)
33		+ sizeof(scsi_mode_param_block_desc)
34		+ sizeof(scsi_modepage_control);
35	scsi_mode_param_dev_spec_da devspec = {
36		_res0_0 : 0,
37		dpo_fua : 0,
38		_res0_6 : 0,
39		write_protected : 0
40	};
41	uint32 allocationLength;
42
43	SHOW_FLOW0(1, "Hi!");
44
45	allocationLength = B_BENDIAN_TO_HOST_INT16(cmd->allocation_length);
46
47	// we answer control page requests and "all pages" requests
48	// (as the latter are the same as the first)
49	if ((cmd->page_code != SCSI_MODEPAGE_CONTROL && cmd->page_code != SCSI_MODEPAGE_ALL)
50		|| (cmd->page_control != SCSI_MODE_SENSE_PC_CURRENT
51			&& cmd->page_control != SCSI_MODE_SENSE_PC_SAVED)) {
52		set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD);
53		return;
54	}
55
56	//param_header = (scsi_mode_param_header_10 *)request->data;
57	param_header.mode_data_length = B_HOST_TO_BENDIAN_INT16(totalLength - 1);
58	param_header.medium_type = 0; 		// XXX standard is a bit vague here
59	param_header.dev_spec_parameter = *(uint8 *)&devspec;
60	param_header.block_desc_length
61		= B_HOST_TO_BENDIAN_INT16(sizeof(scsi_mode_param_block_desc));
62
63	copy_sg_data(request, 0, allocationLength, &param_header,
64		sizeof(param_header), false);
65
66	/*block_desc = (scsi_mode_param_block_desc *)(request->data
67		+ sizeof(*param_header));*/
68	memset(&block_desc, 0, sizeof(block_desc));
69	// density is reserved (0), descriptor apply to entire medium (num_blocks=0)
70	// remains the blocklen to be set
71	block_desc.high_blocklen = 0;
72	block_desc.med_blocklen = 512 >> 8;
73	block_desc.low_blocklen = 512 & 0xff;
74
75	copy_sg_data(request, sizeof(param_header), allocationLength,
76		&block_desc, sizeof(block_desc), false);
77
78	/*contr = (scsi_modepage_contr *)(request->data
79		+ sizeof(*param_header)
80		+ ((uint16)param_header->high_block_desc_len << 8)
81		+ param_header->low_block_desc_len);*/
82
83	memset(&control, 0, sizeof(control));
84	control.RLEC = false;
85	control.DQue = !device->CQ_enabled;
86	control.QErr = false;
87		// when a command fails we requeue all
88		// lost commands automagically
89	control.QAM = SCSI_QAM_UNRESTRICTED;
90
91	copy_sg_data(request, sizeof(param_header)
92		+ B_BENDIAN_TO_HOST_INT16(param_header.block_desc_length),
93		allocationLength, &control, sizeof(control), false);
94
95	// the number of bytes that were transferred to buffer is
96	// restricted by allocation length and by request data buffer size
97	totalLength = min(totalLength, allocationLength);
98	totalLength = min(totalLength, request->data_length);
99
100	request->data_resid = request->data_length - totalLength;
101}
102
103
104/*! Emulate modifying control page */
105static bool
106ata_mode_select_control_page(ide_device_info *device, ide_qrequest *qrequest,
107	scsi_modepage_control *page)
108{
109	if (page->header.page_length != sizeof(*page) - sizeof(page->header)) {
110		set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_PARAM_LIST_LENGTH_ERR);
111		return false;
112	}
113
114	// we only support enabling/disabling command queuing
115	enable_CQ(device, !page->DQue);
116	return true;
117}
118
119
120/*! Emulate MODE SELECT 10 command */
121static void
122ata_mode_select_10(ide_device_info *device, ide_qrequest *qrequest)
123{
124	scsi_ccb *request = qrequest->request;
125	scsi_cmd_mode_select_10 *cmd = (scsi_cmd_mode_select_10 *)request->cdb;
126	scsi_mode_param_header_10 param_header;
127	scsi_modepage_header page_header;
128	uint32 totalLength;
129	uint32 modepageOffset;
130	char modepage_buffer[64];	// !!! enlarge this to support longer mode pages
131
132	if (cmd->save_pages || cmd->pf != 1) {
133		set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD);
134		return;
135	}
136
137	totalLength = min(request->data_length,
138		B_BENDIAN_TO_HOST_INT16(cmd->param_list_length));
139
140	// first, retrieve page header to get size of different chunks
141	//param_header = (scsi_mode_param_header_10 *)request->data;
142	if (!copy_sg_data(request, 0, totalLength, &param_header, sizeof(param_header), true))
143		goto err;
144
145	totalLength = min(totalLength,
146		B_BENDIAN_TO_HOST_INT16(param_header.mode_data_length) + 1UL);
147
148	// this is the start of the first mode page;
149	// we ignore the block descriptor silently
150	modepageOffset = sizeof(param_header)
151		+ B_BENDIAN_TO_HOST_INT16(param_header.block_desc_length);
152
153	// go through list of pages
154	while (modepageOffset < totalLength) {
155		uint32 pageLength;
156
157		// get header to know how long page is
158		if (!copy_sg_data(request, modepageOffset, totalLength,
159				&page_header, sizeof(page_header), true))
160			goto err;
161
162		// get size of one page and copy it to buffer
163		pageLength = page_header.page_length + sizeof(scsi_modepage_header);
164
165		// the buffer has a maximum size - this is really standard compliant but
166		// sufficient for our needs
167		if (pageLength > sizeof(modepage_buffer))
168			goto err;
169
170		if (!copy_sg_data(request, modepageOffset, totalLength,
171				&modepage_buffer, min(pageLength, sizeof(modepage_buffer)), true))
172			goto err;
173
174		// modify page;
175		// currently, we only support the control mode page
176		switch (page_header.page_code) {
177			case SCSI_MODEPAGE_CONTROL:
178				if (!ata_mode_select_control_page(device, qrequest,
179						(scsi_modepage_control *)modepage_buffer))
180					return;
181				break;
182
183			default:
184				set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST,
185					SCSIS_ASC_INV_PARAM_LIST_FIELD);
186				return;
187		}
188
189		modepageOffset += pageLength;
190	}
191
192	if (modepageOffset != totalLength)
193		goto err;
194
195	request->data_resid = request->data_length - totalLength;
196	return;
197
198	// if we arrive here, data length was incorrect
199err:
200	set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_PARAM_LIST_LENGTH_ERR);
201}
202
203
204/*! Emulate TEST UNIT READY */
205static bool
206ata_test_unit_ready(ide_device_info *device, ide_qrequest *qrequest)
207{
208	SHOW_FLOW0(3, "");
209
210	if (!device->infoblock.RMSN_supported
211		|| device->infoblock._127_RMSN_support != 1)
212		return true;
213
214	// ask device about status
215	device->tf_param_mask = 0;
216	device->tf.write.command = IDE_CMD_GET_MEDIA_STATUS;
217
218	if (!send_command(device, qrequest, true, 15, ide_state_sync_waiting))
219		return false;
220
221	// bits ide_error_mcr | ide_error_mc | ide_error_wp are also valid
222	// but not requested by TUR; ide_error_wp can safely be ignored, but
223	// we don't want to loose media change (request) reports
224	if (!check_output(device, true,
225			ide_error_nm | ide_error_abrt | ide_error_mcr | ide_error_mc,
226			false)) {
227		// SCSI spec is unclear here: we shouldn't report "media change (request)"
228		// but what to do if there is one? anyway - we report them
229		;
230	}
231
232	return true;
233}
234
235
236/*! Flush internal device cache */
237static bool
238ata_flush_cache(ide_device_info *device, ide_qrequest *qrequest)
239{
240	// we should also ask for FLUSH CACHE support, but everyone denies it
241	// (looks like they cheat to gain some performance advantage, but
242	//  that's pretty useless: everyone does it...)
243	if (!device->infoblock.write_cache_supported)
244		return true;
245
246	device->tf_param_mask = 0;
247	device->tf.lba.command = device->use_48bits ? IDE_CMD_FLUSH_CACHE_EXT
248		: IDE_CMD_FLUSH_CACHE;
249
250	// spec says that this may take more then 30s, how much more?
251	if (!send_command(device, qrequest, true, 60, ide_state_sync_waiting))
252		return false;
253
254	wait_for_sync(device->bus);
255
256	return check_output(device, true, ide_error_abrt, false);
257}
258
259
260/*!	Load or eject medium
261	load = true - load medium
262*/
263static bool
264ata_load_eject(ide_device_info *device, ide_qrequest *qrequest, bool load)
265{
266	if (load) {
267		// ATA doesn't support loading
268		set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_PARAM_NOT_SUPPORTED);
269		return false;
270	}
271
272	device->tf_param_mask = 0;
273	device->tf.lba.command = IDE_CMD_MEDIA_EJECT;
274
275	if (!send_command(device, qrequest, true, 15, ide_state_sync_waiting))
276		return false;
277
278	wait_for_sync(device->bus);
279
280	return check_output(device, true, ide_error_abrt | ide_error_nm, false);
281}
282
283
284/*! Emulate PREVENT ALLOW command */
285static bool
286ata_prevent_allow(ide_device_info *device, bool prevent)
287{
288	set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_ILL_FUNCTION);
289	return false;
290}
291
292
293/*! Emulate INQUIRY command */
294static void
295ata_inquiry(ide_device_info *device, ide_qrequest *qrequest)
296{
297	scsi_ccb *request = qrequest->request;
298	scsi_res_inquiry data;
299	scsi_cmd_inquiry *cmd = (scsi_cmd_inquiry *)request->cdb;
300	uint32 allocation_length = cmd->allocation_length;
301	uint32 transfer_size;
302
303	if (cmd->evpd || cmd->page_code) {
304		set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD);
305		return;
306	}
307
308	memset(&data, 0, sizeof(data));
309
310	data.device_type = scsi_dev_direct_access;
311	data.device_qualifier = scsi_periph_qual_connected;
312
313	data.device_type_modifier = 0;
314	data.removable_medium = false;
315
316	data.ansi_version = 2;
317	data.ecma_version = 0;
318	data.iso_version = 0;
319
320	data.response_data_format = 2;
321	data.term_iop = false;
322		// to be changed if we support TERM I/O
323
324	data.additional_length = sizeof(scsi_res_inquiry) - 4;
325
326	data.soft_reset = false;
327	data.cmd_queue = device->queue_depth > 1;
328	data.linked = false;
329
330	// these values are free-style
331	data.sync = false;
332	data.write_bus16 = true;
333	data.write_bus32 = false;
334
335	data.relative_address = false;
336
337	// the following fields are *much* to small, sigh...
338	memcpy(data.vendor_ident, device->infoblock.model_number,
339		sizeof(data.vendor_ident));
340	memcpy(data.product_ident, device->infoblock.model_number + 8,
341		sizeof(data.product_ident));
342	memcpy(data.product_rev, "    ", sizeof(data.product_rev));
343
344	copy_sg_data(request, 0, allocation_length, &data, sizeof(data), false);
345
346	transfer_size = min(sizeof(data), allocation_length);
347	transfer_size = min(transfer_size, request->data_length);
348
349	request->data_resid = request->data_length - transfer_size;
350}
351
352
353/*! Emulate READ CAPACITY command */
354static void
355read_capacity(ide_device_info *device, ide_qrequest *qrequest)
356{
357	scsi_ccb *request = qrequest->request;
358	scsi_res_read_capacity data;
359	scsi_cmd_read_capacity *cmd = (scsi_cmd_read_capacity *)request->cdb;
360	uint32 lastBlock;
361
362	if (cmd->pmi || cmd->lba) {
363		set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD);
364		return;
365	}
366
367	// TODO: 512 bytes fixed block size?
368	data.block_size = B_HOST_TO_BENDIAN_INT32(512);
369
370	lastBlock = device->total_sectors - 1;
371	data.lba = B_HOST_TO_BENDIAN_INT32(lastBlock);
372
373	copy_sg_data(request, 0, request->data_length, &data, sizeof(data), false);
374	request->data_resid = max(request->data_length - sizeof(data), 0);
375}
376
377
378/*! Execute SCSI command */
379void
380ata_exec_io(ide_device_info *device, ide_qrequest *qrequest)
381{
382	scsi_ccb *request = qrequest->request;
383
384	SHOW_FLOW(3, "command=%x", request->cdb[0]);
385
386	// ATA devices have one LUN only
387	if (request->target_lun != 0) {
388		request->subsys_status = SCSI_SEL_TIMEOUT;
389		finish_request(qrequest, false);
390		return;
391	}
392
393	// starting a request means deleting sense, so don't do it if
394	// the command wants to read it
395	if (request->cdb[0] != SCSI_OP_REQUEST_SENSE)
396		start_request(device, qrequest);
397
398	switch (request->cdb[0]) {
399		case SCSI_OP_TEST_UNIT_READY:
400			ata_test_unit_ready(device, qrequest);
401			break;
402
403		case SCSI_OP_REQUEST_SENSE:
404			ide_request_sense(device, qrequest);
405			return;
406
407		case SCSI_OP_FORMAT: /* FORMAT UNIT */
408			// we could forward request to disk, but modern disks cannot
409			// be formatted anyway, so we just refuse request
410			// (exceptions are removable media devices, but to my knowledge
411			// they don't have to be formatted as well)
412			set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_OPCODE);
413			break;
414
415		case SCSI_OP_INQUIRY:
416			ata_inquiry(device, qrequest);
417			break;
418
419		case SCSI_OP_MODE_SELECT_10:
420			ata_mode_select_10(device, qrequest);
421			break;
422
423		case SCSI_OP_MODE_SENSE_10:
424			ata_mode_sense_10(device, qrequest);
425			break;
426
427		case SCSI_OP_MODE_SELECT_6:
428		case SCSI_OP_MODE_SENSE_6:
429			// we've told SCSI bus manager to emulates these commands
430			set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_OPCODE);
431			break;
432
433		case SCSI_OP_RESERVE:
434		case SCSI_OP_RELEASE:
435			// though mandatory, this doesn't make much sense in a
436			// single initiator environment; so what
437			set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_OPCODE);
438			break;
439
440		case SCSI_OP_START_STOP: {
441			scsi_cmd_ssu *cmd = (scsi_cmd_ssu *)request->cdb;
442
443			// with no LoEj bit set, we should only allow/deny further access
444			// we ignore that (unsupported for ATA)
445			// with LoEj bit set, we should additionally either load or eject the medium
446			// (start = 0 - eject; start = 1 - load)
447
448			if (!cmd->start)
449				// we must always flush cache if start = 0
450				ata_flush_cache(device, qrequest);
451
452			if (cmd->load_eject)
453				ata_load_eject(device, qrequest, cmd->start);
454
455			break;
456		}
457
458		case SCSI_OP_PREVENT_ALLOW: {
459			scsi_cmd_prevent_allow *cmd = (scsi_cmd_prevent_allow *)request->cdb;
460
461			ata_prevent_allow(device, cmd->prevent);
462			break;
463		}
464
465		case SCSI_OP_READ_CAPACITY:
466			read_capacity(device, qrequest);
467			break;
468
469		case SCSI_OP_VERIFY_6:
470			// does anyone uses this function?
471			// effectly, it does a read-and-compare, which IDE doesn't support
472			set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_OPCODE);
473			break;
474
475		case SCSI_OP_SYNCHRONIZE_CACHE:
476			// we ignore range and immediate bit, we always immediately flush everything
477			ata_flush_cache(device, qrequest);
478			break;
479
480		// sadly, there are two possible read/write operation codes;
481		// at least, the third one, read/write(12), is not valid for DAS
482		case SCSI_OP_READ_6:
483		case SCSI_OP_WRITE_6:
484		{
485			scsi_cmd_rw_6 *cmd = (scsi_cmd_rw_6 *)request->cdb;
486			uint32 pos;
487			size_t length;
488
489			pos = ((uint32)cmd->high_lba << 16) | ((uint32)cmd->mid_lba << 8)
490				| (uint32)cmd->low_lba;
491			length = cmd->length != 0 ? cmd->length : 256;
492
493			SHOW_FLOW(3, "READ6/WRITE6 pos=%lx, length=%lx", pos, length);
494
495			ata_send_rw(device, qrequest, pos, length, cmd->opcode == SCSI_OP_WRITE_6);
496			return;
497		}
498
499		case SCSI_OP_READ_10:
500		case SCSI_OP_WRITE_10:
501		{
502			scsi_cmd_rw_10 *cmd = (scsi_cmd_rw_10 *)request->cdb;
503			uint32 pos;
504			size_t length;
505
506			pos = B_BENDIAN_TO_HOST_INT32(cmd->lba);
507			length = B_BENDIAN_TO_HOST_INT16(cmd->length);
508
509			if (length != 0) {
510				ata_send_rw(device, qrequest, pos, length, cmd->opcode == SCSI_OP_WRITE_10);
511			} else {
512				// we cannot transfer zero blocks (apart from LBA48)
513				finish_request(qrequest, false);
514			}
515			return;
516		}
517
518		default:
519			set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_OPCODE);
520	}
521
522	finish_checksense(qrequest);
523}
524