usb_moded-modules.c 7.33 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 23 24 25 26 27 28
 * @file usb_moded-modules.c
 *
 * Copyright (C) 2010 Nokia Corporation. All rights reserved.
 * Copyright (C) 2012-2018 Jolla. All rights reserved.
 *
 * @author: Philippe De Swert <philippe.de-swert@nokia.com>
 * @author: Philippe De Swert <phdeswer@lumi.maa>
 * @author: Philippe De Swert <philippedeswert@gmail.com>
 * @author: Philippe De Swert <philippe.deswert@jollamobile.com>
 * @author: Thomas Perl <m@thp.io>
 * @author: Slava Monich <slava.monich@jolla.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
 */
29

30 31 32 33
#include "usb_moded-modules.h"

#include "usb_moded-log.h"

34 35 36
#include <stdlib.h>
#include <string.h>

37 38
#include <libkmod.h>

39
#include <glib.h>
40

41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
/* ========================================================================= *
 * Prototypes
 * ========================================================================= */

/* -- modules -- */

bool        modules_init                     (void);
bool        modules_in_use                   (void);
void        modules_quit                     (void);
int         modules_load_module              (const char *module);
int         modules_unload_module            (const char *module);

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

57 58 59 60 61 62 63 64
/** Availability of kernel module based gadget configuration functionality
 *
 * -1 = not checked yet
 *  0 = not available
 *  1 = chosen as means of gadget configuration for usb-moded
 */
static int modules_probed = -1;

65 66
/* kmod context - initialized at start in usbmoded_init by ctx_init()
 *  and cleaned up by ctx_cleanup() functions */
67
static struct kmod_ctx *modules_ctx = 0;
68

69 70 71 72 73 74
/* ========================================================================= *
 * Functions
 * ========================================================================= */

static bool modules_have_module(const char *module)
{
75 76
    LOG_REGISTER_CONTEXT;

77 78 79 80 81
    // TODO: not fully untested due to lack of suitable hw

    bool ack = false;
    struct kmod_list *list = 0;

82
    if( kmod_module_new_from_lookup(modules_ctx, module, &list) < 0 )
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
        goto EXIT;

    if( list == 0 )
        goto EXIT;

    ack = true;

EXIT:
    if( list )
        kmod_module_unref_list(list);

    log_debug("module %s does%s exist", module, ack ? "" : "not ");
    return ack;

}

bool modules_in_use(void)
{
101 102
    LOG_REGISTER_CONTEXT;

103 104 105 106 107 108 109 110
    if( modules_probed < 0 )
        log_debug("modules_in_use() called before modules_probe()");

    return modules_probed > 0;
}

static bool modules_probe(void)
{
111 112
    LOG_REGISTER_CONTEXT;

113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
    static const char * const lut[] = {
        MODULE_MASS_STORAGE,
        MODULE_FILE_STORAGE,
        MODULE_DEVELOPER,
        MODULE_MTP,
        0
    };

    if( modules_probed == -1 ) {
        modules_probed = false;
        /* Check if we have at least one of the kernel modules we
         * expect to use for something.
         */
        for( size_t i = 0; lut[i] ; ++i ) {
            if( modules_have_module(lut[i]) ) {
                modules_probed = true;
                break;
            }
        }
        log_warning("MODULES %sdetected", modules_probed ? "" : "not ");
    }

    return modules_in_use();
}

138 139 140 141
/** kmod module init
 *
 * @return true if modules backend is ready for use, false otherwise
 */
142
bool modules_init(void)
143
{
144 145
    LOG_REGISTER_CONTEXT;

146 147
    bool ack = false;

148 149
    if( !modules_ctx ) {
        if( !(modules_ctx = kmod_new(NULL, NULL)) )
150 151 152
            goto EXIT;
    }

153
    if( kmod_load_resources(modules_ctx) < 0 )
154 155 156 157 158 159 160 161
        goto EXIT;

    if( !modules_probe() )
        goto EXIT;

    ack = true;
EXIT:
    return ack;
162 163 164
}

/* kmod module cleanup */
165
void modules_quit(void)
166
{
167 168
    LOG_REGISTER_CONTEXT;

169 170
    if( modules_ctx )
        kmod_unref(modules_ctx), modules_ctx = 0;
171 172
}

173
/** load module
174 175 176 177 178
 *
 * @param module Name of the module to load
 * @return 0 on success, non-zero on failure
 *
 */
179
int modules_load_module(const char *module)
180
{
181 182
    LOG_REGISTER_CONTEXT;

183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
    int ret = 0;

    const int probe_flags = KMOD_PROBE_APPLY_BLACKLIST;
    struct kmod_module *mod;
    char *charging_args = NULL;
    char *load = NULL;

    if(!strcmp(module, MODULE_NONE))
        return 0;

    if( !modules_in_use() ) {
        log_warning("load module %s - without module support", module);
        return -1;
    }

    /* copy module to load as it might be modified if we're trying charging mode */
    load = strdup(module);
    if(!strcmp(module, MODULE_CHARGING) || !strcmp(module, MODULE_CHARGE_FALLBACK))
    {
        /* split the string in module name and argument, they are the same for MODULE_CHARGE_FALLBACK
203
         * so no need to handle them separately  */
204 205 206
        gchar **strings;

        /* since the mass_storage module is the newer one and we check against it to avoid
207 208
         * loading failures we use it here, as we fall back to g_file_storage if g_mass_storage
         * fails to load */
209 210 211 212 213 214 215 216 217
        strings = g_strsplit(MODULE_CHARGE_FALLBACK, " ", 2);
        //log_debug("module args = %s, module = %s\n", strings[1], strings[0]);
        charging_args = strdup(strings[1]);
        /* load was already assigned. Free it to re-assign */
        free(load);
        load = strdup(strings[0]);
        g_strfreev(strings);

    }
218
    ret = kmod_module_new_from_name(modules_ctx, load, &mod);
219
    /* since kmod_module_new_from_name does not check if the module
220
     * exists we test it's path in case we deal with the mass-storage one */
221 222 223 224
    if(!strcmp(module, MODULE_MASS_STORAGE) &&
       (kmod_module_get_path(mod) == NULL))
    {
        log_debug("Fallback on older g_file_storage\n");
225
        ret = kmod_module_new_from_name(modules_ctx, MODULE_FILE_STORAGE, &mod);
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
    }

    if(!charging_args)
        ret = kmod_module_probe_insert_module(mod, probe_flags, NULL, NULL, NULL, NULL);
    else
    {
        ret = kmod_module_probe_insert_module(mod, probe_flags, charging_args, NULL, NULL, NULL);
        free(charging_args);
    }
    kmod_module_unref(mod);
    free(load);

    if( ret == 0)
        log_info("Module %s loaded successfully\n", module);
    else
        log_info("Module %s failed to load\n", module);
242
    return ret;
243 244 245
}

/** unload module
246
 *
247 248 249 250
 * @param module Name of the module to unload
 * @return 0 on success, non-zero on failure
 *
 */
251
int modules_unload_module(const char *module)
252
{
253 254
    LOG_REGISTER_CONTEXT;

255
    int ret = 0;
256

257
    struct kmod_module *mod;
258

259 260
    if(!strcmp(module, MODULE_NONE))
        return 0;
261

262 263 264 265
    if( !modules_in_use() ) {
        log_warning("unload module %s - without module support", module);
        return -1;
    }
266

267
    kmod_module_new_from_name(modules_ctx, module, &mod);
268 269 270
    ret = kmod_module_remove_module(mod, KMOD_REMOVE_NOWAIT);
    kmod_module_unref(mod);

271
    return ret;
272
}