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