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