Logo Search packages:      
Sourcecode: pbbuttonsd version File versions  Download package

module_cdrom.c

/* --------------------------------------------------------------------------
 * module_cdrom.c
 * code for module cdrom
 *
 * Copyright 2002 Matthias Grimm
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 * -------------------------------------------------------------------------*/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <linux/cdrom.h>
#include <errno.h>
#include <pbb.h>
#include <time.h>

#include "pbbinput.h"
#include "gettext_macros.h"
#include "input_manager.h"
#include "config_manager.h"
#include "module_cdrom.h"
#include "support.h"
#include "debug.h"

struct moddata_cdrom {
      char *cdromdev;         /* pathname of the cdrom device */
      unsigned short keyejectcd;
      unsigned short modejectcd;
      struct timeval tv;      /* time of key press */
      int keydelaytime;      /* delay in ms */
      int cdejected;
} modbase_cdrom;

int
cdrom_init ()
{
      struct moddata_cdrom *base = &modbase_cdrom;
      static char devbuffer_cdrom[STDBUFFERLEN];
      int sid;

      sprintf(devbuffer_cdrom, DEFAULT_CDROM);

      base->cdromdev          = devbuffer_cdrom;
      base->keyejectcd  = KEY_EJECTCD;
      base->modejectcd  = MOD_NONE;
      base->keydelaytime      = 0;

      if ((sid = registerCfgSection ("MODULE CDROM")) == 0) {
            print_msg (PBB_ERR, _("Can't register configuration section %s, out of memory."), "MODULE CDROM");
            return E_NOMEM;
      } else {
            registerCfgOptionString (sid, "dev_CDROM", TAG_CDROMDEVICE, 0,
                        NULL);
            registerCfgOptionKey (sid, "EjectCDKey", TAG_EJECTCDKEY, TAG_EJECTCDMOD, 0,
                        NULL);
            registerCfgOptionInt (sid, "EjectCDKeyDelay", TAG_EJECTCDKEYDELAY, 0,
                        NULL);
      }

      register_function (QUERYQUEUE, cdrom_query);
      register_function (CONFIGQUEUE, cdrom_configure);
      return 0;
}

int
cdrom_open (struct tagitem *taglist)
{
      struct moddata_cdrom *base = &modbase_cdrom;
      char *devname;
      int rc;

      devname = (char *) tagfind (taglist, TAG_CDROMDEVICE, (long) DEFAULT_CDROM);
      if ((rc = copy_path (devname, base->cdromdev, TYPE_BLKDEV, CPFLG_MAYBEMISSED))) {
            print_msg (PBB_ERR, _("Please check your CDROM settings in configuration file.\n"));
            return rc;
      }
            
#if defined(DEBUG) && CDROMPERF
      cdrom_testperformance ();
#endif      
      register_function (KBDQUEUE, cdrom_keyboard);
      return 0;
}

int
cdrom_close ()
{
      return 0;
}

int
cdrom_exit ()
{
      return 0;
}

void
cdrom_keyboard (struct tagitem *taglist)
{
      struct moddata_cdrom *base = &modbase_cdrom;
      int code, value, mod;

      code = (int) tagfind (taglist, TAG_KEYCODE, 0);
      value = (int) tagfind (taglist, TAG_KEYREPEAT, 0);
      mod = (int) tagfind (taglist, TAG_MODIFIER, 0);

      if (value) {
            if (value == 1) base->cdejected = 0;
            if ((code == base->keyejectcd) && (mod == base->modejectcd))
                  if ((base->keydelaytime == 0) || (keydelayms(&base->tv, value, base->keydelaytime)))
                        if (base->cdejected == 0) {
                              base->cdejected = 1;
                              cdrom_eject ();
                        }
      }
}

void
cdrom_query (struct tagitem *taglist)
{
      cdrom_handle_tags(MODE_QUERY, taglist);
}

void
cdrom_configure (struct tagitem *taglist)
{
      cdrom_handle_tags(MODE_CONFIG, taglist);
}

void
cdrom_handle_tags (int cfgure, struct tagitem *taglist)
{
      struct moddata_cdrom *base = &modbase_cdrom;
      int err;

      while (taglist->tag != TAG_END) {
            switch (taglist->tag) {
            case TAG_EJECTCD:
                  if (cfgure) {
                        singletag_to_clients(CHANGEVALUE, TAG_EJECTCD, 1);
                        if ((cdrom_eject()) == 0)
                              singletag_to_clients(CHANGEVALUE, TAG_EJECTCD, 0);
                  } else
                        tagerror (taglist, E_NOREAD);
                  break;
            case TAG_CDROMDEVICE:
                  if (cfgure) {
                        if ((err = copy_path ((char *) taglist->data, base->cdromdev, TYPE_BLKDEV, CPFLG_MAYBEMISSED)))
                              tagerror (taglist, err);
                  } else
                        taglist->data = (long) base->cdromdev;
                  break;
            case TAG_EJECTCDKEY:
                  if (cfgure) base->keyejectcd = taglist->data;
                  else        taglist->data = base->keyejectcd;
                  break;
            case TAG_EJECTCDMOD:
                  if (cfgure) base->modejectcd = taglist->data;
                  else        taglist->data = base->modejectcd;
                  break;
            case TAG_EJECTCDKEYDELAY:
                  if (cfgure) base->keydelaytime = taglist->data;
                  else        taglist->data = base->keydelaytime;
                  break;
            }
            taglist++;
      }
}

/* This function checks is a given name could be found in
 * a list of names. It returns a boolean walue:
 *  1 = list contains name
 *  0 = name could not be found in list
 */
int
cdrom_cmpDevName(struct listhead *list, char *name)
{
      struct listnode *it = list->first;
      
      while ((it = it->next))
            if (strcmp (name, (char*) getNodeData(it->pred)) == 0)
                  return 1;
      return 0;
}

/* This function adds 'name' to a double linked list. The
 * memory for the node and the name is dynamically allocated.
 * The name is copied to the newly allocated memory so that
 * the caller must no longer take care about it.
 * The function returns -1 on error (out of memory).
 */
int
cdrom_addDevName(struct listhead *list, char *name)
{     
      struct listnode *node;
      
      if((node = newListNode (strlen(name)+1))) {
            strcpy ((char*)getNodeData(node), name);
            appendListNode (list, node);
            return 0;
      }
      return -1;  /* out of memory */
}

/* This function gets the mountpoint of a given device. The source
 * of mountpoints is the file /proc/mounts. The function compares
 * if the device is mounted and where. Symlinks will be resolved
 * so that if a link to the device was mounted the mountpoint
 * would be found too.
 *
 * symlink chain: /dev/cdrom -> /dev/cd1 -> /dev/hdc
 * cdrom mounted: /dev/cd1
 *
 * The mountpount will be found either /dev/cdrom or /dev/hdc is
 * given.
 */
char *
cdrom_getmountpoint(char *device)
{
      static char buffer[100];
      struct listhead devs;
      char linkbuf[50], *mountpoint = NULL, *token;
      FILE *fd;
      int n;

      initListHeader (&devs);

      strcpy (buffer, device);
      do {
            if (cdrom_addDevName (&devs, buffer))
                  return NULL; /* out of memory */
            if ((n = readlink(buffer, linkbuf, sizeof(linkbuf)-1)) >= 0) {
                  linkbuf[n] = '\0';  /* readlink() doesn't terminate buffer */
                  if (linkbuf[0] == '/')  /* link is absolute */
                        strcpy (buffer, linkbuf);
                  else if ((token = strrchr (buffer, '/'))) {
                        *(++token) = 0; /* terminate path behind last '/' */
                        strcat (buffer, linkbuf);
                  } else
                        /* this should never happen. It means the startpath is already
                         * relative, so that we can't evaluate the path
                         */
                        break;
            }
      } while (n != -1);
      
      if ((fd = fopen ("/proc/mounts","r"))) {
            while (fgets (buffer, sizeof (buffer), fd)) {
                  if (buffer[0] != '/') continue;  /* check only real devices */
#if defined(DEBUG) && CDROM
                  printf("mount  = %s", buffer);
#endif
                  if ((token = strtok (buffer," \n"))) {
                        strcpy (buffer, token);
                        do {
#if defined(DEBUG) && CDROM
                          printf("  dev  = %s\n", buffer);
#endif
                              if ((cdrom_cmpDevName (&devs, buffer))) {
                                    mountpoint = strtok(0," \n");
                                    goto out;
                              }     
                              if ((n = readlink(buffer, linkbuf, sizeof(linkbuf)-1)) >= 0)
                                    linkbuf[n] = '\0';  /* readlink() doesn't terminate buffer */
                              if (linkbuf[0] == '/')  /* link is absolute */
                                    strcpy (buffer, linkbuf);
                              else if ((token = strrchr (buffer, '/'))) {
                                    *(++token) = 0; /* terminate path behind last '/' */
                                    strcat (buffer, linkbuf);
                              } else
                                    /* this should never happen. It means the startpath is already
                                     * relative, so that we can't evaluate the path
                                     */
                                    break;
                        } while (n != -1);
                  }
            }
out:
            fclose(fd);
      }
      
      freeList (&devs);
      return mountpoint;
}

/* This function eject the cdrom or close the tray if the CDROM is
 * already ejected. If neseccary the cdrom will be unmounted first.
 * Following return codes are possible:
 *   0: Drive is busy,
 *   1: CDROM has been ejected or the tray was opened,
 *   2: The Tray has been closed,
 * The external helper program umount is used because the system
 * call umount() doesn't update /etc/mtab and this conflicts with
 * umount's normal operation.
 */
int
cdrom_eject()
{
      struct moddata_cdrom *base = &modbase_cdrom;
      int fd, err = 0, rc = 0;
      unsigned int t1, t2;
      char *mp;

      singletag_to_clients(CHANGEVALUE, TAG_EJECTCD, 0); /* busy */
      if ((mp = cdrom_getmountpoint(base->cdromdev)))
            err = call_script ("/bin/umount %.30s", mp);

#if defined(DEBUG) && CDROM
      printf ("mp=%s, err=%d, %s (%d)\n", mp, err, strerror(errno), errno);
#endif

      if (mp == NULL || err == 0 || errno == EINVAL) {
            if ((fd = open(base->cdromdev, O_RDONLY | O_NONBLOCK | O_NDELAY)) >= 0) {
                  
#if defined(DEBUG) && CDROMPERF
                  err = ioctl (fd, CDROM_GET_CAPABILITY);
                  printf ("capabilities: %x\n", err);
#endif
                  err = ioctl (fd, CDROM_DRIVE_STATUS);
                  switch (err) {
                  case 2:  /* CDS_TRAY_OPEN */  
                  default:
                        singletag_to_clients(CHANGEVALUE, TAG_EJECTCD, 2); /* close */
                        t1 = cdrom_gettime ();
                        ioctl (fd, CDROMCLOSETRAY);
                        t2 = cdrom_gettime ();
#if defined(DEBUG) && CDROMPERF
                        printf ("close Tray: %4d (%4u-%4u)\n", t2-t1, t2, t1);
#endif
                        rc = 2;
                        if (t2-t1 > 100)
                              break;
                  case 1:  /* CDS_NO_DISK */
                  case 4:  /* CDS_DISK_OK */
                        singletag_to_clients(CHANGEVALUE, TAG_EJECTCD, 1); /* eject */
#if defined(DEBUG) && CDROMPERF
                        t1 = cdrom_gettime ();
#endif
                        ioctl (fd, CDROMEJECT);
#if defined(DEBUG) && CDROMPERF
                        t2 = cdrom_gettime ();
                        printf ("open Tray : %4d (%4u-%4u)\n", t2-t1, t2, t1);
#endif
                        rc = 1;
                  }
                  close (fd);
            } else
                  print_msg (PBB_WARN, _("Can't open %s. Eject CDROM won't work.\n"), base->cdromdev);
      }
      return rc;
}

unsigned int
cdrom_gettime ()
{
      struct timeval tv;

      gettimeofday(&tv, NULL);
      return (tv.tv_sec * 1000 + tv.tv_usec / 1000 );
}

#if defined(DEBUG) && CDROMPERF
void
cdrom_testperformance ()
{
      struct moddata_cdrom *base = &modbase_cdrom;
      int fd;
      unsigned int t1, t2;

      printf ("\nCDROM Performancedata: \n");
      if ((fd = open(base->cdromdev, O_RDONLY | O_NONBLOCK | O_NDELAY)) >= 0) {

            printf ("Close already closed Tray: ");
            t1 = cdrom_gettime();
            ioctl (fd, CDROMCLOSETRAY);
            t2 = cdrom_gettime();
            printf ("%4d (%4u-%4u)\n", t2-t1, t2, t1);
            sleep(1);
            
            printf ("Open closed Tray         : ");
            t1 = cdrom_gettime();
            ioctl (fd, CDROMEJECT);
            t2 = cdrom_gettime();
            printf ("%4d (%4u-%4u)\n", t2-t1, t2, t1);
            sleep(1);
            
            printf ("Open already open Tray   : ");
            t1 = cdrom_gettime();
            ioctl (fd, CDROMEJECT);
            t2 = cdrom_gettime();
            printf ("%4d (%4u-%4u)\n", t2-t1, t2, t1);
            sleep(1);
            
            printf ("Close open Tray          : ");
            t1 = cdrom_gettime();
            ioctl (fd, CDROMCLOSETRAY);
            t2 = cdrom_gettime();
            printf ("%4d (%4u-%4u)\n", t2-t1, t2, t1);
            sleep(1);
            
            printf ("Close already closed Tray: ");
            t1 = cdrom_gettime();
            ioctl (fd, CDROMCLOSETRAY);
            t2 = cdrom_gettime();
            printf ("%4d (%4u-%4u)\n\n", t2-t1, t2, t1);
            
            close (fd);
      } else
            printf ("CDROM open failed!\n\n");
}
#endif


Generated by  Doxygen 1.6.0   Back to index