/
usb_moded-modesetting.c
592 lines (512 loc) · 16.1 KB
1
2
3
4
/**
@file usb_moded-modesetting.c
Copyright (C) 2010 Nokia Corporation. All rights reserved.
5
Copyright (C) 2013-2016 Jolla Ltd.
6
7
@author: Philippe De Swert <philippe.de-swert@nokia.com>
8
9
10
11
@author: Philippe De Swert <philippe.deswert@jollamobile.com>
@author: Bernd Wachter <bernd.wachter@jollamobile.com>
@author: Slava Monich <slava.monich@jolla.com>
@author: Simo Piiroinen <simo.piiroinen@jollamobile.com>
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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
*/
28
#define _GNU_SOURCE
29
30
31
32
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
33
#include <sys/stat.h>
34
#include <limits.h>
35
36
37
38
39
#include <glib.h>
#include "usb_moded.h"
#include "usb_moded-modules.h"
40
#include "usb_moded-modes.h"
41
42
43
44
45
46
#include "usb_moded-log.h"
#include "usb_moded-dbus.h"
#include "usb_moded-dbus-private.h"
#include "usb_moded-appsync.h"
#include "usb_moded-config.h"
#include "usb_moded-modesetting.h"
47
#include "usb_moded-network.h"
48
#include "usb_moded-android.h"
49
50
static void report_mass_storage_blocker(const char *mountpoint, int try);
51
static guint delayed_network = 0;
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
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
#if LOG_ENABLE_DEBUG
static char *strip(char *str)
{
unsigned char *src = (unsigned char *)str;
unsigned char *dst = (unsigned char *)str;
while( *src > 0 && *src <= 32 ) ++src;
for( ;; )
{
while( *src > 32 ) *dst++ = *src++;
while( *src > 0 && *src <= 32 ) ++src;
if( *src == 0 ) break;
*dst++ = ' ';
}
*dst = 0;
return str;
}
static char *read_from_file(const char *path, size_t maxsize)
{
int fd = -1;
ssize_t done = 0;
char *data = 0;
char *text = 0;
if((fd = open(path, O_RDONLY)) == -1)
{
/* Silently ignore things that could result
* from missing / read-only files */
if( errno != ENOENT && errno != EACCES )
log_warning("%s: open: %m", path);
goto cleanup;
}
if( !(data = malloc(maxsize + 1)) )
goto cleanup;
if((done = read(fd, data, maxsize)) == -1)
{
log_warning("%s: read: %m", path);
goto cleanup;
}
text = realloc(data, done + 1), data = 0;
text[done] = 0;
strip(text);
cleanup:
free(data);
if(fd != -1) close(fd);
return text;
}
#endif /* LOG_ENABLE_DEBUG */
108
109
110
111
int write_to_file(const char *path, const char *text)
{
int err = -1;
int fd = -1;
112
113
size_t todo = 0;
114
115
116
117
118
/* if either path or the text to be written are not there
we return an error */
if(!text || !path)
return err;
119
120
121
122
123
124
125
126
127
#if LOG_ENABLE_DEBUG
if(log_level >= LOG_DEBUG)
{
char *prev = read_from_file(path, 0x1000);
log_debug("WRITE '%s' : '%s' --> '%s'", path, prev ?: "???", text);
free(prev);
}
#endif
128
todo = strlen(text);
129
130
131
132
/* no O_CREAT -> writes only to already existing files */
if( (fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY))) == -1 )
{
133
log_warning("open(%s): %m", path);
134
135
136
137
138
139
140
141
goto cleanup;
}
while( todo > 0 )
{
ssize_t n = TEMP_FAILURE_RETRY(write(fd, text, todo));
if( n < 0 )
{
142
log_warning("write(%s): %m", path);
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
goto cleanup;
}
todo -= n;
text += n;
}
err = 0;
cleanup:
if( fd != -1 ) TEMP_FAILURE_RETRY(close(fd));
return err;
}
158
159
static gboolean network_retry(gpointer data)
{
160
delayed_network = 0;
161
162
163
164
usb_network_up(data);
return(FALSE);
}
165
static int set_mass_storage_mode(struct mode_list_elem *data)
166
167
{
gchar *command;
168
char command2[256], *real_path = NULL, *mountpath;
169
char *mount;
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
gchar **mounts;
int ret = 0, i = 0, mountpoints = 0, fua = 0, try = 0;
/* send unmount signal so applications can release their grasp on the fs, do this here so they have time to act */
usb_moded_send_signal(USB_PRE_UNMOUNT);
fua = find_sync();
mount = find_mounts();
if(mount)
{
mounts = g_strsplit(mount, ",", 0);
/* check amount of mountpoints */
for(i=0 ; mounts[i] != NULL; i++)
{
mountpoints++;
}
186
187
188
189
190
191
192
193
194
195
196
if(strcmp(data->mode_module, MODULE_NONE))
{
/* check if the file storage module has been loaded with sufficient luns in the parameter,
if not, unload and reload or load it. Since mountpoints start at 0 the amount of them is one more than their id */
sprintf(command2, "/sys/devices/platform/musb_hdrc/gadget/gadget-lun%d/file", (mountpoints - 1) );
if(access(command2, R_OK) == -1)
{
log_debug("%s does not exist, unloading and reloading mass_storage\n", command2);
usb_moded_unload_module(MODULE_MASS_STORAGE);
sprintf(command2, "modprobe %s luns=%d \n", MODULE_MASS_STORAGE, mountpoints);
log_debug("usb-load command = %s \n", command2);
197
ret = usb_moded_system(command2);
198
199
200
201
if(ret)
return(ret);
}
}
202
203
204
205
/* umount filesystems */
for(i=0 ; mounts[i] != NULL; i++)
{
/* check if filesystem is mounted or not, if ret = 1 it is already unmounted */
206
207
208
209
210
211
real_path = realpath(mounts[i], NULL);
if(real_path)
mountpath = real_path;
else
mountpath = mounts[i];
umount: command = g_strconcat("mount | grep ", mountpath, NULL);
212
ret = usb_moded_system(command);
213
214
215
g_free(command);
if(!ret)
{
216
/* no check for / needed as that will fail to umount anyway */
217
command = g_strconcat("umount ", mountpath, NULL);
218
log_debug("unmount command = %s\n", command);
219
ret = usb_moded_system(command);
220
221
222
g_free(command);
if(ret != 0)
{
223
if(try != 3)
224
225
226
227
{
try++;
sleep(1);
log_err("Umount failed. Retrying\n");
228
report_mass_storage_blocker(mount, 1);
229
230
231
232
233
goto umount;
}
else
{
log_err("Unmounting %s failed\n", mount);
234
report_mass_storage_blocker(mount, 2);
235
236
237
238
239
240
241
242
243
244
usb_moded_send_error_signal(UMOUNT_ERROR);
return(ret);
}
}
}
else
/* already unmounted. Set return value to 0 since there is no error */
ret = 0;
}
245
/* activate mounts after sleeping 1s to be sure enumeration happened and autoplay will work in windows*/
246
sleep(1);
247
for(i=0 ; mounts[i] != NULL; i++)
248
{
249
250
251
252
253
if(strcmp(data->mode_module, MODULE_NONE))
{
sprintf(command2, "echo %i > /sys/devices/platform/musb_hdrc/gadget/gadget-lun%d/nofua", fua, i);
log_debug("usb lun = %s active\n", command2);
254
usb_moded_system(command2);
255
256
257
258
259
260
261
262
263
264
265
266
267
sprintf(command2, "/sys/devices/platform/musb_hdrc/gadget/gadget-lun%d/file", i);
log_debug("usb lun = %s active\n", command2);
write_to_file(command2, mounts[i]);
}
else
{
write_to_file("/sys/class/android_usb/android0/enable", "0");
write_to_file("/sys/class/android_usb/android0/functions", "mass_storage");
//write_to_file("/sys/class/android_usb/f_mass_storage/lun/nofua", fua);
write_to_file("/sys/class/android_usb/f_mass_storage/lun/file", mount);
write_to_file("/sys/class/android_usb/android0/enable", "1");
}
268
269
}
g_strfreev(mounts);
270
g_free(mount);
271
272
if(real_path)
free(real_path);
273
274
275
276
277
278
279
280
281
282
}
/* only send data in use signal in case we actually succeed */
if(!ret)
usb_moded_send_signal(DATA_IN_USE);
return(ret);
}
283
static int unset_mass_storage_mode(struct mode_list_elem *data)
284
285
{
gchar *command;
286
char command2[256], *real_path = NULL, *mountpath;
287
char *mount;
288
289
290
291
292
293
294
295
296
297
gchar **mounts;
int ret = 1, i = 0;
mount = find_mounts();
if(mount)
{
mounts = g_strsplit(mount, ",", 0);
for(i=0 ; mounts[i] != NULL; i++)
{
/* check if it is still or already mounted, if so (ret==0) skip mounting */
298
299
300
301
302
303
real_path = realpath(mounts[i], NULL);
if(real_path)
mountpath = real_path;
else
mountpath = mounts[i];
command = g_strconcat("mount | grep ", mountpath, NULL);
304
ret = usb_moded_system(command);
305
306
307
g_free(command);
if(ret)
{
308
command = g_strconcat("mount ", mountpath, NULL);
309
log_debug("mount command = %s\n",command);
310
ret = usb_moded_system(command);
311
g_free(command);
312
313
/* mount returns 0 if success */
if(ret != 0 )
314
315
{
log_err("Mounting %s failed\n", mount);
316
317
if(ret)
{
318
g_free(mount);
319
320
321
mount = find_alt_mount();
if(mount)
{
322
323
command = g_strconcat("mount -t tmpfs tmpfs -o ro --size=512K ", mount, NULL);
log_debug("Total failure, mount ro tmpfs as fallback\n");
324
ret = usb_moded_system(command);
325
326
g_free(command);
}
327
usb_moded_send_error_signal(RE_MOUNT_FAILED);
328
329
330
331
}
}
}
332
333
if(data != NULL)
{
334
if(!strcmp(data->mode_module, MODULE_NONE))
335
{
336
337
log_debug("Disable android mass storage\n");
write_to_file("/sys/class/android_usb/f_mass_storage/lun/file", "0");
338
339
340
write_to_file("/sys/class/android_usb/android0/enable", "0");
}
}
341
342
343
344
else
{
sprintf(command2, "echo \"\" > /sys/devices/platform/musb_hdrc/gadget/gadget-lun%d/file", i);
log_debug("usb lun = %s inactive\n", command2);
345
usb_moded_system(command2);
346
}
347
348
}
g_strfreev(mounts);
349
g_free(mount);
350
351
if(real_path)
free(real_path);
352
353
354
355
356
357
}
return(ret);
}
358
static void report_mass_storage_blocker(const char *mountpoint, int try)
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
{
FILE *stream = 0;
gchar *lsof_command = 0;
int count = 0;
lsof_command = g_strconcat("lsof ", mountpoint, NULL);
if( (stream = popen(lsof_command, "r")) )
{
char *text = 0;
size_t size = 0;
while( getline(&text, &size, stream) >= 0 )
{
/* skip the first line as it does not contain process info */
if(count != 0)
{
gchar **split = 0;
split = g_strsplit((const gchar*)text, " ", 2);
378
log_err("Mass storage blocked by process %s\n", split[0]);
379
380
381
382
383
384
385
386
usb_moded_send_error_signal(split[0]);
g_strfreev(split);
}
count++;
}
pclose(stream);
}
g_free(lsof_command);
387
388
if(try == 2)
log_err("Setting Mass storage blocked. Giving up.\n");
389
390
391
}
392
int set_dynamic_mode(void)
393
{
394
395
struct mode_list_elem *data;
396
int ret = 1;
397
int network = 1;
398
399
400
data = get_usb_mode_data();
401
if(!data)
402
return(ret);
403
404
if(data->mass_storage)
405
{
406
return set_mass_storage_mode(data);
407
408
}
409
#ifdef APP_SYNC
410
if(data->appsync)
411
if(activate_sync(data->mode_name)) /* returns 1 on error */
412
413
{
log_debug("Appsync failure");
414
return(ret);
415
}
416
#endif
417
/* make sure things are disabled before changing functionality */
418
if(data->softconnect_disconnect)
419
420
421
{
write_to_file(data->softconnect_path, data->softconnect_disconnect);
}
422
/* set functionality first, then enable */
423
424
if(data->android_extra_sysfs_value && data->android_extra_sysfs_path)
{
425
ret = write_to_file(data->android_extra_sysfs_path, data->android_extra_sysfs_value);
426
427
428
429
430
}
if(data->android_extra_sysfs_value2 && data->android_extra_sysfs_path2)
{
write_to_file(data->android_extra_sysfs_path2, data->android_extra_sysfs_value2);
}
431
432
433
434
if(data->sysfs_path)
{
write_to_file(data->sysfs_path, data->sysfs_value);
}
435
436
437
438
439
if(data->idProduct)
{
/* only works for android since the idProduct is a module parameter */
set_android_productid(data->idProduct);
}
440
441
442
443
444
445
if(data->idVendorOverride)
{
/* only works for android since the idProduct is a module parameter */
set_android_vendorid(data->idVendorOverride);
}
446
/* enable the device */
447
448
if(data->softconnect)
{
449
ret = write_to_file(data->softconnect_path, data->softconnect);
450
}
451
452
/* functionality should be enabled, so we can enable the network now */
453
if(data->network)
454
{
455
#ifdef DEBIAN
456
char command[256];
457
458
g_snprintf(command, 256, "ifdown %s ; ifup %s", data->network_interface, data->network_interface);
459
usb_moded_system(command);
460
#else
461
usb_network_down(data);
462
network = usb_network_up(data);
463
#endif /* DEBIAN */
464
}
465
466
467
/* try a second time to bring up the network if it failed the first time,
this can happen with functionfs based gadgets (which is why we sleep for a bit */
468
if(network != 0 && data->network)
469
{
470
log_debug("Retry setting up the network later\n");
471
472
if(delayed_network)
g_source_remove(delayed_network);
473
delayed_network = g_timeout_add_seconds(3, network_retry, data);
474
475
}
476
477
478
/* Needs to be called before application post synching so
that the dhcp server has the right config */
if(data->nat || data->dhcp_server)
479
usb_network_set_up_dhcpd(data);
480
481
482
/* no need to execute the post sync if there was an error setting the mode */
if(data->appsync && !ret)
483
484
485
{
/* let's sleep for a bit (350ms) to allow interfaces to settle before running postsync */
usleep(350000);
486
activate_sync_post(data->mode_name);
487
}
488
489
490
491
492
493
#ifdef CONNMAN
if(data->connman_tethering)
connman_set_tethering(data->connman_tethering, TRUE);
#endif
494
495
496
if(ret)
usb_moded_send_error_signal(MODE_SETTING_FAILED);
return(ret);
497
}
498
499
500
501
502
503
504
void unset_dynamic_mode(void)
{
struct mode_list_elem *data;
data = get_usb_mode_data();
505
506
507
508
509
510
511
if(delayed_network)
{
g_source_remove(delayed_network);
delayed_network = 0;
}
512
513
514
515
/* the modelist could be empty */
if(!data)
return;
516
517
if(!strcmp(data->mode_name, MODE_MASS_STORAGE))
{
518
unset_mass_storage_mode(data);
519
520
521
return;
}
522
523
524
525
526
#ifdef CONNMAN
if(data->connman_tethering)
connman_set_tethering(data->connman_tethering, FALSE);
#endif
527
528
529
530
531
if(data->network)
{
usb_network_down(data);
}
532
/* disconnect before changing functionality */
533
if(data->softconnect_disconnect)
534
535
536
{
write_to_file(data->softconnect_path, data->softconnect_disconnect);
}
537
538
539
540
if(data->sysfs_path)
{
write_to_file(data->sysfs_path, data->sysfs_reset_value);
}
541
542
543
544
545
546
547
548
/* restore vendorid if the mode had an override */
if(data->idVendorOverride)
{
char *id;
id = get_android_vendor_id();
set_android_vendorid(id);
g_free(id);
}
549
550
551
552
553
554
/* enable after the changes have been made */
if(data->softconnect)
{
write_to_file(data->softconnect_path, data->softconnect);
}
555
}
556
557
558
559
560
561
562
563
564
/** clean up mode changes or extra actions to perform after a mode change
* @param module Name of module currently in use
* @return 0 on success, non-zero on failure
*
*/
int usb_moded_mode_cleanup(const char *module)
{
565
566
log_debug("Cleaning up mode\n");
567
568
if(!module)
{
569
log_warning("No module found to unload. Skipping cleanup\n");
570
571
572
return 0;
}
573
#ifdef APP_SYNC
574
/* Stop applications started due to entering this mode */
575
appsync_stop(FALSE);
576
#endif /* APP_SYNC */
577
578
if(!strcmp(module, MODULE_MASS_STORAGE)|| !strcmp(module, MODULE_FILE_STORAGE))
579
{
580
581
/* no clean-up needs to be done when we come from charging mode. We need
to check since we use fake mass-storage for charging */
582
if(!strcmp(MODE_CHARGING, get_usb_mode()) || !strcmp(MODE_CHARGING_FALLBACK, get_usb_mode()))
583
return 0;
584
unset_mass_storage_mode(NULL);
585
586
}
587
else if(get_usb_mode_data())
588
unset_dynamic_mode();
589
590
return(0);
591
}