1 /*
2  *  Copyright (C) 2009 David Mohr <david@mcbf.net>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU Library General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  *
18  */
19 
20 #ifdef	HAVE_CONFIG_H
21 #include <config.h>
22 #endif /* !HAVE_CONFIG_H */
23 
24 #include <libxfce4util/libxfce4util.h>
25 #include <unistd.h>
26 
27 #include "xfburn-device.h"
28 #include "xfburn-udev-manager.h"
29 
30 /*- globals -*/
31 
32 enum {
33   PROP_0,
34   PROP_NAME,
35   PROP_ADDRESS,
36   PROP_REVISION,
37   PROP_ACCESSIBLE,
38   PROP_SUPPORTED_SPEEDS,
39   PROP_DISC_STATUS,
40   PROP_PROFILE_NO,
41   PROP_PROFILE_NAME,
42   PROP_ERASABLE,
43   PROP_CDR,
44   PROP_CDRW,
45   PROP_DVDR,
46   PROP_DVDPLUSR,
47   PROP_DVDRAM,
48   PROP_BD,
49   PROP_TAO_BLOCK_TYPES,
50   PROP_SAO_BLOCK_TYPES,
51   PROP_RAW_BLOCK_TYPES,
52   PROP_PACKET_BLOCK_TYPES,
53 };
54 
55 /*- private prototypes -*/
56 
57 #define CAN_BURN(priv) ((priv)->cdr || (priv)->cdrw || (priv)->dvdr || (priv)->dvdram)
58 #define DEVICE_INFO_PRINTF(device) "%s can burn: %d [cdr: %d, cdrw: %d, dvdr: %d, dvdram: %d]", (device)->name, CAN_BURN (device), (device)->cdr, (device)->cdrw, (device)->dvdr, (device)->dvdram
59 
60 
61 /*****************/
62 /*- class setup -*/
63 /*****************/
64 typedef struct _XfburnDevicePrivate XfburnDevicePrivate;
65 
66 struct _XfburnDevicePrivate {
67   gchar *name;
68   gchar *addr;
69   gchar *rev;
70   gboolean details_known;
71 
72   gint buffer_size;
73   gboolean dummy_write;
74 
75   gboolean cdr;
76   gboolean cdrw;
77   gboolean dvdr;
78   gboolean dvdplusr;
79   gboolean dvdram;
80   gboolean bd;
81 
82   GSList *supported_speeds;
83 
84   gint tao_block_types;
85   gint sao_block_types;
86   gint raw_block_types;
87   gint packet_block_types;
88 
89   enum burn_disc_status disc_status;
90   int profile_no;
91   char profile_name[80];
92   int is_erasable;
93 };
94 
G_DEFINE_TYPE_WITH_PRIVATE(XfburnDevice,xfburn_device,G_TYPE_OBJECT)95 G_DEFINE_TYPE_WITH_PRIVATE (XfburnDevice, xfburn_device, G_TYPE_OBJECT)
96 
97 #define GET_PRIVATE(o) \
98   (xfburn_device_get_instance_private (o))
99 
100 static void
101 xfburn_device_get_property (GObject *object, guint property_id,
102                               GValue *value, GParamSpec *pspec)
103 {
104   XfburnDevicePrivate *priv = GET_PRIVATE (XFBURN_DEVICE (object));
105 
106   switch (property_id) {
107     case PROP_NAME:
108       g_value_set_string (value, priv->name);
109       break;
110     case PROP_ADDRESS:
111       g_value_set_string (value, priv->addr);
112       break;
113     case PROP_REVISION:
114       g_value_set_string (value, priv->rev);
115       break;
116     case PROP_SUPPORTED_SPEEDS:
117       g_value_set_pointer (value, priv->supported_speeds);
118       break;
119     case PROP_DISC_STATUS:
120       g_value_set_int (value, priv->disc_status);
121       break;
122     case PROP_PROFILE_NO:
123       g_value_set_int (value, priv->profile_no);
124       break;
125     case PROP_PROFILE_NAME:
126       g_value_set_string (value, priv->profile_name);
127       break;
128     case PROP_ERASABLE:
129       g_value_set_boolean (value, priv->is_erasable);
130       break;
131     case PROP_CDR:
132       g_value_set_boolean (value, priv->cdr);
133       break;
134     case PROP_CDRW:
135       g_value_set_boolean (value, priv->cdrw);
136       break;
137     case PROP_DVDR:
138       g_value_set_boolean (value, priv->dvdr);
139       break;
140     case PROP_DVDPLUSR:
141       g_value_set_boolean (value, priv->dvdplusr);
142       break;
143     case PROP_DVDRAM:
144       g_value_set_boolean (value, priv->dvdram);
145       break;
146     case PROP_BD:
147       g_value_set_boolean (value, priv->bd);
148       break;
149     case PROP_TAO_BLOCK_TYPES:
150       g_value_set_int (value, priv->tao_block_types);
151       break;
152     case PROP_SAO_BLOCK_TYPES:
153       g_value_set_int (value, priv->sao_block_types);
154       break;
155     case PROP_RAW_BLOCK_TYPES:
156       g_value_set_int (value, priv->raw_block_types);
157       break;
158     case PROP_PACKET_BLOCK_TYPES:
159       g_value_set_int (value, priv->packet_block_types);
160       break;
161     default:
162       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
163   }
164 }
165 
166 static void
xfburn_device_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)167 xfburn_device_set_property (GObject *object, guint property_id,
168                               const GValue *value, GParamSpec *pspec)
169 {
170   XfburnDevicePrivate *priv = GET_PRIVATE (XFBURN_DEVICE (object));
171 
172   switch (property_id) {
173     case PROP_NAME:
174       priv->name = g_value_dup_string (value);
175       break;
176     case PROP_ADDRESS:
177       priv->addr = g_value_dup_string (value);
178       break;
179     case PROP_REVISION:
180       priv->rev = g_value_dup_string (value);
181       break;
182     case PROP_SUPPORTED_SPEEDS:
183       priv->supported_speeds = g_value_get_pointer (value);
184       break;
185     case PROP_DISC_STATUS:
186       priv->disc_status = g_value_get_int (value);
187       break;
188     case PROP_PROFILE_NO:
189       priv->profile_no = g_value_get_int (value);
190       break;
191     case PROP_PROFILE_NAME:
192       strncpy (priv->profile_name, g_value_get_string(value), 80);
193       break;
194     case PROP_ERASABLE:
195       priv->is_erasable = g_value_get_boolean (value);
196       break;
197     case PROP_CDR:
198       priv->cdr = g_value_get_boolean (value);
199       break;
200     case PROP_CDRW:
201       priv->cdrw = g_value_get_boolean (value);
202       break;
203     case PROP_DVDR:
204       priv->dvdr = g_value_get_boolean (value);
205       break;
206     case PROP_DVDPLUSR:
207       priv->dvdplusr = g_value_get_boolean (value);
208       break;
209     case PROP_DVDRAM:
210       priv->dvdram = g_value_get_boolean (value);
211       break;
212     case PROP_BD:
213       priv->bd = g_value_get_boolean (value);
214       break;
215     case PROP_TAO_BLOCK_TYPES:
216       priv->tao_block_types = g_value_get_int (value);
217       break;
218     case PROP_SAO_BLOCK_TYPES:
219       priv->sao_block_types = g_value_get_int (value);
220       break;
221     case PROP_RAW_BLOCK_TYPES:
222       priv->raw_block_types = g_value_get_int (value);
223       break;
224     case PROP_PACKET_BLOCK_TYPES:
225       priv->packet_block_types = g_value_get_int (value);
226       break;
227     default:
228       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
229   }
230 }
231 
232 static void
xfburn_device_finalize(GObject * object)233 xfburn_device_finalize (GObject *object)
234 {
235   XfburnDevice *dev = XFBURN_DEVICE (object);
236   XfburnDevicePrivate *priv = GET_PRIVATE (dev);
237 
238   g_free (priv->name);
239 
240   g_slist_free (priv->supported_speeds);
241 
242   G_OBJECT_CLASS (xfburn_device_parent_class)->finalize (object);
243 }
244 
245 static void
xfburn_device_class_init(XfburnDeviceClass * klass)246 xfburn_device_class_init (XfburnDeviceClass *klass)
247 {
248   GObjectClass *object_class = G_OBJECT_CLASS (klass);
249 
250   object_class->get_property = xfburn_device_get_property;
251   object_class->set_property = xfburn_device_set_property;
252   object_class->finalize = xfburn_device_finalize;
253 
254   g_object_class_install_property (object_class, PROP_NAME,
255                                    g_param_spec_string ("name", _("Display name"),
256                                                         _("Display name"), NULL, G_PARAM_READABLE));
257   g_object_class_install_property (object_class, PROP_ADDRESS,
258                                    g_param_spec_string ("address", _("Device address"),
259                                                         _("Device address"), "", G_PARAM_READWRITE));
260   g_object_class_install_property (object_class, PROP_REVISION,
261                                    g_param_spec_string ("revision", _("Device revision"),
262                                                         _("Device Revision"), "", G_PARAM_READWRITE));
263   g_object_class_install_property (object_class, PROP_SUPPORTED_SPEEDS,
264                                    g_param_spec_pointer ("supported-speeds", _("Burn speeds supported by the device"),
265                                                         _("Burn speeds supported by the device"), G_PARAM_READABLE));
266   g_object_class_install_property (object_class, PROP_DISC_STATUS,
267                                    g_param_spec_int ("disc-status", _("Disc status"),
268                                                      _("Disc status"), 0, 6, 0, G_PARAM_READABLE));
269   g_object_class_install_property (object_class, PROP_PROFILE_NO,
270                                    g_param_spec_int ("profile-no", _("Profile no. as reported by libburn"),
271                                                      _("Profile no. as reported by libburn"), 0, 0xffff, 0, G_PARAM_READABLE));
272   g_object_class_install_property (object_class, PROP_PROFILE_NAME,
273                                    g_param_spec_string ("profile-name", _("Profile name as reported by libburn"),
274                                                         _("Profile name as reported by libburn"), "", G_PARAM_READABLE));
275   g_object_class_install_property (object_class, PROP_ERASABLE,
276                                    g_param_spec_boolean ("erasable", _("Is the disc erasable"),
277                                                         _("Is the disc erasable"), FALSE, G_PARAM_READABLE));
278   g_object_class_install_property (object_class, PROP_CDR,
279                                    g_param_spec_boolean ("cdr", _("Can burn CDR"),
280                                                         _("Can burn CDR"), FALSE, G_PARAM_READWRITE));
281   g_object_class_install_property (object_class, PROP_CDRW,
282                                    g_param_spec_boolean ("cdrw", _("Can burn CDRW"),
283                                                         _("Can burn CDRW"), FALSE, G_PARAM_READWRITE));
284   g_object_class_install_property (object_class, PROP_DVDR,
285                                    g_param_spec_boolean ("dvdr", _("Can burn DVDR"),
286                                                         _("Can burn DVDR"), FALSE, G_PARAM_READWRITE));
287   g_object_class_install_property (object_class, PROP_DVDPLUSR,
288                                    g_param_spec_boolean ("dvdplusr", _("Can burn DVDPLUSR"),
289                                                         _("Can burn DVDPLUSR"), FALSE, G_PARAM_READWRITE));
290   g_object_class_install_property (object_class, PROP_DVDRAM,
291                                    g_param_spec_boolean ("dvdram", _("Can burn DVDRAM"),
292                                                         _("Can burn DVDRAM"), FALSE, G_PARAM_READWRITE));
293   g_object_class_install_property (object_class, PROP_BD,
294                                    g_param_spec_boolean ("bd", _("Can burn Blu-ray"),
295                                                         _("Can burn Blu-ray"), FALSE, G_PARAM_READWRITE));
296   g_object_class_install_property (object_class, PROP_TAO_BLOCK_TYPES,
297                                    g_param_spec_int ("tao-block-types", _("libburn TAO block types"),
298                                                      _("libburn TAO block types"), 0, G_MAXINT, 0, G_PARAM_READABLE));
299   g_object_class_install_property (object_class, PROP_SAO_BLOCK_TYPES,
300                                    g_param_spec_int ("sao-block-types", _("libburn SAO block types"),
301                                                      _("libburn SAO block types"), 0, G_MAXINT, 0, G_PARAM_READABLE));
302   g_object_class_install_property (object_class, PROP_RAW_BLOCK_TYPES,
303                                    g_param_spec_int ("raw-block-types", _("libburn RAW block types"),
304                                                      _("libburn RAW block types"), 0, G_MAXINT, 0, G_PARAM_READABLE));
305   g_object_class_install_property (object_class, PROP_PACKET_BLOCK_TYPES,
306                                    g_param_spec_int ("packet-block-types", _("libburn PACKET block types"),
307                                                      _("libburn PACKET block types"), 0, G_MAXINT, 0, G_PARAM_READABLE));
308 }
309 
310 static void
xfburn_device_init(XfburnDevice * self)311 xfburn_device_init (XfburnDevice *self)
312 {
313   /* FIXME: initialize profile_name, or is that handled by the property? */
314 }
315 
316 
317 /***************/
318 /*- internals -*/
319 /***************/
320 
321 static gboolean
no_speed_duplicate(GSList * speed_list,gint speed)322 no_speed_duplicate (GSList *speed_list, gint speed)
323 {
324   GSList *el = speed_list;
325 
326   while (el) {
327     gint el_speed = GPOINTER_TO_INT (el->data);
328 
329     if (el_speed == speed)
330       return FALSE;
331 
332     el = g_slist_next (el);
333   }
334 
335   return TRUE;
336 }
337 
338 /* sort the speed list in ascending order */
339 static gint
cmp_ints(gconstpointer a,gconstpointer b)340 cmp_ints (gconstpointer a, gconstpointer b)
341 {
342   return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
343 }
344 
345 static void
refresh_disc(XfburnDevice * device,struct burn_drive_info * drive_info)346 refresh_disc (XfburnDevice * device, struct burn_drive_info *drive_info)
347 {
348   XfburnDevicePrivate *priv = GET_PRIVATE (device);
349   gint ret;
350 
351   /* check if there is a disc in the drive */
352   while ((priv->disc_status = burn_disc_get_status (drive_info->drive)) == BURN_DISC_UNREADY)
353     usleep(100001);
354 
355   DBG ("disc_status = %d", priv->disc_status);
356 
357   if ((ret = burn_disc_get_profile(drive_info->drive, &(priv->profile_no), priv->profile_name)) != 1) {
358     g_warning ("no profile could be retrieved");
359   }
360   priv->is_erasable = burn_disc_erasable (drive_info->drive);
361   DBG ("profile_no = 0x%x (%s), %s erasable", priv->profile_no, priv->profile_name, (priv->is_erasable ? "" : "NOT"));
362 
363 #if 0 /* this doesn't seem to work */
364   if (burn_disc_read_atip (drive_info->drive) == 1) {
365     int start, end;
366 
367     if (burn_drive_get_start_end_lba (drive_info->drive, &start, &end, 0) == 1) {
368       DBG ("lba start = %d, end = %d", start, end);
369     } else
370       DBG ("Getting start/end lba failed");
371   } else
372     DBG ("Reading ATIP failed");
373 #endif
374 }
375 
376 static void
refresh_speed_list(XfburnDevice * device,struct burn_drive_info * drive_info)377 refresh_speed_list (XfburnDevice * device, struct burn_drive_info *drive_info)
378 {
379   XfburnDevicePrivate *priv = GET_PRIVATE (device);
380 
381   struct burn_speed_descriptor *speed_list = NULL;
382   gint ret;
383 
384   /* fill new list */
385   ret = burn_drive_get_speedlist (drive_info->drive, &speed_list);
386   /* speed_list = NULL; DEBUG */
387 
388   if (ret > 0 && speed_list != NULL) {
389     struct burn_speed_descriptor *el = speed_list;
390 
391     while (el) {
392       gint speed = el->write_speed;
393 
394       /* FIXME: why do we need no_speed_duplicate? */
395       if (speed > 0 && no_speed_duplicate (priv->supported_speeds, speed)) {
396           priv->supported_speeds = g_slist_prepend (priv->supported_speeds, GINT_TO_POINTER (speed));
397       }
398 
399       el = el->next;
400     }
401 
402     burn_drive_free_speedlist (&speed_list);
403     priv->supported_speeds = g_slist_sort (priv->supported_speeds, &cmp_ints);
404   } else if (ret == 0 || speed_list == NULL) {
405     g_warning ("reported speed list is empty for device:");
406     // FIXME
407     //g_warning (DEVICE_INFO_PRINTF (priv));
408     g_warning ("%s can burn: %d [cdr: %d, cdrw: %d, dvdr: %d, dvdram: %d]", (priv)->name, ((priv)->cdr || (priv)->cdrw || (priv)->dvdr || (priv)->dvdram), (priv)->cdr, (priv)->cdrw, (priv)->dvdr, (priv)->dvdram);
409   } else {
410     /* ret < 0 */
411     g_error ("severe error while retrieving speed list");
412   }
413 }
414 
415 /*******************/
416 /*- public methods-*/
417 /*******************/
418 
419 void
xfburn_device_fillin_libburn_info(XfburnDevice * device,struct burn_drive_info * drive)420 xfburn_device_fillin_libburn_info (XfburnDevice *device, struct burn_drive_info *drive)
421 {
422   XfburnDevicePrivate *priv = GET_PRIVATE (device);
423 
424   priv->details_known = TRUE;
425 
426   priv->cdr = drive->write_cdr;
427   priv->cdrw = drive->write_cdrw;
428 
429   priv->dvdr = drive->write_dvdr;
430   priv->dvdram = drive->write_dvdram;
431 
432   priv->buffer_size = drive->buffer_size;
433   priv->dummy_write = drive->write_simulate;
434 
435   DBG ("libburn will determine BD support based on the disk in the drive");
436 
437   /* write modes */
438   priv->tao_block_types = drive->tao_block_types;
439   priv->sao_block_types = drive->sao_block_types;
440   priv->raw_block_types = drive->raw_block_types;
441   priv->packet_block_types = drive->packet_block_types;
442 
443   DBG (DEVICE_INFO_PRINTF (priv));
444 }
445 
446 gboolean
xfburn_device_grab(XfburnDevice * device,struct burn_drive_info ** drive_info)447 xfburn_device_grab (XfburnDevice * device, struct burn_drive_info **drive_info)
448 {
449   XfburnDevicePrivate *priv = GET_PRIVATE (device);
450   int ret = 0;
451   gchar drive_addr[BURN_DRIVE_ADR_LEN];
452   int i;
453   const int max_checks = 4;
454 #ifdef HAVE_GUDEV
455   XfburnUdevManager *udevman = xfburn_udev_manager_get_global ();
456 #endif
457 
458   ret = burn_drive_convert_fs_adr (priv->addr, drive_addr);
459   if (ret <= 0) {
460     g_error ("Device address does not lead to a burner '%s' (ret=%d).", priv->addr, ret);
461     return FALSE;
462   }
463 
464   /* we need to try to grab several times, because
465    * the drive might be busy detecting the disc */
466   for (i=1; i<=max_checks; i++) {
467     ret = burn_drive_scan_and_grab (drive_info, drive_addr, 0);
468     //DBG ("grab (%s)-> %d", drive_addr, ret);
469     if (ret > 0)
470       break;
471     else if  (i < max_checks) {
472 #ifdef HAVE_GUDEV
473       if (!xfburn_udev_manager_check_ask_umount (udevman, device))
474         usleep(i*100001);
475 #else
476       usleep(i*100001);
477 #endif
478     }
479   }
480 
481   if (ret <= 0) {
482     g_warning ("Unable to grab the drive at path '%s' (ret=%d).", priv->addr, ret);
483     return FALSE;
484   }
485 
486   return TRUE;
487 }
488 
489 gboolean
xfburn_device_refresh_info(XfburnDevice * device,gboolean get_speed_info)490 xfburn_device_refresh_info (XfburnDevice * device, gboolean get_speed_info)
491 {
492   XfburnDevicePrivate *priv = GET_PRIVATE (device);
493 
494   struct burn_drive_info *drive_info = NULL;
495   gboolean ret;
496 
497   if (G_UNLIKELY (device == NULL)) {
498     g_warning ("Hmm, why can we refresh when there is no drive?");
499     return FALSE;
500   }
501 
502   /* reset other internal structures */
503   priv->profile_no = 0;
504   *(priv->profile_name) = '\0';
505   priv->is_erasable = 0;
506 
507   /* empty previous speed list */
508   g_slist_free (priv->supported_speeds);
509   priv->supported_speeds = NULL;
510 
511   if (!xfburn_device_grab (device, &drive_info)) {
512     ret = FALSE;
513     g_warning ("Couldn't grab drive in order to update speed list.");
514     priv->disc_status = BURN_DISC_UNGRABBED;
515   } else {
516     if (!priv->details_known)
517       xfburn_device_fillin_libburn_info (device, drive_info);
518 
519     ret = TRUE;
520     refresh_disc (device, drive_info);
521     if (get_speed_info)
522       refresh_speed_list (device, drive_info);
523 
524     xfburn_device_release (drive_info, 0);
525   }
526 
527   return ret;
528 }
529 
530 gboolean
xfburn_device_release(struct burn_drive_info * drive_info,gboolean eject)531 xfburn_device_release (struct burn_drive_info *drive_info, gboolean eject)
532 {
533   int ret;
534 
535   burn_drive_snooze (drive_info->drive, 0);
536   burn_drive_release (drive_info->drive, eject);
537 
538   ret = burn_drive_info_forget (drive_info, 0);
539 
540   if (G_LIKELY (ret == 1))
541     return TRUE;
542   else if (ret == 2) {
543     DBG ("drive_info already forgotten");
544     return TRUE;
545   } else if (ret == 0) {
546     DBG ("could not forget drive_info");
547     return FALSE;
548   } else if (ret < 0) {
549     DBG ("some other failure while forgetting drive_info");
550     return FALSE;
551   }
552 
553   /* we should never reach this */
554   return FALSE;
555 }
556 
557 const gchar *
xfburn_device_set_name(XfburnDevice * device,const gchar * vendor,const gchar * product)558 xfburn_device_set_name (XfburnDevice *device, const gchar *vendor, const gchar *product)
559 {
560   XfburnDevicePrivate *priv = GET_PRIVATE (device);
561 
562   priv->name = g_strconcat (vendor, " ", product, NULL);
563 
564   return priv->name;
565 }
566 
567 gboolean
xfburn_device_can_burn(XfburnDevice * device)568 xfburn_device_can_burn (XfburnDevice *device)
569 {
570   XfburnDevicePrivate *priv = GET_PRIVATE (device);
571 
572   return CAN_BURN (priv);
573 }
574 
575 gboolean
xfburn_device_can_dummy_write(XfburnDevice * device)576 xfburn_device_can_dummy_write (XfburnDevice *device)
577 {
578   XfburnDevicePrivate *priv = GET_PRIVATE (device);
579 
580   return priv->dummy_write;
581 }
582 
583 XfburnDevice*
xfburn_device_new(void)584 xfburn_device_new (void)
585 {
586   return g_object_new (XFBURN_TYPE_DEVICE, NULL);
587 }
588 
589 
590