1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2012 Richard Hughes <richard@hughsie.com>
4 *
5 * Licensed under the GNU General Public License Version 2
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 #include <config.h>
23 #include <gio/gio.h>
24 #include <cd-plugin.h>
25 #include <gudev/gudev.h>
26 #include <cd-device.h>
27
28 struct CdPluginPrivate {
29 GUdevClient *udev_client;
30 GHashTable *devices;
31 };
32
33 /**
34 * cd_plugin_get_description:
35 */
36 const gchar *
cd_plugin_get_description(void)37 cd_plugin_get_description (void)
38 {
39 return "Add and remove scanner devices using the SANE udev database";
40 }
41
42 /**
43 * cd_plugin_config_enabled:
44 */
45 gboolean
cd_plugin_config_enabled(void)46 cd_plugin_config_enabled (void)
47 {
48 #ifdef HAVE_SANE
49 return FALSE;
50 #else
51 return TRUE;
52 #endif
53 }
54
55 /**
56 * cd_plugin_get_scanner_id_for_udev_device:
57 **/
58 static gchar *
cd_plugin_get_scanner_id_for_udev_device(GUdevDevice * udev_device)59 cd_plugin_get_scanner_id_for_udev_device (GUdevDevice *udev_device)
60 {
61 GString *string;
62 const gchar *tmp;
63
64 /* get id */
65 string = g_string_new ("sysfs");
66 tmp = g_udev_device_get_property (udev_device, "ID_VENDOR");
67 if (tmp != NULL)
68 g_string_append_printf (string, "-%s", tmp);
69 tmp = g_udev_device_get_property (udev_device, "ID_MODEL");
70 if (tmp != NULL)
71 g_string_append_printf (string, "-%s", tmp);
72
73 /* fallback */
74 if (string->len == 5) {
75 tmp = g_udev_device_get_device_file (udev_device);
76 g_string_append_printf (string, "-%s", tmp);
77 }
78
79 return g_string_free (string, FALSE);
80 }
81
82 /**
83 * cd_plugin_add:
84 **/
85 static void
cd_plugin_add(CdPlugin * plugin,GUdevDevice * udev_device)86 cd_plugin_add (CdPlugin *plugin, GUdevDevice *udev_device)
87 {
88 const gchar *devclass;
89 const gchar *seat;
90 g_autofree gchar *id = NULL;
91 g_autofree gchar *model = NULL;
92 g_autofree gchar *vendor = NULL;
93 g_autoptr(CdDevice) device = NULL;
94
95 /* is a scanner? */
96 if (!g_udev_device_has_property (udev_device, "libsane_matched"))
97 return;
98
99 /* skip hubs */
100 devclass = g_udev_device_get_sysfs_attr (udev_device, "bDeviceClass");
101 if (devclass == NULL || g_strcmp0 (devclass, "09") == 0)
102 return;
103
104 /* replace underscores with spaces */
105 model = g_strdup (g_udev_device_get_property (udev_device,
106 "ID_MODEL"));
107 if (model != NULL) {
108 g_strdelimit (model, "_\r\n", ' ');
109 g_strchomp (model);
110 }
111 vendor = g_strdup (g_udev_device_get_property (udev_device,
112 "ID_VENDOR"));
113 if (vendor != NULL) {
114 g_strdelimit (vendor, "_\r\n", ' ');
115 g_strchomp (vendor);
116 }
117
118 /* generate ID */
119 id = cd_plugin_get_scanner_id_for_udev_device (udev_device);
120
121 /* assume device belongs to "seat0" if not tagged */
122 seat = g_udev_device_get_property (udev_device, "ID_SEAT");
123 if (seat == NULL)
124 seat = "seat0";
125
126 /* create new device */
127 device = cd_device_new ();
128 cd_device_set_id (device, id);
129 cd_device_set_property_internal (device,
130 "Kind",
131 "scanner",
132 FALSE,
133 NULL);
134 if (model != NULL) {
135 cd_device_set_property_internal (device,
136 "Model",
137 model,
138 FALSE,
139 NULL);
140 }
141 if (vendor != NULL) {
142 cd_device_set_property_internal (device,
143 "Vendor",
144 vendor,
145 FALSE,
146 NULL);
147 }
148 cd_device_set_property_internal (device,
149 "Colorspace",
150 "rgb",
151 FALSE,
152 NULL);
153 cd_device_set_property_internal (device,
154 "Serial",
155 g_udev_device_get_sysfs_path (udev_device),
156 FALSE,
157 NULL);
158 cd_device_set_property_internal (device,
159 "Seat",
160 seat,
161 FALSE,
162 NULL);
163
164 /* keep track so we can remove with the same device */
165 g_hash_table_insert (plugin->priv->devices,
166 g_strdup (g_udev_device_get_sysfs_path (udev_device)),
167 g_object_ref (device));
168
169 g_debug ("CdPlugin: emit add: %s", id);
170 cd_plugin_device_added (plugin, device);
171 }
172
173 /**
174 * cd_plugin_uevent_cb:
175 **/
176 static void
cd_plugin_uevent_cb(GUdevClient * udev_client,const gchar * action,GUdevDevice * udev_device,CdPlugin * plugin)177 cd_plugin_uevent_cb (GUdevClient *udev_client,
178 const gchar *action,
179 GUdevDevice *udev_device,
180 CdPlugin *plugin)
181 {
182 const gchar *sysfs_path;
183 CdDevice *device;
184
185 /* remove */
186 if (g_strcmp0 (action, "remove") == 0) {
187
188 /* is this a scanner device we added */
189 sysfs_path = g_udev_device_get_sysfs_path (udev_device);
190 device = g_hash_table_lookup (plugin->priv->devices, sysfs_path);
191 if (device == NULL)
192 return;
193
194 g_debug ("CdPlugin: remove %s", sysfs_path);
195 cd_plugin_device_removed (plugin, device);
196 g_hash_table_remove (plugin->priv->devices, sysfs_path);
197 return;
198 }
199
200 /* add */
201 if (g_strcmp0 (action, "add") == 0) {
202 cd_plugin_add (plugin, udev_device);
203 return;
204 }
205 }
206
207 /**
208 * cd_plugin_coldplug:
209 */
210 void
cd_plugin_coldplug(CdPlugin * plugin)211 cd_plugin_coldplug (CdPlugin *plugin)
212 {
213 GList *devices;
214 GList *l;
215 GUdevDevice *udev_device;
216
217 /* add all USB scanner devices */
218 devices = g_udev_client_query_by_subsystem (plugin->priv->udev_client,
219 "usb");
220 for (l = devices; l != NULL; l = l->next) {
221 udev_device = l->data;
222 cd_plugin_add (plugin, udev_device);
223 }
224 g_list_foreach (devices, (GFunc) g_object_unref, NULL);
225 g_list_free (devices);
226
227 /* watch udev for changes */
228 g_signal_connect (plugin->priv->udev_client, "uevent",
229 G_CALLBACK (cd_plugin_uevent_cb), plugin);
230 }
231
232 /**
233 * cd_plugin_initialize:
234 */
235 void
cd_plugin_initialize(CdPlugin * plugin)236 cd_plugin_initialize (CdPlugin *plugin)
237 {
238 const gchar *subsystems[] = { "usb", NULL };
239
240 /* create private */
241 plugin->priv = CD_PLUGIN_GET_PRIVATE (CdPluginPrivate);
242 plugin->priv->devices = g_hash_table_new_full (g_str_hash,
243 g_str_equal,
244 g_free,
245 (GDestroyNotify) g_object_unref);
246 plugin->priv->udev_client = g_udev_client_new (subsystems);
247 }
248
249 /**
250 * cd_plugin_destroy:
251 */
252 void
cd_plugin_destroy(CdPlugin * plugin)253 cd_plugin_destroy (CdPlugin *plugin)
254 {
255 g_object_unref (plugin->priv->udev_client);
256 g_hash_table_unref (plugin->priv->devices);
257 }
258