summaryrefslogtreecommitdiffstats
path: root/common/usbgadget.c
blob: d4437b51694ad2f3c3c466489f053a82ad5658e0 (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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// 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>
#include <system-partitions.h>

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

static struct file_list *parse(const char *files)
{
	struct file_list *list;

	if (!files)
		return NULL;

	list = file_list_parse(files);
	if (IS_ERR(list)) {
		pr_err("Parsing file list \"%s\" failed: %pe\n", files, list);
		return NULL;
	}

	return list;
}

static inline struct file_list *get_dfu_function(void)
{
	if (dfu_function && *dfu_function)
		return file_list_parse(dfu_function);
	if (!system_partitions_empty())
		return system_partitions_get();
	return NULL;
}

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;

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

	if (dfu) {
		opts->dfu_opts.files = parse(dfu_opts);
		if (IS_ENABLED(CONFIG_USB_GADGET_DFU) && file_list_empty(opts->dfu_opts.files)) {
			file_list_free(opts->dfu_opts.files);
			opts->dfu_opts.files = get_dfu_function();
		}
	}

	if (fastboot) {
		opts->fastboot_opts.files = parse(fastboot_opts);
		if (IS_ENABLED(CONFIG_FASTBOOT_BASE) && file_list_empty(opts->fastboot_opts.files)) {
			file_list_free(opts->fastboot_opts.files);
			opts->fastboot_opts.files = get_fastboot_partitions();
		}

		opts->fastboot_opts.export_bbu = export_bbu;
	}

	opts->create_acm = acm;

	if (usb_multi_count_functions(opts) == 0) {
		pr_warn("No functions to register\n");
		ret = COMMAND_ERROR_USAGE;
		goto err;
	}

	/*
	 * Creating a gadget with both DFU and Fastboot may not work.
	 * fastboot 1:8.1.0+r23-5 can deal with it, but dfu-util 0.9
	 * seems to assume that the device only has a single configuration
	 * That's not our fault though. Emit a warning and continue
	 */
	if (!file_list_empty(opts->fastboot_opts.files) && !file_list_empty(opts->dfu_opts.files))
		pr_warn("Both DFU and Fastboot enabled. dfu-util may not like this!\n");

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

	ret = usb_multi_register(opts);
	if (ret)
		goto err;

	return 0;
err:
	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 (!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)
{
	globalvar_add_simple_bool("usbgadget.acm", &acm);
	globalvar_add_simple_string("usbgadget.dfu_function", &dfu_function);

	return 0;
}
device_initcall(usbgadget_globalvars_init);

static int usbgadget_autostart_init(void)
{
	if (IS_ENABLED(CONFIG_USB_GADGET_AUTOSTART))
		globalvar_add_bool("usbgadget.autostart", usbgadget_autostart_set, &autostart, NULL);
	return 0;
}
postenvironment_initcall(usbgadget_autostart_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");