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