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