summaryrefslogtreecommitdiffstats
path: root/common/usbgadget.c
blob: feec0b6634be1ba3795e1b177705d2a83625d2a7 (plain)
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2017 Oleksij Rempel <o.rempel@pengutronix.de>, Pengutronix
 */
#define pr_fmt(fmt) "usbgadget: " fmt

#include <common.h>
#include <command.h>
#include <errno.h>
#include <environment.h>
#include <malloc.h>
#include <getopt.h>
#include <fs.h>
#include <xfuncs.h>
#include <usb/usbserial.h>
#include <usb/dfu.h>
#include <usb/gadget-multi.h>
#include <globalvar.h>
#include <magicvar.h>

static int autostart;
static int acm;
static char *dfu_function;

static struct file_list *parse(const char *files)
{
	struct file_list *list = file_list_parse(files);
	if (IS_ERR(list)) {
		pr_err("Parsing file list \"%s\" failed: %pe\n", files, list);
		return NULL;
	}
	return list;
}

int usbgadget_register(bool dfu, const char *dfu_opts,
		       bool fastboot, const char *fastboot_opts,
		       bool acm, bool export_bbu)
{
	int ret;
	struct device_d *dev;
	struct f_multi_opts *opts;
	const char *fastboot_partitions = get_fastboot_partitions();

	if (dfu && !dfu_opts && dfu_function && *dfu_function)
		dfu_opts = dfu_function;

	if (IS_ENABLED(CONFIG_FASTBOOT_BASE) && fastboot && !fastboot_opts &&
	    fastboot_partitions && *fastboot_partitions)
		fastboot_opts = fastboot_partitions;

	if (!dfu_opts && !fastboot_opts && !acm)
		return COMMAND_ERROR_USAGE;

	/*
	 * Creating a gadget with both DFU and Fastboot doesn't work.
	 * Both client tools seem to assume that the device only has
	 * a single configuration
	 */
	if (fastboot_opts && dfu_opts) {
		pr_err("Only one of Fastboot and DFU allowed\n");
		return -EINVAL;
	}

	opts = xzalloc(sizeof(*opts));
	opts->release = usb_multi_opts_release;

	if (fastboot_opts) {
		opts->fastboot_opts.files = parse(fastboot_opts);
		opts->fastboot_opts.export_bbu = export_bbu;
	}

	if (dfu_opts)
		opts->dfu_opts.files = parse(dfu_opts);

	if (!opts->dfu_opts.files && !opts->fastboot_opts.files && !acm) {
		pr_warn("No functions to register\n");
		free(opts);
		return 0;
	}

	opts->create_acm = acm;

	dev = get_device_by_name("otg");
	if (dev)
		dev_set_param(dev, "mode", "peripheral");

	ret = usb_multi_register(opts);
	if (ret)
		usb_multi_opts_release(opts);

	return ret;
}

static int usbgadget_autostart_set(struct param_d *param, void *ctx)
{
	static bool started;
	bool fastboot_bbu = get_fastboot_bbu();
	int err;

	if (!IS_ENABLED(CONFIG_USB_GADGET_AUTOSTART) || !autostart || started)
		return 0;

	err = usbgadget_register(true, NULL, true, NULL, acm, fastboot_bbu);
	if (!err)
		started = true;

	return err;
}

static int usbgadget_globalvars_init(void)
{
	if (IS_ENABLED(CONFIG_USB_GADGET_AUTOSTART)) {
		globalvar_add_bool("usbgadget.autostart", usbgadget_autostart_set,
				   &autostart, NULL);
		globalvar_add_simple_bool("usbgadget.acm", &acm);
	}
	globalvar_add_simple_string("usbgadget.dfu_function", &dfu_function);

	return 0;
}
device_initcall(usbgadget_globalvars_init);

BAREBOX_MAGICVAR(global.usbgadget.autostart,
		 "usbgadget: Automatically start usbgadget on boot");
BAREBOX_MAGICVAR(global.usbgadget.acm,
		 "usbgadget: Create CDC ACM function");
BAREBOX_MAGICVAR(global.usbgadget.dfu_function,
		 "usbgadget: Create DFU function");