xref: /haiku/src/add-ons/kernel/drivers/disk/scsi/scsi_cd/scsi_cd.cpp (revision f75a7bf508f3156d63a14f8fd77c5e0ca4d08c42)
1 /*
2  * Copyright 2004-2008, Haiku, Inc. All RightsReserved.
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 	IORequest request;
658 	status_t status = request.Init(pos, buffer, length, false, 0);
659 	if (status != B_OK)
660 		return status;
661 
662 	status = handle->info->io_scheduler->ScheduleRequest(&request);
663 	if (status != B_OK)
664 		return status;
665 
666 	status = request.Wait(0, 0);
667 	if (status == B_OK)
668 		*_length = length;
669 	else
670 		dprintf("cd_read(): request.Wait() returned: %s\n", strerror(status));
671 
672 	return status;
673 }
674 
675 
676 static status_t
677 cd_write(void* cookie, off_t pos, const void* buffer, size_t* _length)
678 {
679 	cd_handle* handle = (cd_handle*)cookie;
680 	size_t length = *_length;
681 
682 	IORequest request;
683 	status_t status = request.Init(pos, (void*)buffer, length, true, 0);
684 	if (status != B_OK)
685 		return status;
686 
687 	status = handle->info->io_scheduler->ScheduleRequest(&request);
688 	if (status != B_OK)
689 		return status;
690 
691 	status = request.Wait(0, 0);
692 	if (status == B_OK)
693 		*_length = length;
694 	else
695 		dprintf("cd_write(): request.Wait() returned: %s\n", strerror(status));
696 
697 	return status;
698 }
699 
700 
701 static status_t
702 cd_io(void* cookie, io_request* request)
703 {
704 	cd_handle* handle = (cd_handle*)cookie;
705 
706 	return handle->info->io_scheduler->ScheduleRequest(request);
707 }
708 
709 
710 static status_t
711 cd_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
712 {
713 	cd_handle* handle = (cd_handle*)cookie;
714 	cd_driver_info *info = handle->info;
715 
716 	TRACE("ioctl(op = %d)\n", op);
717 
718 	switch (op) {
719 		case B_GET_DEVICE_SIZE:
720 		{
721 			status_t status = update_capacity(info);
722 			if (status != B_OK)
723 				return status;
724 
725 			size_t size = info->capacity * info->block_size;
726 			return user_memcpy(buffer, &size, sizeof(size_t));
727 		}
728 
729 		case B_GET_GEOMETRY:
730 		{
731 			if (buffer == NULL /*|| length != sizeof(device_geometry)*/)
732 				return B_BAD_VALUE;
733 
734 		 	device_geometry geometry;
735 			status_t status = get_geometry(handle, &geometry);
736 			if (status != B_OK)
737 				return status;
738 
739 			return user_memcpy(buffer, &geometry, sizeof(device_geometry));
740 		}
741 
742 		case B_GET_ICON_NAME:
743 			return user_strlcpy((char*)buffer, "devices/drive-optical",
744 				B_FILE_NAME_LENGTH);
745 
746 		case B_GET_VECTOR_ICON:
747 		{
748 			device_icon iconData;
749 			if (length != sizeof(device_icon))
750 				return B_BAD_VALUE;
751 			if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK)
752 				return B_BAD_ADDRESS;
753 
754 			if (iconData.icon_size >= (int32)sizeof(kCDIcon)) {
755 				if (user_memcpy(iconData.icon_data, kCDIcon,
756 						sizeof(kCDIcon)) != B_OK)
757 					return B_BAD_ADDRESS;
758 			}
759 
760 			iconData.icon_size = sizeof(kCDIcon);
761 			return user_memcpy(buffer, &iconData, sizeof(device_icon));
762 		}
763 
764 		case B_SCSI_GET_TOC:
765 			// TODO: we pass a user buffer here!
766 			return get_toc(info, (scsi_toc *)buffer);
767 
768 		case B_EJECT_DEVICE:
769 		case B_SCSI_EJECT:
770 			return load_eject(info, false);
771 
772 		case B_LOAD_MEDIA:
773 			return load_eject(info, true);
774 
775 		case B_SCSI_GET_POSITION:
776 		{
777 			if (buffer == NULL)
778 				return B_BAD_VALUE;
779 
780 			scsi_position position;
781 			status_t status = get_position(info, &position);
782 			if (status != B_OK)
783 				return status;
784 
785 			return user_memcpy(buffer, &position, sizeof(scsi_position));
786 		}
787 
788 		case B_SCSI_GET_VOLUME:
789 			// TODO: we pass a user buffer here!
790 			return get_set_volume(info, (scsi_volume *)buffer, false);
791 		case B_SCSI_SET_VOLUME:
792 			// TODO: we pass a user buffer here!
793 			return get_set_volume(info, (scsi_volume *)buffer, true);
794 
795 		case B_SCSI_PLAY_TRACK:
796 		{
797 			scsi_play_track track;
798 			if (user_memcpy(&track, buffer, sizeof(scsi_play_track)) != B_OK)
799 				return B_BAD_ADDRESS;
800 
801 			return play_track_index(info, &track);
802 		}
803 		case B_SCSI_PLAY_POSITION:
804 		{
805 			scsi_play_position position;
806 			if (user_memcpy(&position, buffer, sizeof(scsi_play_position))
807 					!= B_OK)
808 				return B_BAD_ADDRESS;
809 
810 			return play_msf(info, &position);
811 		}
812 
813 		case B_SCSI_STOP_AUDIO:
814 			return stop_audio(info);
815 		case B_SCSI_PAUSE_AUDIO:
816 			return pause_resume(info, false);
817 		case B_SCSI_RESUME_AUDIO:
818 			return pause_resume(info, true);
819 
820 		case B_SCSI_SCAN:
821 		{
822 			scsi_scan scanBuffer;
823 			if (user_memcpy(&scanBuffer, buffer, sizeof(scsi_scan)) != B_OK)
824 				return B_BAD_ADDRESS;
825 
826 			return scan(info, &scanBuffer);
827 		}
828 		case B_SCSI_READ_CD:
829 			// TODO: we pass a user buffer here!
830 			return read_cd(info, (scsi_read_cd *)buffer);
831 
832 		default:
833 			return sSCSIPeripheral->ioctl(handle->scsi_periph_handle, op,
834 				buffer, length);
835 	}
836 }
837 
838 
839 //	#pragma mark - scsi_periph callbacks
840 
841 
842 static void
843 cd_set_capacity(cd_driver_info* info, uint64 capacity, uint32 blockSize)
844 {
845 	TRACE("cd_set_capacity(info = %p, capacity = %Ld, blockSize = %ld)\n",
846 		info, capacity, blockSize);
847 
848 	// get log2, if possible
849 	uint32 blockShift = log2(blockSize);
850 
851 	if ((1UL << blockShift) != blockSize)
852 		blockShift = 0;
853 
854 	info->capacity = capacity;
855 
856 	if (info->block_size != blockSize) {
857 		if (info->block_size != 0) {
858 			dprintf("old %ld, new %ld\n", info->block_size, blockSize);
859 			panic("updating DMAResource not yet implemented...");
860 		}
861 
862 		// TODO: we need to replace the DMAResource in our IOScheduler
863 		status_t status = info->dma_resource->Init(info->node, blockSize, 1024,
864 			32);
865 		if (status != B_OK)
866 			panic("initializing DMAResource failed: %s", strerror(status));
867 
868 		info->io_scheduler = new(std::nothrow) IOScheduler(info->dma_resource);
869 		if (info->io_scheduler == NULL)
870 			panic("allocating IOScheduler failed.");
871 
872 		// TODO: use whole device name here
873 		status = info->io_scheduler->Init("scsi");
874 		if (status != B_OK)
875 			panic("initializing IOScheduler failed: %s", strerror(status));
876 
877 		info->io_scheduler->SetCallback(do_io, info);
878 	}
879 
880 	info->block_size = blockSize;
881 }
882 
883 
884 static void
885 cd_media_changed(cd_driver_info* info, scsi_ccb* request)
886 {
887 	// do a capacity check
888 	// TBD: is this a good idea (e.g. if this is an empty CD)?
889 	sSCSIPeripheral->check_capacity(info->scsi_periph_device, request);
890 }
891 
892 
893 scsi_periph_callbacks callbacks = {
894 	(void (*)(periph_device_cookie, uint64, uint32))cd_set_capacity,
895 	(void (*)(periph_device_cookie, scsi_ccb *))cd_media_changed
896 };
897 
898 
899 //	#pragma mark - driver module API
900 
901 
902 static float
903 cd_supports_device(device_node* parent)
904 {
905 	const char* bus;
906 	uint8 deviceType;
907 
908 	// make sure parent is really the SCSI bus manager
909 	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
910 		return -1;
911 
912 	if (strcmp(bus, "scsi"))
913 		return 0.0;
914 
915 	// check whether it's really a CD-ROM or WORM
916 	if (sDeviceManager->get_attr_uint8(parent, SCSI_DEVICE_TYPE_ITEM,
917 			&deviceType, true) != B_OK
918 		|| (deviceType != scsi_dev_CDROM && deviceType != scsi_dev_WORM))
919 		return 0.0;
920 
921 	return 0.6;
922 }
923 
924 
925 /*!	Called whenever a new device was added to system;
926 	if we really support it, we create a new node that gets
927 	server by the block_io module
928 */
929 static status_t
930 cd_register_device(device_node* node)
931 {
932 	const scsi_res_inquiry* deviceInquiry = NULL;
933 	size_t inquiryLength;
934 	uint32 maxBlocks;
935 
936 	// get inquiry data
937 	if (sDeviceManager->get_attr_raw(node, SCSI_DEVICE_INQUIRY_ITEM,
938 			(const void**)&deviceInquiry, &inquiryLength, true) != B_OK
939 		|| inquiryLength < sizeof(deviceInquiry))
940 		return B_ERROR;
941 
942 	// get block limit of underlying hardware to lower it (if necessary)
943 	if (sDeviceManager->get_attr_uint32(node, B_DMA_MAX_TRANSFER_BLOCKS,
944 			&maxBlocks, true) != B_OK)
945 		maxBlocks = INT_MAX;
946 
947 	// using 10 byte commands, at most 0xffff blocks can be transmitted at once
948 	// (sadly, we cannot update this value later on if only 6 byte commands
949 	//  are supported, but the block_io module can live with that)
950 	maxBlocks = min_c(maxBlocks, 0xffff);
951 
952 	// ready to register
953 	device_attr attrs[] = {
954 		{"removable", B_UINT8_TYPE, {ui8: deviceInquiry->removable_medium}},
955 		{B_DMA_MAX_TRANSFER_BLOCKS, B_UINT32_TYPE, {ui32: maxBlocks}},
956 		{ NULL }
957 	};
958 
959 	return sDeviceManager->register_node(node, SCSI_CD_DRIVER_MODULE_NAME,
960 		attrs, NULL, NULL);
961 }
962 
963 
964 static status_t
965 cd_init_driver(device_node* node, void** _cookie)
966 {
967 	TRACE("cd_init_driver");
968 
969 	uint8 removable;
970 	status_t status = sDeviceManager->get_attr_uint8(node, "removable",
971 		&removable, false);
972 	if (status != B_OK)
973 		return status;
974 
975 	cd_driver_info* info = (cd_driver_info*)malloc(sizeof(cd_driver_info));
976 	if (info == NULL)
977 		return B_NO_MEMORY;
978 
979 	memset(info, 0, sizeof(cd_driver_info));
980 
981 	info->dma_resource = new(std::nothrow) DMAResource;
982 	if (info->dma_resource == NULL) {
983 		free(info);
984 		return B_NO_MEMORY;
985 	}
986 
987 	info->node = node;
988 	info->removable = removable;
989 
990 	// set capacity to zero, so it get checked on first opened handle
991 	info->capacity = 0;
992 	info->block_size = 0;
993 
994 	sDeviceManager->get_attr_uint8(node, SCSI_DEVICE_TYPE_ITEM,
995 		&info->device_type, true);
996 
997 	device_node *parent = sDeviceManager->get_parent_node(node);
998 	sDeviceManager->get_driver(parent, (driver_module_info**)&info->scsi,
999 		(void**)&info->scsi_device);
1000 	sDeviceManager->put_node(parent);
1001 
1002 	status = sSCSIPeripheral->register_device((periph_device_cookie)info,
1003 		&callbacks, info->scsi_device, info->scsi, info->node,
1004 		info->removable, 10, &info->scsi_periph_device);
1005 	if (status != B_OK) {
1006 		free(info);
1007 		return status;
1008 	}
1009 
1010 	*_cookie = info;
1011 	return B_OK;
1012 }
1013 
1014 
1015 static void
1016 cd_uninit_driver(void* _cookie)
1017 {
1018 	cd_driver_info* info = (cd_driver_info*)_cookie;
1019 
1020 	sSCSIPeripheral->unregister_device(info->scsi_periph_device);
1021 	free(info);
1022 }
1023 
1024 
1025 static status_t
1026 cd_register_child_devices(void* _cookie)
1027 {
1028 	cd_driver_info* info = (cd_driver_info*)_cookie;
1029 
1030 	char* name = sSCSIPeripheral->compose_device_name(info->node, "disk/scsi");
1031 	if (name == NULL)
1032 		return B_ERROR;
1033 
1034 	status_t status = sDeviceManager->publish_device(info->node, name,
1035 		SCSI_CD_DEVICE_MODULE_NAME);
1036 
1037 	free(name);
1038 	return status;
1039 }
1040 
1041 
1042 module_dependency module_dependencies[] = {
1043 	{SCSI_PERIPH_MODULE_NAME, (module_info**)&sSCSIPeripheral},
1044 	{B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager},
1045 	{}
1046 };
1047 
1048 struct device_module_info sSCSICDDevice = {
1049 	{
1050 		SCSI_CD_DEVICE_MODULE_NAME,
1051 		0,
1052 		NULL
1053 	},
1054 
1055 	cd_init_device,
1056 	cd_uninit_device,
1057 	NULL,	// remove,
1058 
1059 	cd_open,
1060 	cd_close,
1061 	cd_free,
1062 	cd_read,
1063 	cd_write,
1064 	cd_io,
1065 	cd_ioctl,
1066 
1067 	NULL,	// select
1068 	NULL,	// deselect
1069 };
1070 
1071 struct driver_module_info sSCSICDDriver = {
1072 	{
1073 		SCSI_CD_DRIVER_MODULE_NAME,
1074 		0,
1075 		NULL
1076 	},
1077 
1078 	cd_supports_device,
1079 	cd_register_device,
1080 	cd_init_driver,
1081 	cd_uninit_driver,
1082 	cd_register_child_devices,
1083 	NULL,	// rescan
1084 	NULL,	// removed
1085 };
1086 
1087 module_info* modules[] = {
1088 	(module_info*)&sSCSICDDriver,
1089 	(module_info*)&sSCSICDDevice,
1090 	NULL
1091 };
1092