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