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