1 /*
2  * Copyright (C) 2018 Red Hat
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * 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
17  * 02111-1307, USA.
18  *
19  */
20 
21 #include "config.h"
22 
23 #include "backends/native/meta-udev.h"
24 
25 #include "backends/native/meta-backend-native.h"
26 #include "backends/native/meta-launcher.h"
27 
28 #define DRM_CARD_UDEV_DEVICE_TYPE "drm_minor"
29 
30 enum
31 {
32   HOTPLUG,
33   DEVICE_ADDED,
34   DEVICE_REMOVED,
35 
36   N_SIGNALS
37 };
38 
39 static guint signals[N_SIGNALS];
40 
41 struct _MetaUdev
42 {
43   GObject parent;
44 
45   MetaBackendNative *backend_native;
46 
47   GUdevClient *gudev_client;
48 
49   gulong uevent_handler_id;
50 };
51 
G_DEFINE_TYPE(MetaUdev,meta_udev,G_TYPE_OBJECT)52 G_DEFINE_TYPE (MetaUdev, meta_udev, G_TYPE_OBJECT)
53 
54 gboolean
55 meta_is_udev_device_platform_device (GUdevDevice *device)
56 {
57   g_autoptr (GUdevDevice) platform_device = NULL;
58 
59   platform_device = g_udev_device_get_parent_with_subsystem (device,
60                                                              "platform",
61                                                              NULL);
62   return !!platform_device;
63 }
64 
65 gboolean
meta_is_udev_device_boot_vga(GUdevDevice * device)66 meta_is_udev_device_boot_vga (GUdevDevice *device)
67 {
68   g_autoptr (GUdevDevice) pci_device = NULL;
69 
70   pci_device = g_udev_device_get_parent_with_subsystem (device, "pci", NULL);
71   if (!pci_device)
72     return FALSE;
73 
74   return g_udev_device_get_sysfs_attr_as_int (pci_device, "boot_vga") == 1;
75 }
76 
77 static gboolean
meta_has_udev_device_tag(GUdevDevice * device,const char * tag)78 meta_has_udev_device_tag (GUdevDevice *device,
79                           const char  *tag)
80 {
81   const char * const * tags;
82   g_autoptr (GUdevDevice) platform_device = NULL;
83 
84   tags = g_udev_device_get_tags (device);
85   if (tags && g_strv_contains (tags, tag))
86     return TRUE;
87 
88   platform_device = g_udev_device_get_parent_with_subsystem (device,
89                                                              "platform",
90                                                              NULL);
91 
92   if (platform_device)
93     return meta_has_udev_device_tag (platform_device, tag);
94   else
95     return FALSE;
96 }
97 
98 gboolean
meta_is_udev_device_disable_modifiers(GUdevDevice * device)99 meta_is_udev_device_disable_modifiers (GUdevDevice *device)
100 {
101   return meta_has_udev_device_tag (device,
102                                    "mutter-device-disable-kms-modifiers");
103 }
104 
105 gboolean
meta_is_udev_device_ignore(GUdevDevice * device)106 meta_is_udev_device_ignore (GUdevDevice *device)
107 {
108   return meta_has_udev_device_tag (device, "mutter-device-ignore");
109 }
110 
111 gboolean
meta_is_udev_device_preferred_primary(GUdevDevice * device)112 meta_is_udev_device_preferred_primary (GUdevDevice *device)
113 {
114   const char * const * tags;
115 
116   tags = g_udev_device_get_tags (device);
117   if (!tags)
118     return FALSE;
119 
120   return g_strv_contains (tags, "mutter-device-preferred-primary");
121 }
122 
123 gboolean
meta_udev_is_drm_device(MetaUdev * udev,GUdevDevice * device)124 meta_udev_is_drm_device (MetaUdev    *udev,
125                          GUdevDevice *device)
126 {
127   const char *seat_id;
128   const char *device_type;
129   const char *device_seat;
130 
131   /* Filter out devices that are not character device, like card0-VGA-1. */
132   if (g_udev_device_get_device_type (device) != G_UDEV_DEVICE_TYPE_CHAR)
133     return FALSE;
134 
135   device_type = g_udev_device_get_property (device, "DEVTYPE");
136   if (g_strcmp0 (device_type, DRM_CARD_UDEV_DEVICE_TYPE) != 0)
137     return FALSE;
138 
139   device_seat = g_udev_device_get_property (device, "ID_SEAT");
140   if (!device_seat)
141     {
142       /* When ID_SEAT is not set, it means seat0. */
143       device_seat = "seat0";
144     }
145 
146   /* Skip devices that do not belong to our seat. */
147   seat_id = meta_backend_native_get_seat_id (udev->backend_native);
148   if (g_strcmp0 (seat_id, device_seat))
149     return FALSE;
150 
151   return TRUE;
152 }
153 
154 GList *
meta_udev_list_drm_devices(MetaUdev * udev,GError ** error)155 meta_udev_list_drm_devices (MetaUdev  *udev,
156                             GError   **error)
157 {
158   g_autoptr (GUdevEnumerator) enumerator = NULL;
159   GList *devices;
160   GList *l;
161 
162   enumerator = g_udev_enumerator_new (udev->gudev_client);
163 
164   g_udev_enumerator_add_match_name (enumerator, "card*");
165   g_udev_enumerator_add_match_tag (enumerator, "seat");
166 
167   /*
168    * We need to explicitly match the subsystem for now.
169    * https://bugzilla.gnome.org/show_bug.cgi?id=773224
170    */
171   g_udev_enumerator_add_match_subsystem (enumerator, "drm");
172 
173   devices = g_udev_enumerator_execute (enumerator);
174   if (!devices)
175     return NULL;
176 
177   for (l = devices; l;)
178     {
179       GUdevDevice *device = l->data;
180       GList *l_next = l->next;
181 
182       if (!meta_udev_is_drm_device (udev, device))
183         {
184           g_object_unref (device);
185           devices = g_list_delete_link (devices, l);
186         }
187 
188       l = l_next;
189     }
190 
191   return devices;
192 }
193 
194 static void
on_uevent(GUdevClient * client,const char * action,GUdevDevice * device,gpointer user_data)195 on_uevent (GUdevClient *client,
196            const char  *action,
197            GUdevDevice *device,
198            gpointer     user_data)
199 {
200   MetaUdev *udev = META_UDEV (user_data);
201 
202   if (!g_udev_device_get_device_file (device))
203     return;
204 
205   if (g_str_equal (action, "add"))
206     g_signal_emit (udev, signals[DEVICE_ADDED], 0, device);
207   else if (g_str_equal (action, "remove"))
208     g_signal_emit (udev, signals[DEVICE_REMOVED], 0, device);
209 
210   if (g_udev_device_get_property_as_boolean (device, "HOTPLUG"))
211     g_signal_emit (udev, signals[HOTPLUG], 0, device);
212 }
213 
214 MetaUdev *
meta_udev_new(MetaBackendNative * backend_native)215 meta_udev_new (MetaBackendNative *backend_native)
216 {
217   MetaUdev *udev;
218 
219   udev = g_object_new (META_TYPE_UDEV, NULL);
220   udev->backend_native = backend_native;
221 
222   return udev;
223 }
224 
225 void
meta_udev_pause(MetaUdev * udev)226 meta_udev_pause (MetaUdev *udev)
227 {
228   g_signal_handler_block (udev->gudev_client, udev->uevent_handler_id);
229 }
230 
231 void
meta_udev_resume(MetaUdev * udev)232 meta_udev_resume (MetaUdev *udev)
233 {
234   g_signal_handler_unblock (udev->gudev_client, udev->uevent_handler_id);
235 }
236 
237 static void
meta_udev_finalize(GObject * object)238 meta_udev_finalize (GObject *object)
239 {
240   MetaUdev *udev = META_UDEV (object);
241 
242   g_clear_signal_handler (&udev->uevent_handler_id, udev->gudev_client);
243   g_clear_object (&udev->gudev_client);
244 
245   G_OBJECT_CLASS (meta_udev_parent_class)->finalize (object);
246 }
247 
248 static void
meta_udev_init(MetaUdev * udev)249 meta_udev_init (MetaUdev *udev)
250 {
251   const char *subsystems[] = { "drm", NULL };
252 
253   udev->gudev_client = g_udev_client_new (subsystems);
254   udev->uevent_handler_id = g_signal_connect (udev->gudev_client,
255                                               "uevent",
256                                               G_CALLBACK (on_uevent), udev);
257 }
258 
259 static void
meta_udev_class_init(MetaUdevClass * klass)260 meta_udev_class_init (MetaUdevClass *klass)
261 {
262   GObjectClass *object_class = G_OBJECT_CLASS (klass);
263 
264   object_class->finalize = meta_udev_finalize;
265 
266   signals[HOTPLUG] =
267     g_signal_new ("hotplug",
268                   G_TYPE_FROM_CLASS (object_class),
269                   G_SIGNAL_RUN_LAST,
270                   0, NULL, NULL, NULL,
271                   G_TYPE_NONE, 1,
272                   G_UDEV_TYPE_DEVICE);
273   signals[DEVICE_ADDED] =
274     g_signal_new ("device-added",
275                   G_TYPE_FROM_CLASS (object_class),
276                   G_SIGNAL_RUN_LAST,
277                   0, NULL, NULL, NULL,
278                   G_TYPE_NONE, 1,
279                   G_UDEV_TYPE_DEVICE);
280   signals[DEVICE_REMOVED] =
281     g_signal_new ("device-removed",
282                   G_TYPE_FROM_CLASS (object_class),
283                   G_SIGNAL_RUN_LAST,
284                   0, NULL, NULL, NULL,
285                   G_TYPE_NONE, 1,
286                   G_UDEV_TYPE_DEVICE);
287 }
288