1 /*
2 Copyright (C) 2003, 2004-2005, 2008-2011, 2014, 2017
3 Rocky Bernstein <rocky@gnu.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 /* This file contains FreeBSD-specific code and implements low-level
20 control of the CD drive. Culled initially I think from xine's or
21 mplayer's FreeBSD code with lots of modifications.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 # define __CDIO_CONFIG_H__ 1
27 #endif
28
29 #include "freebsd.h"
30
31 #ifdef HAVE_FREEBSD_CDROM
32
33 #ifdef HAVE_SYS_PARAM_H
34 #include <sys/param.h>
35 #endif
36
37 #include <netinet/in.h>
38
39 /* For freebsd_dev_lock() */
40 #include <sys/file.h>
41
42 #ifdef HAVE_SYS_TYPES_H
43 # include <sys/types.h>
44 #endif
45 #ifdef _HAVE_SYS_STAT_H
46 # include <sys/stat.h>
47 #endif
48 #ifdef HAVE_FCNTL_H
49 # include <fcntl.h>
50 #endif
51
52 #include <cdio/sector.h>
53
54 static lba_t get_track_lba_freebsd(void *p_user_data, track_t i_track);
55
56 static access_mode_t
str_to_access_mode_freebsd(const char * psz_access_mode)57 str_to_access_mode_freebsd(const char *psz_access_mode)
58 {
59 const access_mode_t default_access_mode = DEFAULT_FREEBSD_AM;
60
61 if (NULL==psz_access_mode) return default_access_mode;
62
63 if (!strcmp(psz_access_mode, "ioctl"))
64 return _AM_IOCTL;
65 else if (!strcmp(psz_access_mode, "CAM"))
66 return _AM_CAM;
67 else if (!strcmp(psz_access_mode, "MMC_RDWR"))
68 return _AM_MMC_RDWR;
69 else if (!strcmp(psz_access_mode, "MMC_RDWR_EXCL"))
70 return _AM_MMC_RDWR_EXCL;
71 else {
72 cdio_warn ("unknown access type: %s. Default used.",
73 psz_access_mode);
74 return default_access_mode;
75 }
76 }
77
78 static void
free_freebsd(void * p_obj)79 free_freebsd (void *p_obj)
80 {
81 _img_private_t *p_env = p_obj;
82
83 if (NULL == p_env) return;
84
85 if (NULL != p_env->device) free(p_env->device);
86
87 switch (p_env->access_mode) {
88 case _AM_CAM:
89 case _AM_MMC_RDWR:
90 case _AM_MMC_RDWR_EXCL:
91 free_freebsd_cam(p_env);
92 break;
93 case _AM_IOCTL:
94 cdio_generic_free(p_obj);
95 break;
96 case _AM_NONE:
97 break;
98 }
99 }
100
101 /* Check a drive to see if it is a CD-ROM
102 Return 1 if a CD-ROM. 0 if it exists but isn't a CD-ROM drive
103 and -1 if no device exists .
104 */
105 static bool
cdio_is_cdrom(char * drive,char * mnttype)106 cdio_is_cdrom(char *drive, char *mnttype)
107 {
108 return cdio_is_cdrom_freebsd_ioctl(drive, mnttype);
109 }
110
111 /*!
112 Reads i_blocks of audio sectors from cd device into data starting from lsn.
113 Returns 0 if no error.
114 */
115 static driver_return_code_t
read_audio_sectors_freebsd(void * p_user_data,void * p_buf,lsn_t i_lsn,unsigned int i_blocks)116 read_audio_sectors_freebsd (void *p_user_data, void *p_buf, lsn_t i_lsn,
117 unsigned int i_blocks)
118 {
119 _img_private_t *p_env = p_user_data;
120 switch (p_env->access_mode) {
121 case _AM_CAM:
122 case _AM_MMC_RDWR:
123 case _AM_MMC_RDWR_EXCL:
124 return mmc_read_sectors( p_env->gen.cdio, p_buf, i_lsn,
125 CDIO_MMC_READ_TYPE_CDDA, i_blocks);
126 case _AM_IOCTL:
127 return read_audio_sectors_freebsd_ioctl(p_user_data, p_buf, i_lsn,
128 i_blocks);
129 case _AM_NONE:
130 cdio_info ("access mode not set");
131 return DRIVER_OP_ERROR;
132 }
133 return DRIVER_OP_ERROR;
134 }
135
136 /*!
137 Reads a single mode2 sector from cd device into data starting
138 from i_lsn. Returns 0 if no error.
139 */
140 static driver_return_code_t
read_mode2_sector_freebsd(void * p_user_data,void * data,lsn_t i_lsn,bool b_form2)141 read_mode2_sector_freebsd (void *p_user_data, void *data, lsn_t i_lsn,
142 bool b_form2)
143 {
144 _img_private_t *p_env = p_user_data;
145
146 switch (p_env->access_mode) {
147 case _AM_CAM:
148 case _AM_MMC_RDWR:
149 case _AM_MMC_RDWR_EXCL:
150 return read_mode2_sector_freebsd_cam(p_env, data, i_lsn, b_form2);
151 case _AM_IOCTL:
152 return read_mode2_sector_freebsd_ioctl(p_env, data, i_lsn, b_form2);
153 case _AM_NONE:
154 cdio_info ("access mode not set");
155 return DRIVER_OP_ERROR;
156 }
157 return DRIVER_OP_ERROR;
158 }
159
160 /*!
161 Reads i_blocks of mode2 sectors from cd device into data starting
162 from lsn.
163 */
164 static driver_return_code_t
read_mode2_sectors_freebsd(void * p_user_data,void * p_data,lsn_t i_lsn,bool b_form2,unsigned int i_blocks)165 read_mode2_sectors_freebsd (void *p_user_data, void *p_data, lsn_t i_lsn,
166 bool b_form2, unsigned int i_blocks)
167 {
168 _img_private_t *p_env = p_user_data;
169
170 if ( (p_env->access_mode == _AM_CAM ||
171 p_env->access_mode == _AM_MMC_RDWR ||
172 p_env->access_mode == _AM_MMC_RDWR_EXCL)
173 && b_form2 ) {
174 /* We have a routine that covers this case without looping. */
175 return read_mode2_sectors_freebsd_cam(p_env, p_data, i_lsn, i_blocks);
176 } else {
177 unsigned int i;
178 uint16_t i_blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE;
179
180 /* For each frame, pick out the data part we need */
181 for (i = 0; i < i_blocks; i++) {
182 int retval = read_mode2_sector_freebsd (p_env,
183 ((char *)p_data) +
184 (i_blocksize * i),
185 i_lsn + i, b_form2);
186 if (retval) return retval;
187 }
188 }
189 return DRIVER_OP_SUCCESS;
190 }
191
192 /*!
193 Return the size of the CD in logical block address (LBA) units.
194 @return the lsn. On error return CDIO_INVALID_LSN.
195 */
196 static lsn_t
get_disc_last_lsn_freebsd(void * p_obj)197 get_disc_last_lsn_freebsd (void *p_obj)
198 {
199 _img_private_t *p_env = p_obj;
200
201 if (!p_env) return CDIO_INVALID_LSN;
202
203 switch (p_env->access_mode) {
204 case _AM_CAM:
205 case _AM_MMC_RDWR:
206 case _AM_MMC_RDWR_EXCL:
207 return get_disc_last_lsn_mmc(p_env);
208 case _AM_IOCTL:
209 return get_disc_last_lsn_freebsd_ioctl(p_env);
210 case _AM_NONE:
211 cdio_info ("access mode not set");
212 return DRIVER_OP_ERROR;
213 }
214 return DRIVER_OP_ERROR;
215 }
216
217 /*!
218 Set the arg "key" with "value" in the source device.
219 Currently "source" and "access-mode" are valid keys.
220 "source" sets the source device in I/O operations
221 "access-mode" sets the the method of CD access
222
223 DRIVER_OP_SUCCESS is returned if no error was found,
224 and nonzero if there as an error.
225 */
226 static driver_return_code_t
set_arg_freebsd(void * p_user_data,const char key[],const char value[])227 set_arg_freebsd (void *p_user_data, const char key[], const char value[])
228 {
229 _img_private_t *p_env = p_user_data;
230
231 if (!strcmp (key, "source"))
232 {
233 if (!value) return DRIVER_OP_ERROR;
234 free (p_env->gen.source_name);
235 p_env->gen.source_name = strdup (value);
236 }
237 else if (!strcmp (key, "access-mode"))
238 {
239 p_env->access_mode = str_to_access_mode_freebsd(value);
240 if (p_env->access_mode == _AM_CAM && !p_env->b_cam_init)
241 return init_freebsd_cam(p_env)
242 ? DRIVER_OP_SUCCESS : DRIVER_OP_ERROR;
243 }
244 else return DRIVER_OP_ERROR;
245
246 return DRIVER_OP_SUCCESS;
247
248 }
249
250 /* Set CD-ROM drive speed */
251 static int
set_speed_freebsd(void * p_user_data,int i_speed)252 set_speed_freebsd (void *p_user_data, int i_speed)
253 {
254 const _img_private_t *p_env = p_user_data;
255
256 if (!p_env) return -1;
257 #ifdef CDRIOCREADSPEED
258 i_speed *= 177;
259 return ioctl(p_env->gen.fd, CDRIOCREADSPEED, &i_speed);
260 #else
261 return -2;
262 #endif
263 }
264
265 /*!
266 Read and cache the CD's Track Table of Contents and track info.
267 Return false if unsuccessful;
268 */
269 static bool
read_toc_freebsd(void * p_user_data)270 read_toc_freebsd (void *p_user_data)
271 {
272 _img_private_t *p_env = p_user_data;
273 track_t i, j;
274
275 /* read TOC header */
276 if ( ioctl(p_env->gen.fd, CDIOREADTOCHEADER, &p_env->tochdr) == -1 ) {
277 cdio_warn("error in ioctl(CDIOREADTOCHEADER): %s\n", strerror(errno));
278 return false;
279 }
280
281 p_env->gen.i_first_track = p_env->tochdr.starting_track;
282 p_env->gen.i_tracks = p_env->tochdr.ending_track -
283 p_env->gen.i_first_track + 1;
284
285 j=0;
286 for (i=p_env->gen.i_first_track; i<=p_env->gen.i_tracks; i++, j++) {
287 struct ioc_read_toc_single_entry *p_toc =
288 &(p_env->tocent[i-p_env->gen.i_first_track]);
289 p_toc->track = i;
290 p_toc->address_format = CD_LBA_FORMAT;
291
292 if ( ioctl(p_env->gen.fd, CDIOREADTOCENTRY, p_toc) ) {
293 cdio_warn("%s %d: %s\n",
294 "error in ioctl CDROMREADTOCENTRY for track",
295 i, strerror(errno));
296 return false;
297 }
298
299 set_track_flags(&(p_env->gen.track_flags[i]), p_toc->entry.control);
300
301 }
302
303 p_env->tocent[j].track = CDIO_CDROM_LEADOUT_TRACK;
304 p_env->tocent[j].address_format = CD_LBA_FORMAT;
305 if ( ioctl(p_env->gen.fd, CDIOREADTOCENTRY, &(p_env->tocent[j]) ) ){
306 cdio_warn("%s: %s\n",
307 "error in ioctl CDROMREADTOCENTRY for leadout track",
308 strerror(errno));
309 return false;
310 }
311
312 p_env->gen.toc_init = true;
313 return true;
314 }
315
316 /*!
317 Get the volume of an audio CD.
318
319 @param p_cdio the CD object to be acted upon.
320 */
321 static driver_return_code_t
audio_get_volume_freebsd(void * p_user_data,cdio_audio_volume_t * p_volume)322 audio_get_volume_freebsd (void *p_user_data,
323 /*out*/ cdio_audio_volume_t *p_volume)
324 {
325
326 const _img_private_t *p_env = p_user_data;
327 return ioctl(p_env->gen.fd, CDIOCGETVOL, p_volume);
328 }
329
330 /*!
331 Pause playing CD through analog output
332
333 @param p_cdio the CD object to be acted upon.
334 */
335 static driver_return_code_t
audio_pause_freebsd(void * p_user_data)336 audio_pause_freebsd (void *p_user_data)
337 {
338
339 const _img_private_t *p_env = p_user_data;
340 return ioctl(p_env->gen.fd, CDIOCPAUSE);
341 }
342
343 /*!
344 Playing starting at given MSF through analog output
345
346 @param p_cdio the CD object to be acted upon.
347 */
348 static driver_return_code_t
audio_play_msf_freebsd(void * p_user_data,msf_t * p_start_msf,msf_t * p_end_msf)349 audio_play_msf_freebsd (void *p_user_data, msf_t *p_start_msf,
350 msf_t *p_end_msf)
351 {
352 const _img_private_t *p_env = p_user_data;
353 struct ioc_play_msf freebsd_play_msf;
354
355 freebsd_play_msf.start_m = cdio_from_bcd8(p_start_msf->m);
356 freebsd_play_msf.start_s = cdio_from_bcd8(p_start_msf->s);
357 freebsd_play_msf.start_f = cdio_from_bcd8(p_start_msf->f);
358
359 freebsd_play_msf.end_m = cdio_from_bcd8(p_end_msf->m);
360 freebsd_play_msf.end_s = cdio_from_bcd8(p_end_msf->s);
361 freebsd_play_msf.end_f = cdio_from_bcd8(p_end_msf->f);
362
363 return ioctl(p_env->gen.fd, CDIOCPLAYMSF, &freebsd_play_msf);
364 }
365
366 /*!
367 Playing CD through analog output at the desired track and index
368
369 @param p_user_data the CD object to be acted upon.
370 @param p_track_index location to start/end.
371 */
372 static driver_return_code_t
audio_play_track_index_freebsd(void * p_user_data,cdio_track_index_t * p_track_index)373 audio_play_track_index_freebsd (void *p_user_data,
374 cdio_track_index_t *p_track_index)
375 {
376 const _img_private_t *p_env = p_user_data;
377 msf_t start_msf;
378 msf_t end_msf;
379 struct ioc_play_msf freebsd_play_msf;
380 lsn_t i_lsn = get_track_lba_freebsd(p_user_data,
381 p_track_index->i_start_track);
382
383 cdio_lsn_to_msf(i_lsn, &start_msf);
384 i_lsn = get_track_lba_freebsd(p_user_data, p_track_index->i_end_track);
385 cdio_lsn_to_msf(i_lsn, &end_msf);
386
387 freebsd_play_msf.start_m = start_msf.m;
388 freebsd_play_msf.start_s = start_msf.s;
389 freebsd_play_msf.start_f = start_msf.f;
390
391 freebsd_play_msf.end_m = end_msf.m;
392 freebsd_play_msf.end_s = end_msf.s;
393 freebsd_play_msf.end_f = end_msf.f;
394
395 return ioctl(p_env->gen.fd, CDIOCPLAYMSF, &freebsd_play_msf);
396
397 }
398
399 /*!
400 Read Audio Subchannel information
401
402 @param p_user_data the CD object to be acted upon.
403 @param p_subchannel returned information
404 */
405 #if 1
406 static driver_return_code_t
audio_read_subchannel_freebsd(void * p_user_data,cdio_subchannel_t * p_subchannel)407 audio_read_subchannel_freebsd (void *p_user_data,
408 /*out*/ cdio_subchannel_t *p_subchannel)
409 {
410 const _img_private_t *p_env = p_user_data;
411 int i_rc;
412 struct cd_sub_channel_info bsdinfo;
413 struct ioc_read_subchannel read_subchannel;
414 memset(& bsdinfo, 0, sizeof(struct cd_sub_channel_info));
415 read_subchannel.address_format = CD_MSF_FORMAT;
416 read_subchannel.data_format = CD_CURRENT_POSITION;
417 read_subchannel.track = 0;
418 read_subchannel.data_len = sizeof(struct cd_sub_channel_info);
419 read_subchannel.data = & bsdinfo;
420 i_rc = ioctl(p_env->gen.fd, CDIOCREADSUBCHANNEL, &read_subchannel);
421 if (0 == i_rc) {
422 p_subchannel->audio_status = bsdinfo.header.audio_status;
423 p_subchannel->address = bsdinfo.what.position.addr_type;
424
425 p_subchannel->control = bsdinfo.what.position.control;
426 p_subchannel->track = bsdinfo.what.position.track_number;
427 p_subchannel->index = bsdinfo.what.position.index_number;
428
429 p_subchannel->abs_addr.m = cdio_to_bcd8 (bsdinfo.what.position.absaddr.msf.minute);
430 p_subchannel->abs_addr.s = cdio_to_bcd8 (bsdinfo.what.position.absaddr.msf.second);
431 p_subchannel->abs_addr.f = cdio_to_bcd8 (bsdinfo.what.position.absaddr.msf.frame);
432 p_subchannel->rel_addr.m = cdio_to_bcd8 (bsdinfo.what.position.reladdr.msf.minute);
433 p_subchannel->rel_addr.s = cdio_to_bcd8 (bsdinfo.what.position.reladdr.msf.second);
434 p_subchannel->rel_addr.f = cdio_to_bcd8 (bsdinfo.what.position.reladdr.msf.frame);
435 }
436 return i_rc;
437 }
438 #endif
439
440 /*!
441 Resume playing an audio CD.
442
443 @param p_cdio the CD object to be acted upon.
444
445 */
446 static driver_return_code_t
audio_resume_freebsd(void * p_user_data)447 audio_resume_freebsd (void *p_user_data)
448 {
449 const _img_private_t *p_env = p_user_data;
450 return ioctl(p_env->gen.fd, CDIOCRESUME, 0);
451 }
452
453 /*!
454 Set the volume of an audio CD.
455
456 @param p_cdio the CD object to be acted upon.
457
458 */
459 static driver_return_code_t
audio_set_volume_freebsd(void * p_user_data,cdio_audio_volume_t * p_volume)460 audio_set_volume_freebsd (void *p_user_data,
461 cdio_audio_volume_t *p_volume)
462 {
463 const _img_private_t *p_env = p_user_data;
464 return ioctl(p_env->gen.fd, CDIOCSETVOL, p_volume);
465 }
466
467 /*!
468 Eject media. Return 1 if successful, 0 otherwise.
469 */
470 static int
eject_media_freebsd(void * p_user_data)471 eject_media_freebsd (void *p_user_data)
472 {
473 _img_private_t *p_env = p_user_data;
474
475 switch (p_env->access_mode) {
476 case _AM_CAM:
477 case _AM_MMC_RDWR:
478 case _AM_MMC_RDWR_EXCL:
479 return eject_media_freebsd_cam(p_env);
480 case _AM_IOCTL:
481 return eject_media_freebsd_ioctl(p_env);
482 case _AM_NONE:
483 cdio_info ("access mode not set");
484 return 0;
485 }
486 return 0;
487 }
488
489 /*!
490 Stop playing an audio CD.
491
492 @param p_user_data the CD object to be acted upon.
493
494 */
495 static driver_return_code_t
audio_stop_freebsd(void * p_user_data)496 audio_stop_freebsd (void *p_user_data)
497 {
498 const _img_private_t *p_env = p_user_data;
499 return ioctl(p_env->gen.fd, CDIOCSTOP);
500 }
501
502 /*!
503 Produce a text composed from the system SCSI address tuple according to
504 habits of Linux 2.4 and 2.6 : "Bus,Host,Channel,Target,Lun" and store
505 it in generic_img_private_t.scsi_tuple.
506 Channel has no meaning on FreeBSD. Expect it to be 0. It is only in
507 the text to avoid an unnecessary difference in format.
508 Bus and Host will always be the same.
509 To be accessed via cdio_get_arg("scsi-tuple-freebsd") or "scsi-tuple".
510 For simplicity the FreeBSD driver also replies on "scsi-tuple-linux".
511 Drivers which implement this code have to return 5 valid decimal numbers
512 separated by comma, or empty text if no such numbers are available.
513 @return 1=success , 0=failure
514 */
515 static int
set_scsi_tuple_freebsd(_img_private_t * env)516 set_scsi_tuple_freebsd(_img_private_t *env)
517 {
518 int bus_no = -1, host_no = -1, channel_no = -1, target_no = -1, lun_no = -1;
519 int ret;
520 char tuple[160];
521
522 ret = obtain_scsi_adr_freebsd_cam(env->gen.source_name,
523 &bus_no, &host_no, &channel_no,
524 &target_no, &lun_no);
525 if (ret != 1)
526 return 0;
527 if (env->gen.scsi_tuple != NULL)
528 free (env->gen.scsi_tuple);
529 env->gen.scsi_tuple = NULL;
530 if (bus_no < 0 || host_no < 0 || channel_no < 0 || target_no < 0 ||
531 lun_no < 0) {
532 env->gen.scsi_tuple = strdup("");
533 return 0;
534 }
535 sprintf(tuple, "%d,%d,%d,%d,%d",
536 bus_no, host_no, channel_no, target_no, lun_no);
537 env->gen.scsi_tuple = strdup(tuple);
538 return 1;
539 }
540
541 static bool
is_mmc_supported(void * user_data)542 is_mmc_supported(void *user_data)
543 {
544 _img_private_t *env = user_data;
545 switch (env->access_mode) {
546 case _AM_IOCTL:
547 case _AM_NONE:
548 return false;
549 case _AM_CAM:
550 case _AM_MMC_RDWR:
551 case _AM_MMC_RDWR_EXCL:
552 return true;
553 }
554 /* Not reached. */
555 return false;
556 }
557
558
559 /*!
560 Return the value associated with the key "arg".
561 */
562 static const char *
get_arg_freebsd(void * user_data,const char key[])563 get_arg_freebsd (void *user_data, const char key[])
564 {
565 _img_private_t *env = user_data;
566
567 if (!strcmp (key, "source")) {
568 return env->gen.source_name;
569 } else if (!strcmp (key, "access-mode")) {
570 switch (env->access_mode) {
571 case _AM_IOCTL:
572 return "ioctl";
573 case _AM_CAM:
574 return "CAM";
575 case _AM_MMC_RDWR:
576 return "MMC_RDWR";
577 case _AM_MMC_RDWR_EXCL:
578 return "MMC_RDWR_EXCL";
579 case _AM_NONE:
580 return "no access method";
581 }
582 } else if (strcmp (key, "scsi-tuple") == 0) {
583 if (env->gen.scsi_tuple == NULL)
584 set_scsi_tuple_freebsd(env);
585 return env->gen.scsi_tuple;
586 } else if (!strcmp (key, "mmc-supported?")) {
587 return is_mmc_supported(user_data) ? "true" : "false";
588 }
589 return NULL;
590 }
591
592 /*!
593 Return the media catalog number MCN.
594
595 Note: string is malloc'd so caller should free() then returned
596 string when done with it.
597
598 FIXME: This is just a guess.
599
600 */
601 static char *
get_mcn_freebsd(const void * p_user_data)602 get_mcn_freebsd (const void *p_user_data) {
603 const _img_private_t *p_env = p_user_data;
604
605 switch (p_env->access_mode) {
606 case _AM_CAM:
607 case _AM_MMC_RDWR:
608 case _AM_MMC_RDWR_EXCL:
609 return mmc_get_mcn(p_env->gen.cdio);
610 case _AM_IOCTL:
611 return mmc_get_mcn(p_env->gen.cdio);
612 case _AM_NONE:
613 cdio_info ("access mode not set");
614 return NULL;
615 }
616 return NULL;
617 }
618
619 /*!
620 Return the international standard recording code ISRC.
621
622 Note: string is malloc'd so caller should free() then returned
623 string when done with it.
624
625 */
626 static char *
get_track_isrc_freebsd(const void * p_user_data,track_t i_track)627 get_track_isrc_freebsd (const void *p_user_data,
628 track_t i_track) {
629 const _img_private_t *p_env = p_user_data;
630
631 switch (p_env->access_mode) {
632 case _AM_CAM:
633 case _AM_MMC_RDWR:
634 case _AM_MMC_RDWR_EXCL:
635 return mmc_get_track_isrc(p_env->gen.cdio, i_track);
636 case _AM_IOCTL:
637 return mmc_get_track_isrc(p_env->gen.cdio, i_track);
638 case _AM_NONE:
639 cdio_info ("access mode not set");
640 return NULL;
641 }
642 return NULL;
643 }
644
645 static void
get_drive_cap_freebsd(const void * p_user_data,cdio_drive_read_cap_t * p_read_cap,cdio_drive_write_cap_t * p_write_cap,cdio_drive_misc_cap_t * p_misc_cap)646 get_drive_cap_freebsd (const void *p_user_data,
647 cdio_drive_read_cap_t *p_read_cap,
648 cdio_drive_write_cap_t *p_write_cap,
649 cdio_drive_misc_cap_t *p_misc_cap)
650 {
651 const _img_private_t *p_env = p_user_data;
652
653 switch (p_env->access_mode) {
654 case _AM_CAM:
655 case _AM_MMC_RDWR:
656 case _AM_MMC_RDWR_EXCL:
657 get_drive_cap_mmc (p_user_data, p_read_cap, p_write_cap, p_misc_cap);
658 case _AM_IOCTL:
659 cdio_info ("get_drive_cap not supported in ioctl access mode");
660 return;
661 case _AM_NONE:
662 cdio_info ("access mode not set");
663 return;
664 }
665 }
666
667 /*!
668 Run a SCSI MMC command.
669
670 p_user_data internal CD structure.
671 i_timeout_ms time in milliseconds we will wait for the command
672 to complete. If this value is -1, use the default
673 time-out value.
674 i_cdb Size of p_cdb
675 p_cdb CDB bytes.
676 e_direction direction the transfer is to go.
677 i_buf Size of buffer
678 p_buf Buffer for data, both sending and receiving
679 */
680 static driver_return_code_t
run_mmc_cmd_freebsd(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)681 run_mmc_cmd_freebsd( void *p_user_data, unsigned int i_timeout_ms,
682 unsigned int i_cdb, const mmc_cdb_t *p_cdb,
683 cdio_mmc_direction_t e_direction,
684 unsigned int i_buf, /*in/out*/ void *p_buf )
685 {
686 const _img_private_t *p_env = p_user_data;
687 int ret;
688
689 switch (p_env->access_mode) {
690 case _AM_CAM:
691 case _AM_MMC_RDWR:
692 case _AM_MMC_RDWR_EXCL:
693 ret = run_mmc_cmd_freebsd_cam( p_user_data, i_timeout_ms, i_cdb, p_cdb,
694 e_direction, i_buf, p_buf );
695 if (ret != 0)
696 return DRIVER_OP_ERROR;
697 return 0;
698 case _AM_IOCTL:
699 return DRIVER_OP_UNSUPPORTED;
700 case _AM_NONE:
701 cdio_info ("access mode not set");
702 return DRIVER_OP_ERROR;
703 }
704 return DRIVER_OP_ERROR;
705 }
706
707 /*!
708 Get format of track.
709
710 FIXME: We're just guessing this from the GNU/Linux code.
711
712 */
713 static track_format_t
get_track_format_freebsd(void * p_user_data,track_t i_track)714 get_track_format_freebsd(void *p_user_data, track_t i_track)
715 {
716 _img_private_t *p_env = p_user_data;
717
718 if (!p_env->gen.toc_init) read_toc_freebsd (p_user_data) ;
719
720 if (i_track > TOTAL_TRACKS || i_track == 0)
721 return TRACK_FORMAT_ERROR;
722
723 i_track -= FIRST_TRACK_NUM;
724
725 /* This is pretty much copied from the "badly broken" cdrom_count_tracks
726 in linux/cdrom.c.
727 */
728 if (p_env->tocent[i_track].entry.control & CDIO_CDROM_DATA_TRACK) {
729 if (p_env->tocent[i_track].address_format == CDIO_CDROM_CDI_TRACK)
730 return TRACK_FORMAT_CDI;
731 else if (p_env->tocent[i_track].address_format == CDIO_CDROM_XA_TRACK)
732 return TRACK_FORMAT_XA;
733 else
734 return TRACK_FORMAT_DATA;
735 } else
736 return TRACK_FORMAT_AUDIO;
737
738 }
739
740 /*!
741 Return true if we have XA data (green, mode2 form1) or
742 XA data (green, mode2 form2). That is track begins:
743 sync - header - subheader
744 12 4 - 8
745
746 FIXME: there's gotta be a better design for this and get_track_format?
747 */
748 static bool
get_track_green_freebsd(void * user_data,track_t i_track)749 get_track_green_freebsd(void *user_data, track_t i_track)
750 {
751 _img_private_t *p_env = user_data;
752
753 if (i_track == CDIO_CDROM_LEADOUT_TRACK) i_track = TOTAL_TRACKS+1;
754
755 if (i_track > TOTAL_TRACKS+1 || i_track == 0)
756 return false;
757
758 /* FIXME: Dunno if this is the right way, but it's what
759 I was using in cdinfo for a while.
760 */
761 return ((p_env->tocent[i_track-FIRST_TRACK_NUM].entry.control & 2) != 0);
762 }
763
764 /*!
765 Return the starting LSN track number
766 i_track in obj. Track numbers start at 1.
767 The "leadout" track is specified either by
768 using i_track LEADOUT_TRACK or the total tracks+1.
769 CDIO_INVALID_LBA is returned if there is no track entry.
770 */
771 static lba_t
get_track_lba_freebsd(void * user_data,track_t i_track)772 get_track_lba_freebsd(void *user_data, track_t i_track)
773 {
774 _img_private_t *p_env = user_data;
775
776 if (!p_env->gen.toc_init) read_toc_freebsd (p_env) ;
777
778 if (i_track == CDIO_CDROM_LEADOUT_TRACK) i_track = TOTAL_TRACKS+1;
779
780 if (i_track > TOTAL_TRACKS+1 || i_track == 0 || !p_env->gen.toc_init) {
781 return CDIO_INVALID_LBA;
782 } else {
783 return cdio_lsn_to_lba(ntohl(p_env->tocent[i_track-FIRST_TRACK_NUM].entry.addr.lba));
784 }
785 }
786
787 #endif /* HAVE_FREEBSD_CDROM */
788
789 /*!
790 Return an array of strings giving possible CD devices.
791 */
792 char **
cdio_get_devices_freebsd(void)793 cdio_get_devices_freebsd (void)
794 {
795 #ifndef HAVE_FREEBSD_CDROM
796 return NULL;
797 #else
798 char drive[40];
799 char **drives = NULL;
800 unsigned int num_drives=0;
801 bool exists = true;
802 char c;
803
804 /* Scan the system for CD-ROM drives.
805 */
806
807 #ifdef USE_ETC_FSTAB
808
809 struct fstab *fs;
810 setfsent();
811
812 /* Check what's in /etc/fstab... */
813 while ( (fs = getfsent()) )
814 {
815 if (strncmp(fs->fs_spec, "/dev/sr", 7))
816 cdio_add_device_list(&drives, fs->fs_spec, &num_drives);
817 }
818
819 #endif
820
821 /* Scan the system for CD-ROM drives.
822 Not always 100% reliable, so use the USE_MNTENT code above first.
823 */
824
825 /* Scan SCSI and CAM devices */
826 exists = true;
827 for ( c='0'; exists && c <='9'; c++ ) {
828 sprintf(drive, "/dev/cd%c%s", c, DEVICE_POSTFIX);
829 exists = cdio_is_cdrom(drive, NULL);
830 if ( exists ) {
831 cdio_add_device_list(&drives, drive, &num_drives);
832 }
833 }
834
835 /* Scan ATAPI devices */
836
837 /* ??? ts 9 Jan 2009
838 For now i assume atapicam running if a cdN device was found.
839 man atapicam strongly discourages to mix usage of CAM and ATAPI device.
840 So on the risk to sabotage systems without atapicam but with real old
841 SCSI drives, i list no ATAPI addresses if there was a CAM/SCSI address.
842
843 exists = !have_cam_drive;
844
845 ts 13 Jan 2009
846 Regrettably USB drives appear as SCSI drives. We rather need to determine
847 whether atapicam runs, or to make pairs of cd and acd.
848
849 */
850 for ( c='0'; exists && c <='9'; c++ ) {
851 sprintf(drive, "/dev/acd%c%s", c, DEVICE_POSTFIX);
852 exists = cdio_is_cdrom(drive, NULL);
853 if ( exists ) {
854 cdio_add_device_list(&drives, drive, &num_drives);
855 }
856 }
857 cdio_add_device_list(&drives, NULL, &num_drives);
858 return drives;
859 #endif /*HAVE_FREEBSD_CDROM*/
860 }
861
862 /*!
863 Return a string containing the default CD device if none is specified.
864 */
865 char *
cdio_get_default_device_freebsd()866 cdio_get_default_device_freebsd()
867 {
868 #ifndef HAVE_FREEBSD_CDROM
869 return NULL;
870 #else
871 char drive[40];
872 bool exists=true;
873 char c;
874
875 /* Scan the system for CD-ROM drives.
876 */
877
878 #ifdef USE_ETC_FSTAB
879
880 struct fstab *fs;
881 setfsent();
882
883 /* Check what's in /etc/fstab... */
884 while ( (fs = getfsent()) )
885 {
886 if (strncmp(fs->fs_spec, "/dev/sr", 7))
887 return strdup(fs->fs_spec);
888 }
889
890 #endif
891
892 /* Scan the system for CD-ROM drives.
893 Not always 100% reliable, so use the USE_MNTENT code above first.
894 */
895
896 /* Scan SCSI and CAM devices */
897 for ( c='0'; exists && c <='9'; c++ ) {
898 sprintf(drive, "/dev/cd%c%s", c, DEVICE_POSTFIX);
899 exists = cdio_is_cdrom(drive, NULL);
900 if ( exists ) {
901 return strdup(drive);
902 }
903 }
904
905 /* Scan are ATAPI devices */
906 exists = true;
907
908 for ( c='0'; exists && c <='9'; c++ ) {
909 sprintf(drive, "/dev/acd%c%s", c, DEVICE_POSTFIX);
910 exists = cdio_is_cdrom(drive, NULL);
911 if ( exists ) {
912 return strdup(drive);
913 }
914 }
915 return NULL;
916 #endif /*HAVE_FREEBSD_CDROM*/
917 }
918
919 /*!
920 Close tray on CD-ROM.
921
922 @param psz_device the CD-ROM drive to be closed.
923
924 */
925 driver_return_code_t
close_tray_freebsd(const char * psz_device)926 close_tray_freebsd (const char *psz_device)
927 {
928 #ifdef HAVE_FREEBSD_CDROM
929 int fd = open (psz_device, O_RDONLY|O_NONBLOCK, 0);
930 int i_rc;
931
932 if((i_rc = ioctl(fd, CDIOCCLOSE)) != 0) {
933 cdio_warn ("ioctl CDIOCCLOSE failed: %s\n", strerror(errno));
934 close(fd);
935 return DRIVER_OP_ERROR;
936 }
937 close(fd);
938 return DRIVER_OP_SUCCESS;
939 #else
940 return DRIVER_OP_NO_DRIVER;
941 #endif /*HAVE_FREEBSD_CDROM*/
942 }
943
944 /*! Find out if media has changed since the last call. @param
945 p_user_data the environment of the CD object to be acted upon.
946 @return 1 if media has changed since last call, 0 if not. Error
947 return codes are the same as driver_return_code_t
948 */
949
950 #ifdef HAVE_FREEBSD_CDROM
951 int
get_media_changed_freebsd(const void * p_user_data)952 get_media_changed_freebsd (const void *p_user_data)
953 {
954 const _img_private_t *p_env = p_user_data;
955 int changed = 0 ;
956
957 changed = mmc_get_media_changed( p_env->gen.cdio );
958
959 return ((changed > 0) ? changed : 0);
960 }
961
962 static const char*
get_access_mode(const char * psz_source)963 get_access_mode(const char *psz_source)
964 {
965 char *psz_src = NULL;
966 if (!psz_source) {
967 psz_src = cdio_get_default_device_freebsd();
968 } else {
969 psz_src = strdup(psz_source);
970 }
971
972 if (psz_src != NULL) {
973 if (!(strncmp(psz_src, "/dev/acd", 8))) {
974 free(psz_src);
975 return "ioctl";
976 } else {
977 char devname[256];
978 int bytes = readlink(psz_src, devname, 255);
979
980 if (bytes > 0) {
981 devname[bytes]=0;
982 if (!(strncmp(devname, "acd", 3))) {
983 free(psz_src);
984 return "ioctl";
985 }
986 }
987 }
988 }
989 if (psz_src != NULL)
990 free(psz_src);
991 return "CAM";
992 }
993
994
995 /* Lock the inode associated to dev_fd and the inode associated to devname.
996 Return OS errno, number of pass device of dev_fd, locked fd to devname,
997 error message.
998 A return value of > 0 means success, <= 0 means failure.
999 */
freebsd_dev_lock(int dev_fd,char * devname,int * os_errno,int * pass_dev_no,int * lock_fd,char msg[4096],int flag)1000 static int freebsd_dev_lock(int dev_fd, char *devname,
1001 int *os_errno, int *pass_dev_no, int *lock_fd, char msg[4096],
1002 int flag)
1003 {
1004 int lock_denied = 0, fd_stbuf_valid, name_stbuf_valid, i, pass_l = 100;
1005 int max_retry = 3, tries;
1006 struct stat fd_stbuf, name_stbuf;
1007 char pass_name[16], *lock_name;
1008
1009 *os_errno = 0;
1010 *pass_dev_no = -1;
1011 *lock_fd = -1;
1012 msg[0] = 0;
1013
1014 fd_stbuf_valid = !fstat(dev_fd, &fd_stbuf);
1015
1016 /* Try to find name of pass device by inode number */
1017 lock_name = (char *) "effective device";
1018 if(fd_stbuf_valid) {
1019 for (i = 0; i < pass_l; i++) {
1020 sprintf(pass_name, "/dev/pass%d", i);
1021 if (stat(pass_name, &name_stbuf) != -1)
1022 if(fd_stbuf.st_ino == name_stbuf.st_ino &&
1023 fd_stbuf.st_dev == name_stbuf.st_dev)
1024 break;
1025 }
1026 if (i < pass_l) {
1027 lock_name = pass_name;
1028 *pass_dev_no = i;
1029 }
1030 }
1031
1032 name_stbuf_valid = !stat(devname, &name_stbuf);
1033 for (tries= 0; tries <= max_retry; tries++) {
1034 lock_denied = flock(dev_fd, LOCK_EX | LOCK_NB);
1035 *os_errno = errno;
1036 if (lock_denied) {
1037 if (errno == EAGAIN && tries < max_retry) {
1038 /* <<< debugging
1039 fprintf(stderr, "\nlibcdio_DEBUG: EAGAIN , tries= %d\n", tries);
1040 */
1041 usleep(2000000);
1042 continue;
1043 }
1044 sprintf(msg,
1045 "Device busy. flock(LOCK_EX) failed on %s of %s",
1046 strlen(lock_name) > 2000 || *pass_dev_no < 0 ?
1047 "pass device" : lock_name,
1048 strlen(devname) > 2000 ? "drive" : devname);
1049 return 0;
1050 }
1051 break;
1052 }
1053
1054 /*
1055 fprintf(stderr, "libburn_DEBUG: flock obtained on %s of %s\n",
1056 lock_name, devname);
1057 */
1058
1059 /* Eventually lock the official device node too */
1060 if (fd_stbuf_valid && name_stbuf_valid &&
1061 (fd_stbuf.st_ino != name_stbuf.st_ino ||
1062 fd_stbuf.st_dev != name_stbuf.st_dev)) {
1063
1064 *lock_fd = open(devname, O_RDONLY);
1065 if (*lock_fd == 0) {
1066 close(*lock_fd);
1067 *lock_fd = -1;
1068 } if (*lock_fd > 0) {
1069 for (tries = 0; tries <= max_retry; tries++) {
1070 lock_denied = flock(*lock_fd, LOCK_EX | LOCK_NB);
1071 if (lock_denied) {
1072 if (errno == EAGAIN && tries < max_retry) {
1073 usleep(2000000);
1074 continue;
1075 }
1076 close(*lock_fd);
1077 *lock_fd = -1;
1078 sprintf(msg, "Device busy. flock(LOCK_EX) failed on %s",
1079 strlen(devname) > 4000 ? "drive" : devname);
1080 return 0;
1081 }
1082 break;
1083 }
1084 }
1085
1086 /*
1087 fprintf(stderr, "libburn_DEBUG: flock obtained on %s\n",
1088 devname);
1089 */
1090
1091 }
1092 return 1;
1093 }
1094
1095 #endif /*HAVE_FREEBSD_CDROM*/
1096
1097 /*!
1098 Initialization routine. This is the only thing that doesn't
1099 get called via a function pointer. In fact *we* are the
1100 ones to set that up.
1101 */
1102 CdIo *
cdio_open_freebsd(const char * psz_source_name)1103 cdio_open_freebsd (const char *psz_source_name)
1104 {
1105 return cdio_open_am_freebsd(psz_source_name, NULL);
1106 }
1107
1108
1109 /*!
1110 Initialization routine. This is the only thing that doesn't
1111 get called via a function pointer. In fact *we* are the
1112 ones to set that up.
1113 */
1114 CdIo *
cdio_open_am_freebsd(const char * psz_orig_source_name,const char * psz_access_mode)1115 cdio_open_am_freebsd (const char *psz_orig_source_name,
1116 const char *psz_access_mode)
1117 {
1118
1119 #ifdef HAVE_FREEBSD_CDROM
1120 CdIo *ret;
1121 _img_private_t *_data;
1122 char *psz_source_name;
1123 int open_access_mode; /* Access mode passed to cdio_generic_init. */
1124
1125 cdio_funcs_t _funcs = {
1126 .audio_get_volume = audio_get_volume_freebsd,
1127 .audio_pause = audio_pause_freebsd,
1128 .audio_play_msf = audio_play_msf_freebsd,
1129 .audio_play_track_index = audio_play_track_index_freebsd,
1130 .audio_read_subchannel = audio_read_subchannel_freebsd,
1131 .audio_resume = audio_resume_freebsd,
1132 .audio_set_volume = audio_set_volume_freebsd,
1133 .audio_stop = audio_stop_freebsd,
1134 .eject_media = eject_media_freebsd,
1135 .free = free_freebsd,
1136 .get_arg = get_arg_freebsd,
1137 .get_blocksize = get_blocksize_mmc,
1138 .get_cdtext = get_cdtext_generic,
1139 .get_cdtext_raw = read_cdtext_generic,
1140 .get_default_device = cdio_get_default_device_freebsd,
1141 .get_devices = cdio_get_devices_freebsd,
1142 .get_disc_last_lsn = get_disc_last_lsn_freebsd,
1143 .get_discmode = get_discmode_generic,
1144 .get_drive_cap = get_drive_cap_freebsd,
1145 .get_first_track_num = get_first_track_num_generic,
1146 .get_media_changed = get_media_changed_freebsd,
1147 .get_mcn = get_mcn_freebsd,
1148 .get_num_tracks = get_num_tracks_generic,
1149 .get_track_channels = get_track_channels_generic,
1150 .get_track_copy_permit = get_track_copy_permit_generic,
1151 .get_track_format = get_track_format_freebsd,
1152 .get_track_green = get_track_green_freebsd,
1153 .get_track_lba = get_track_lba_freebsd,
1154 .get_track_preemphasis = get_track_preemphasis_generic,
1155 .get_track_msf = NULL,
1156 .get_track_isrc = get_track_isrc_freebsd,
1157 .lseek = cdio_generic_lseek,
1158 .read = cdio_generic_read,
1159 .read_audio_sectors = read_audio_sectors_freebsd,
1160 .read_data_sectors = read_data_sectors_mmc,
1161 .read_mode2_sector = read_mode2_sector_freebsd,
1162 .read_mode2_sectors = read_mode2_sectors_freebsd,
1163 .read_toc = read_toc_freebsd,
1164 .run_mmc_cmd = run_mmc_cmd_freebsd,
1165 .set_arg = set_arg_freebsd,
1166 .set_blocksize = set_blocksize_mmc,
1167 .set_speed = set_speed_freebsd,
1168 };
1169
1170 if (!psz_access_mode)
1171 psz_access_mode = get_access_mode(psz_orig_source_name);
1172
1173 _data = calloc(1, sizeof (_img_private_t));
1174 _data->access_mode = str_to_access_mode_freebsd(psz_access_mode);
1175 _data->gen.init = false;
1176 _data->gen.fd = -1;
1177 _data->gen.toc_init = false;
1178 _data->gen.b_cdtext_error = false;
1179
1180 if (NULL == psz_orig_source_name) {
1181 psz_source_name=cdio_get_default_device_freebsd();
1182 if (NULL == psz_source_name) {
1183 cdio_generic_free (_data);
1184 return NULL;
1185 }
1186 _data->device = psz_source_name;
1187 set_arg_freebsd(_data, "source", psz_source_name);
1188 } else {
1189 if (cdio_is_device_generic(psz_orig_source_name)) {
1190 set_arg_freebsd(_data, "source", psz_orig_source_name);
1191 _data->device = strdup(psz_orig_source_name);
1192 } else {
1193 /* The below would be okay if all device drivers worked this way. */
1194 #if 0
1195 cdio_info ("source %s is a not a device", psz_orig_source_name);
1196 #endif
1197 cdio_generic_free (_data);
1198 return NULL;
1199 }
1200 }
1201
1202 ret = cdio_new ((void *)_data, &_funcs);
1203 if (ret == NULL) {
1204 cdio_generic_free (_data);
1205 return NULL;
1206 }
1207
1208 open_access_mode = 0;
1209 if (_AM_MMC_RDWR == _data->access_mode) {
1210 open_access_mode |= O_RDWR;
1211 } else if (_AM_MMC_RDWR_EXCL == _data->access_mode) {
1212 open_access_mode |= O_RDWR;
1213 } else {
1214 open_access_mode |= O_RDONLY;
1215 }
1216 /*
1217 fprintf(stderr,
1218 "libcdio_DEBUG: am = %d (MMC_RDWR_EXCL = %d), open = %d (O_RDWR = %d)\n",
1219 _data->access_mode, _AM_MMC_RDWR_EXCL, open_access_mode, O_RDWR);
1220 */
1221
1222 if (cdio_generic_init(_data, open_access_mode)) {
1223 if (_AM_MMC_RDWR_EXCL == _data->access_mode) {
1224 int os_errno, pass_dev_no = -1, flock_fd = -1, lock_result;
1225 char msg[4096];
1226
1227 lock_result = freebsd_dev_lock(_data->gen.fd, _data->gen.source_name,
1228 &os_errno, &pass_dev_no, &flock_fd, msg, 0);
1229 if (lock_result <= 0) {
1230 cdio_warn ("%s", msg);
1231 goto err_exit;
1232 }
1233 /* One should rather keep this fd open until _data->gen.fd gets closed.
1234 It eventually locks a device sibling of _data->gen.source_name.
1235 */
1236 if (flock_fd > 0)
1237 close(flock_fd);
1238 }
1239
1240 if ( _data->access_mode == _AM_IOCTL ) {
1241 return ret;
1242 } else {
1243 if (init_freebsd_cam(_data))
1244 return ret;
1245 }
1246 }
1247
1248 err_exit:
1249 free(ret);
1250 cdio_generic_free(_data);
1251 return NULL;
1252
1253 #else
1254 return NULL;
1255 #endif /* HAVE_FREEBSD_CDROM */
1256 }
1257
1258 bool
cdio_have_freebsd(void)1259 cdio_have_freebsd (void)
1260 {
1261 #ifdef HAVE_FREEBSD_CDROM
1262 return true;
1263 #else
1264 return false;
1265 #endif /* HAVE_FREEBSD_CDROM */
1266 }
1267