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