Commit 692c2cb3 authored by spiiroin's avatar spiiroin

[usb_moded] Add configfs support. Fixes JB#42083

Add custom Makefile for faster smoke-test rebuilds without spending time
in configure, autotools, document generation and/or rolling rpm packages.

Add multiple include protection to all header files.

Move function prototypes away from header files that included in the
development package.

Mass rename functions to have a prefix giving context for the function.
For the most part the prefix is derived from the name of the compilation
module containing the function.

Mass normalize indentation to four spaces in all source files.

Augment author lists on each source file based on git committers.

Remove "inline" attributes from not-really-inlineable functions.

Remove .gitignore.

Remove unused modules-android.c source file.

Define _GNU_SOURCE for whole build rather than in individual files.

Fix resource leaks here and there.

Try to avoid hard-coding android usb paths.

Add configfs backend for usb cadget configuration.

Probe supported backends (configfs, android usb, or kmod) on usb-moded
startup and make them mutually exclusive with each other.

Allow configfs and android usb control structures some time to show up
if usb-moded is started during device bootup.

Refactor udev event listener to utilize a state machine. Simplifies
book keeping and makes pc/charger detection heuristics easier to control.

Refactor mode selection to utilize three interconnected mini state machines.
Allows separating logically wanted mode from mode programmed to usb and
mode exposed over D-Bus.
Signed-off-by: spiiroin's avatarSimo Piiroinen <simo.piiroinen@jollamobile.com>
parent b1f55178
This diff is collapsed.
*.o
config.h*
000*
src/.deps/*
RPMS*
installroot*
autom4te.cache
cov-int
docs/html
This diff is collapsed.
/* config.h. Generated from config.h.in by configure. */
/* config.h.in. Generated from configure.ac by autoheader. */
/* Name of package */
#define PACKAGE "usb_moded"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT ""
/* Define to the full name of this package. */
#define PACKAGE_NAME "usb_moded"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "usb_moded 0.86.0+mer24"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "usb_moded"
/* Define to the home page for this package. */
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "0.86.0+mer24"
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Version number of package */
#define VERSION "0.86.0+mer24"
/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */
#!/usr/bin/env python
# -*- encoding: utf8 -*-
# ----------------------------------------------------------------------------
# Copyright (C) 2012-2018 Jolla Ltd.
# Contact: Simo Piiroinen <simo.piiroinen@jollamobile.com>
# License: GPLv2
# ----------------------------------------------------------------------------
import sys,os
def is_local(path):
return not path.startswith("/")
def print_rule(dest, srce):
print "%s\\\n" % "\\\n\t".join(["%s:" % dest] + srce)
def set_extension(path, ext):
return os.path.splitext(path)[0] + ext
def normalize_path(srce):
return os.path.normpath(srce)
def fix_directory(dest, srce):
srce = os.path.dirname(srce)
dest = os.path.basename(dest)
return os.path.join(srce, dest)
if __name__ == "__main__":
data = sys.stdin.readlines()
data = map(lambda x:x.rstrip(), data)
data.reverse()
deps = []
# Note: dependencies are sorted only to keep
# future diffs cleaner
while data:
line = data.pop()
while line.endswith('\\'):
line = line[:-1] + data.pop()
if not ':' in line:
continue
dest,srce = line.split(":",1)
srce = srce.split()
srce,temp = srce[0],srce[1:]
# take dest path primary srce
dest = fix_directory(dest, srce)
# remove secondary deps with absolute path
temp = filter(is_local, temp)
# sort secondary sources
temp.sort()
srce = [srce] + temp
srce = map(normalize_path, srce)
deps.append((dest,srce))
for dest,srce in sorted(deps):
# emit normal compilation dependencies
print_rule(dest, srce)
# and for -fPIC in case it is needed
dest = set_extension(dest, ".pic.o")
print_rule(dest, srce)
......@@ -11,7 +11,7 @@ Source1: usb_moded.conf
BuildRequires: pkgconfig(dbus-1)
BuildRequires: pkgconfig(dbus-glib-1)
BuildRequires: pkgconfig(glib-2.0)
BuildRequires: pkgconfig(udev)
BuildRequires: pkgconfig(libudev)
BuildRequires: pkgconfig(libkmod)
BuildRequires: doxygen
BuildRequires: pkgconfig(libsystemd)
......@@ -357,6 +357,7 @@ touch %{buildroot}/%{_sysconfdir}/udhcpd.conf
install -d $RPM_BUILD_ROOT/lib/systemd/system/basic.target.wants/
install -m 644 -D systemd/%{name}.service %{buildroot}/lib/systemd/system/%{name}.service
ln -s ../%{name}.service $RPM_BUILD_ROOT/lib/systemd/system/basic.target.wants/%{name}.service
install -d %{buildroot}/usr/lib/systemd/user
install -m 644 -D systemd/usb-moded-args.conf %{buildroot}/var/lib/environment/usb-moded/usb-moded-args.conf
install -m 755 -D systemd/turn-usb-rescue-mode-off %{buildroot}/%{_bindir}/turn-usb-rescue-mode-off
install -m 644 -D systemd/usb-rescue-mode-off.service %{buildroot}/lib/systemd/system/usb-rescue-mode-off.service
......
#!/bin/sh
PROGNAME="$(basename $0)"
# ============================================================================
# ENV
# ============================================================================
CONFIGFS_ROOT="/config"
GADGET_DIR="$CONFIGFS_ROOT/usb_gadget/g1"
CONFIG_DIR="$GADGET_DIR/configs/b.1"
FUNCTION_DIR="$GADGET_DIR/functions"
UDC_CONTROL="$GADGET_DIR/UDC"
UDC_ENABLE="$(ls /sys/class/udc)"
UDC_DISABLE=""
# "charging_only"
FUNCTION_MASS_STORAGE="mass_storage.usb0"
# "developer_mode"
FUNCTION_RNDIS="rndis_bam.rndis"
# "mtp_mode"
FUNCTION_MTP="ffs.mtp"
# ============================================================================
# LOG
# ============================================================================
LOG_LEVEL=6
log_critical() { test $LOG_LEVEL -ge 2 && echo >&2 "$PROGNAME: C: $*" ; }
log_error() { test $LOG_LEVEL -ge 3 && echo >&2 "$PROGNAME: E: $*" ; }
log_warning() { test $LOG_LEVEL -ge 4 && echo >&2 "$PROGNAME: W: $*" ; }
log_notice() { test $LOG_LEVEL -ge 5 && echo >&2 "$PROGNAME: N: $*" ; }
log_info() { test $LOG_LEVEL -ge 6 && echo >&2 "$PROGNAME: I: $*" ; }
log_debug() { test $LOG_LEVEL -ge 7 && echo >&2 "$PROGNAME: D: $*" ; }
# ============================================================================
# CONFIGFS
# ============================================================================
configfs_validate() {
log_debug "Validate ConfigFS mountpoint"
mountpoint -q $CONFIGFS_ROOT || exit 1
test -d "$GADGET_DIR" || exit 1
test -f "$UDC_CONTROL" || exit 1
test -d "$CONFIG_DIR" || exit 1
test -d "$FUNCTION_DIR" || exit 1
}
# ============================================================================
# UDC
# ============================================================================
udc_control() {
local prev="$(cat $UDC_CONTROL)"
local next="$1"
if [ "$prev" != "$next" ]; then
echo > "$UDC_CONTROL" "$next"
fi
}
udc_disable() {
log_debug "Disable UDC"
udc_control "$UDC_DISABLE"
}
udc_enable() {
log_debug "Enable UDC"
udc_control "$UDC_ENABLE"
}
# ============================================================================
# FUNCTION
# ============================================================================
function_register() {
log_debug "Register function: $1"
test -d "$FUNCTION_DIR/$1" || mkdir "$FUNCTION_DIR/$1"
}
function_activate() {
log_debug "Activate function: $1"
test -h "$CONFIG_DIR/$1" || ln -s "$FUNCTION_DIR/$1" "$CONFIG_DIR/$1"
}
function_deactivate() {
log_debug "Deactivate function: $1"
test -h "$CONFIG_DIR/$1" && rm "$CONFIG_DIR/$1"
}
function_deactivate_all() {
local err=0
for f in $CONFIG_DIR/*; do
# symlink == function
test -h $f || continue
if ! function_deactivate $(basename $f); then
err=1
fi
done
return $err
}
function_list_active() {
log_debug "List active functions"
for f in $CONFIG_DIR/*; do
# symlink == function
test -h $f || continue
log_debug "Active function: $(basename $f)"
done
return 0
}
function_set_attr() {
local f="$1"
local k="$2"
local v="$3"
log_debug "Function $f: set $k = $v"
echo > "$FUNCTION_DIR/$f/$k" "$v"
}
# ============================================================================
# RNDIS
# ============================================================================
rndis_down() {
log_debug "Disable rndis interface"
ifconfig rndis0 down 2> /dev/null || :
}
rndis_up() {
log_debug "Enable rndis interface"
ifconfig rndis0 192.168.2.15 255.255.255.0 2> /dev/null || :
}
udhcpd_configure() {
log_debug "Configure udhcpd"
cat > /etc/udhcpd.conf <<-EOF
start 192.168.2.1
end 192.168.2.15
interface rndis0
option subnet 255.255.255.0
option lease 3600
max_leases 15
EOF
}
udhcpd_start() {
log_debug "Start udhcpd"
systemctl start udhcpd.service
}
udhcpd_stop() {
log_debug "Stop udhcpd"
systemctl stop udhcpd.service
}
# ============================================================================
# MTP
# ============================================================================
MTPD_ENV="LANG=en_GB.utf8 MSYNCD_LOGGING_LEVEL=7"
MTPD_BIN="/usr/lib/mtp/mtp_service"
MTPD_LOG="/tmp/mtpd.log"
mtp_mount() {
log_debug "Mount mtp endpoint directory"
if ! mountpoint -q /dev/mtp ; then
/bin/mount -o uid=100000,gid=100000 -t functionfs mtp /dev/mtp
fi
}
mtp_unmount() {
log_debug "Unmount mtp endpoint directory"
if mountpoint -q /dev/mtp ; then
/bin/umount /dev/mtp
fi
}
mtpd_start() {
log_debug "Start mtpd"
(/bin/su - nemo -c "$MTPD_ENV $MTPD_BIN" 2>$MTPD_LOG) &
local seen=0
for i in $(seq 10); do
sleep 0.333
if killall -0 "$MTPD_BIN" ; then
seen=$((seen+1))
test "$seen" -ge 3 && return 0
else
log_debug "Wait for mtpd ..."
fi
done
log_warning "Could not start mtpd"
return 1
}
mtpd_stop() {
log_debug "Stop mtpd"
/usr/bin/killall "$MTPD_BIN" || :
}
# ============================================================================
# USB_MODE
# ============================================================================
# -- undefined --
usb_mode_enter_undefined() {
log_debug "ENTER UNKNOWN MODE"
# same as enter undefined...
udc_disable
function_deactivate_all
}
usb_mode_leave_undefined() {
log_debug "LEAVE UNKNOWN MODE"
udc_disable
function_deactivate_all
}
# -- charging_only --
usb_mode_enter_charging_only() {
log_debug "ENTER CHARGING_ONLY"
function_register $FUNCTION_MASS_STORAGE
function_activate $FUNCTION_MASS_STORAGE
udc_enable
}
usb_mode_leave_charging_only() {
log_debug "LEAVE CHARGING_ONLY"
udc_disable
function_deactivate_all
}
# -- developer_mode --
usb_mode_enter_developer_mode() {
log_debug "ENTER DEVELOPER_MODE"
function_register $FUNCTION_RNDIS
function_set_attr $FUNCTION_RNDIS wceis 1
function_activate $FUNCTION_RNDIS
udc_enable
sleep 0.5
rndis_up
sleep 0.5
udhcpd_configure
udhcpd_start
}
usb_mode_leave_developer_mode() {
log_debug "LEAVE DEVELOPER_MODE"
udhcpd_stop
rndis_down
udc_disable
function_deactivate_all
}
# -- mtp_mode --
usb_mode_enter_mtp_mode() {
log_debug "ENTER MTP_MODE"
function_register $FUNCTION_MTP
function_activate $FUNCTION_MTP
mtp_mount
mtpd_start
udc_enable
}
usb_mode_leave_mtp_mode() {
log_debug "LEAVE MTP_MODE"
udc_disable
mtpd_stop
mtp_unmount
function_deactivate_all
}
# -- mode switch --
usb_mode_enter() {
case $1 in
mtp_mode)
usb_mode_enter_mtp_mode
;;
developer_mode)
usb_mode_enter_developer_mode
;;
charging_only)
usb_mode_enter_charging_only
;;
undefined)
usb_mode_enter_undefined
;;
*)
log_warning "Unknown mode: $1"
usb_mode_enter_undefined
;;
esac
}
usb_mode_leave() {
case $1 in
mtp_mode)
usb_mode_leave_mtp_mode
;;
developer_mode)
usb_mode_leave_developer_mode
;;
charging_only)
usb_mode_leave_charging_only
;;
undefined)
usb_mode_leave_undefined
;;
*)
log_warning "Unknown mode: $1"
usb_mode_leave_undefined
;;
esac
}
usb_mode_switch() {
configfs_validate
local next="$1"
local curr="$(cat /tmp/usb-mode)"
test ! -z "$curr" || curr="undefined"
if [ "$curr" != "$next" ]; then
log_info "SWITCH FROM: $curr TO: $next"
usb_mode_leave "$curr"
echo > /tmp/usb-mode "$next"
usb_mode_enter "$next"
else
log_info "ALREADY IN: $next"
fi
}
# ============================================================================
# MAIN
# ============================================================================
# try to guess if we were executed or sourced
if [ "$PROGNAME" = "set_usb_mode.sh" ]; then
TARGET_MODE="$1"
case $TARGET_MODE in
cha*)
TARGET_MODE="charging_only"
;;
dev*)
TARGET_MODE="developer_mode"
;;
mtp*)
TARGET_MODE="mtp_mode"
;;
und*)
TARGET_MODE="undefined"
;;
-v|--verbose)
LOG_LEVEL=$((LOG_LEVEL+1))
;;
-q|--quiet)
LOG_LEVEL=$((LOG_LEVEL-1))
;;
-s|--silent)
LOG_LEVEL=0
;;
-h|--help|--usage)
cat <<-EOF
Usage:
$PROGNAME <mode>
Valid modes are:
- charging_only
- developer_mode
- mtp_mode
- undefined
EOF
exit 0
;;
*)
cat >&2 <<-EOF
unknown option: ${TARGET_MODE:-<nothing>}
(Use $PROGNAME --usage to show help)
EOF
exit 1
;;
esac
usb_mode_switch "$TARGET_MODE"
fi
USB_MODED_CFLAGS += -D_GNU_SOURCE
sbin_PROGRAMS = usb_moded \
usb_moded_util
......@@ -9,7 +11,7 @@ usb_moded_LDFLAGS = \
usb_moded_LDADD = \
$(USB_MODED_LIBS)
usb_moded_SOURCES = \
usb_moded.c \
usb_moded.h \
......@@ -17,7 +19,7 @@ usb_moded_SOURCES = \
usb_moded-dbus.c \
usb_moded-dbus.h \
usb_moded-dbus-private.h \
usb_moded-hw-ab.h \
usb_moded-udev.h \
usb_moded-config-private.h \
usb_moded-modules.h \
usb_moded-log.h \
......@@ -35,6 +37,8 @@ usb_moded_SOURCES = \
usb_moded-udev.c \
usb_moded-trigger.c \
usb_moded-modules.c \
usb_moded-configfs.c \
usb_moded-configfs.h \
usb_moded-android.h \
usb_moded-android.c
......
/**
@file usb_moded-android.c
Copyright (C) 2013 Jolla. All rights reserved.
@author: Philippe De Swert <philippe.deswert@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
*/
* @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
*/
#include <stdio.h>
#include <glib.h>
......@@ -27,13 +28,59 @@
#include "usb_moded-android.h"
#include "usb_moded-log.h"
#include "usb_moded-modesetting.h"
#include "usb_moded-config.h"
#include "usb_moded-config-private.h"
#include "usb_moded-mac.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/* ========================================================================= *
* Functions
* ========================================================================= */
/* -- android -- */
bool android_in_use (void);
static bool android_probe (void);
gchar *android_get_serial (void);
bool android_init_values (void);
bool android_set_charging_mode(void);
bool android_set_productid (const char *id);
bool android_set_vendorid (const char *id);
/* ========================================================================= *
* Data
* ========================================================================= */
static int android_probed = -1;
/* ========================================================================= *
* Functions
* ========================================================================= */
bool android_in_use(void)
{
if( android_probed < 0 )
log_debug("android_in_use() called before android_probe()");
return android_probed > 0;
}
static bool android_probe(void)
{
if( android_probed <= 0 ) {
android_probed = access(ANDROID0_ENABLE, F_OK) == 0;
log_warning("ANDROID0 %sdetected", android_probed ? "" : "not ");
}
return android_in_use();
}
/** Read android serial number from kernel command line
*/
static gchar *get_android_serial(void)
gchar *
android_get_serial(void)
{
static const char path[] = "/proc/cmdline";
static const char find[] = "androidboot.serialno=";
......@@ -81,129 +128,119 @@ EXIT:
/** initialize the basic android values
*/
void android_init_values(void)
bool android_init_values(void)
{
gchar *text;
/* If the directory is not there, no point emitting warnings
* about every file that is not going to be there either. */
if( access("/sys/class/android_usb/android0", F_OK) != 0 )
{
goto EXIT;
}
/* Disable */
write_to_file("/sys/class/android_usb/android0/enable", "0");
/* Configure */
if( (text = get_android_serial()) )
{
write_to_file("/sys/class/android_usb/android0/iSerial", text);
g_free(text);
}
text = get_android_manufacturer();
if(text)
{
write_to_file("/sys/class/android_usb/android0/iManufacturer", text);
g_free(text);
}
text = get_android_vendor_id();
if(text)
{
write_to_file("/sys/class/android_usb/android0/idVendor", text);
g_free(text);
}
text = get_android_product();
if(text)
{
write_to_file("/sys/class/android_usb/android0/iProduct", text);
g_free(text);
}
text = get_android_product_id();
if(text)
{
write_to_file("/sys/class/android_usb/android0/idProduct", text);
g_free(text);
}
text = read_mac();
if(text)
{
write_to_file("/sys/class/android_usb/f_rndis/ethaddr", text);
g_free(text);
}
/* For rndis to be discovered correctly in M$ Windows (vista and later) */
write_to_file("/sys/class/android_usb/f_rndis/wceis", "1");
/* Some devices can have enumeration issues due to incomplete
* configuration on the 1st connect after bootup. Briefly setting
* up for example mass_storage function can be utilized as a
* workaround in such cases. */
if(!init_done_p()) {
const char *function = get_android_bootup_function();
if(function) {
write_to_file("/sys/class/android_usb/android0/functions", function);
write_to_file("/sys/class/android_usb/android0/enable", "1");
write_to_file("/sys/class/android_usb/android0/enable", "0");
gchar *text;
if( !android_probe() )
goto EXIT;
/* Disable */
write_to_file(ANDROID0_ENABLE, "0");