1 /*
2 Copyright (C) 2008, 2010-2012, 2017, 2018 Rocky Bernstein <rocky@gnu.org>
3 Copyright (C) 2014 Robert Kausch <robert.kausch@freac.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /* Changes up to version 0.76 */
20 /*
21 * Copyright (c) 2003
22 * Matthias Drochner. All rights reserved.
23 *
24 * Redistribution and use in source and binary forms, with or without
25 * modification, are permitted provided that the following conditions
26 * are met:
27 * 1. Redistributions of source code must retain the above copyright
28 * notice, this list of conditions and the following disclaimer.
29 * 2. Redistributions in binary form must reproduce the above copyright
30 * notice, this list of conditions and the following disclaimer in the
31 * documentation and/or other materials provided with the distribution.
32 *
33 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
34 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
35 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
36 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
37 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
38 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
39 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
40 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
41 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
42 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43 */
44
45 /*
46 * NetBSD and OpenBSD support for libcdio.
47 */
48
49 #ifdef HAVE_CONFIG_H
50 # include "config.h"
51 #endif
52
53 #ifdef HAVE_STDBOOL_H
54 # include <stdbool.h>
55 #endif
56
57 #include <cdio/sector.h>
58 #include <cdio/util.h>
59 #include "cdio_assert.h"
60 #include "cdio_private.h"
61
62 #ifndef USE_MMC_SUBCHANNEL
63 #define USE_MMC_SUBCHANNEL 0
64 #endif
65
66 #if defined(__NetBSD__) && (defined(__i386__) || defined(__amd64__))
67 #define DEFAULT_CDIO_DEVICE "/dev/rcd0d"
68 #else
69 #define DEFAULT_CDIO_DEVICE "/dev/rcd0c"
70 #endif
71
72 #define MAX_CD_DEVICES 64
73
74 #ifdef HAVE_STRING_H
75 #include <string.h>
76 #endif
77
78 #ifdef HAVE_NETBSD_CDROM
79 #include <stdio.h>
80 #include <stdlib.h>
81 #include <errno.h>
82
83 #include <unistd.h>
84 #include <fcntl.h>
85 #include <sys/stat.h>
86 #include <sys/types.h>
87 #include <sys/ioctl.h>
88 #include <sys/cdio.h>
89 #include <sys/scsiio.h>
90 #include <sys/sysctl.h>
91 #include <sys/disklabel.h>
92
93 #define TOTAL_TRACKS (_obj->tochdr.ending_track \
94 - _obj->tochdr.starting_track + 1)
95 #define FIRST_TRACK_NUM (_obj->tochdr.starting_track)
96
97 typedef enum {
98 _AM_NONE,
99 _AM_IOCTL,
100 _AM_READ_CD,
101 _AM_MMC_RDWR,
102 _AM_MMC_RDWR_EXCL,
103 } access_mode_t;
104
105 typedef struct {
106 /* Things common to all drivers like this.
107 This must be first. */
108 generic_img_private_t gen;
109
110 access_mode_t access_mode;
111
112 bool toc_valid;
113 struct ioc_toc_header tochdr;
114 struct cd_toc_entry tocent[100];
115
116 bool sessionformat_valid;
117 int sessionformat[100]; /* format of the session the track is in */
118 } _img_private_t;
119
120 static driver_return_code_t
run_scsi_cmd_netbsd(void * p_user_data,unsigned int i_timeout_ms,unsigned int i_cdb,const mmc_cdb_t * p_cdb,cdio_mmc_direction_t e_direction,unsigned int i_buf,void * p_buf)121 run_scsi_cmd_netbsd(void *p_user_data, unsigned int i_timeout_ms,
122 unsigned int i_cdb, const mmc_cdb_t *p_cdb,
123 cdio_mmc_direction_t e_direction,
124 unsigned int i_buf, void *p_buf )
125 {
126 const _img_private_t *_obj = p_user_data;
127 scsireq_t req;
128
129 memset(&req, 0, sizeof(req));
130 memcpy(&req.cmd[0], p_cdb, i_cdb);
131 req.cmdlen = i_cdb;
132 req.datalen = i_buf;
133 req.databuf = p_buf;
134 req.timeout = i_timeout_ms;
135 req.flags = e_direction == SCSI_MMC_DATA_READ ? SCCMD_READ : SCCMD_WRITE;
136
137 if (ioctl(_obj->gen.fd, SCIOCCOMMAND, &req) < 0) {
138 cdio_info("SCIOCCOMMAND: %s", strerror(errno));
139 return -1;
140 }
141 if (req.retsts != SCCMD_OK) {
142 cdio_info("SCIOCCOMMAND cmd 0x%02x sts %d\n", req.cmd[0], req.retsts);
143 return -1;
144 }
145
146 return 0;
147 }
148
149 static access_mode_t
str_to_access_mode_netbsd(const char * psz_access_mode)150 str_to_access_mode_netbsd(const char *psz_access_mode)
151 {
152 const access_mode_t default_access_mode = _AM_IOCTL;
153
154 if (NULL==psz_access_mode) return default_access_mode;
155
156 if (!strcmp(psz_access_mode, "IOCTL"))
157 return _AM_IOCTL;
158 else if (!strcmp(psz_access_mode, "READ_CD"))
159 return _AM_READ_CD;
160 else if (!strcmp(psz_access_mode, "MMC_RDWR"))
161 return _AM_MMC_RDWR;
162 else if (!strcmp(psz_access_mode, "MMC_RDWR_EXCL"))
163 return _AM_MMC_RDWR_EXCL;
164 else {
165 cdio_warn ("unknown access type: %s. Default IOCTL used.",
166 psz_access_mode);
167 return default_access_mode;
168 }
169 }
170
171 static int
read_audio_sectors_netbsd(void * user_data,void * data,lsn_t lsn,unsigned int nblocks)172 read_audio_sectors_netbsd(void *user_data, void *data, lsn_t lsn,
173 unsigned int nblocks)
174 {
175 scsireq_t req;
176 _img_private_t *_obj = user_data;
177
178 memset(&req, 0, sizeof(req));
179 req.cmd[0] = 0xbe;
180 req.cmd[1] = 0;
181 req.cmd[2] = (lsn >> 24) & 0xff;
182 req.cmd[3] = (lsn >> 16) & 0xff;
183 req.cmd[4] = (lsn >> 8) & 0xff;
184 req.cmd[5] = (lsn >> 0) & 0xff;
185 req.cmd[6] = (nblocks >> 16) & 0xff;
186 req.cmd[7] = (nblocks >> 8) & 0xff;
187 req.cmd[8] = (nblocks >> 0) & 0xff;
188 req.cmd[9] = 0x78;
189 req.cmdlen = 10;
190
191 req.datalen = nblocks * CDIO_CD_FRAMESIZE_RAW;
192 req.databuf = data;
193 req.timeout = 10000;
194 req.flags = SCCMD_READ;
195
196 if (ioctl(_obj->gen.fd, SCIOCCOMMAND, &req) < 0) {
197 cdio_info("SCIOCCOMMAND: %s", strerror(errno));
198 return 1;
199 }
200 if (req.retsts != SCCMD_OK) {
201 cdio_info("SCIOCCOMMAND cmd 0xbe sts %d\n", req.retsts);
202 return 1;
203 }
204
205 return 0;
206 }
207
208 /*!
209 Reads a single mode1 sector from cd device into data starting
210 from lsn. Returns 0 if no error.
211 */
212 static driver_return_code_t
_read_mode1_sector_netbsd(void * p_user_data,void * p_data,lsn_t lsn,bool b_form2)213 _read_mode1_sector_netbsd (void *p_user_data, void *p_data, lsn_t lsn,
214 bool b_form2)
215 {
216
217 return cdio_generic_read_form1_sector(p_user_data, p_data, lsn);
218 }
219
220 /*!
221 Reads i_blocks of mode2 sectors from cd device into data starting
222 from lsn.
223 Returns 0 if no error.
224 */
225 static driver_return_code_t
_read_mode1_sectors_netbsd(void * p_user_data,void * p_data,lsn_t lsn,bool b_form2,uint32_t i_blocks)226 _read_mode1_sectors_netbsd (void *p_user_data, void *p_data, lsn_t lsn,
227 bool b_form2, uint32_t i_blocks)
228 {
229 _img_private_t *p_env = p_user_data;
230 unsigned int i;
231 int retval;
232 unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE;
233
234 for (i = 0; i < i_blocks; i++) {
235 if ( (retval = _read_mode1_sector_netbsd (p_env,
236 ((char *)p_data) + (blocksize*i),
237 lsn + i, b_form2)) )
238 return retval;
239 }
240 return DRIVER_OP_SUCCESS;
241 }
242
243 static int
read_mode2_sector_netbsd(void * user_data,void * data,lsn_t lsn,bool mode2_form2)244 read_mode2_sector_netbsd(void *user_data, void *data, lsn_t lsn,
245 bool mode2_form2)
246 {
247 scsireq_t req;
248 _img_private_t *_obj = user_data;
249 char buf[M2RAW_SECTOR_SIZE] = { 0, };
250
251 memset(&req, 0, sizeof(req));
252 req.cmd[0] = 0xbe;
253 req.cmd[1] = 0;
254 req.cmd[2] = (lsn >> 24) & 0xff;
255 req.cmd[3] = (lsn >> 16) & 0xff;
256 req.cmd[4] = (lsn >> 8) & 0xff;
257 req.cmd[5] = (lsn >> 0) & 0xff;
258 req.cmd[6] = 0;
259 req.cmd[7] = 0;
260 req.cmd[8] = 1;
261 req.cmd[9] = 0x58; /* subheader + userdata + ECC */
262 req.cmdlen = 10;
263
264 req.datalen = M2RAW_SECTOR_SIZE;
265 req.databuf = buf;
266 req.timeout = 10000;
267 req.flags = SCCMD_READ;
268
269 if (ioctl(_obj->gen.fd, SCIOCCOMMAND, &req) < 0) {
270 cdio_info("SCIOCCOMMAND: %s", strerror(errno));
271 return 1;
272 }
273 if (req.retsts != SCCMD_OK) {
274 cdio_info("SCIOCCOMMAND cmd 0xbe sts %d\n", req.retsts);
275 return 1;
276 }
277
278 if (mode2_form2)
279 memcpy(data, buf, M2RAW_SECTOR_SIZE);
280 else
281 memcpy(data, buf + CDIO_CD_SUBHEADER_SIZE, CDIO_CD_FRAMESIZE);
282
283 return 0;
284 }
285
286 static int
read_mode2_sectors_netbsd(void * user_data,void * data,lsn_t lsn,bool mode2_form2,unsigned int nblocks)287 read_mode2_sectors_netbsd(void *user_data, void *data, lsn_t lsn,
288 bool mode2_form2, unsigned int nblocks)
289 {
290 int i, res;
291 char *buf = data;
292
293 for (i = 0; i < nblocks; i++) {
294 res = read_mode2_sector_netbsd(user_data, buf, lsn, mode2_form2);
295 if (res)
296 return res;
297
298 buf += (mode2_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE);
299 lsn++;
300 }
301
302 return 0;
303 }
304
305 static int
set_arg_netbsd(void * p_user_data,const char key[],const char value[])306 set_arg_netbsd(void *p_user_data, const char key[], const char value[])
307 {
308 _img_private_t *p_env = p_user_data;
309
310 if (!strcmp (key, "source"))
311 {
312 if (!value) return DRIVER_OP_ERROR;
313 free (p_env->gen.source_name);
314 p_env->gen.source_name = strdup (value);
315 }
316 else if (!strcmp (key, "access-mode"))
317 {
318 p_env->access_mode = str_to_access_mode_netbsd(key);
319 }
320 else return DRIVER_OP_ERROR;
321
322 return DRIVER_OP_SUCCESS;
323 }
324
325 static bool
_cdio_read_toc(_img_private_t * _obj)326 _cdio_read_toc(_img_private_t *_obj)
327 {
328 int res;
329 struct ioc_read_toc_entry req;
330
331 res = ioctl(_obj->gen.fd, CDIOREADTOCHEADER, &_obj->tochdr);
332 if (res < 0) {
333 cdio_warn("error in ioctl(CDIOREADTOCHEADER): %s\n",
334 strerror(errno));
335 return false;
336 }
337
338 req.address_format = CD_MSF_FORMAT;
339 req.starting_track = FIRST_TRACK_NUM;
340 req.data_len = (TOTAL_TRACKS + 1) /* leadout! */
341 * sizeof(struct cd_toc_entry);
342 req.data = _obj->tocent;
343
344 res = ioctl(_obj->gen.fd, CDIOREADTOCENTRIES, &req);
345 if (res < 0) {
346 cdio_warn("error in ioctl(CDROMREADTOCENTRIES): %s\n",
347 strerror(errno));
348 return false;
349 }
350
351 _obj->toc_valid = 1;
352 _obj->gen.i_first_track = FIRST_TRACK_NUM;
353 _obj->gen.i_tracks = TOTAL_TRACKS;
354 _obj->gen.toc_init = true;
355 return true;
356 }
357
358 static bool
read_toc_netbsd(void * p_user_data)359 read_toc_netbsd (void *p_user_data)
360 {
361
362 return _cdio_read_toc(p_user_data);
363 }
364
365 static int
_cdio_read_discinfo(_img_private_t * _obj)366 _cdio_read_discinfo(_img_private_t *_obj)
367 {
368 scsireq_t req;
369 #define FULLTOCBUF (4 + 1000*11)
370 unsigned char buf[FULLTOCBUF] = { 0, };
371 int i, j;
372
373 memset(&req, 0, sizeof(req));
374 req.cmd[0] = 0x43; /* READ TOC/PMA/ATIP */
375 req.cmd[1] = 0x02;
376 req.cmd[2] = 0x02; /* full TOC */
377 req.cmd[3] = 0;
378 req.cmd[4] = 0;
379 req.cmd[5] = 0;
380 req.cmd[6] = 0;
381 req.cmd[7] = FULLTOCBUF / 256;
382 req.cmd[8] = FULLTOCBUF % 256;
383 req.cmd[9] = 0;
384 req.cmdlen = 10;
385
386 req.datalen = FULLTOCBUF;
387 req.databuf = buf;
388 req.timeout = 10000;
389 req.flags = SCCMD_READ;
390
391 if (ioctl(_obj->gen.fd, SCIOCCOMMAND, &req) < 0) {
392 cdio_info("SCIOCCOMMAND: %s", strerror(errno));
393 return 1;
394 }
395 if (req.retsts != SCCMD_OK) {
396 cdio_info("SCIOCCOMMAND cmd 0x43 sts %d\n", req.retsts);
397 return 1;
398 }
399 #if 1
400 printf("discinfo:");
401 for (i = 0; i < 4; i++)
402 printf(" %02x", buf[i]);
403 printf("\n");
404 for (i = 0; i < buf[1] - 2; i++) {
405 printf(" %02x", buf[i + 4]);
406 if (!((i + 1) % 11))
407 printf("\n");
408 }
409 #endif
410
411 for (i = 4; i < req.datalen_used; i += 11) {
412 if (buf[i + 3] == 0xa0) { /* POINT */
413 /* XXX: assume entry 0xa1 follows */
414 for (j = buf[i + 8] - 1; j <= buf[i + 11 + 8] - 1; j++)
415 _obj->sessionformat[j] = buf[i + 9];
416 }
417 }
418
419 _obj->sessionformat_valid = true;
420 return 0;
421 }
422
423 static int
eject_media_netbsd(void * user_data)424 eject_media_netbsd(void *user_data) {
425
426 _img_private_t *_obj = user_data;
427 int fd, res, ret = 0;
428
429 fd = open(_obj->gen.source_name, O_RDONLY|O_NONBLOCK);
430 if (fd < 0)
431 return 2;
432
433 res = ioctl(fd, CDIOCALLOW);
434 if (res < 0) {
435 cdio_warn("ioctl(fd, CDIOCALLOW) failed: %s\n",
436 strerror(errno));
437 /* go on... */
438 }
439 res = ioctl(fd, CDIOCEJECT);
440 if (res < 0) {
441 cdio_warn("ioctl(CDIOCEJECT) failed: %s\n",
442 strerror(errno));
443 ret = 1;
444 }
445
446 close(fd);
447 return ret;
448 }
449
450 static bool
is_mmc_supported(void * user_data)451 is_mmc_supported(void *user_data)
452 {
453 _img_private_t *env = user_data;
454 return (_AM_NONE == env->access_mode) ? false : true;
455 }
456
457 /*!
458 Return the value associated with the key "arg".
459 */
460 static const char *
get_arg_netbsd(void * env,const char key[])461 get_arg_netbsd(void *env, const char key[])
462 {
463 _img_private_t *_obj = env;
464
465 if (!strcmp(key, "source")) {
466 return _obj->gen.source_name;
467 } else if (!strcmp(key, "access-mode")) {
468 switch (_obj->access_mode) {
469 case _AM_IOCTL:
470 return "IOCTL";
471 case _AM_READ_CD:
472 return "READ_CD";
473 case _AM_MMC_RDWR:
474 return "MMC_RDWR";
475 case _AM_MMC_RDWR_EXCL:
476 return "MMC_RDWR_EXCL";
477 case _AM_NONE:
478 return "no access method";
479 }
480 } else if (!strcmp (key, "mmc-supported?")) {
481 return is_mmc_supported(env) ? "true" : "false";
482 }
483 return NULL;
484 }
485
486 static track_t
get_first_track_num_netbsd(void * user_data)487 get_first_track_num_netbsd(void *user_data)
488 {
489 _img_private_t *_obj = user_data;
490 int res;
491
492 if (!_obj->toc_valid) {
493 res = _cdio_read_toc(_obj);
494 if (!res)
495 return CDIO_INVALID_TRACK;
496 }
497
498 return FIRST_TRACK_NUM;
499 }
500
501 static track_t
get_num_tracks_netbsd(void * user_data)502 get_num_tracks_netbsd(void *user_data)
503 {
504 _img_private_t *_obj = user_data;
505 int res;
506
507 if (!_obj->toc_valid) {
508 res = _cdio_read_toc(_obj);
509 if (!res)
510 return CDIO_INVALID_TRACK;
511 }
512
513 return TOTAL_TRACKS;
514 }
515
516 /*!
517 Return the international standard recording code ISRC.
518
519 Note: string is malloc'd so caller should free() then returned
520 string when done with it.
521
522 */
523 static char *
get_track_isrc_netbsd(const void * p_user_data,track_t i_track)524 get_track_isrc_netbsd (const void *p_user_data, track_t i_track) {
525
526 const _img_private_t *p_env = p_user_data;
527 return mmc_get_track_isrc( p_env->gen.cdio, i_track );
528 }
529
530 static driver_return_code_t
audio_get_volume_netbsd(void * p_user_data,cdio_audio_volume_t * p_volume)531 audio_get_volume_netbsd(void *p_user_data, cdio_audio_volume_t *p_volume)
532 {
533 const _img_private_t *p_env = p_user_data;
534 return (ioctl(p_env->gen.fd, CDIOCGETVOL, p_volume));
535 }
536
537 static driver_return_code_t
audio_pause_netbsd(void * p_user_data)538 audio_pause_netbsd(void *p_user_data)
539 {
540 const _img_private_t *p_env = p_user_data;
541 return (ioctl(p_env->gen.fd, CDIOCPAUSE));
542 }
543
544 static driver_return_code_t
audio_stop_netbsd(void * p_user_data)545 audio_stop_netbsd(void *p_user_data)
546 {
547 const _img_private_t *p_env = p_user_data;
548 return (ioctl(p_env->gen.fd, CDIOCSTOP));
549 }
550
551 static driver_return_code_t
audio_resume_netbsd(void * p_user_data)552 audio_resume_netbsd(void *p_user_data)
553 {
554 const _img_private_t *p_env = p_user_data;
555 return (ioctl(p_env->gen.fd, CDIOCRESUME));
556 }
557
558 static driver_return_code_t
audio_set_volume_netbsd(void * p_user_data,cdio_audio_volume_t * p_volume)559 audio_set_volume_netbsd(void *p_user_data, cdio_audio_volume_t *p_volume)
560 {
561 const _img_private_t *p_env = p_user_data;
562 return (ioctl(p_env->gen.fd, CDIOCSETVOL, p_volume));
563 }
564
565 /*!
566 Get format of track.
567 */
568 static track_format_t
get_track_format_netbsd(void * user_data,track_t track_num)569 get_track_format_netbsd(void *user_data, track_t track_num)
570 {
571 _img_private_t *_obj = user_data;
572 int res, first_track = 0, track_idx = 0;
573
574 if (!_obj->toc_valid) {
575 res = _cdio_read_toc(_obj);
576 if (!res)
577 return TRACK_FORMAT_ERROR;
578 }
579
580 first_track = _obj->gen.i_first_track;
581
582 if (!_obj->gen.toc_init ||
583 track_num > (first_track + _obj->gen.i_tracks) ||
584 track_num < first_track)
585 return (CDIO_INVALID_TRACK);
586
587 track_idx = track_num - first_track;
588
589 if (_obj->tocent[track_idx].control & 0x04) {
590 if (!_obj->sessionformat_valid) {
591 res = _cdio_read_discinfo(_obj);
592 if (res)
593 return TRACK_FORMAT_ERROR;
594 }
595
596 if (_obj->sessionformat[track_idx] == 0x10)
597 return TRACK_FORMAT_CDI;
598 else if (_obj->sessionformat[track_idx] == 0x20)
599 return TRACK_FORMAT_XA;
600 else
601 return TRACK_FORMAT_DATA;
602 } else
603 return TRACK_FORMAT_AUDIO;
604 }
605
606 /*!
607 Return true if we have XA data (green, mode2 form1) or
608 XA data (green, mode2 form2). That is track begins:
609 sync - header - subheader
610 12 4 - 8
611
612 FIXME: there's gotta be a better design for this and get_track_format?
613 */
614 static bool
get_track_green_netbsd(void * user_data,track_t track_num)615 get_track_green_netbsd(void *user_data, track_t track_num)
616 {
617
618 return (get_track_format_netbsd(user_data, track_num)
619 == TRACK_FORMAT_XA);
620 }
621
622 /*!
623 Return the starting MSF (minutes/secs/frames) for track number
624 track_num in obj. Track numbers usually start at something
625 greater than 0, usually 1.
626
627 The "leadout" track is specified by passing i_track as either
628 LEADOUT_TRACK or the track number of the last audio track plus one.
629
630 False is returned if there is no track entry.
631 */
632 static bool
get_track_msf_netbsd(void * user_data,track_t track_num,msf_t * msf)633 get_track_msf_netbsd(void *user_data, track_t track_num, msf_t *msf)
634 {
635 _img_private_t *_obj = user_data;
636 int res, first_track = 0, track_idx = 0;
637
638 if (!msf)
639 return false;
640
641 if (!_obj->toc_valid) {
642 res = _cdio_read_toc(_obj);
643 if (!res)
644 return false;
645 }
646
647 if (track_num == CDIO_CDROM_LEADOUT_TRACK)
648 track_num = _obj->gen.i_tracks + _obj->gen.i_first_track;
649
650 first_track = _obj->gen.i_first_track;
651
652 if (!_obj->gen.toc_init ||
653 track_num > (first_track + _obj->gen.i_tracks) ||
654 track_num < first_track)
655 return (CDIO_INVALID_TRACK);
656
657 track_idx = track_num - first_track;
658 msf->m = cdio_to_bcd8(_obj->tocent[track_idx].addr.msf.minute);
659 msf->s = cdio_to_bcd8(_obj->tocent[track_idx].addr.msf.second);
660 msf->f = cdio_to_bcd8(_obj->tocent[track_idx].addr.msf.frame);
661
662 return true;
663 }
664
665 /*!
666 Return the size of the CD in logical block address (LBA) units.
667 @return the lsn. On error return CDIO_INVALID_LSN.
668
669 Also note that in one at least one test the corresponding MMC gives
670 a different answer, so there may be some disagreement about what is in
671 fact the last lsn.
672 */
673 static lsn_t
get_disc_last_lsn_netbsd(void * user_data)674 get_disc_last_lsn_netbsd(void *user_data)
675 {
676 msf_t msf;
677
678 get_track_msf_netbsd(user_data, CDIO_CDROM_LEADOUT_TRACK, &msf);
679
680 return (((msf.m * 60) + msf.s) * CDIO_CD_FRAMES_PER_SEC + msf.f);
681 }
682
683
684 static driver_return_code_t
get_last_session_netbsd(void * p_user_data,lsn_t * i_last_session)685 get_last_session_netbsd(void *p_user_data, lsn_t *i_last_session)
686 {
687 const _img_private_t *p_env = p_user_data;
688 int addr = 0;
689
690 if (ioctl(p_env->gen.fd, CDIOREADMSADDR, &addr) == 0) {
691 *i_last_session = addr;
692 return (DRIVER_OP_SUCCESS);
693 } else {
694 cdio_warn("ioctl CDIOREADMSADDR failed: %s\n",
695 strerror(errno));
696 return (DRIVER_OP_ERROR);
697 }
698 }
699 #endif /* HAVE_NETBSD_CDROM */
700
701 char **
cdio_get_devices_netbsd(void)702 cdio_get_devices_netbsd (void)
703 {
704 #ifndef HAVE_NETBSD_CDROM
705 return NULL;
706 #else
707 char drive[16];
708 char **drives = NULL;
709 unsigned int num_drives = 0;
710 int cdfd;
711 int n;
712
713 /* Search for open(2)able /dev/rcd* devices */
714 for (n = 0; n <= MAX_CD_DEVICES; n++) {
715 snprintf(drive, sizeof(drive), "/dev/rcd%d%c", n, 'a' + RAW_PART);
716 if (!cdio_is_device_quiet_generic(drive))
717 continue;
718 if ((cdfd = open(drive, O_RDONLY|O_NONBLOCK, 0)) == -1)
719 continue;
720 close(cdfd);
721 cdio_add_device_list(&drives, drive, &num_drives);
722 }
723 cdio_add_device_list(&drives, NULL, &num_drives);
724 return (drives);
725 #endif /* HAVE_NETBSD_CDROM */
726 }
727
728 #ifdef HAVE_NETBSD_CDROM
729 static driver_return_code_t
audio_play_msf_netbsd(void * p_user_data,msf_t * p_start_msf,msf_t * p_end_msf)730 audio_play_msf_netbsd(void *p_user_data, msf_t *p_start_msf, msf_t *p_end_msf)
731 {
732 const _img_private_t *p_env = p_user_data;
733 struct ioc_play_msf a;
734
735 a.start_m = cdio_from_bcd8(p_start_msf->m);
736 a.start_s = cdio_from_bcd8(p_start_msf->s);
737 a.start_f = cdio_from_bcd8(p_start_msf->f);
738 a.end_m = cdio_from_bcd8(p_end_msf->m);
739 a.end_s = cdio_from_bcd8(p_end_msf->s);
740 a.end_f = cdio_from_bcd8(p_end_msf->f);
741
742 return (ioctl(p_env->gen.fd, CDIOCPLAYMSF, (char *)&a));
743 }
744
745 #if !USE_MMC_SUBCHANNEL
746 static driver_return_code_t
audio_read_subchannel_netbsd(void * p_user_data,cdio_subchannel_t * subchannel)747 audio_read_subchannel_netbsd(void *p_user_data, cdio_subchannel_t *subchannel)
748 {
749 const _img_private_t *p_env = p_user_data;
750 struct ioc_read_subchannel s;
751 struct cd_sub_channel_info data;
752
753 bzero(&s, sizeof(s));
754 s.data = &data;
755 s.data_len = sizeof(data);
756 s.address_format = CD_MSF_FORMAT;
757 s.data_format = CD_CURRENT_POSITION;
758
759 if (ioctl(p_env->gen.fd, CDIOCREADSUBCHANNEL, &s) != -1) {
760 subchannel->control = s.data->what.position.control;
761 subchannel->track = s.data->what.position.track_number;
762 subchannel->index = s.data->what.position.index_number;
763
764 subchannel->abs_addr.m =
765 cdio_to_bcd8(s.data->what.position.absaddr.msf.minute);
766 subchannel->abs_addr.s =
767 cdio_to_bcd8(s.data->what.position.absaddr.msf.second);
768 subchannel->abs_addr.f =
769 cdio_to_bcd8(s.data->what.position.absaddr.msf.frame);
770 subchannel->rel_addr.m =
771 cdio_to_bcd8(s.data->what.position.reladdr.msf.minute);
772 subchannel->rel_addr.s =
773 cdio_to_bcd8(s.data->what.position.reladdr.msf.second);
774 subchannel->rel_addr.f =
775 cdio_to_bcd8(s.data->what.position.reladdr.msf.frame);
776 subchannel->audio_status = s.data->header.audio_status;
777
778 return (DRIVER_OP_SUCCESS);
779 } else {
780 cdio_warn("ioctl CDIOCREADSUBCHANNEL failed: %s\n",
781 strerror(errno));
782 return (DRIVER_OP_ERROR);
783 }
784 }
785 #endif
786 #endif /* HAVE_NETBSD_CDROM */
787
788 /*!
789 Return a string containing the default CD device.
790 */
791 char *
cdio_get_default_device_netbsd()792 cdio_get_default_device_netbsd()
793 {
794 return strdup(DEFAULT_CDIO_DEVICE);
795 }
796
797 /*!
798 Close tray on CD-ROM.
799
800 @param psz_device the CD-ROM drive to be closed.
801
802 */
803 driver_return_code_t
close_tray_netbsd(const char * psz_device)804 close_tray_netbsd (const char *psz_device)
805 {
806 #ifdef HAVE_NETBSD_CDROM
807 return DRIVER_OP_UNSUPPORTED;
808 #else
809 return DRIVER_OP_NO_DRIVER;
810 #endif
811 }
812
813 #ifdef HAVE_NETBSD_CDROM
814 static cdio_funcs_t _funcs = {
815 .audio_get_volume = audio_get_volume_netbsd,
816 .audio_pause = audio_pause_netbsd,
817 .audio_play_msf = audio_play_msf_netbsd,
818 .audio_play_track_index= NULL,
819 #if USE_MMC_SUBCHANNEL
820 .audio_read_subchannel = audio_read_subchannel_mmc,
821 #else
822 .audio_read_subchannel = audio_read_subchannel_netbsd,
823 #endif
824 .audio_stop = audio_stop_netbsd,
825 .audio_resume = audio_resume_netbsd,
826 .audio_set_volume = audio_set_volume_netbsd,
827 .eject_media = eject_media_netbsd,
828 .free = cdio_generic_free,
829 .get_arg = get_arg_netbsd,
830 .get_blocksize = get_blocksize_mmc,
831 .get_cdtext = get_cdtext_generic,
832 .get_cdtext_raw = read_cdtext_generic,
833 .get_default_device = cdio_get_default_device_netbsd,
834 .get_devices = cdio_get_devices_netbsd,
835 .get_disc_last_lsn = get_disc_last_lsn_netbsd,
836 .get_last_session = get_last_session_netbsd,
837 .get_media_changed = get_media_changed_mmc,
838 .get_discmode = get_discmode_generic,
839 .get_drive_cap = get_drive_cap_mmc,
840 .get_first_track_num = get_first_track_num_netbsd,
841 .get_hwinfo = NULL,
842 .get_mcn = get_mcn_mmc,
843 .get_num_tracks = get_num_tracks_netbsd,
844 .get_track_channels = get_track_channels_generic,
845 .get_track_copy_permit = get_track_copy_permit_generic,
846 .get_track_format = get_track_format_netbsd,
847 .get_track_green = get_track_green_netbsd,
848 /* Not because we can't talk LBA, but the driver assumes MSF throughout */
849 .get_track_lba = NULL,
850 .get_track_preemphasis = get_track_preemphasis_generic,
851 .get_track_msf = get_track_msf_netbsd,
852 .get_track_isrc = get_track_isrc_netbsd,
853 .lseek = cdio_generic_lseek,
854 .read = cdio_generic_read,
855 .read_audio_sectors = read_audio_sectors_netbsd,
856 .read_data_sectors = read_data_sectors_generic,
857 .read_mode1_sector = _read_mode1_sector_netbsd,
858 .read_mode1_sectors = _read_mode1_sectors_netbsd,
859 .read_mode2_sector = read_mode2_sector_netbsd,
860 .read_mode2_sectors = read_mode2_sectors_netbsd,
861 .read_toc = read_toc_netbsd,
862 .run_mmc_cmd = run_scsi_cmd_netbsd,
863 .set_arg = set_arg_netbsd,
864 };
865 #endif /*HAVE_NETBSD_CDROM*/
866
867 /*!
868 Initialization routine. This is the only thing that doesn't
869 get called via a function pointer. In fact *we* are the
870 ones to set that up.
871 */
872 CdIo_t *
cdio_open_netbsd(const char * source_name)873 cdio_open_netbsd(const char *source_name)
874 {
875 #ifdef HAVE_NETBSD_CDROM
876 CdIo_t *ret;
877 _img_private_t *_data;
878 int open_access_mode; /* Access mode passed to cdio_generic_init. */
879
880 _data = calloc(1, sizeof(_img_private_t));
881
882 _data->gen.init = false;
883 _data->toc_valid = false;
884 _data->sessionformat_valid = false;
885 _data->gen.fd = -1;
886 _data->gen.b_cdtext_error = false;
887
888 set_arg_netbsd(_data, "source",
889 (source_name ? source_name : DEFAULT_CDIO_DEVICE));
890
891 if (source_name && !cdio_is_device_generic(source_name))
892 goto err_exit;
893
894 ret = cdio_new(&_data->gen, &_funcs);
895 if (ret == NULL) {
896 goto err_exit;
897 }
898
899 ret->driver_id = DRIVER_NETBSD;
900
901 open_access_mode = O_NONBLOCK;
902 if (_AM_MMC_RDWR == _data->access_mode)
903 open_access_mode |= O_RDWR;
904 else if (_AM_MMC_RDWR_EXCL == _data->access_mode)
905 open_access_mode |= O_RDWR | O_EXCL;
906 else
907 open_access_mode |= O_RDONLY;
908 if (cdio_generic_init(_data, open_access_mode)) {
909 return ret;
910 }
911 free(ret);
912
913 err_exit:
914 cdio_generic_free(_data);
915 return NULL;
916
917 #else
918 return NULL;
919 #endif /* HAVE_NETBSD_CDROM */
920
921 }
922
923 /*!
924 Initialization routine. This is the only thing that doesn't
925 get called via a function pointer. In fact *we* are the
926 ones to set that up.
927 */
928 CdIo_t *
cdio_open_am_netbsd(const char * source_name,const char * am)929 cdio_open_am_netbsd(const char *source_name, const char *am)
930 {
931 return (cdio_open_netbsd(source_name));
932 }
933
934 bool
cdio_have_netbsd(void)935 cdio_have_netbsd (void)
936 {
937 #ifdef HAVE_NETBSD_CDROM
938 return true;
939 #else
940 return false;
941 #endif /* HAVE_NETBSD_CDROM */
942 }
943
944
945 /*
946 * Local variables:
947 * c-file-style: "gnu"
948 * tab-width: 8
949 * indent-tabs-mode: nil
950 * End:
951 */
952