usb_moded-android.c 8.82 KB
Newer Older
1
/**
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 * @file usb_moded-android.c
 *
 * Copyright (C) 2013-2018 Jolla. All rights reserved.
 *
 * @author: Philippe De Swert <philippe.deswert@jollamobile.com>
 * @author: Simo Piiroinen <simo.piiroinen@jollamobile.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
 */
23 24

#include "usb_moded-android.h"
25

26
#include "usb_moded-config-private.h"
27
#include "usb_moded-log.h"
28
#include "usb_moded-mac.h"
29
#include "usb_moded-modesetting.h"
30

31
#include <unistd.h>
32 33 34
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
35 36 37 38 39 40 41

/* ========================================================================= *
 * Functions
 * ========================================================================= */

/* -- android -- */

42
bool         android_in_use           (void);
43 44
static bool  android_probe            (void);
gchar       *android_get_serial       (void);
45 46
bool         android_init             (void);
void         android_quit             (void);
47
bool         android_set_enabled      (bool enable);
48
bool         android_set_charging_mode(void);
49
bool         android_set_function     (const char *function);
50 51
bool         android_set_productid    (const char *id);
bool         android_set_vendorid     (const char *id);
52
bool         android_set_attr         (const char *function, const char *attr, const char *value);
53 54 55 56 57 58 59 60 61 62 63

/* ========================================================================= *
 * Data
 * ========================================================================= */

static int android_probed = -1;

/* ========================================================================= *
 * Functions
 * ========================================================================= */

64 65 66
static bool
android_write_file(const char *path, const char *text)
{
67 68
    LOG_REGISTER_CONTEXT;

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
    bool ack = false;

    if( !path || !text )
        goto EXIT;

    log_debug("WRITE %s '%s'", path, text);

    char buff[64];
    snprintf(buff, sizeof buff, "%s\n", text);

    if( write_to_file(path, buff) == -1 )
        goto EXIT;

    ack = true;

EXIT:

    return ack;
}

89 90
bool
android_in_use(void)
91
{
92 93
    LOG_REGISTER_CONTEXT;

94 95 96 97 98 99
    if( android_probed < 0 )
        log_debug("android_in_use() called before android_probe()");

    return android_probed > 0;
}

100 101
static bool
android_probe(void)
102
{
103 104
    LOG_REGISTER_CONTEXT;

105 106 107 108 109 110 111
    if( android_probed <= 0 ) {
        android_probed = access(ANDROID0_ENABLE, F_OK) == 0;
        log_warning("ANDROID0 %sdetected", android_probed ? "" : "not ");
    }

    return android_in_use();
}
112

113 114
/** Read android serial number from kernel command line
 */
115 116
gchar *
android_get_serial(void)
117
{
118 119
    LOG_REGISTER_CONTEXT;

120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
    static const char path[] = "/proc/cmdline";
    static const char find[] = "androidboot.serialno=";
    static const char pbrk[] = " \t\r\n,";

    char   *res  = 0;
    FILE   *file = 0;
    size_t  size = 0;
    char   *data = 0;

    if( !(file = fopen(path, "r")) ) {
        log_warning("%s: %s: %m", path, "can't open");
        goto EXIT;
    }

    if( getline(&data, &size, file) < 0 ) {
        log_warning("%s: %s: %m", path, "can't read");
        goto EXIT;
    }

    char *beg = strstr(data, find);
    if( !beg ) {
        log_warning("%s: no serial found", path);
        goto EXIT;
    }

    beg += sizeof find - 1;
    size_t len = strcspn(beg, pbrk);
    if( len < 1 ) {
        log_warning("%s: empty serial found", path);
        goto EXIT;
    }

    res = g_strndup(beg, len);

EXIT:

    free(data);

    if( file )
        fclose(file);

    return res;
}

164
/** initialize the basic android values
165 166
 *
 * @return true if android usb backend is ready for use, false otherwise
167
 */
168
bool
169
android_init(void)
170
{
171 172
    LOG_REGISTER_CONTEXT;

173 174 175 176 177 178
    gchar *text;

    if( !android_probe() )
        goto EXIT;

    /* Disable */
179
    android_set_enabled(false);
180 181 182 183

    /* Configure */
    if( (text = android_get_serial()) )
    {
184
        android_write_file(ANDROID0_SERIAL, text);
185
        g_free(text);
186 187
    }

188 189 190
    text = config_get_android_manufacturer();
    if(text)
    {
191
        android_write_file(ANDROID0_MANUFACTURER, text);
192 193 194 195 196
        g_free(text);
    }
    text = config_get_android_vendor_id();
    if(text)
    {
197
        android_set_vendorid(text);
198 199 200 201 202
        g_free(text);
    }
    text = config_get_android_product();
    if(text)
    {
203
        android_write_file(ANDROID0_PRODUCT, text);
204 205 206 207 208
        g_free(text);
    }
    text = config_get_android_product_id();
    if(text)
    {
209
        android_set_productid(text);
210 211 212 213 214
        g_free(text);
    }
    text = mac_read_mac();
    if(text)
    {
215
        android_set_attr("f_rndis", "ethaddr", text);
216 217 218
        g_free(text);
    }
    /* For rndis to be discovered correctly in M$ Windows (vista and later) */
219 220 221 222 223 224
    android_set_attr("f_rndis", "wceis", "1");

    /* Make sure remnants off mass-storage mode do not cause
     * issues for charging_fallback & co */
    android_set_attr("f_mass_storage", "lun/nofua", "0");
    android_set_attr("f_mass_storage", "lun/file", "");
225

226
EXIT:
227
    return android_in_use();
228 229
}

230 231 232 233 234 235 236 237
/** Cleanup resources allocated by android usb backend
 */
void
android_quit(void)
{
    /* For now this exists for symmetry with other backends only */
}

238 239 240
bool
android_set_enabled(bool enable)
{
241 242
    LOG_REGISTER_CONTEXT;

243 244 245
    bool ack = false;
    if( android_in_use() ) {
        const char *val = enable ? "1" : "0";
246
        ack = android_write_file(ANDROID0_ENABLE, val) != -1;
247 248 249 250 251
    }
    log_debug("ANDROID %s(%d) -> %d", __func__, enable, ack);
    return ack;
}

252 253
/* Set a charging mode for the android gadget
 *
254
 * @return true if successful, false on failure
255
 */
256 257
bool
android_set_charging_mode(void)
258
{
259 260
    LOG_REGISTER_CONTEXT;

261
    bool ack = false;
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278

    if( !android_in_use() )
        goto EXIT;

    if( !android_set_function("mass_storage") )
        goto EXIT;

     /* TODO: make configurable */
    if( !android_set_productid("0AFE") )
        goto EXIT;

    if( !android_set_enabled(true) )
        goto EXIT;

    ack = true;

EXIT:
279 280
    log_debug("ANDROID %s() -> %d", __func__, ack);
    return ack;
281
}
282

283 284 285 286 287 288 289
/* Set a function for the android gadget
 *
 * @return true if successful, false on failure
 */
bool
android_set_function(const char *function)
{
290 291
    LOG_REGISTER_CONTEXT;

292 293 294 295 296 297 298 299 300 301 302
    bool ack = false;

    if( !function )
        goto EXIT;

    if( !android_in_use() )
        goto EXIT;

    if( !android_set_enabled(false) )
        goto EXIT;

303
    if( android_write_file(ANDROID0_FUNCTIONS, function) == -1 )
304 305 306 307 308 309 310 311 312 313 314 315
        goto EXIT;

    /* Leave disabled, so that caller can adjust attributes
     * etc before enabling */

    ack = true;
EXIT:

    log_debug("ANDROID %s(%s) -> %d", __func__, function, ack);
    return ack;
}

316 317
/* Set a product id for the android gadget
 *
318
 * @return true if successful, false on failure
319
 */
320 321
bool
android_set_productid(const char *id)
322
{
323 324
    LOG_REGISTER_CONTEXT;

325
    bool ack = false;
326

327
    if( id && android_in_use() ) {
328 329 330 331 332 333 334
        char str[16];
        char *end = 0;
        unsigned num = strtol(id, &end, 16);
        if( end > id && *end == 0 ) {
            snprintf(str, sizeof str, "%04x", num);
            id = str;
        }
335
        ack = android_write_file(ANDROID0_ID_PRODUCT, id) != -1;
336 337 338
    }
    log_debug("ANDROID %s(%s) -> %d", __func__, id, ack);
    return ack;
339
}
340 341 342

/* Set a vendor id for the android gadget
 *
343
 * @return true if successful, false on failure
344
 */
345 346
bool
android_set_vendorid(const char *id)
347
{
348 349
    LOG_REGISTER_CONTEXT;

350
    bool ack = false;
351
    if( id && android_in_use() ) {
352 353 354 355 356 357 358
        char str[16];
        char *end = 0;
        unsigned num = strtol(id, &end, 16);
        if( end > id && *end == 0 ) {
            snprintf(str, sizeof str, "%04x", num);
            id = str;
        }
359
        ack = android_write_file(ANDROID0_ID_VENDOR, id) != -1;
360 361 362
    }
    log_debug("ANDROID %s(%s) -> %d", __func__, id, ack);
    return ack;
363
}
364 365 366 367 368 369 370 371

/** Set function attribute
 *
 * @return true if successful, false on failure
 */
bool
android_set_attr(const char *function, const char *attr, const char *value)
{
372 373
    LOG_REGISTER_CONTEXT;

374 375 376 377 378 379 380 381 382 383 384 385
    bool ack = false;

    if( function && attr && value && android_in_use() ) {
        char path[256];
        snprintf(path, sizeof path, "%s/%s/%s",
                 ANDROID0_DIRECTORY, function, attr);
        ack = android_write_file(path, value);
    }
    log_debug("ANDROID %s(%s, %s, %s) -> %d", __func__,
              function, attr, value, ack);
    return ack;
}