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
40static 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
65static scsi_periph_interface *sSCSIPeripheral;
66static device_manager_info *sDeviceManager;
67
68
69#define SCSI_CD_STD_TIMEOUT 10
70
71
72static status_t
73update_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*/
93static uint64
94test_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
196static status_t
197get_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
231static status_t
232get_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
287err:
288	info->scsi->free_ccb(ccb);
289
290	return res;
291}
292
293
294static status_t
295load_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
312static status_t
313get_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
332static status_t
333get_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, sizeof(buffer));
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 */
446static status_t
447play_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 */
471static status_t
472play_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
520static status_t
521stop_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
535static status_t
536pause_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
551static status_t
552scan(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
600static status_t
601read_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
658static int
659log2(uint32 x)
660{
661	int y;
662
663	for (y = 31; y >= 0; --y) {
664		if (x == (1UL << y))
665			break;
666	}
667
668	return y;
669}
670
671
672static status_t
673do_io(void* cookie, IOOperation* operation)
674{
675	cd_driver_info* info = (cd_driver_info*)cookie;
676
677	// TODO: this can go away as soon as we pushed the IOOperation to the upper
678	// layers - we can then set scsi_periph::io() as callback for the scheduler
679	size_t bytesTransferred;
680	status_t status = sSCSIPeripheral->io(info->scsi_periph_device, operation,
681		&bytesTransferred);
682
683	info->io_scheduler->OperationCompleted(operation, status, bytesTransferred);
684	return status;
685}
686
687
688//	#pragma mark - device module API
689
690
691static status_t
692cd_init_device(void* _info, void** _cookie)
693{
694	cd_driver_info* info = (cd_driver_info*)_info;
695
696	update_capacity(info);
697		// Get initial capacity, but ignore the result; we do not care
698		// whether or not a media is present
699
700	*_cookie = info;
701	return B_OK;
702}
703
704
705static void
706cd_uninit_device(void* _cookie)
707{
708	cd_driver_info* info = (cd_driver_info*)_cookie;
709
710	delete info->io_scheduler;
711	delete info->dma_resource;
712}
713
714
715static status_t
716cd_open(void* _info, const char* path, int openMode, void** _cookie)
717{
718	cd_driver_info* info = (cd_driver_info*)_info;
719
720	cd_handle* handle = (cd_handle*)malloc(sizeof(cd_handle));
721	if (handle == NULL)
722		return B_NO_MEMORY;
723
724	handle->info = info;
725
726	status_t status = sSCSIPeripheral->handle_open(info->scsi_periph_device,
727		(periph_handle_cookie)handle, &handle->scsi_periph_handle);
728	if (status < B_OK) {
729		free(handle);
730		return status;
731	}
732
733	*_cookie = handle;
734	return B_OK;
735}
736
737
738static status_t
739cd_close(void* cookie)
740{
741	cd_handle* handle = (cd_handle*)cookie;
742	TRACE("close()\n");
743
744	sSCSIPeripheral->handle_close(handle->scsi_periph_handle);
745	return B_OK;
746}
747
748
749static status_t
750cd_free(void* cookie)
751{
752	cd_handle* handle = (cd_handle*)cookie;
753	TRACE("free()\n");
754
755	sSCSIPeripheral->handle_free(handle->scsi_periph_handle);
756	free(handle);
757	return B_OK;
758}
759
760
761static status_t
762cd_read(void* cookie, off_t pos, void* buffer, size_t* _length)
763{
764	cd_handle* handle = (cd_handle*)cookie;
765	size_t length = *_length;
766
767	if (handle->info->capacity == 0)
768		return B_DEV_NO_MEDIA;
769
770	IORequest request;
771	status_t status = request.Init(pos, (addr_t)buffer, length, false, 0);
772	if (status != B_OK)
773		return status;
774
775	status = handle->info->io_scheduler->ScheduleRequest(&request);
776	if (status != B_OK)
777		return status;
778
779	status = request.Wait(0, 0);
780	if (status == B_OK)
781		*_length = length;
782	else
783		dprintf("cd_read(): request.Wait() returned: %s\n", strerror(status));
784
785	return status;
786}
787
788
789static status_t
790cd_write(void* cookie, off_t pos, const void* buffer, size_t* _length)
791{
792	cd_handle* handle = (cd_handle*)cookie;
793	size_t length = *_length;
794
795	if (handle->info->capacity == 0)
796		return B_DEV_NO_MEDIA;
797
798	IORequest request;
799	status_t status = request.Init(pos, (addr_t)buffer, length, true, 0);
800	if (status != B_OK)
801		return status;
802
803	status = handle->info->io_scheduler->ScheduleRequest(&request);
804	if (status != B_OK)
805		return status;
806
807	status = request.Wait(0, 0);
808	if (status == B_OK)
809		*_length = length;
810	else
811		dprintf("cd_write(): request.Wait() returned: %s\n", strerror(status));
812
813	return status;
814}
815
816
817static status_t
818cd_io(void* cookie, io_request* request)
819{
820	cd_handle* handle = (cd_handle*)cookie;
821
822	if (handle->info->capacity == 0) {
823		notify_io_request(request, B_DEV_NO_MEDIA);
824		return B_DEV_NO_MEDIA;
825	}
826
827	return handle->info->io_scheduler->ScheduleRequest(request);
828}
829
830
831static status_t
832cd_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
833{
834	cd_handle* handle = (cd_handle*)cookie;
835	cd_driver_info *info = handle->info;
836
837	TRACE("ioctl(op = %lu)\n", op);
838
839	switch (op) {
840		case B_GET_DEVICE_SIZE:
841		{
842			status_t status = update_capacity(info);
843			if (status != B_OK)
844				return status;
845
846			size_t size = info->capacity * info->block_size;
847			return user_memcpy(buffer, &size, sizeof(size_t));
848		}
849
850		case B_GET_GEOMETRY:
851		{
852			if (buffer == NULL /*|| length != sizeof(device_geometry)*/)
853				return B_BAD_VALUE;
854
855		 	device_geometry geometry;
856			status_t status = get_geometry(handle, &geometry);
857			if (status != B_OK)
858				return status;
859
860			return user_memcpy(buffer, &geometry, sizeof(device_geometry));
861		}
862
863		case B_GET_ICON_NAME:
864			return user_strlcpy((char*)buffer, "devices/drive-optical",
865				B_FILE_NAME_LENGTH);
866
867		case B_GET_VECTOR_ICON:
868		{
869			device_icon iconData;
870			if (length != sizeof(device_icon))
871				return B_BAD_VALUE;
872			if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK)
873				return B_BAD_ADDRESS;
874
875			if (iconData.icon_size >= (int32)sizeof(kCDIcon)) {
876				if (user_memcpy(iconData.icon_data, kCDIcon,
877						sizeof(kCDIcon)) != B_OK)
878					return B_BAD_ADDRESS;
879			}
880
881			iconData.icon_size = sizeof(kCDIcon);
882			return user_memcpy(buffer, &iconData, sizeof(device_icon));
883		}
884
885		case B_SCSI_GET_TOC:
886			// TODO: we pass a user buffer here!
887			return get_toc(info, (scsi_toc *)buffer);
888
889		case B_EJECT_DEVICE:
890		case B_SCSI_EJECT:
891			return load_eject(info, false);
892
893		case B_LOAD_MEDIA:
894			return load_eject(info, true);
895
896		case B_SCSI_GET_POSITION:
897		{
898			if (buffer == NULL)
899				return B_BAD_VALUE;
900
901			scsi_position position;
902			status_t status = get_position(info, &position);
903			if (status != B_OK)
904				return status;
905
906			return user_memcpy(buffer, &position, sizeof(scsi_position));
907		}
908
909		case B_SCSI_GET_VOLUME:
910			// TODO: we pass a user buffer here!
911			return get_set_volume(info, (scsi_volume *)buffer, false);
912		case B_SCSI_SET_VOLUME:
913			// TODO: we pass a user buffer here!
914			return get_set_volume(info, (scsi_volume *)buffer, true);
915
916		case B_SCSI_PLAY_TRACK:
917		{
918			scsi_play_track track;
919			if (user_memcpy(&track, buffer, sizeof(scsi_play_track)) != B_OK)
920				return B_BAD_ADDRESS;
921
922			return play_track_index(info, &track);
923		}
924		case B_SCSI_PLAY_POSITION:
925		{
926			scsi_play_position position;
927			if (user_memcpy(&position, buffer, sizeof(scsi_play_position))
928					!= B_OK)
929				return B_BAD_ADDRESS;
930
931			return play_msf(info, &position);
932		}
933
934		case B_SCSI_STOP_AUDIO:
935			return stop_audio(info);
936		case B_SCSI_PAUSE_AUDIO:
937			return pause_resume(info, false);
938		case B_SCSI_RESUME_AUDIO:
939			return pause_resume(info, true);
940
941		case B_SCSI_SCAN:
942		{
943			scsi_scan scanBuffer;
944			if (user_memcpy(&scanBuffer, buffer, sizeof(scsi_scan)) != B_OK)
945				return B_BAD_ADDRESS;
946
947			return scan(info, &scanBuffer);
948		}
949		case B_SCSI_READ_CD:
950			// TODO: we pass a user buffer here!
951			return read_cd(info, (scsi_read_cd *)buffer);
952
953		default:
954			return sSCSIPeripheral->ioctl(handle->scsi_periph_handle, op,
955				buffer, length);
956	}
957}
958
959
960//	#pragma mark - scsi_periph callbacks
961
962
963static void
964cd_set_capacity(cd_driver_info* info, uint64 capacity, uint32 blockSize)
965{
966	TRACE("cd_set_capacity(info = %p, capacity = %Ld, blockSize = %ld)\n",
967		info, capacity, blockSize);
968
969	// get log2, if possible
970	uint32 blockShift = log2(blockSize);
971
972	if ((1UL << blockShift) != blockSize)
973		blockShift = 0;
974
975	if (info->block_size != blockSize) {
976		if (capacity == 0) {
977			// there is obviously no medium in the drive, don't try to update
978			// the DMA resource
979			return;
980		}
981
982		if (info->block_size != 0) {
983			dprintf("old %" B_PRId32 ", new %" B_PRId32 "\n", info->block_size,
984				blockSize);
985			panic("updating DMAResource not yet implemented...");
986		}
987
988		// TODO: we need to replace the DMAResource in our IOScheduler
989		status_t status = info->dma_resource->Init(info->node, blockSize, 1024,
990			32);
991		if (status != B_OK)
992			panic("initializing DMAResource failed: %s", strerror(status));
993
994		// Allocate the I/O scheduler. If there seems to be sufficient memory
995		// we use an IOCache, since that adds caching at the lowest I/O layer
996		// and thus dramatically reduces I/O operations and seeks. The
997		// disadvantage is that it increases free memory (physical pages)
998		// fragmentation, which makes large contiguous allocations more likely
999		// to fail.
1000		size_t freeMemory = vm_page_num_free_pages();
1001		if (freeMemory > 180 * 1024 * 1024 / B_PAGE_SIZE) {
1002			info->io_scheduler = new(std::nothrow) IOCache(info->dma_resource,
1003				1024 * 1024);
1004		} else {
1005			dprintf("scsi_cd: Using IOSchedulerSimple instead of IOCache to "
1006				"avoid memory allocation issues.\n");
1007			info->io_scheduler = new(std::nothrow) IOSchedulerSimple(
1008				info->dma_resource);
1009		}
1010
1011		if (info->io_scheduler == NULL)
1012			panic("allocating IOScheduler failed.");
1013
1014		// TODO: use whole device name here
1015		status = info->io_scheduler->Init("scsi");
1016		if (status != B_OK)
1017			panic("initializing IOScheduler failed: %s", strerror(status));
1018
1019		info->io_scheduler->SetCallback(do_io, info);
1020		info->block_size = blockSize;
1021	}
1022
1023	if (info->original_capacity != capacity && info->io_scheduler != NULL) {
1024		info->original_capacity = capacity;
1025
1026		// For CDs, it's obviously relatively normal that they report a larger
1027		// capacity than it can actually address. Therefore we'll manually
1028		// correct the value here.
1029		test_capacity(info);
1030
1031		info->io_scheduler->SetDeviceCapacity(info->capacity * blockSize);
1032	}
1033}
1034
1035
1036static void
1037cd_media_changed(cd_driver_info* info, scsi_ccb* request)
1038{
1039	// do a capacity check
1040	// TODO: is this a good idea (e.g. if this is an empty CD)?
1041	info->original_capacity = 0;
1042	info->capacity = 0;
1043	sSCSIPeripheral->check_capacity(info->scsi_periph_device, request);
1044
1045	if (info->io_scheduler != NULL)
1046		info->io_scheduler->MediaChanged();
1047}
1048
1049
1050scsi_periph_callbacks callbacks = {
1051	(void (*)(periph_device_cookie, uint64, uint32))cd_set_capacity,
1052	(void (*)(periph_device_cookie, scsi_ccb *))cd_media_changed
1053};
1054
1055
1056//	#pragma mark - driver module API
1057
1058
1059static float
1060cd_supports_device(device_node* parent)
1061{
1062	const char* bus;
1063	uint8 deviceType;
1064
1065	// make sure parent is really the SCSI bus manager
1066	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
1067		return -1;
1068
1069	if (strcmp(bus, "scsi"))
1070		return 0.0;
1071
1072	// check whether it's really a CD-ROM or WORM
1073	if (sDeviceManager->get_attr_uint8(parent, SCSI_DEVICE_TYPE_ITEM,
1074			&deviceType, true) != B_OK
1075		|| (deviceType != scsi_dev_CDROM && deviceType != scsi_dev_WORM))
1076		return 0.0;
1077
1078	return 0.6;
1079}
1080
1081
1082/*!	Called whenever a new device was added to system;
1083	if we really support it, we create a new node that gets
1084	server by the block_io module
1085*/
1086static status_t
1087cd_register_device(device_node* node)
1088{
1089	const scsi_res_inquiry* deviceInquiry = NULL;
1090	size_t inquiryLength;
1091	uint32 maxBlocks;
1092
1093	// get inquiry data
1094	if (sDeviceManager->get_attr_raw(node, SCSI_DEVICE_INQUIRY_ITEM,
1095			(const void**)&deviceInquiry, &inquiryLength, true) != B_OK
1096		|| inquiryLength < sizeof(deviceInquiry))
1097		return B_ERROR;
1098
1099	// get block limit of underlying hardware to lower it (if necessary)
1100	if (sDeviceManager->get_attr_uint32(node, B_DMA_MAX_TRANSFER_BLOCKS,
1101			&maxBlocks, true) != B_OK)
1102		maxBlocks = INT_MAX;
1103
1104	// using 10 byte commands, at most 0xffff blocks can be transmitted at once
1105	// (sadly, we cannot update this value later on if only 6 byte commands
1106	//  are supported, but the block_io module can live with that)
1107	maxBlocks = min_c(maxBlocks, 0xffff);
1108
1109	// ready to register
1110	device_attr attrs[] = {
1111		{"removable", B_UINT8_TYPE, {ui8: deviceInquiry->removable_medium}},
1112		{B_DMA_MAX_TRANSFER_BLOCKS, B_UINT32_TYPE, {ui32: maxBlocks}},
1113		{ NULL }
1114	};
1115
1116	return sDeviceManager->register_node(node, SCSI_CD_DRIVER_MODULE_NAME,
1117		attrs, NULL, NULL);
1118}
1119
1120
1121static status_t
1122cd_init_driver(device_node* node, void** _cookie)
1123{
1124	TRACE("cd_init_driver\n");
1125
1126	uint8 removable;
1127	status_t status = sDeviceManager->get_attr_uint8(node, "removable",
1128		&removable, false);
1129	if (status != B_OK)
1130		return status;
1131
1132	cd_driver_info* info = (cd_driver_info*)malloc(sizeof(cd_driver_info));
1133	if (info == NULL)
1134		return B_NO_MEMORY;
1135
1136	memset(info, 0, sizeof(cd_driver_info));
1137
1138	info->dma_resource = new(std::nothrow) DMAResource;
1139	if (info->dma_resource == NULL) {
1140		free(info);
1141		return B_NO_MEMORY;
1142	}
1143
1144	info->node = node;
1145	info->removable = removable;
1146
1147	// set capacity to zero, so it get checked on first opened handle
1148	info->original_capacity = 0;
1149	info->capacity = 0;
1150	info->block_size = 0;
1151
1152	sDeviceManager->get_attr_uint8(node, SCSI_DEVICE_TYPE_ITEM,
1153		&info->device_type, true);
1154
1155	device_node *parent = sDeviceManager->get_parent_node(node);
1156	sDeviceManager->get_driver(parent, (driver_module_info**)&info->scsi,
1157		(void**)&info->scsi_device);
1158	sDeviceManager->put_node(parent);
1159
1160	status = sSCSIPeripheral->register_device((periph_device_cookie)info,
1161		&callbacks, info->scsi_device, info->scsi, info->node,
1162		info->removable, 10, &info->scsi_periph_device);
1163	if (status != B_OK) {
1164		free(info);
1165		return status;
1166	}
1167
1168	*_cookie = info;
1169	return B_OK;
1170}
1171
1172
1173static void
1174cd_uninit_driver(void* _cookie)
1175{
1176	cd_driver_info* info = (cd_driver_info*)_cookie;
1177
1178	sSCSIPeripheral->unregister_device(info->scsi_periph_device);
1179	free(info);
1180}
1181
1182
1183static status_t
1184cd_register_child_devices(void* _cookie)
1185{
1186	cd_driver_info* info = (cd_driver_info*)_cookie;
1187
1188	char* name = sSCSIPeripheral->compose_device_name(info->node, "disk/scsi");
1189	if (name == NULL)
1190		return B_ERROR;
1191
1192	status_t status = sDeviceManager->publish_device(info->node, name,
1193		SCSI_CD_DEVICE_MODULE_NAME);
1194
1195	free(name);
1196	return status;
1197}
1198
1199
1200module_dependency module_dependencies[] = {
1201	{SCSI_PERIPH_MODULE_NAME, (module_info**)&sSCSIPeripheral},
1202	{B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager},
1203	{}
1204};
1205
1206struct device_module_info sSCSICDDevice = {
1207	{
1208		SCSI_CD_DEVICE_MODULE_NAME,
1209		0,
1210		NULL
1211	},
1212
1213	cd_init_device,
1214	cd_uninit_device,
1215	NULL,	// remove,
1216
1217	cd_open,
1218	cd_close,
1219	cd_free,
1220	cd_read,
1221	cd_write,
1222	cd_io,
1223	cd_ioctl,
1224
1225	NULL,	// select
1226	NULL,	// deselect
1227};
1228
1229struct driver_module_info sSCSICDDriver = {
1230	{
1231		SCSI_CD_DRIVER_MODULE_NAME,
1232		0,
1233		NULL
1234	},
1235
1236	cd_supports_device,
1237	cd_register_device,
1238	cd_init_driver,
1239	cd_uninit_driver,
1240	cd_register_child_devices,
1241	NULL,	// rescan
1242	NULL,	// removed
1243};
1244
1245module_info* modules[] = {
1246	(module_info*)&sSCSICDDriver,
1247	(module_info*)&sSCSICDDevice,
1248	NULL
1249};
1250