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