Skip to content

Latest commit

 

History

History
485 lines (410 loc) · 12.5 KB

usb_moded-udev.c

File metadata and controls

485 lines (410 loc) · 12.5 KB
 
May 16, 2011
May 16, 2011
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
@file usb_moded-udev.c
Copyright (C) 2011 Nokia Corporation. All rights reserved.
@author: Philippe De Swert <philippe.de-swert@nokia.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the Lesser GNU General Public License
version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the Lesser GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA
*/
Mar 22, 2011
Mar 22, 2011
24
25
#include <stdio.h>
#include <stdlib.h>
Apr 26, 2016
Apr 26, 2016
26
#include <stdbool.h>
Mar 22, 2011
Mar 22, 2011
27
28
29
30
31
32
33
#include <locale.h>
#include <unistd.h>
#include <poll.h>
#include <libudev.h>
Apr 6, 2011
Apr 6, 2011
34
35
36
#include <glib.h>
#include "usb_moded-log.h"
Apr 7, 2011
Apr 7, 2011
37
#include "usb_moded-config.h"
Apr 6, 2011
Apr 6, 2011
38
39
#include "usb_moded-hw-ab.h"
#include "usb_moded.h"
Apr 22, 2016
Apr 22, 2016
40
#include "usb_moded-modes.h"
Mar 22, 2011
Mar 22, 2011
41
42
/* global variables */
Apr 20, 2011
Apr 20, 2011
43
44
45
46
static struct udev *udev;
static struct udev_monitor *mon;
static GIOChannel *iochannel;
static guint watch_id;
Jul 7, 2016
Jul 7, 2016
47
static char *dev_name = 0;
Oct 29, 2013
Oct 29, 2013
48
static int cleanup = 0;
Apr 22, 2016
Apr 22, 2016
49
/* track cable and charger connects disconnects */
Apr 26, 2016
Apr 26, 2016
50
51
static int cable = 0, charger = 0;
static guint cable_connection_timeout_id = 0;
Mar 22, 2011
Mar 22, 2011
52
Mar 13, 2017
Mar 13, 2017
53
/** Bookkeeping data for power supply locating heuristics */
Dec 17, 2014
Dec 17, 2014
54
typedef struct power_device {
Mar 13, 2017
Mar 13, 2017
55
/** Device path used by udev */
Dec 17, 2014
Dec 17, 2014
56
const char *syspath;
Mar 13, 2017
Mar 13, 2017
57
/** Likelyhood of being power supply */
Dec 17, 2014
Dec 17, 2014
58
59
60
int score;
} power_device;
Apr 6, 2011
Apr 6, 2011
61
/* static function definitions */
Apr 20, 2011
Apr 20, 2011
62
63
static gboolean monitor_udev(GIOChannel *iochannel G_GNUC_UNUSED, GIOCondition cond,
gpointer data G_GNUC_UNUSED);
Apr 26, 2016
Apr 26, 2016
64
65
66
67
68
69
static void udev_parse(struct udev_device *dev, bool initial);
static void setup_cable_connection(void);
static void setup_charger_connection(void);
static void cancel_cable_connection_timeout(void);
static void schedule_cable_connection_timeout(void);
static gboolean cable_connection_timeout_cb(gpointer data);
Apr 8, 2011
Apr 8, 2011
70
Aug 17, 2011
Aug 17, 2011
71
72
static void notify_issue (gpointer data)
{
Oct 29, 2013
Oct 29, 2013
73
74
75
/* we do not want to restart when we try to clean up */
if(cleanup)
return;
Aug 17, 2011
Aug 17, 2011
76
77
78
79
80
81
log_debug("USB connection watch destroyed, restarting it\n!");
/* restart trigger */
hwal_cleanup();
hwal_init();
}
Dec 17, 2014
Dec 17, 2014
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
static int check_device_is_usb_power_supply(const char *syspath)
{
struct udev *udev;
struct udev_device *dev = 0;
const char *udev_name;
int score = 0;
udev = udev_new();
dev = udev_device_new_from_syspath(udev, syspath);
if(!dev)
return 0;
udev_name = udev_device_get_sysname(dev);
/* try to assign a weighed score */
/* check it is no battery */
if(strstr(udev_name, "battery") || strstr(udev_name, "BAT"))
return 0;
/* if it contains usb in the name it very likely is good */
if(strstr(udev_name, "usb"))
score = score + 10;
/* often charger is also mentioned in the name */
if(strstr(udev_name, "charger"))
score = score + 5;
/* present property is used to detect activity, however online is better */
if(udev_device_get_property_value(dev, "POWER_SUPPLY_PRESENT"))
score = score + 5;
if(udev_device_get_property_value(dev, "POWER_SUPPLY_ONLINE"))
score = score + 10;
/* type is used to detect if it is a cable or dedicated charger.
Bonus points if it is there. */
if(udev_device_get_property_value(dev, "POWER_SUPPLY_TYPE"))
score = score + 10;
/* clean up */
udev_device_unref(dev);
udev_unref(udev);
return(score);
}
Apr 6, 2011
Apr 6, 2011
124
gboolean hwal_init(void)
Mar 22, 2011
Mar 22, 2011
125
{
Nov 7, 2014
Nov 7, 2014
126
char *udev_path = NULL, *udev_subsystem = NULL;
Apr 20, 2011
Apr 20, 2011
127
struct udev_device *dev;
Dec 17, 2014
Dec 17, 2014
128
129
130
struct udev_enumerate *list;
struct udev_list_entry *list_entry, *first_entry;
struct power_device power_dev;
Apr 20, 2011
Apr 20, 2011
131
int ret = 0;
Oct 29, 2013
Oct 29, 2013
132
133
cleanup = 0;
Mar 22, 2011
Mar 22, 2011
134
135
136
137
138
139
/* Create the udev object */
udev = udev_new();
if (!udev)
{
log_err("Can't create udev\n");
Apr 20, 2011
Apr 20, 2011
140
return FALSE;
Apr 6, 2011
Apr 6, 2011
141
}
Apr 7, 2011
Apr 7, 2011
142
143
144
udev_path = find_udev_path();
if(udev_path)
Jan 13, 2012
Jan 13, 2012
145
{
Apr 7, 2011
Apr 7, 2011
146
dev = udev_device_new_from_syspath(udev, udev_path);
Jul 7, 2016
Jul 7, 2016
147
g_free(udev_path);
Jan 13, 2012
Jan 13, 2012
148
}
Apr 7, 2011
Apr 7, 2011
149
150
else
dev = udev_device_new_from_syspath(udev, "/sys/class/power_supply/usb");
Apr 6, 2011
Apr 6, 2011
151
152
if (!dev)
{
Dec 17, 2014
Dec 17, 2014
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
log_debug("Trying to guess $power_supply device.\n");
power_dev.score = 0;
power_dev.syspath = 0;
list = udev_enumerate_new(udev);
udev_enumerate_add_match_subsystem(list, "power_supply");
udev_enumerate_scan_devices(list);
first_entry = udev_enumerate_get_list_entry(list);
udev_list_entry_foreach(list_entry, first_entry)
{
udev_path = (char *)udev_list_entry_get_name(list_entry);
ret = check_device_is_usb_power_supply(udev_path);
if(ret)
{
if(ret > power_dev.score)
{
power_dev.score = ret;
power_dev.syspath = udev_path;
}
}
}
/* check if we found anything with some kind of score */
if(power_dev.score > 0)
{
dev = udev_device_new_from_syspath(udev, power_dev.syspath);
}
if(!dev)
{
log_err("Unable to find $power_supply device.");
/* communicate failure, mainloop will exit and call appropriate clean-up */
return FALSE;
}
Apr 6, 2011
Apr 6, 2011
186
}
Dec 17, 2014
Dec 17, 2014
187
188
189
dev_name = strdup(udev_device_get_sysname(dev));
log_debug("device name = %s\n", dev_name);
Apr 6, 2011
Apr 6, 2011
190
191
192
mon = udev_monitor_new_from_netlink (udev, "udev");
if (!mon)
{
Apr 21, 2011
Apr 21, 2011
193
log_err("Unable to monitor the netlink\n");
Apr 20, 2011
Apr 20, 2011
194
195
/* communicate failure, mainloop will exit and call appropriate clean-up */
return FALSE;
Mar 22, 2011
Mar 22, 2011
196
}
Jan 13, 2012
Jan 13, 2012
197
198
199
200
udev_subsystem = find_udev_subsystem();
if(udev_subsystem)
{
ret = udev_monitor_filter_add_match_subsystem_devtype(mon, udev_subsystem, NULL);
Jul 7, 2016
Jul 7, 2016
201
g_free(udev_subsystem);
Jan 13, 2012
Jan 13, 2012
202
203
204
}
else
ret = udev_monitor_filter_add_match_subsystem_devtype(mon, "power_supply", NULL);
Apr 21, 2011
Apr 21, 2011
205
if(ret != 0)
Apr 21, 2011
Apr 21, 2011
206
207
208
209
{
log_err("Udev match failed.\n");
return FALSE;
}
Apr 20, 2011
Apr 20, 2011
210
ret = udev_monitor_enable_receiving (mon);
Apr 21, 2011
Apr 21, 2011
211
if(ret != 0)
Apr 21, 2011
Apr 21, 2011
212
213
214
215
{
log_err("Failed to enable monitor recieving.\n");
return FALSE;
}
Apr 8, 2011
Apr 8, 2011
216
217
/* check if we are already connected */
Apr 26, 2016
Apr 26, 2016
218
udev_parse(dev, true);
Apr 6, 2011
Apr 6, 2011
219
Apr 20, 2011
Apr 20, 2011
220
iochannel = g_io_channel_unix_new(udev_monitor_get_fd(mon));
Aug 17, 2011
Aug 17, 2011
221
watch_id = g_io_add_watch_full(iochannel, 0, G_IO_IN, monitor_udev, NULL,notify_issue);
Apr 20, 2011
Apr 20, 2011
222
Apr 20, 2011
Apr 20, 2011
223
/* everything went well */
Oct 22, 2013
Oct 22, 2013
224
udev_device_unref(dev);
Apr 20, 2011
Apr 20, 2011
225
return TRUE;
Apr 6, 2011
Apr 6, 2011
226
}
Mar 22, 2011
Mar 22, 2011
227
Apr 20, 2011
Apr 20, 2011
228
229
static gboolean monitor_udev(GIOChannel *iochannel G_GNUC_UNUSED, GIOCondition cond,
gpointer data G_GNUC_UNUSED)
Apr 6, 2011
Apr 6, 2011
230
{
Apr 20, 2011
Apr 20, 2011
231
232
struct udev_device *dev;
Apr 26, 2016
Apr 26, 2016
233
234
235
236
237
gboolean continue_watching = TRUE;
/* No code paths are allowed to bypass the release_wakelock() call below */
acquire_wakelock(USB_MODED_WAKELOCK_PROCESS_INPUT);
Apr 20, 2011
Apr 20, 2011
238
if(cond & G_IO_IN)
Mar 22, 2011
Mar 22, 2011
239
{
Apr 20, 2011
Apr 20, 2011
240
/* This normally blocks but G_IO_IN indicates that we can read */
Apr 6, 2011
Apr 6, 2011
241
dev = udev_monitor_receive_device (mon);
Apr 26, 2016
Apr 26, 2016
242
243
244
245
246
247
if (!dev)
{
/* if we get something else something bad happened stop watching to avoid busylooping */
continue_watching = FALSE;
}
else
Apr 6, 2011
Apr 6, 2011
248
{
Apr 21, 2011
Apr 21, 2011
249
/* check if it is the actual device we want to check */
Apr 26, 2016
Apr 26, 2016
250
if(!strcmp(dev_name, udev_device_get_sysname(dev)))
Oct 22, 2013
Oct 22, 2013
251
{
Apr 26, 2016
Apr 26, 2016
252
253
254
255
if(!strcmp(udev_device_get_action(dev), "change"))
{
udev_parse(dev, false);
}
Mar 22, 2011
Mar 22, 2011
256
}
Apr 26, 2016
Apr 26, 2016
257
Apr 21, 2011
Apr 21, 2011
258
udev_device_unref(dev);
Mar 22, 2011
Mar 22, 2011
259
}
Apr 6, 2011
Apr 6, 2011
260
}
Apr 26, 2016
Apr 26, 2016
261
262
263
264
265
266
267
268
269
if(cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
{
/* Unhandled errors turn io watch to virtual busyloop too */
continue_watching = FALSE;
}
release_wakelock(USB_MODED_WAKELOCK_PROCESS_INPUT);
Jul 7, 2016
Jul 7, 2016
270
if (!continue_watching && watch_id )
Apr 26, 2016
Apr 26, 2016
271
{
Jul 7, 2016
Jul 7, 2016
272
watch_id = 0;
Apr 26, 2016
Apr 26, 2016
273
274
275
276
log_crit("udev io watch disabled");
}
return continue_watching;
Mar 22, 2011
Mar 22, 2011
277
278
}
Apr 6, 2011
Apr 6, 2011
279
void hwal_cleanup(void)
Mar 22, 2011
Mar 22, 2011
280
{
Oct 29, 2013
Oct 29, 2013
281
282
283
284
cleanup = 1;
log_debug("HWhal cleanup\n");
Dec 12, 2012
Dec 12, 2012
285
286
287
288
289
290
291
292
293
294
if(watch_id != 0)
{
g_source_remove(watch_id);
watch_id = 0;
}
if(iochannel != NULL)
{
g_io_channel_unref(iochannel);
iochannel = NULL;
}
Apr 26, 2016
Apr 26, 2016
295
cancel_cable_connection_timeout();
Jul 7, 2016
Jul 7, 2016
296
free(dev_name);
Apr 6, 2011
Apr 6, 2011
297
udev_monitor_unref(mon);
Mar 22, 2011
Mar 22, 2011
298
299
300
udev_unref(udev);
}
Apr 26, 2016
Apr 26, 2016
301
static void setup_cable_connection(void)
Apr 22, 2016
Apr 22, 2016
302
{
Apr 26, 2016
Apr 26, 2016
303
304
305
306
307
308
309
310
311
312
313
314
315
316
cancel_cable_connection_timeout();
log_debug("UDEV:USB pc cable connected\n");
cable = 1;
charger = 0;
set_usb_connected(TRUE);
}
static void setup_charger_connection(void)
{
cancel_cable_connection_timeout();
log_debug("UDEV:USB dedicated charger connected\n");
Oct 18, 2016
Oct 18, 2016
317
318
319
320
321
322
323
324
325
326
327
328
329
330
if (cable) {
/* The connection was initially reported incorrectly
* as pc cable, then later on declared as charger.
*
* Clear "connected" boolean flag so that the
* set_charger_connected() call below acts as if charger
* were detected already on connect: mode gets declared
* as dedicated charger (and the mode selection dialog
* shown by ui gets closed).
*/
set_usb_connection_state(FALSE);
}
Apr 22, 2016
Apr 22, 2016
331
charger = 1;
Apr 26, 2016
Apr 26, 2016
332
333
334
335
336
337
338
339
340
341
342
cable = 0;
set_charger_connected(TRUE);
}
static gboolean cable_connection_timeout_cb(gpointer data)
{
log_debug("connect delay: timeout");
cable_connection_timeout_id = 0;
setup_cable_connection();
Apr 22, 2016
Apr 22, 2016
343
344
345
return FALSE;
}
Apr 26, 2016
Apr 26, 2016
346
static void cancel_cable_connection_timeout(void)
Apr 12, 2011
Apr 12, 2011
347
{
Apr 26, 2016
Apr 26, 2016
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
if (cable_connection_timeout_id) {
log_debug("connect delay: cancel");
g_source_remove(cable_connection_timeout_id);
cable_connection_timeout_id = 0;
}
}
static void schedule_cable_connection_timeout(void)
{
/* Ignore If already connected */
if (get_usb_connection_state())
return;
if (!cable_connection_timeout_id && cable_connection_delay > 0) {
/* Dedicated charger might be initially misdetected as
* pc cable. Delay a bit befor accepting the state. */
log_debug("connect delay: started (%d ms)",
cable_connection_delay);
cable_connection_timeout_id =
g_timeout_add(cable_connection_delay,
cable_connection_timeout_cb,
NULL);
}
else {
/* If more udev events indicating cable connection
* are received while waiting, accept immediately. */
setup_cable_connection();
}
}
static void udev_parse(struct udev_device *dev, bool initial)
{
Dec 1, 2017
Dec 1, 2017
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
/* udev properties we are interested in */
const char *power_supply_present = 0;
const char *power_supply_online = 0;
const char *power_supply_type = 0;
/* Assume there is no usb connection until proven otherwise */
bool connected = false;
/* Unless debug logging has been request via command line,
* suppress warnings about potential property issues and/or
* fallback strategies applied (to avoid spamming due to the
* code below seeing the same property values over and over
* again also in stable states).
*/
bool warnings = log_p(LOG_DEBUG);
Apr 22, 2016
Apr 22, 2016
397
398
399
400
401
/*
* Check for present first as some drivers use online for when charging
* is enabled
*/
Dec 1, 2017
Dec 1, 2017
402
403
404
405
power_supply_present = udev_device_get_property_value(dev, "POWER_SUPPLY_PRESENT");
if (!power_supply_present) {
power_supply_present =
power_supply_online = udev_device_get_property_value(dev, "POWER_SUPPLY_ONLINE");
Apr 22, 2016
Apr 22, 2016
406
}
Mar 22, 2011
Mar 22, 2011
407
Dec 1, 2017
Dec 1, 2017
408
if (power_supply_present && !strcmp(power_supply_present, "1"))
Dec 1, 2017
Dec 1, 2017
409
410
411
412
413
414
415
416
417
418
419
connected = true;
/* Transition period = Connection status derived from udev
* events disagrees with usb-moded side bookkeeping. */
if (connected != get_usb_connection_state()) {
/* Enable udev property diagnostic logging */
warnings = true;
/* Block suspend briefly */
delay_suspend();
}
Apr 22, 2016
Apr 22, 2016
420
/* disconnect */
Dec 1, 2017
Dec 1, 2017
421
if (!connected) {
Dec 1, 2017
Dec 1, 2017
422
423
424
if (warnings && !power_supply_present)
log_err("No usable power supply indicator\n");
Apr 26, 2016
Apr 26, 2016
425
426
427
log_debug("DISCONNECTED");
cancel_cable_connection_timeout();
Apr 22, 2016
Apr 22, 2016
428
429
430
if (charger) {
log_debug("UDEV:USB dedicated charger disconnected\n");
Apr 25, 2016
Apr 25, 2016
431
set_charger_connected(FALSE);
Apr 22, 2016
Apr 22, 2016
432
433
434
435
436
437
438
439
440
441
}
if (cable) {
log_debug("UDEV:USB cable disconnected\n");
set_usb_connected(FALSE);
}
cable = 0;
charger = 0;
}
Apr 26, 2016
Apr 26, 2016
442
else {
Dec 1, 2017
Dec 1, 2017
443
444
if (warnings && power_supply_online)
log_warning("Using online property\n");
Apr 26, 2016
Apr 26, 2016
445
Dec 1, 2017
Dec 1, 2017
446
power_supply_type = udev_device_get_property_value(dev, "POWER_SUPPLY_TYPE");
Apr 26, 2016
Apr 26, 2016
447
448
449
450
451
/*
* Power supply type might not exist also :(
* Send connected event but this will not be able
* to discriminate between charger/cable.
*/
Dec 1, 2017
Dec 1, 2017
452
453
454
455
if (!power_supply_type) {
if( warnings )
log_warning("Fallback since cable detection might not be accurate. "
"Will connect on any voltage on charger.\n");
Apr 26, 2016
Apr 26, 2016
456
457
458
schedule_cable_connection_timeout();
goto cleanup;
}
Apr 22, 2016
Apr 22, 2016
459
Dec 1, 2017
Dec 1, 2017
460
log_debug("CONNECTED - POWER_SUPPLY_TYPE = %s", power_supply_type);
Apr 22, 2016
Apr 22, 2016
461
Dec 1, 2017
Dec 1, 2017
462
463
if (!strcmp(power_supply_type, "USB") ||
!strcmp(power_supply_type, "USB_CDP")) {
Apr 26, 2016
Apr 26, 2016
464
465
466
467
if( initial )
setup_cable_connection();
else
schedule_cable_connection_timeout();
Apr 22, 2016
Apr 22, 2016
468
}
Dec 1, 2017
Dec 1, 2017
469
470
471
else if (!strcmp(power_supply_type, "USB_DCP") ||
!strcmp(power_supply_type, "USB_HVDCP") ||
!strcmp(power_supply_type, "USB_HVDCP_3")) {
Apr 26, 2016
Apr 26, 2016
472
setup_charger_connection();
Apr 22, 2016
Apr 22, 2016
473
}
Dec 1, 2017
Dec 1, 2017
474
else if( !strcmp(power_supply_type, "Unknown")) {
Apr 26, 2016
Apr 26, 2016
475
476
477
// nop
}
else {
Dec 1, 2017
Dec 1, 2017
478
479
if (warnings)
log_warning("unhandled power supply type: %s", power_supply_type);
Apr 22, 2016
Apr 22, 2016
480
481
}
}
Apr 26, 2016
Apr 26, 2016
482
483
484
cleanup:
return;
Apr 12, 2011
Apr 12, 2011
485
}