xref: /haiku/src/add-ons/kernel/drivers/disk/scsi/scsi_cd/scsi_cd.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
1 /*
2  * Copyright 2004-2012, Haiku, Inc. All rights reserved.
3  * Copyright 2002-2003, Thomas Kurschel. All rights reserved.
4  *
5  * Distributed under the terms of the MIT License.
6  */
7 
8 
9 /*!	Peripheral driver to handle CD-ROM drives. To be more
10 	precisely, it supports CD-ROM and WORM drives (well -
11 	I've never _seen_ a WORM driver).
12 
13 	Much work is done by scsi_periph and block_io.
14 */
15 
16 
17 #include "scsi_cd.h"
18 
19 #include <stdlib.h>
20 #include <string.h>
21 
22 #include <algorithm>
23 
24 #include <fs/devfs.h>
25 #include <io_requests.h>
26 #include <vm/vm_page.h>
27 
28 #include "IOCache.h"
29 #include "IOSchedulerSimple.h"
30 
31 
32 //#define TRACE_CD_DISK
33 #ifdef TRACE_CD_DISK
34 #	define TRACE(x...) dprintf("scsi_cd: " x)
35 #else
36 #	define TRACE(x...) ;
37 #endif
38 
39 
40 static const uint8 kCDIcon[] = {
41 	0x6e, 0x63, 0x69, 0x66, 0x05, 0x05, 0x00, 0x02, 0x03, 0x06, 0x05, 0xb8,
42 	0x12, 0xa5, 0xbe, 0x03, 0xe1, 0x3d, 0xe7, 0x84, 0xb8, 0x02, 0x10, 0x49,
43 	0xf7, 0x9f, 0x49, 0xed, 0xd8, 0x00, 0xf1, 0xf1, 0xf1, 0x36, 0xd9, 0xdd,
44 	0xf4, 0x8a, 0x99, 0x96, 0xb9, 0xb4, 0xb8, 0xbe, 0xdb, 0xff, 0xf4, 0xf4,
45 	0xf4, 0x04, 0xeb, 0xd0, 0x02, 0x00, 0x06, 0x02, 0x3c, 0x92, 0xc0, 0x38,
46 	0x8f, 0x5f, 0xb8, 0x54, 0x50, 0x3c, 0x57, 0x63, 0x48, 0xd8, 0xdf, 0x48,
47 	0x89, 0x5b, 0x00, 0x41, 0x37, 0xa9, 0xff, 0xb9, 0xb9, 0xb9, 0x04, 0x01,
48 	0x7e, 0x04, 0x02, 0x04, 0x3f, 0x2c, 0x4e, 0x2c, 0x30, 0x2c, 0x22, 0x40,
49 	0x22, 0x34, 0x22, 0x4c, 0x3f, 0x54, 0x30, 0x54, 0x4e, 0x54, 0x5c, 0x40,
50 	0x5c, 0x4c, 0x5c, 0x34, 0x02, 0x04, 0x3f, 0x3a, 0x43, 0x3a, 0x3b, 0x3a,
51 	0x39, 0x3e, 0x39, 0x3c, 0x39, 0x40, 0x3f, 0x42, 0x3b, 0x42, 0x43, 0x42,
52 	0x45, 0x3e, 0x45, 0x40, 0x45, 0x3c, 0x02, 0x04, 0x4b, 0x3e, 0x4b, 0x3a,
53 	0x4b, 0x42, 0x3f, 0x46, 0x47, 0x46, 0x37, 0x46, 0x33, 0x3e, 0x33, 0x42,
54 	0x33, 0x3a, 0x3f, 0xbb, 0xf7, 0x37, 0xbb, 0xf7, 0x47, 0xbb, 0xf7, 0x02,
55 	0x04, 0x40, 0x2a, 0x54, 0x2a, 0x50, 0x2c, 0x5c, 0x40, 0x5c, 0x34, 0x5c,
56 	0x4c, 0x40, 0x56, 0x50, 0x54, 0x54, 0x56, 0x60, 0x40, 0x60, 0x4c, 0x60,
57 	0x34, 0x06, 0x0a, 0x04, 0x01, 0x03, 0x00, 0x0a, 0x00, 0x02, 0x00, 0x01,
58 	0x18, 0x15, 0xff, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x00, 0x02, 0x00,
59 	0x01, 0x18, 0x00, 0x15, 0x01, 0x17, 0x86, 0x00, 0x04, 0x0a, 0x01, 0x02,
60 	0x00, 0x02, 0x00, 0x0a, 0x02, 0x02, 0x02, 0x01, 0x00, 0x0a, 0x03, 0x01,
61 	0x02, 0x10, 0x01, 0x17, 0x82, 0x00, 0x04
62 };
63 
64 
65 static scsi_periph_interface *sSCSIPeripheral;
66 static device_manager_info *sDeviceManager;
67 
68 
69 #define SCSI_CD_STD_TIMEOUT 10
70 
71 
72 static status_t
73 update_capacity(cd_driver_info *info)
74 {
75 	TRACE("update_capacity()\n");
76 
77 	scsi_ccb *ccb = info->scsi->alloc_ccb(info->scsi_device);
78 	if (ccb == NULL)
79 		return B_NO_MEMORY;
80 
81 	status_t status = sSCSIPeripheral->check_capacity(
82 		info->scsi_periph_device, ccb);
83 
84 	info->scsi->free_ccb(ccb);
85 
86 	return status;
87 }
88 
89 
90 /*!	Iteratively correct the reported capacity by trying to read from the device
91 	close to its end.
92 */
93 static uint64
94 test_capacity(cd_driver_info *info)
95 {
96 	static const size_t kMaxEntries = 4;
97 	const uint32 blockSize = info->block_size;
98 	const size_t kBufferSize = blockSize * 4;
99 
100 	TRACE("test_capacity: read with buffer size %" B_PRIuSIZE ", block size %"
101 		B_PRIu32", capacity %llu\n", kBufferSize, blockSize,
102 		info->original_capacity);
103 
104 	info->capacity = info->original_capacity;
105 
106 	size_t numBlocks = B_PAGE_SIZE / blockSize;
107 	uint64 offset = info->original_capacity;
108 	if (offset <= numBlocks)
109 		return B_OK;
110 
111 	offset -= numBlocks;
112 
113 	scsi_ccb *request = info->scsi->alloc_ccb(info->scsi_device);
114 	if (request == NULL)
115 		return B_NO_MEMORY;
116 
117 	// Allocate buffer
118 
119 	physical_entry entries[4];
120 	size_t numEntries = 0;
121 
122 	vm_page_reservation reservation;
123 	vm_page_reserve_pages(&reservation,
124 		(kBufferSize - 1 + B_PAGE_SIZE) / B_PAGE_SIZE, VM_PRIORITY_SYSTEM);
125 
126 	for (size_t left = kBufferSize; numEntries < kMaxEntries && left > 0;
127 			numEntries++) {
128 		size_t bytes = std::min(left, (size_t)B_PAGE_SIZE);
129 
130 		vm_page* page = vm_page_allocate_page(&reservation,
131 			PAGE_STATE_WIRED | VM_PAGE_ALLOC_BUSY);
132 
133 		entries[numEntries].address = page->physical_page_number * B_PAGE_SIZE;
134 		entries[numEntries].size = bytes;;
135 
136 		left -= bytes;
137 	}
138 
139 	vm_page_unreserve_pages(&reservation);
140 
141 	// Read close to the end of the device to find out its real end
142 
143 	// Only try 1 second before the end (= 75 blocks)
144 	while (offset > info->original_capacity - 75) {
145 		size_t bytesTransferred;
146 		status_t status = sSCSIPeripheral->read_write(info->scsi_periph_device,
147 			request, offset, numBlocks, entries, numEntries, false,
148 			&bytesTransferred);
149 
150 		TRACE("test_capacity: read from offset %llu: %s\n", offset,
151 			strerror(status));
152 
153 		if (status == B_OK || (request->sense[0] & 0x7f) != 0x70)
154 			break;
155 
156 		switch (request->sense[2]) {
157 			case SCSIS_KEY_MEDIUM_ERROR:
158 			case SCSIS_KEY_ILLEGAL_REQUEST:
159 			case SCSIS_KEY_VOLUME_OVERFLOW:
160 			{
161 				// find out the problematic sector
162 				uint32 errorBlock = (request->sense[3] << 24U)
163 					| (request->sense[4] << 16U) | (request->sense[5] << 8U)
164 					| request->sense[6];
165 				if (errorBlock >= offset)
166 					info->capacity = errorBlock;
167 				break;
168 			}
169 
170 			default:
171 				break;
172 		}
173 
174 		if (numBlocks > offset)
175 			break;
176 
177 		offset -= numBlocks;
178 	}
179 
180 	info->scsi->free_ccb(request);
181 
182 	for (size_t i = 0; i < numEntries; i++) {
183 		vm_page_set_state(vm_lookup_page(entries[i].address / B_PAGE_SIZE),
184 			PAGE_STATE_FREE);
185 	}
186 
187 	if (info->capacity != info->original_capacity) {
188 		dprintf("scsi_cd: adjusted capacity from %" B_PRIu64 " to %" B_PRIu64
189 			" blocks.\n", info->original_capacity, info->capacity);
190 	}
191 
192 	return B_OK;
193 }
194 
195 
196 static status_t
197 get_geometry(cd_handle *handle, device_geometry *geometry)
198 {
199 	cd_driver_info *info = handle->info;
200 
201 	status_t status = update_capacity(info);
202 
203 	// it seems that Be expects B_GET_GEOMETRY to always succeed unless
204 	// the medium has been changed; e.g. if we report B_DEV_NO_MEDIA, the
205 	// info is ignored by the CDPlayer and CDBurner
206 	if (status == B_DEV_MEDIA_CHANGED)
207 		return B_DEV_MEDIA_CHANGED;
208 
209 	devfs_compute_geometry_size(geometry, info->capacity, info->block_size);
210 
211 	geometry->device_type = info->device_type;
212 	geometry->removable = info->removable;
213 
214 	// TBD: for all but CD-ROMs, read mode sense - medium type
215 	// (bit 7 of block device specific parameter for Optical Memory Block Device)
216 	// (same for Direct-Access Block Devices)
217 	// (same for write-once block devices)
218 	// (same for optical memory block devices)
219 	geometry->read_only = true;
220 	geometry->write_once = info->device_type == scsi_dev_WORM;
221 
222 	TRACE("scsi_disk: get_geometry(): %ld, %ld, %ld, %ld, %d, %d, %d, %d\n",
223 		geometry->bytes_per_sector, geometry->sectors_per_track,
224 		geometry->cylinder_count, geometry->head_count, geometry->device_type,
225 		geometry->removable, geometry->read_only, geometry->write_once);
226 
227 	return B_OK;
228 }
229 
230 
231 static status_t
232 get_toc(cd_driver_info *info, scsi_toc *toc)
233 {
234 	scsi_ccb *ccb;
235 	status_t res;
236 	scsi_cmd_read_toc *cmd;
237 	size_t dataLength;
238 	scsi_toc_general *shortResponse = (scsi_toc_general *)toc->toc_data;
239 
240 	TRACE("get_toc()\n");
241 
242 	ccb = info->scsi->alloc_ccb(info->scsi_device);
243 	if (ccb == NULL)
244 		return B_NO_MEMORY;
245 
246 	// first read number of tracks only
247 	ccb->flags = SCSI_DIR_IN;
248 
249 	cmd = (scsi_cmd_read_toc *)ccb->cdb;
250 
251 	memset(cmd, 0, sizeof(*cmd));
252 	cmd->opcode = SCSI_OP_READ_TOC;
253 	cmd->time = 1;
254 	cmd->format = SCSI_TOC_FORMAT_TOC;
255 	cmd->track = 1;
256 	cmd->allocation_length = B_HOST_TO_BENDIAN_INT16(sizeof(scsi_toc_general));
257 
258 	ccb->cdb_length = sizeof(*cmd);
259 
260 	ccb->sort = -1;
261 	ccb->timeout = SCSI_CD_STD_TIMEOUT;
262 
263 	ccb->data = toc->toc_data;
264 	ccb->sg_list = NULL;
265 	ccb->data_length = sizeof(toc->toc_data);
266 
267 	res = sSCSIPeripheral->safe_exec(info->scsi_periph_device, ccb);
268 	if (res != B_OK)
269 		goto err;
270 
271 	// then read all track infos
272 	// (little hint: number of tracks is last - first + 1;
273 	//  but scsi_toc_toc has already one track, so we get
274 	//  last - first extra tracks; finally, we want the lead-out as
275 	//  well, so we add an extra track)
276 	dataLength = (shortResponse->last - shortResponse->first + 1)
277 		* sizeof(scsi_toc_track) + sizeof(scsi_toc_toc);
278 	dataLength = min_c(dataLength, sizeof(toc->toc_data));
279 
280 	TRACE("  tracks: %d - %d, data length %d\n", shortResponse->first,
281 		shortResponse->last, (int)dataLength);
282 
283 	cmd->allocation_length = B_HOST_TO_BENDIAN_INT16(dataLength);
284 
285 	res = sSCSIPeripheral->safe_exec(info->scsi_periph_device, ccb);
286 
287 err:
288 	info->scsi->free_ccb(ccb);
289 
290 	return res;
291 }
292 
293 
294 static status_t
295 load_eject(cd_driver_info *info, bool load)
296 {
297 	TRACE("load_eject()\n");
298 
299 	scsi_ccb *ccb = info->scsi->alloc_ccb(info->scsi_device);
300 	if (ccb == NULL)
301 		return B_NO_MEMORY;
302 
303 	err_res result = sSCSIPeripheral->send_start_stop(
304 		info->scsi_periph_device, ccb, load, true);
305 
306 	info->scsi->free_ccb(ccb);
307 
308 	return result.error_code;
309 }
310 
311 
312 static status_t
313 get_position(cd_driver_info *info, scsi_position *position)
314 {
315 	scsi_cmd_read_subchannel cmd;
316 
317 	TRACE("get_position()\n");
318 
319 	memset(&cmd, 0, sizeof(cmd));
320 	cmd.opcode = SCSI_OP_READ_SUB_CHANNEL;
321 	cmd.time = 1;
322 	cmd.subq = 1;
323 	cmd.parameter_list = scsi_sub_channel_parameter_list_cd_pos;
324 	cmd.track = 0;
325 	cmd.allocation_length = B_HOST_TO_BENDIAN_INT16(sizeof(scsi_position));
326 
327 	return sSCSIPeripheral->simple_exec(info->scsi_periph_device,
328 		&cmd, sizeof(cmd), position, sizeof(*position), SCSI_DIR_IN);
329 }
330 
331 
332 static status_t
333 get_set_volume(cd_driver_info *info, scsi_volume *volume, bool set)
334 {
335 	scsi_cmd_mode_sense_6 cmd;
336 	scsi_mode_param_header_6 header;
337 	size_t len;
338 	void *buffer;
339 	scsi_modepage_audio	*page;
340 	status_t res;
341 
342 	TRACE("get_set_volume()\n");
343 
344 	// determine size of block descriptor
345 	memset(&cmd, 0, sizeof(cmd));
346 	cmd.opcode = SCSI_OP_MODE_SENSE_6;
347 	cmd.page_code = SCSI_MODEPAGE_AUDIO;
348 	cmd.page_control = SCSI_MODE_SENSE_PC_CURRENT;
349 	cmd.allocation_length = sizeof(header);
350 
351 	memset(&header, -2, sizeof(header));
352 
353 	res = sSCSIPeripheral->simple_exec(info->scsi_periph_device, &cmd,
354 		sizeof(cmd), &header, sizeof(header), SCSI_DIR_IN);
355 	if (res != B_OK)
356 		return res;
357 
358 	TRACE("  block_desc_len=%d", header.block_desc_length);
359 #if 0
360 	// ToDo: why this??
361 	return B_ERROR;
362 #endif
363 
364 	// retrieve param header, block descriptor and actual codepage
365 	len = sizeof(header) + header.block_desc_length
366 		+ sizeof(scsi_modepage_audio);
367 
368 	buffer = malloc(len);
369 	if (buffer == NULL)
370 		return B_NO_MEMORY;
371 
372 	memset(buffer, -1, len);
373 
374 	cmd.allocation_length = len;
375 
376 	res = sSCSIPeripheral->simple_exec(info->scsi_periph_device, &cmd,
377 		sizeof(cmd), buffer, len, SCSI_DIR_IN);
378 	if (res != B_OK) {
379 		free(buffer);
380 		return res;
381 	}
382 
383 	TRACE("  mode_data_len=%d, block_desc_len=%d",
384 		((scsi_mode_param_header_6 *)buffer)->mode_data_length,
385 		((scsi_mode_param_header_6 *)buffer)->block_desc_length);
386 
387 	// find control page and retrieve values
388 	page = (scsi_modepage_audio *)((char *)buffer + sizeof(header)
389 		+ header.block_desc_length);
390 
391 	TRACE("  page=%p, codepage=%d", page, page->header.page_code);
392 
393 	if (!set) {
394 		volume->port0_channel = page->ports[0].channel;
395 		volume->port0_volume  = page->ports[0].volume;
396 		volume->port1_channel = page->ports[1].channel;
397 		volume->port1_volume  = page->ports[1].volume;
398 		volume->port2_channel = page->ports[2].channel;
399 		volume->port2_volume  = page->ports[2].volume;
400 		volume->port3_channel = page->ports[3].channel;
401 		volume->port3_volume  = page->ports[3].volume;
402 
403 #if 0
404 		SHOW_FLOW(3, "1: %d - %d", volume->port0_channel, volume->port0_volume);
405 		SHOW_FLOW(3, "2: %d - %d", volume->port1_channel, volume->port1_volume);
406 		SHOW_FLOW(3, "3: %d - %d", volume->port2_channel, volume->port2_volume);
407 		SHOW_FLOW(3, "4: %d - %d", volume->port3_channel, volume->port3_volume);
408 #endif
409 		res = B_OK;
410 	} else {
411 		scsi_cmd_mode_select_6 cmd;
412 
413 		if (volume->flags & 0x01)
414 			page->ports[0].channel = volume->port0_channel;
415 		if (volume->flags & 0x02)
416 			page->ports[0].volume = volume->port0_volume;
417 		if (volume->flags & 0x04)
418 			page->ports[1].channel = volume->port1_channel;
419 		if (volume->flags & 0x08)
420 			page->ports[1].volume = volume->port1_volume;
421 		if (volume->flags & 0x10)
422 			page->ports[2].channel = volume->port2_channel;
423 		if (volume->flags & 0x20)
424 			page->ports[2].volume = volume->port2_volume;
425 		if (volume->flags & 0x40)
426 			page->ports[3].channel = volume->port3_channel;
427 		if (volume->flags & 0x80)
428 			page->ports[3].volume = volume->port3_volume;
429 
430 		memset(&cmd, 0, sizeof(cmd));
431 		cmd.opcode = SCSI_OP_MODE_SELECT_6;
432 		cmd.pf = 1;
433 		cmd.param_list_length = sizeof(header) + header.block_desc_length
434 			+ sizeof(*page);
435 
436 		res = sSCSIPeripheral->simple_exec(info->scsi_periph_device,
437 			&cmd, sizeof(cmd), buffer, len, SCSI_DIR_OUT);
438 	}
439 
440 	free(buffer);
441 	return res;
442 }
443 
444 
445 /*!	Play audio cd; time is in MSF */
446 static status_t
447 play_msf(cd_driver_info *info, const scsi_play_position *position)
448 {
449 	scsi_cmd_play_msf cmd;
450 
451 	TRACE("play_msf(): %d:%d:%d-%d:%d:%d\n", position->start_m,
452 		position->start_s, position->start_f, position->end_m, position->end_s,
453 		position->end_f);
454 
455 	memset(&cmd, 0, sizeof(cmd));
456 
457 	cmd.opcode = SCSI_OP_PLAY_MSF;
458 	cmd.start_minute = position->start_m;
459 	cmd.start_second = position->start_s;
460 	cmd.start_frame = position->start_f;
461 	cmd.end_minute = position->end_m;
462 	cmd.end_second = position->end_s;
463 	cmd.end_frame = position->end_f;
464 
465 	return sSCSIPeripheral->simple_exec(info->scsi_periph_device,
466 		&cmd, sizeof(cmd), NULL, 0, SCSI_DIR_NONE);
467 }
468 
469 
470 /*! Play audio cd; time is in track/index */
471 static status_t
472 play_track_index(cd_driver_info *info, const scsi_play_track *buf)
473 {
474 	scsi_toc generic_toc;
475 	scsi_toc_toc *toc;
476 	status_t res;
477 	int start_track, end_track;
478 	scsi_play_position position;
479 
480 	TRACE("play_track_index(): %d-%d\n", buf->start_track, buf->end_track);
481 
482 	// the corresponding command PLAY AUDIO TRACK/INDEX	is deprecated,
483 	// so we have to simulate it by converting track to time via TOC
484 	res = get_toc(info, &generic_toc);
485 	if (res != B_OK)
486 		return res;
487 
488 	toc = (scsi_toc_toc *)&generic_toc.toc_data[0];
489 
490 	start_track = buf->start_track;
491 	end_track = buf->end_track;
492 
493 	if (start_track > toc->last_track)
494 		return B_BAD_INDEX;
495 
496 	if (end_track > toc->last_track)
497 		end_track = toc->last_track + 1;
498 
499 	if (end_track < toc->last_track + 1)
500 		++end_track;
501 
502 	start_track -= toc->first_track;
503 	end_track -= toc->first_track;
504 
505 	if (start_track < 0 || end_track < 0)
506 		return B_BAD_INDEX;
507 
508 	position.start_m = toc->tracks[start_track].start.time.minute;
509 	position.start_s = toc->tracks[start_track].start.time.second;
510 	position.start_f = toc->tracks[start_track].start.time.frame;
511 
512 	position.end_m = toc->tracks[end_track].start.time.minute;
513 	position.end_s = toc->tracks[end_track].start.time.second;
514 	position.end_f = toc->tracks[end_track].start.time.frame;
515 
516 	return play_msf(info, &position);
517 }
518 
519 
520 static status_t
521 stop_audio(cd_driver_info *info)
522 {
523 	scsi_cmd_stop_play cmd;
524 
525 	TRACE("stop_audio()\n");
526 
527 	memset( &cmd, 0, sizeof( cmd ));
528 	cmd.opcode = SCSI_OP_STOP_PLAY;
529 
530 	return sSCSIPeripheral->simple_exec(info->scsi_periph_device,
531 		&cmd, sizeof(cmd), NULL, 0, SCSI_DIR_NONE);
532 }
533 
534 
535 static status_t
536 pause_resume(cd_driver_info *info, bool resume)
537 {
538 	scsi_cmd_pause_resume cmd;
539 
540 	TRACE("pause_resume()\n");
541 
542 	memset(&cmd, 0, sizeof(cmd));
543 	cmd.opcode = SCSI_OP_PAUSE_RESUME;
544 	cmd.resume = resume;
545 
546 	return sSCSIPeripheral->simple_exec(info->scsi_periph_device,
547 		&cmd, sizeof(cmd), NULL, 0, SCSI_DIR_NONE);
548 }
549 
550 
551 static status_t
552 scan(cd_driver_info *info, const scsi_scan *buf)
553 {
554 	scsi_cmd_scan cmd;
555 	scsi_position curPos;
556 	scsi_cd_current_position *cdPos;
557 
558 	TRACE("scan(direction =% d)\n", buf->direction);
559 
560 	status_t res = get_position(info, &curPos);
561 	if (res != B_OK)
562 		return res;
563 
564 	cdPos = (scsi_cd_current_position *)((char *)&curPos
565 		+ sizeof(scsi_subchannel_data_header));
566 
567 	if (buf->direction == 0) {
568 		scsi_play_position playPos;
569 
570 		// to stop scan, we issue play command with "open end"
571 		playPos.start_m = cdPos->absolute_address.time.minute;
572 		playPos.start_s = cdPos->absolute_address.time.second;
573 		playPos.start_f = cdPos->absolute_address.time.frame;
574 		playPos.end_m = 99;
575 		playPos.end_s = 59;
576 		playPos.end_f = 24;
577 
578 		return play_msf(info, &playPos);
579 	}
580 
581 	memset(&cmd, 0, sizeof(cmd));
582 
583 	cmd.opcode = SCSI_OP_SCAN;
584 	cmd.direct = buf->direction < 0;
585 	cmd.start.time = cdPos->absolute_address.time;
586 	cmd.type = scsi_scan_msf;
587 
588 	/*
589 	tmp = (uint8 *)&cmd;
590 	dprintf("%d %d %d %d %d %d %d %d %d %d %d %d\n",
591 		tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5],
592 		tmp[6], tmp[7], tmp[8], tmp[9], tmp[10], tmp[11]);
593 	*/
594 
595 	return sSCSIPeripheral->simple_exec(info->scsi_periph_device,
596 		&cmd, sizeof(cmd), NULL, 0, SCSI_DIR_NONE);
597 }
598 
599 
600 static status_t
601 read_cd(cd_driver_info *info, const scsi_read_cd *readCD)
602 {
603 	scsi_cmd_read_cd *cmd;
604 	uint32 lba, length;
605 	scsi_ccb *ccb;
606 	status_t res;
607 
608 	// we use safe_exec instead of simple_exec as we want to set
609 	// the sorting order manually (only makes much sense if you grab
610 	// multiple tracks at once, but we are prepared)
611 	ccb = info->scsi->alloc_ccb(info->scsi_device);
612 
613 	if (ccb == NULL)
614 		return B_NO_MEMORY;
615 
616 	cmd = (scsi_cmd_read_cd *)ccb->cdb;
617 	memset(cmd, 0, sizeof(*cmd));
618 	cmd->opcode = SCSI_OP_READ_CD;
619 	cmd->sector_type = 1;
620 
621 	// skip first two seconds, they are lead-in
622 	lba = (readCD->start_m * 60 + readCD->start_s) * 75 + readCD->start_f
623 		- 2 * 75;
624 	length = (readCD->length_m * 60 + readCD->length_s) * 75 + readCD->length_f;
625 
626 	cmd->lba = B_HOST_TO_BENDIAN_INT32(lba);
627 	cmd->high_length = (length >> 16) & 0xff;
628 	cmd->mid_length = (length >> 8) & 0xff;
629 	cmd->low_length = length & 0xff;
630 
631 	cmd->error_field = scsi_read_cd_error_none;
632 	cmd->edc_ecc = 0;
633 	cmd->user_data = 1;
634 	cmd->header_code = scsi_read_cd_header_none;
635 	cmd->sync = 0;
636 	cmd->sub_channel_selection = scsi_read_cd_sub_channel_none;
637 
638 	ccb->cdb_length = sizeof(*cmd);
639 
640 	ccb->flags = SCSI_DIR_IN | SCSI_DIS_DISCONNECT;
641 	ccb->sort = lba;
642 	// are 10 seconds enough for timeout?
643 	ccb->timeout = 10;
644 
645 	// TODO: we pass a user buffer here!
646 	ccb->data = (uint8 *)readCD->buffer;
647 	ccb->sg_list = NULL;
648 	ccb->data_length = readCD->buffer_length;
649 
650 	res = sSCSIPeripheral->safe_exec(info->scsi_periph_device, ccb);
651 
652 	info->scsi->free_ccb(ccb);
653 
654 	return res;
655 }
656 
657 
658 static status_t
659 do_io(void* cookie, IOOperation* operation)
660 {
661 	cd_driver_info* info = (cd_driver_info*)cookie;
662 
663 	// TODO: this can go away as soon as we pushed the IOOperation to the upper
664 	// layers - we can then set scsi_periph::io() as callback for the scheduler
665 	size_t bytesTransferred;
666 	status_t status = sSCSIPeripheral->io(info->scsi_periph_device, operation,
667 		&bytesTransferred);
668 
669 	info->io_scheduler->OperationCompleted(operation, status, bytesTransferred);
670 	return status;
671 }
672 
673 
674 //	#pragma mark - device module API
675 
676 
677 static status_t
678 cd_init_device(void* _info, void** _cookie)
679 {
680 	cd_driver_info* info = (cd_driver_info*)_info;
681 
682 	update_capacity(info);
683 		// Get initial capacity, but ignore the result; we do not care
684 		// whether or not a media is present
685 
686 	*_cookie = info;
687 	return B_OK;
688 }
689 
690 
691 static void
692 cd_uninit_device(void* _cookie)
693 {
694 	cd_driver_info* info = (cd_driver_info*)_cookie;
695 
696 	delete info->io_scheduler;
697 	delete info->dma_resource;
698 	info->io_scheduler = NULL;
699 	info->dma_resource = NULL;
700 }
701 
702 
703 static status_t
704 cd_open(void* _info, const char* path, int openMode, void** _cookie)
705 {
706 	cd_driver_info* info = (cd_driver_info*)_info;
707 
708 	cd_handle* handle = (cd_handle*)malloc(sizeof(cd_handle));
709 	if (handle == NULL)
710 		return B_NO_MEMORY;
711 
712 	handle->info = info;
713 
714 	status_t status = sSCSIPeripheral->handle_open(info->scsi_periph_device,
715 		(periph_handle_cookie)handle, &handle->scsi_periph_handle);
716 	if (status < B_OK) {
717 		free(handle);
718 		return status;
719 	}
720 
721 	*_cookie = handle;
722 	return B_OK;
723 }
724 
725 
726 static status_t
727 cd_close(void* cookie)
728 {
729 	cd_handle* handle = (cd_handle*)cookie;
730 	TRACE("close()\n");
731 
732 	sSCSIPeripheral->handle_close(handle->scsi_periph_handle);
733 	return B_OK;
734 }
735 
736 
737 static status_t
738 cd_free(void* cookie)
739 {
740 	cd_handle* handle = (cd_handle*)cookie;
741 	TRACE("free()\n");
742 
743 	sSCSIPeripheral->handle_free(handle->scsi_periph_handle);
744 	free(handle);
745 	return B_OK;
746 }
747 
748 
749 static status_t
750 cd_read(void* cookie, off_t pos, void* buffer, size_t* _length)
751 {
752 	cd_handle* handle = (cd_handle*)cookie;
753 	size_t length = *_length;
754 
755 	if (handle->info->capacity == 0)
756 		return B_DEV_NO_MEDIA;
757 
758 	IORequest request;
759 	status_t status = request.Init(pos, (addr_t)buffer, length, false, 0);
760 	if (status != B_OK)
761 		return status;
762 
763 	if (handle->info->io_scheduler == NULL)
764 		return B_DEV_NO_MEDIA;
765 
766 	status = handle->info->io_scheduler->ScheduleRequest(&request);
767 	if (status != B_OK)
768 		return status;
769 
770 	status = request.Wait(0, 0);
771 	if (status == B_OK)
772 		*_length = length;
773 	else
774 		dprintf("cd_read(): request.Wait() returned: %s\n", strerror(status));
775 
776 	return status;
777 }
778 
779 
780 static status_t
781 cd_write(void* cookie, off_t pos, const void* buffer, size_t* _length)
782 {
783 	cd_handle* handle = (cd_handle*)cookie;
784 	size_t length = *_length;
785 
786 	if (handle->info->capacity == 0)
787 		return B_DEV_NO_MEDIA;
788 
789 	IORequest request;
790 	status_t status = request.Init(pos, (addr_t)buffer, length, true, 0);
791 	if (status != B_OK)
792 		return status;
793 
794 	if (handle->info->io_scheduler == NULL)
795 		return B_DEV_NO_MEDIA;
796 
797 	status = handle->info->io_scheduler->ScheduleRequest(&request);
798 	if (status != B_OK)
799 		return status;
800 
801 	status = request.Wait(0, 0);
802 	if (status == B_OK)
803 		*_length = length;
804 	else
805 		dprintf("cd_write(): request.Wait() returned: %s\n", strerror(status));
806 
807 	return status;
808 }
809 
810 
811 static status_t
812 cd_io(void* cookie, io_request* request)
813 {
814 	cd_handle* handle = (cd_handle*)cookie;
815 
816 	if (handle->info->capacity == 0) {
817 		notify_io_request(request, B_DEV_NO_MEDIA);
818 		return B_DEV_NO_MEDIA;
819 	}
820 
821 	return handle->info->io_scheduler->ScheduleRequest(request);
822 }
823 
824 
825 static status_t
826 cd_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
827 {
828 	cd_handle* handle = (cd_handle*)cookie;
829 	cd_driver_info *info = handle->info;
830 
831 	TRACE("ioctl(op = %lu)\n", op);
832 
833 	switch (op) {
834 		case B_GET_DEVICE_SIZE:
835 		{
836 			status_t status = update_capacity(info);
837 			if (status != B_OK)
838 				return status;
839 
840 			size_t size = info->capacity * info->block_size;
841 			return user_memcpy(buffer, &size, sizeof(size_t));
842 		}
843 
844 		case B_GET_GEOMETRY:
845 		{
846 			if (buffer == NULL /*|| length != sizeof(device_geometry)*/)
847 				return B_BAD_VALUE;
848 
849 		 	device_geometry geometry;
850 			status_t status = get_geometry(handle, &geometry);
851 			if (status != B_OK)
852 				return status;
853 
854 			return user_memcpy(buffer, &geometry, sizeof(device_geometry));
855 		}
856 
857 		case B_GET_ICON_NAME:
858 			return user_strlcpy((char*)buffer, "devices/drive-optical",
859 				B_FILE_NAME_LENGTH);
860 
861 		case B_GET_VECTOR_ICON:
862 		{
863 			device_icon iconData;
864 			if (length != sizeof(device_icon))
865 				return B_BAD_VALUE;
866 			if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK)
867 				return B_BAD_ADDRESS;
868 
869 			if (iconData.icon_size >= (int32)sizeof(kCDIcon)) {
870 				if (user_memcpy(iconData.icon_data, kCDIcon,
871 						sizeof(kCDIcon)) != B_OK)
872 					return B_BAD_ADDRESS;
873 			}
874 
875 			iconData.icon_size = sizeof(kCDIcon);
876 			return user_memcpy(buffer, &iconData, sizeof(device_icon));
877 		}
878 
879 		case B_SCSI_GET_TOC:
880 			// TODO: we pass a user buffer here!
881 			return get_toc(info, (scsi_toc *)buffer);
882 
883 		case B_EJECT_DEVICE:
884 		case B_SCSI_EJECT:
885 			return load_eject(info, false);
886 
887 		case B_LOAD_MEDIA:
888 			return load_eject(info, true);
889 
890 		case B_SCSI_GET_POSITION:
891 		{
892 			if (buffer == NULL)
893 				return B_BAD_VALUE;
894 
895 			scsi_position position;
896 			status_t status = get_position(info, &position);
897 			if (status != B_OK)
898 				return status;
899 
900 			return user_memcpy(buffer, &position, sizeof(scsi_position));
901 		}
902 
903 		case B_SCSI_GET_VOLUME:
904 			// TODO: we pass a user buffer here!
905 			return get_set_volume(info, (scsi_volume *)buffer, false);
906 		case B_SCSI_SET_VOLUME:
907 			// TODO: we pass a user buffer here!
908 			return get_set_volume(info, (scsi_volume *)buffer, true);
909 
910 		case B_SCSI_PLAY_TRACK:
911 		{
912 			scsi_play_track track;
913 			if (user_memcpy(&track, buffer, sizeof(scsi_play_track)) != B_OK)
914 				return B_BAD_ADDRESS;
915 
916 			return play_track_index(info, &track);
917 		}
918 		case B_SCSI_PLAY_POSITION:
919 		{
920 			scsi_play_position position;
921 			if (user_memcpy(&position, buffer, sizeof(scsi_play_position))
922 					!= B_OK)
923 				return B_BAD_ADDRESS;
924 
925 			return play_msf(info, &position);
926 		}
927 
928 		case B_SCSI_STOP_AUDIO:
929 			return stop_audio(info);
930 		case B_SCSI_PAUSE_AUDIO:
931 			return pause_resume(info, false);
932 		case B_SCSI_RESUME_AUDIO:
933 			return pause_resume(info, true);
934 
935 		case B_SCSI_SCAN:
936 		{
937 			scsi_scan scanBuffer;
938 			if (user_memcpy(&scanBuffer, buffer, sizeof(scsi_scan)) != B_OK)
939 				return B_BAD_ADDRESS;
940 
941 			return scan(info, &scanBuffer);
942 		}
943 		case B_SCSI_READ_CD:
944 			// TODO: we pass a user buffer here!
945 			return read_cd(info, (scsi_read_cd *)buffer);
946 
947 		default:
948 			return sSCSIPeripheral->ioctl(handle->scsi_periph_handle, op,
949 				buffer, length);
950 	}
951 }
952 
953 
954 //	#pragma mark - scsi_periph callbacks
955 
956 
957 static void
958 cd_set_capacity(cd_driver_info* info, uint64 capacity, uint32 blockSize)
959 {
960 	TRACE("cd_set_capacity(info = %p, capacity = %Ld, blockSize = %ld)\n",
961 		info, capacity, blockSize);
962 
963 	if (info->block_size != blockSize) {
964 		if (capacity == 0) {
965 			// there is obviously no medium in the drive, don't try to update
966 			// the DMA resource
967 			return;
968 		}
969 
970 		if (info->block_size != 0) {
971 			dprintf("old %" B_PRId32 ", new %" B_PRId32 "\n", info->block_size,
972 				blockSize);
973 			panic("updating DMAResource not yet implemented...");
974 		}
975 
976 		// TODO: we need to replace the DMAResource in our IOScheduler
977 		status_t status = info->dma_resource->Init(info->node, blockSize, 1024,
978 			32);
979 		if (status != B_OK)
980 			panic("initializing DMAResource failed: %s", strerror(status));
981 
982 		// Allocate the I/O scheduler. If there seems to be sufficient memory
983 		// we use an IOCache, since that adds caching at the lowest I/O layer
984 		// and thus dramatically reduces I/O operations and seeks. The
985 		// disadvantage is that it increases free memory (physical pages)
986 		// fragmentation, which makes large contiguous allocations more likely
987 		// to fail.
988 		size_t freeMemory = vm_page_num_free_pages();
989 		if (freeMemory > 180 * 1024 * 1024 / B_PAGE_SIZE) {
990 			info->io_scheduler = new(std::nothrow) IOCache(info->dma_resource,
991 				1024 * 1024);
992 		} else {
993 			dprintf("scsi_cd: Using IOSchedulerSimple instead of IOCache to "
994 				"avoid memory allocation issues.\n");
995 			info->io_scheduler = new(std::nothrow) IOSchedulerSimple(
996 				info->dma_resource);
997 		}
998 
999 		if (info->io_scheduler == NULL)
1000 			panic("allocating IOScheduler failed.");
1001 
1002 		// TODO: use whole device name here
1003 		status = info->io_scheduler->Init("scsi");
1004 		if (status != B_OK)
1005 			panic("initializing IOScheduler failed: %s", strerror(status));
1006 
1007 		info->io_scheduler->SetCallback(do_io, info);
1008 		info->block_size = blockSize;
1009 	}
1010 
1011 	if (info->original_capacity != capacity && info->io_scheduler != NULL) {
1012 		info->original_capacity = capacity;
1013 
1014 		// For CDs, it's obviously relatively normal that they report a larger
1015 		// capacity than it can actually address. Therefore we'll manually
1016 		// correct the value here.
1017 		test_capacity(info);
1018 
1019 		info->io_scheduler->SetDeviceCapacity(info->capacity * blockSize);
1020 	}
1021 }
1022 
1023 
1024 static void
1025 cd_media_changed(cd_driver_info* info, scsi_ccb* request)
1026 {
1027 	// do a capacity check
1028 	// TODO: is this a good idea (e.g. if this is an empty CD)?
1029 	info->original_capacity = 0;
1030 	info->capacity = 0;
1031 	sSCSIPeripheral->check_capacity(info->scsi_periph_device, request);
1032 
1033 	if (info->io_scheduler != NULL)
1034 		info->io_scheduler->MediaChanged();
1035 }
1036 
1037 
1038 scsi_periph_callbacks callbacks = {
1039 	(void (*)(periph_device_cookie, uint64, uint32))cd_set_capacity,
1040 	(void (*)(periph_device_cookie, scsi_ccb *))cd_media_changed
1041 };
1042 
1043 
1044 //	#pragma mark - driver module API
1045 
1046 
1047 static float
1048 cd_supports_device(device_node* parent)
1049 {
1050 	const char* bus;
1051 	uint8 deviceType;
1052 
1053 	// make sure parent is really the SCSI bus manager
1054 	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
1055 		return -1;
1056 
1057 	if (strcmp(bus, "scsi"))
1058 		return 0.0;
1059 
1060 	// check whether it's really a CD-ROM or WORM
1061 	if (sDeviceManager->get_attr_uint8(parent, SCSI_DEVICE_TYPE_ITEM,
1062 			&deviceType, true) != B_OK
1063 		|| (deviceType != scsi_dev_CDROM && deviceType != scsi_dev_WORM))
1064 		return 0.0;
1065 
1066 	return 0.6;
1067 }
1068 
1069 
1070 /*!	Called whenever a new device was added to system;
1071 	if we really support it, we create a new node that gets
1072 	server by the block_io module
1073 */
1074 static status_t
1075 cd_register_device(device_node* node)
1076 {
1077 	const scsi_res_inquiry* deviceInquiry = NULL;
1078 	size_t inquiryLength;
1079 	uint32 maxBlocks;
1080 
1081 	// get inquiry data
1082 	if (sDeviceManager->get_attr_raw(node, SCSI_DEVICE_INQUIRY_ITEM,
1083 			(const void**)&deviceInquiry, &inquiryLength, true) != B_OK
1084 		|| inquiryLength < sizeof(scsi_res_inquiry))
1085 		return B_ERROR;
1086 
1087 	// get block limit of underlying hardware to lower it (if necessary)
1088 	if (sDeviceManager->get_attr_uint32(node, B_DMA_MAX_TRANSFER_BLOCKS,
1089 			&maxBlocks, true) != B_OK)
1090 		maxBlocks = INT_MAX;
1091 
1092 	// using 10 byte commands, at most 0xffff blocks can be transmitted at once
1093 	// (sadly, we cannot update this value later on if only 6 byte commands
1094 	//  are supported, but the block_io module can live with that)
1095 	maxBlocks = min_c(maxBlocks, 0xffff);
1096 
1097 	// ready to register
1098 	device_attr attrs[] = {
1099 		{B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "SCSI CD-ROM Drive"}},
1100 		{"removable", B_UINT8_TYPE, {ui8: deviceInquiry->removable_medium}},
1101 		{B_DMA_MAX_TRANSFER_BLOCKS, B_UINT32_TYPE, {ui32: maxBlocks}},
1102 		{ NULL }
1103 	};
1104 
1105 	return sDeviceManager->register_node(node, SCSI_CD_DRIVER_MODULE_NAME,
1106 		attrs, NULL, NULL);
1107 }
1108 
1109 
1110 static status_t
1111 cd_init_driver(device_node* node, void** _cookie)
1112 {
1113 	TRACE("cd_init_driver\n");
1114 
1115 	uint8 removable;
1116 	status_t status = sDeviceManager->get_attr_uint8(node, "removable",
1117 		&removable, false);
1118 	if (status != B_OK)
1119 		return status;
1120 
1121 	cd_driver_info* info = (cd_driver_info*)malloc(sizeof(cd_driver_info));
1122 	if (info == NULL)
1123 		return B_NO_MEMORY;
1124 
1125 	memset(info, 0, sizeof(cd_driver_info));
1126 
1127 	info->dma_resource = new(std::nothrow) DMAResource;
1128 	if (info->dma_resource == NULL) {
1129 		free(info);
1130 		return B_NO_MEMORY;
1131 	}
1132 
1133 	info->node = node;
1134 	info->removable = removable;
1135 
1136 	// set capacity to zero, so it get checked on first opened handle
1137 	info->original_capacity = 0;
1138 	info->capacity = 0;
1139 	info->block_size = 0;
1140 
1141 	sDeviceManager->get_attr_uint8(node, SCSI_DEVICE_TYPE_ITEM,
1142 		&info->device_type, true);
1143 
1144 	device_node *parent = sDeviceManager->get_parent_node(node);
1145 	sDeviceManager->get_driver(parent, (driver_module_info**)&info->scsi,
1146 		(void**)&info->scsi_device);
1147 	sDeviceManager->put_node(parent);
1148 
1149 	status = sSCSIPeripheral->register_device((periph_device_cookie)info,
1150 		&callbacks, info->scsi_device, info->scsi, info->node,
1151 		info->removable, 10, &info->scsi_periph_device);
1152 	if (status != B_OK) {
1153 		free(info);
1154 		return status;
1155 	}
1156 
1157 	*_cookie = info;
1158 	return B_OK;
1159 }
1160 
1161 
1162 static void
1163 cd_uninit_driver(void* _cookie)
1164 {
1165 	cd_driver_info* info = (cd_driver_info*)_cookie;
1166 
1167 	sSCSIPeripheral->unregister_device(info->scsi_periph_device);
1168 	free(info);
1169 }
1170 
1171 
1172 static status_t
1173 cd_register_child_devices(void* _cookie)
1174 {
1175 	cd_driver_info* info = (cd_driver_info*)_cookie;
1176 
1177 	char* name = sSCSIPeripheral->compose_device_name(info->node, "disk/scsi");
1178 	if (name == NULL)
1179 		return B_ERROR;
1180 
1181 	status_t status = sDeviceManager->publish_device(info->node, name,
1182 		SCSI_CD_DEVICE_MODULE_NAME);
1183 
1184 	free(name);
1185 	return status;
1186 }
1187 
1188 
1189 module_dependency module_dependencies[] = {
1190 	{SCSI_PERIPH_MODULE_NAME, (module_info**)&sSCSIPeripheral},
1191 	{B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager},
1192 	{}
1193 };
1194 
1195 struct device_module_info sSCSICDDevice = {
1196 	{
1197 		SCSI_CD_DEVICE_MODULE_NAME,
1198 		0,
1199 		NULL
1200 	},
1201 
1202 	cd_init_device,
1203 	cd_uninit_device,
1204 	NULL,	// remove,
1205 
1206 	cd_open,
1207 	cd_close,
1208 	cd_free,
1209 	cd_read,
1210 	cd_write,
1211 	cd_io,
1212 	cd_ioctl,
1213 
1214 	NULL,	// select
1215 	NULL,	// deselect
1216 };
1217 
1218 struct driver_module_info sSCSICDDriver = {
1219 	{
1220 		SCSI_CD_DRIVER_MODULE_NAME,
1221 		0,
1222 		NULL
1223 	},
1224 
1225 	cd_supports_device,
1226 	cd_register_device,
1227 	cd_init_driver,
1228 	cd_uninit_driver,
1229 	cd_register_child_devices,
1230 	NULL,	// rescan
1231 	NULL,	// removed
1232 };
1233 
1234 module_info* modules[] = {
1235 	(module_info*)&sSCSICDDriver,
1236 	(module_info*)&sSCSICDDevice,
1237 	NULL
1238 };
1239