udev.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:12k
源码类别:

midi

开发平台:

Unix_Linux

  1. /**
  2.  * @file udev.c
  3.  * @brief List of multimedia devices for VLC media player
  4.  */
  5. /*****************************************************************************
  6.  * Copyright © 2009 Rémi Denis-Courmont
  7.  *
  8.  * This library is free software; you can redistribute it and/or
  9.  * modify it under the terms of the GNU Lesser General Public License
  10.  * as published by the Free Software Foundation; either version 2.1
  11.  * of the License, or (at your option) any later version.
  12.  *
  13.  * This library is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  * GNU General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU Lesser General Public
  19.  * License along with this library; if not, write to the Free Software
  20.  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  21.  ****************************************************************************/
  22. #ifdef HAVE_CONFIG_H
  23. # include <config.h>
  24. #endif
  25. #include <libudev.h>
  26. #include <vlc_common.h>
  27. #include <vlc_services_discovery.h>
  28. #include <vlc_plugin.h>
  29. #include <search.h>
  30. #include <poll.h>
  31. #include <errno.h>
  32. static int OpenV4L (vlc_object_t *);
  33. static int OpenDisc (vlc_object_t *);
  34. static void Close (vlc_object_t *);
  35. /*
  36.  * Module descriptor
  37.  */
  38. vlc_module_begin ()
  39.     set_shortname (N_("Devices"))
  40.     set_description (N_("Capture devices"))
  41.     set_category (CAT_PLAYLIST)
  42.     set_subcategory (SUBCAT_PLAYLIST_SD)
  43.     set_capability ("services_discovery", 0)
  44.     set_callbacks (OpenV4L, Close)
  45.     add_shortcut ("v4l")
  46.     add_submodule ()
  47.     set_shortname (N_("Discs"))
  48.     set_description (N_("Discs"))
  49.     set_category (CAT_PLAYLIST)
  50.     set_subcategory (SUBCAT_PLAYLIST_SD)
  51.     set_capability ("services_discovery", 0)
  52.     set_callbacks (OpenDisc, Close)
  53.     add_shortcut ("disc")
  54. vlc_module_end ()
  55. struct device
  56. {
  57.     dev_t devnum; /* must be first */
  58.     input_item_t *item;
  59.     services_discovery_t *sd;
  60. };
  61. struct subsys
  62. {
  63.     const char *name;
  64.     char * (*get_mrl) (struct udev_device *dev);
  65.     char * (*get_name) (struct udev_device *dev);
  66.     char * (*get_cat) (struct udev_device *dev);
  67.     int item_type;
  68. };
  69. struct services_discovery_sys_t
  70. {
  71.     const struct subsys *subsys;
  72.     struct udev_monitor *monitor;
  73.     vlc_thread_t         thread;
  74.     void                *root;
  75. };
  76. /**
  77.  * Compares two devices (to support binary search).
  78.  */
  79. static int cmpdev (const void *a, const void *b)
  80. {
  81.     const dev_t *da = a, *db = b;
  82.     dev_t delta = *da - *db;
  83.     if (sizeof (delta) > sizeof (int))
  84.         return delta ? ((delta > 0) ? 1 : -1) : 0;
  85.     return (signed)delta;
  86. }
  87. static void DestroyDevice (void *data)
  88. {
  89.     struct device *d = data;
  90.     if (d->sd)
  91.         services_discovery_RemoveItem (d->sd, d->item);
  92.     vlc_gc_decref (d->item);
  93.     free (d);
  94. }
  95. static char *decode_property (struct udev_device *, const char *);
  96. /**
  97.  * Adds a udev device.
  98.  */
  99. static int AddDevice (services_discovery_t *sd, struct udev_device *dev)
  100. {
  101.     services_discovery_sys_t *p_sys = sd->p_sys;
  102.     char *mrl = p_sys->subsys->get_mrl (dev);
  103.     if (mrl == NULL)
  104.         return 0; /* don't know if it was an error... */
  105.     char *name = p_sys->subsys->get_name (dev);
  106.     input_item_t *item = input_item_NewWithType (VLC_OBJECT (sd), mrl,
  107.                                                  name ? name : mrl,
  108.                                                  0, NULL, 0, -1,
  109.                                                  p_sys->subsys->item_type);
  110.     msg_Dbg (sd, "adding %s (%s)", mrl, name);
  111.     free (name);
  112.     free (mrl);
  113.     if (item == NULL)
  114.         return -1;
  115.     struct device *d = malloc (sizeof (*d));
  116.     if (d == NULL)
  117.     {
  118.         vlc_gc_decref (item);
  119.         return -1;
  120.     }
  121.     d->devnum = udev_device_get_devnum (dev);
  122.     d->item = item;
  123.     d->sd = NULL;
  124.     struct device **dp = tsearch (d, &p_sys->root, cmpdev);
  125.     if (dp == NULL) /* Out-of-memory */
  126.     {
  127.         DestroyDevice (d);
  128.         return -1;
  129.     }
  130.     if (*dp != d) /* Overwrite existing device */
  131.     {
  132.         DestroyDevice (*dp);
  133.         *dp = d;
  134.     }
  135.     name = p_sys->subsys->get_cat (dev);
  136.     services_discovery_AddItem (sd, item, name ? name : "Generic");
  137.     d->sd = sd;
  138.     free (name);
  139.     return 0;
  140. }
  141. /**
  142.  * Removes a udev device (if present).
  143.  */
  144. static void RemoveDevice (services_discovery_t *sd, struct udev_device *dev)
  145. {
  146.     services_discovery_sys_t *p_sys = sd->p_sys;
  147.     dev_t num = udev_device_get_devnum (dev);
  148.     struct device **dp = tfind (&(dev_t){ num }, &p_sys->root, cmpdev);
  149.     if (dp == NULL)
  150.         return;
  151.     struct device *d = *dp;
  152.     tdelete (d, &p_sys->root, cmpdev);
  153.     DestroyDevice (d);
  154. }
  155. static void *Run (void *);
  156. /**
  157.  * Probes and initializes.
  158.  */
  159. static int Open (vlc_object_t *obj, const struct subsys *subsys)
  160. {
  161.     services_discovery_t *sd = (services_discovery_t *)obj;
  162.     services_discovery_sys_t *p_sys = malloc (sizeof (*p_sys));
  163.     if (p_sys == NULL)
  164.         return VLC_ENOMEM;
  165.     sd->p_sys = p_sys;
  166.     p_sys->subsys = subsys;
  167.     p_sys->root = NULL;
  168.     struct udev_monitor *mon = NULL;
  169.     struct udev *udev = udev_new ();
  170.     if (udev == NULL)
  171.         goto error;
  172.     mon = udev_monitor_new_from_netlink (udev, "udev");
  173.     if (mon == NULL
  174.      || udev_monitor_filter_add_match_subsystem_devtype (mon, subsys->name,
  175.                                                          NULL))
  176.         goto error;
  177.     p_sys->monitor = mon;
  178.     /* Enumerate existing devices */
  179.     struct udev_enumerate *devenum = udev_enumerate_new (udev);
  180.     if (devenum == NULL)
  181.         goto error;
  182.     if (udev_enumerate_add_match_subsystem (devenum, subsys->name))
  183.     {
  184.         udev_enumerate_unref (devenum);
  185.         goto error;
  186.     }
  187.     udev_monitor_enable_receiving (mon);
  188.     /* Note that we enumerate _after_ monitoring is enabled so that we do not
  189.      * loose device events occuring while we are enumerating. We could still
  190.      * loose events if the Netlink socket receive buffer overflows. */
  191.     udev_enumerate_scan_devices (devenum);
  192.     struct udev_list_entry *devlist = udev_enumerate_get_list_entry (devenum);
  193.     struct udev_list_entry *deventry;
  194.     udev_list_entry_foreach (deventry, devlist)
  195.     {
  196.         const char *path = udev_list_entry_get_name (deventry);
  197.         struct udev_device *dev = udev_device_new_from_syspath (udev, path);
  198.         AddDevice (sd, dev);
  199.         udev_device_unref (dev);
  200.     }
  201.     udev_enumerate_unref (devenum);
  202.     if (vlc_clone (&p_sys->thread, Run, sd, VLC_THREAD_PRIORITY_LOW))
  203.     {   /* Fallback without thread */
  204.         udev_monitor_unref (mon);
  205.         udev_unref (udev);
  206.         p_sys->monitor = NULL;
  207.     }
  208.     return VLC_SUCCESS;
  209. error:
  210.     if (mon != NULL)
  211.         udev_monitor_unref (mon);
  212.     if (udev != NULL)
  213.         udev_unref (udev);
  214.     free (p_sys);
  215.     return VLC_EGENERIC;
  216. }
  217. /**
  218.  * Releases resources
  219.  */
  220. static void Close (vlc_object_t *obj)
  221. {
  222.     services_discovery_t *sd = (services_discovery_t *)obj;
  223.     services_discovery_sys_t *p_sys = sd->p_sys;
  224.     if (p_sys->monitor != NULL)
  225.     {
  226.         struct udev *udev = udev_monitor_get_udev (p_sys->monitor);
  227.         vlc_cancel (p_sys->thread);
  228.         vlc_join (p_sys->thread, NULL);
  229.         udev_monitor_unref (p_sys->monitor);
  230.         udev_unref (udev);
  231.     }
  232.     tdestroy (p_sys->root, DestroyDevice);
  233.     free (p_sys);
  234. }
  235. static void *Run (void *data)
  236. {
  237.     services_discovery_t *sd = data;
  238.     services_discovery_sys_t *p_sys = sd->p_sys;
  239.     struct udev_monitor *mon = p_sys->monitor;
  240.     int fd = udev_monitor_get_fd (mon);
  241.     struct pollfd ufd = { .fd = fd, .events = POLLIN, };
  242.     for (;;)
  243.     {
  244.         while (poll (&ufd, 1, -1) == -1)
  245.             if (errno != EINTR)
  246.                 break;
  247.         int canc = vlc_savecancel ();
  248.         struct udev_device *dev = udev_monitor_receive_device (mon);
  249.         if (dev == NULL)
  250.             continue;
  251.         const char *action = udev_device_get_action (dev);
  252.         if (!strcmp (action, "add"))
  253.             AddDevice (sd, dev);
  254.         else if (!strcmp (action, "remove"))
  255.             RemoveDevice (sd, dev);
  256.         else if (!strcmp (action, "change"))
  257.         {
  258.             RemoveDevice (sd, dev);
  259.             AddDevice (sd, dev);
  260.         }
  261.         udev_device_unref (dev);
  262.         vlc_restorecancel (canc);
  263.     }
  264.     return NULL;
  265. }
  266. /**
  267.  * Converts an hexadecimal digit to an integer.
  268.  */
  269. static int hex (char c)
  270. {
  271.     if (c >= '0' && c <= '9')
  272.         return c - '0';
  273.     if (c >= 'A' && c <= 'F')
  274.         return c + 10 - 'A';
  275.     if (c >= 'a' && c <= 'f')
  276.         return c + 10 - 'a';
  277.     return -1;
  278. }
  279. /**
  280.  * Decodes a udev hexadecimal-encoded property.
  281.  */
  282. static char *decode (const char *enc)
  283. {
  284.     char *ret = enc ? strdup (enc) : NULL;
  285.     if (ret == NULL)
  286.         return NULL;
  287.     char *out = ret;
  288.     for (const char *in = ret; *in; out++)
  289.     {
  290.         int h1, h2;
  291.         if ((in[0] == '\') && (in[1] == 'x')
  292.          && ((h1 = hex (in[2])) != -1)
  293.          && ((h2 = hex (in[3])) != -1))
  294.         {
  295.             *out = (h1 << 4) | h2;
  296.             in += 4;
  297.         }
  298.         else
  299.         {
  300.             *out = *in;
  301.             in++;
  302.         }
  303.     }
  304.     *out = 0;
  305.     return ret;
  306. }
  307. static char *decode_property (struct udev_device *dev, const char *name)
  308. {
  309.     return decode (udev_device_get_property_value (dev, name));
  310. }
  311. /*** Video4Linux support ***/
  312. static bool is_v4l_legacy (struct udev_device *dev)
  313. {
  314.     const char *version;
  315.     version = udev_device_get_property_value (dev, "ID_V4L_VERSION");
  316.     return version && !strcmp (version, "1");
  317. }
  318. static char *v4l_get_mrl (struct udev_device *dev)
  319. {
  320.     /* Determine media location */
  321.     const char *scheme = "v4l2";
  322.     if (is_v4l_legacy (dev))
  323.         scheme = "v4l";
  324.     const char *node = udev_device_get_devnode (dev);
  325.     char *mrl;
  326.     if (asprintf (&mrl, "%s://%s", scheme, node) == -1)
  327.         mrl = NULL;
  328.     return mrl;
  329. }
  330. static char *v4l_get_name (struct udev_device *dev)
  331. {
  332.     const char *prd = udev_device_get_property_value (dev, "ID_V4L_PRODUCT");
  333.     return prd ? strdup (prd) : NULL;
  334. }
  335. static char *v4l_get_cat (struct udev_device *dev)
  336. {
  337.     return decode_property (dev, "ID_VENDOR_ENC");
  338. }
  339. int OpenV4L (vlc_object_t *obj)
  340. {
  341.     static const struct subsys subsys = {
  342.         "video4linux", v4l_get_mrl, v4l_get_name, v4l_get_cat, ITEM_TYPE_CARD,
  343.     };
  344.     return Open (obj, &subsys);
  345. }
  346. /*** Discs support ***/
  347. static char *disc_get_mrl (struct udev_device *dev)
  348. {
  349.     const char *val;
  350.     val = udev_device_get_property_value (dev, "ID_CDROM");
  351.     if (val == NULL)
  352.         return NULL; /* Ignore non-optical block devices */
  353.     val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_STATE");
  354.     if ((val == NULL) || !strcmp (val, "blank"))
  355.         return NULL; /* ignore empty drives and virgin recordable discs */
  356.     const char *scheme = "file";
  357.     val = udev_device_get_property_value (dev,
  358.                                           "ID_CDROM_MEDIA_TRACK_COUNT_AUDIO");
  359.     if (val && atoi (val))
  360.         scheme = "cdda"; /* Audio CD rather than file system */
  361. #if 0 /* we can use file:// for DVDs */
  362.     val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_DVD");
  363.     if (val && atoi (val))
  364.         scheme = "dvd";
  365. #endif
  366.     val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_BD");
  367.     if (val && atoi (val))
  368.         scheme = "bd";
  369. #ifdef LOL
  370.     val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_HDDVD");
  371.     if (val && atoi (val))
  372.         scheme = "hddvd";
  373. #endif
  374.     val = udev_device_get_devnode (dev);
  375.     char *mrl;
  376.     if (asprintf (&mrl, "%s://%s", scheme, val) == -1)
  377.         mrl = NULL;
  378.     return mrl;
  379. }
  380. static char *disc_get_name (struct udev_device *dev)
  381. {
  382.     return decode_property (dev, "ID_FS_LABEL_ENC");
  383. }
  384. static char *disc_get_cat (struct udev_device *dev)
  385. {
  386.     const char *val;
  387.     const char *cat = "Unknown";
  388.     val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_CD");
  389.     if (val && atoi (val))
  390.         cat = "CD";
  391.     val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_DVD");
  392.     if (val && atoi (val))
  393.         cat = "DVD";
  394.     val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_BD");
  395.     if (val && atoi (val))
  396.         cat = "Blue-ray disc";
  397.     val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_HDDVD");
  398.     if (val && atoi (val))
  399.         cat = "HD DVD";
  400.     return strdup (cat);
  401. }
  402. int OpenDisc (vlc_object_t *obj)
  403. {
  404.     static const struct subsys subsys = {
  405.         "block", disc_get_mrl, disc_get_name, disc_get_cat, ITEM_TYPE_DISC,
  406.     };
  407.     return Open (obj, &subsys);
  408. }