1672fc84aSRobert Mustacchi /*
2672fc84aSRobert Mustacchi  * This file and its contents are supplied under the terms of the
3672fc84aSRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4672fc84aSRobert Mustacchi  * You may only use this file in accordance with the terms of version
5672fc84aSRobert Mustacchi  * 1.0 of the CDDL.
6672fc84aSRobert Mustacchi  *
7672fc84aSRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8672fc84aSRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9672fc84aSRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10672fc84aSRobert Mustacchi  */
11672fc84aSRobert Mustacchi 
12672fc84aSRobert Mustacchi /*
13672fc84aSRobert Mustacchi  * Copyright (c) 2018, Joyent, Inc.
14672fc84aSRobert Mustacchi  */
15672fc84aSRobert Mustacchi 
16672fc84aSRobert Mustacchi /*
17672fc84aSRobert Mustacchi  * This module parses the private file format used for describing
18672fc84aSRobert Mustacchi  * platform-specific USB overrides.
19672fc84aSRobert Mustacchi  *
20672fc84aSRobert Mustacchi  * FILE FORMAT
21672fc84aSRobert Mustacchi  * -----------
22672fc84aSRobert Mustacchi  *
23672fc84aSRobert Mustacchi  * A USB topology file contains a series of lines which are separated by new
24672fc84aSRobert Mustacchi  * lines. Leading and trailing whitespace on a line are ignored and empty lines
25672fc84aSRobert Mustacchi  * are ignored as well. The '#' character is used as a comment character. There
26672fc84aSRobert Mustacchi  * are a series of keywords that are supported which are used to indicate
27672fc84aSRobert Mustacchi  * different control aspects. These keywords are all treated in a
28672fc84aSRobert Mustacchi  * case-insensitive fashion. There are both top-level keywords and keywords that
29672fc84aSRobert Mustacchi  * are only accepted within the context of a scope.
30672fc84aSRobert Mustacchi  *
31672fc84aSRobert Mustacchi  * Top-level keywords
32672fc84aSRobert Mustacchi  * ------------------
33672fc84aSRobert Mustacchi  *
34672fc84aSRobert Mustacchi  * The following keywords are accepted, but must not be found inside a nested
35672fc84aSRobert Mustacchi  * scope:
36672fc84aSRobert Mustacchi  *
37672fc84aSRobert Mustacchi  *   'disable-acpi'		Disables the use of ACPI for this platform. This
38672fc84aSRobert Mustacchi  *				includes getting information about the port's
39672fc84aSRobert Mustacchi  *				type and visibility. This implies
40672fc84aSRobert Mustacchi  *				'disable-acpi-match'.
41672fc84aSRobert Mustacchi  *
42672fc84aSRobert Mustacchi  *   'disable-acpi-match'	Disables the act of trying to match ports based
43672fc84aSRobert Mustacchi  *				on ACPI.
44672fc84aSRobert Mustacchi  *
45672fc84aSRobert Mustacchi  *
46672fc84aSRobert Mustacchi  *   'enable-acpi-match'	Explicitly enables ACPI port matching on the
47672fc84aSRobert Mustacchi  *				platform based on ACPI.
48672fc84aSRobert Mustacchi  *
49672fc84aSRobert Mustacchi  *   'enable-metadata-match'	Enables port matching based on metadata. This is
50672fc84aSRobert Mustacchi  *				most commonly used on platforms that have ehci
51672fc84aSRobert Mustacchi  *				and xhci controllers that share ports.
52672fc84aSRobert Mustacchi  *
53672fc84aSRobert Mustacchi  *   'port'			Begins a port stanza that describes a single
54672fc84aSRobert Mustacchi  *				physical port. This stanza will continue until
55672fc84aSRobert Mustacchi  *				the 'end-port' keyword is encountered.
56672fc84aSRobert Mustacchi  *
57672fc84aSRobert Mustacchi  * Port Keywords
58672fc84aSRobert Mustacchi  * -------------
59672fc84aSRobert Mustacchi  *
60672fc84aSRobert Mustacchi  * Some port keywords take arguments and others do not. When an argument exists,
61672fc84aSRobert Mustacchi  * will occur on the subsequent line. Ports have a series of directives that
62672fc84aSRobert Mustacchi  * describe metadata as well as directives that describe how to determine the
63672fc84aSRobert Mustacchi  * port.
64672fc84aSRobert Mustacchi  *
65672fc84aSRobert Mustacchi  *   'label'			Indicates that the next line contains the
66672fc84aSRobert Mustacchi  *				human-readable label for the port.
67672fc84aSRobert Mustacchi  *
68672fc84aSRobert Mustacchi  *   'chassis'			Indicates that this port is part of the chassis
69672fc84aSRobert Mustacchi  *				and should not be enumerated elsewhere.
70672fc84aSRobert Mustacchi  *
71672fc84aSRobert Mustacchi  *   'external'			Indicates that this port is externally visible.
72672fc84aSRobert Mustacchi  *
73672fc84aSRobert Mustacchi  *   'internal'			Indicates that this port is internal to the
74672fc84aSRobert Mustacchi  *				chassis and cannot be accessed without opening
75672fc84aSRobert Mustacchi  *				the chassis.
76672fc84aSRobert Mustacchi  *
77672fc84aSRobert Mustacchi  *   'port-type'		Indicates that the next line contains a number
78672fc84aSRobert Mustacchi  *				which corresponds to the type of the port. The
79672fc84aSRobert Mustacchi  *				port numbers are based on the ACPI table and
80672fc84aSRobert Mustacchi  *				may be in either base 10 or hexadecimal.
81672fc84aSRobert Mustacchi  *
82672fc84aSRobert Mustacchi  *   'acpi-path'		Indicates that the next line contains an ACPI
83672fc84aSRobert Mustacchi  *				based name that matches the port.
84672fc84aSRobert Mustacchi  *
85672fc84aSRobert Mustacchi  *   'end-port'			Closes the port-clause.
86672fc84aSRobert Mustacchi  */
87672fc84aSRobert Mustacchi 
88672fc84aSRobert Mustacchi #include <libnvpair.h>
89672fc84aSRobert Mustacchi #include <sys/types.h>
90672fc84aSRobert Mustacchi #include <sys/stat.h>
91672fc84aSRobert Mustacchi #include <fcntl.h>
92672fc84aSRobert Mustacchi #include <fm/topo_list.h>
93672fc84aSRobert Mustacchi #include <fm/topo_mod.h>
94672fc84aSRobert Mustacchi #include <stdio.h>
95672fc84aSRobert Mustacchi #include <string.h>
96672fc84aSRobert Mustacchi #include <strings.h>
97672fc84aSRobert Mustacchi #include <libnvpair.h>
98672fc84aSRobert Mustacchi #include <sys/debug.h>
99672fc84aSRobert Mustacchi #include <ctype.h>
100672fc84aSRobert Mustacchi #include <unistd.h>
101672fc84aSRobert Mustacchi 
102672fc84aSRobert Mustacchi #include "topo_usb.h"
103672fc84aSRobert Mustacchi #include "topo_usb_int.h"
104672fc84aSRobert Mustacchi 
105672fc84aSRobert Mustacchi /*
106672fc84aSRobert Mustacchi  * Maximum number of characters we expect to encounter in a line.
107672fc84aSRobert Mustacchi  */
108672fc84aSRobert Mustacchi #define	TOPO_USB_META_LINE_MAX	1000
109672fc84aSRobert Mustacchi 
110672fc84aSRobert Mustacchi /*
111672fc84aSRobert Mustacchi  * This constant is the default set of flags that we'd like to apply when there
112672fc84aSRobert Mustacchi  * is no configuration file present to determine the desired behavior. If one is
113672fc84aSRobert Mustacchi  * present, we always defer to what it asks for.
114672fc84aSRobert Mustacchi  *
115672fc84aSRobert Mustacchi  * It's a difficult decision to enable ACPI by default or not. Unfortunately,
116672fc84aSRobert Mustacchi  * we've encountered some systems where the ACPI information is wrong. However,
117672fc84aSRobert Mustacchi  * we've encountered a larger number where it is correct. When it's correct,
118672fc84aSRobert Mustacchi  * this greatly simplifies some of the work that we have to do. Our default
119672fc84aSRobert Mustacchi  * disposition at the moment is to opt to decide its correct as that ends up
120672fc84aSRobert Mustacchi  * giving us much better information.
121672fc84aSRobert Mustacchi  */
122672fc84aSRobert Mustacchi #define	USB_TOPO_META_DEFAULT_FLAGS	TOPO_USB_M_ACPI_MATCH
123672fc84aSRobert Mustacchi 
124672fc84aSRobert Mustacchi typedef enum {
125672fc84aSRobert Mustacchi 	TOPO_USB_P_START,
126672fc84aSRobert Mustacchi 	TOPO_USB_P_PORT,
127672fc84aSRobert Mustacchi 	TOPO_USB_P_LABEL,
128672fc84aSRobert Mustacchi 	TOPO_USB_P_PORT_TYPE,
129672fc84aSRobert Mustacchi 	TOPO_USB_P_ACPI_PATH
130672fc84aSRobert Mustacchi } topo_usb_parse_state_t;
131672fc84aSRobert Mustacchi 
132672fc84aSRobert Mustacchi typedef struct topo_usb_parse {
133672fc84aSRobert Mustacchi 	topo_usb_parse_state_t	tp_state;
134672fc84aSRobert Mustacchi 	topo_list_t		*tp_ports;
135672fc84aSRobert Mustacchi 	topo_usb_meta_port_t	*tp_cport;
136672fc84aSRobert Mustacchi 	topo_usb_meta_flags_t	tp_flags;
137672fc84aSRobert Mustacchi } topo_usb_parse_t;
138672fc84aSRobert Mustacchi 
139672fc84aSRobert Mustacchi /*
140672fc84aSRobert Mustacchi  * Read the next line in the file with content. Trim trailing and leading
141672fc84aSRobert Mustacchi  * whitespace and trim comments out. If this results in an empty line, read the
142672fc84aSRobert Mustacchi  * next. Returns zero if we hit EOF. Otherwise, returns one if data, or negative
143672fc84aSRobert Mustacchi  * one if an error occurred.
144672fc84aSRobert Mustacchi  */
145672fc84aSRobert Mustacchi static int
topo_usb_getline(topo_mod_t * mod,char * buf,size_t len,FILE * f,char ** first)146672fc84aSRobert Mustacchi topo_usb_getline(topo_mod_t *mod, char *buf, size_t len, FILE *f, char **first)
147672fc84aSRobert Mustacchi {
148672fc84aSRobert Mustacchi 	while (fgets(buf, len, f) != NULL) {
149672fc84aSRobert Mustacchi 		char *c;
150672fc84aSRobert Mustacchi 		size_t i;
151672fc84aSRobert Mustacchi 
152672fc84aSRobert Mustacchi 		if ((c = strrchr(buf, '\n')) == NULL) {
153672fc84aSRobert Mustacchi 			topo_mod_dprintf(mod, "failed to find new line in "
154672fc84aSRobert Mustacchi 			    "metadata file");
155672fc84aSRobert Mustacchi 			return (-1);
156672fc84aSRobert Mustacchi 		}
157672fc84aSRobert Mustacchi 
158672fc84aSRobert Mustacchi 		while (isspace(*c) != 0 && c >= buf) {
159672fc84aSRobert Mustacchi 			*c = '\0';
160672fc84aSRobert Mustacchi 			c--;
161672fc84aSRobert Mustacchi 			continue;
162672fc84aSRobert Mustacchi 		}
163672fc84aSRobert Mustacchi 
164672fc84aSRobert Mustacchi 		if ((c = strchr(buf, '#')) != 0) {
165672fc84aSRobert Mustacchi 			*c = '\0';
166672fc84aSRobert Mustacchi 		}
167672fc84aSRobert Mustacchi 
168672fc84aSRobert Mustacchi 		for (i = 0; buf[i] != '\0'; i++) {
169672fc84aSRobert Mustacchi 			if (isspace(buf[i]) == 0)
170672fc84aSRobert Mustacchi 				break;
171672fc84aSRobert Mustacchi 		}
172672fc84aSRobert Mustacchi 
173672fc84aSRobert Mustacchi 		if (buf[i] == '\0')
174672fc84aSRobert Mustacchi 			continue;
175672fc84aSRobert Mustacchi 		*first = &buf[i];
176672fc84aSRobert Mustacchi 		return (1);
177672fc84aSRobert Mustacchi 	}
178672fc84aSRobert Mustacchi 
179672fc84aSRobert Mustacchi 	return (0);
180672fc84aSRobert Mustacchi }
181672fc84aSRobert Mustacchi 
182672fc84aSRobert Mustacchi static boolean_t
topo_usb_parse_start(topo_mod_t * mod,topo_usb_parse_t * parse,const char * line)183672fc84aSRobert Mustacchi topo_usb_parse_start(topo_mod_t *mod, topo_usb_parse_t *parse, const char *line)
184672fc84aSRobert Mustacchi {
185672fc84aSRobert Mustacchi 	topo_usb_meta_port_t *port;
186672fc84aSRobert Mustacchi 
187672fc84aSRobert Mustacchi 	VERIFY3S(parse->tp_state, ==, TOPO_USB_P_START);
188672fc84aSRobert Mustacchi 	VERIFY3P(parse->tp_cport, ==, NULL);
189672fc84aSRobert Mustacchi 
190672fc84aSRobert Mustacchi 	if (strcasecmp(line, "disable-acpi") == 0) {
191672fc84aSRobert Mustacchi 		parse->tp_flags |= TOPO_USB_M_NO_ACPI;
192672fc84aSRobert Mustacchi 		parse->tp_flags &= ~TOPO_USB_M_ACPI_MATCH;
193672fc84aSRobert Mustacchi 		return (B_TRUE);
194672fc84aSRobert Mustacchi 	} else if (strcasecmp(line, "disable-acpi-match") == 0) {
195672fc84aSRobert Mustacchi 		parse->tp_flags &= ~TOPO_USB_M_ACPI_MATCH;
196672fc84aSRobert Mustacchi 		return (B_TRUE);
197672fc84aSRobert Mustacchi 	} else if (strcasecmp(line, "enable-acpi-match") == 0) {
198672fc84aSRobert Mustacchi 		parse->tp_flags |= TOPO_USB_M_ACPI_MATCH;
199672fc84aSRobert Mustacchi 		return (B_TRUE);
200672fc84aSRobert Mustacchi 	} else if (strcasecmp(line, "enable-metadata-match") == 0) {
201672fc84aSRobert Mustacchi 		parse->tp_flags |= TOPO_USB_M_METADATA_MATCH;
202672fc84aSRobert Mustacchi 		return (B_TRUE);
203672fc84aSRobert Mustacchi 	} else if (strcasecmp(line, "port") != 0) {
204672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "expected 'port', encountered %s",
205672fc84aSRobert Mustacchi 		    line);
206672fc84aSRobert Mustacchi 		return (B_FALSE);
207672fc84aSRobert Mustacchi 	}
208672fc84aSRobert Mustacchi 
209672fc84aSRobert Mustacchi 	if ((port = topo_mod_zalloc(mod, sizeof (topo_usb_meta_port_t))) ==
210672fc84aSRobert Mustacchi 	    NULL) {
211672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to allocate metadata port");
212672fc84aSRobert Mustacchi 		return (B_FALSE);
213672fc84aSRobert Mustacchi 	}
214672fc84aSRobert Mustacchi 	port->tmp_port_type = 0xff;
215672fc84aSRobert Mustacchi 
216672fc84aSRobert Mustacchi 	parse->tp_cport = port;
217672fc84aSRobert Mustacchi 	parse->tp_state = TOPO_USB_P_PORT;
218672fc84aSRobert Mustacchi 	return (B_TRUE);
219672fc84aSRobert Mustacchi }
220672fc84aSRobert Mustacchi 
221672fc84aSRobert Mustacchi static boolean_t
topo_usb_parse_port(topo_mod_t * mod,topo_usb_parse_t * parse,const char * line)222672fc84aSRobert Mustacchi topo_usb_parse_port(topo_mod_t *mod, topo_usb_parse_t *parse, const char *line)
223672fc84aSRobert Mustacchi {
224672fc84aSRobert Mustacchi 	VERIFY3S(parse->tp_state, ==, TOPO_USB_P_PORT);
225672fc84aSRobert Mustacchi 	VERIFY3P(parse->tp_cport, !=, NULL);
226672fc84aSRobert Mustacchi 
227672fc84aSRobert Mustacchi 	if (strcasecmp(line, "label") == 0) {
228672fc84aSRobert Mustacchi 		parse->tp_state = TOPO_USB_P_LABEL;
229672fc84aSRobert Mustacchi 	} else if (strcasecmp(line, "chassis") == 0) {
230672fc84aSRobert Mustacchi 		parse->tp_cport->tmp_flags |= TOPO_USB_F_CHASSIS;
231672fc84aSRobert Mustacchi 	} else if (strcasecmp(line, "external") == 0) {
232672fc84aSRobert Mustacchi 		parse->tp_cport->tmp_flags |= TOPO_USB_F_EXTERNAL;
233672fc84aSRobert Mustacchi 	} else if (strcasecmp(line, "internal") == 0) {
234672fc84aSRobert Mustacchi 		parse->tp_cport->tmp_flags |= TOPO_USB_F_INTERNAL;
235672fc84aSRobert Mustacchi 	} else if (strcasecmp(line, "port-type") == 0) {
236672fc84aSRobert Mustacchi 		parse->tp_state = TOPO_USB_P_PORT_TYPE;
237672fc84aSRobert Mustacchi 	} else if (strcasecmp(line, "acpi-path") == 0) {
238672fc84aSRobert Mustacchi 		parse->tp_state = TOPO_USB_P_ACPI_PATH;
239672fc84aSRobert Mustacchi 	} else if (strcasecmp(line, "end-port") == 0) {
240672fc84aSRobert Mustacchi 		topo_list_append(parse->tp_ports, parse->tp_cport);
241672fc84aSRobert Mustacchi 		parse->tp_cport = NULL;
242672fc84aSRobert Mustacchi 		parse->tp_state = TOPO_USB_P_START;
243672fc84aSRobert Mustacchi 	} else {
244672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "illegal directive in port block: %s",
245672fc84aSRobert Mustacchi 		    line);
246672fc84aSRobert Mustacchi 		return (B_FALSE);
247672fc84aSRobert Mustacchi 	}
248672fc84aSRobert Mustacchi 
249672fc84aSRobert Mustacchi 	return (B_TRUE);
250672fc84aSRobert Mustacchi }
251672fc84aSRobert Mustacchi 
252672fc84aSRobert Mustacchi static boolean_t
topo_usb_parse_label(topo_mod_t * mod,topo_usb_parse_t * parse,const char * line)253672fc84aSRobert Mustacchi topo_usb_parse_label(topo_mod_t *mod, topo_usb_parse_t *parse, const char *line)
254672fc84aSRobert Mustacchi {
255672fc84aSRobert Mustacchi 	size_t i, len;
256672fc84aSRobert Mustacchi 
257672fc84aSRobert Mustacchi 	VERIFY3S(parse->tp_state, ==, TOPO_USB_P_LABEL);
258672fc84aSRobert Mustacchi 
259672fc84aSRobert Mustacchi 	len = strlen(line);
260672fc84aSRobert Mustacchi 	for (i = 0; i < len; i++) {
261672fc84aSRobert Mustacchi 		if (isascii(line[i]) == 0 || isprint(line[i]) == 0) {
262*6597d6fcSRobert Mustacchi 			topo_mod_dprintf(mod, "label character %zu is "
263672fc84aSRobert Mustacchi 			    "invalid: 0x%x", i, line[i]);
264672fc84aSRobert Mustacchi 			return (B_FALSE);
265672fc84aSRobert Mustacchi 		}
266672fc84aSRobert Mustacchi 	}
267672fc84aSRobert Mustacchi 
268672fc84aSRobert Mustacchi 	if (parse->tp_cport->tmp_label != NULL) {
269672fc84aSRobert Mustacchi 		topo_mod_strfree(mod, parse->tp_cport->tmp_label);
270672fc84aSRobert Mustacchi 	}
271672fc84aSRobert Mustacchi 
272672fc84aSRobert Mustacchi 	if ((parse->tp_cport->tmp_label = topo_mod_strdup(mod, line)) == NULL) {
273672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to duplicate label for port");
274672fc84aSRobert Mustacchi 		return (B_FALSE);
275672fc84aSRobert Mustacchi 	}
276672fc84aSRobert Mustacchi 
277672fc84aSRobert Mustacchi 	parse->tp_state = TOPO_USB_P_PORT;
278672fc84aSRobert Mustacchi 
279672fc84aSRobert Mustacchi 	return (B_TRUE);
280672fc84aSRobert Mustacchi }
281672fc84aSRobert Mustacchi 
282672fc84aSRobert Mustacchi static boolean_t
topo_usb_parse_port_type(topo_mod_t * mod,topo_usb_parse_t * parse,const char * line)283672fc84aSRobert Mustacchi topo_usb_parse_port_type(topo_mod_t *mod, topo_usb_parse_t *parse,
284672fc84aSRobert Mustacchi     const char *line)
285672fc84aSRobert Mustacchi {
286672fc84aSRobert Mustacchi 	unsigned long val;
287672fc84aSRobert Mustacchi 	char *eptr;
288672fc84aSRobert Mustacchi 
289672fc84aSRobert Mustacchi 	VERIFY3S(parse->tp_state, ==, TOPO_USB_P_PORT_TYPE);
290672fc84aSRobert Mustacchi 
291672fc84aSRobert Mustacchi 	errno = 0;
292672fc84aSRobert Mustacchi 	val = strtoul(line, &eptr, 0);
293022bfefbSJohn Levon 	if (errno != 0 || *eptr != '\0' || val >= UINT_MAX) {
294672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "encountered bad value for port-type "
295672fc84aSRobert Mustacchi 		    "line: %s", line);
296672fc84aSRobert Mustacchi 		return (B_FALSE);
297672fc84aSRobert Mustacchi 	}
298672fc84aSRobert Mustacchi 
299672fc84aSRobert Mustacchi 	parse->tp_cport->tmp_port_type = (uint_t)val;
300672fc84aSRobert Mustacchi 
301672fc84aSRobert Mustacchi 	parse->tp_state = TOPO_USB_P_PORT;
302672fc84aSRobert Mustacchi 	return (B_TRUE);
303672fc84aSRobert Mustacchi }
304672fc84aSRobert Mustacchi 
305672fc84aSRobert Mustacchi static boolean_t
topo_usb_parse_path(topo_mod_t * mod,topo_usb_parse_t * parse,topo_usb_path_type_t ptype,const char * line)306672fc84aSRobert Mustacchi topo_usb_parse_path(topo_mod_t *mod, topo_usb_parse_t *parse,
307672fc84aSRobert Mustacchi     topo_usb_path_type_t ptype, const char *line)
308672fc84aSRobert Mustacchi {
309672fc84aSRobert Mustacchi 	char *fspath;
310672fc84aSRobert Mustacchi 	topo_usb_meta_port_path_t *path;
311672fc84aSRobert Mustacchi 
312672fc84aSRobert Mustacchi 	VERIFY(parse->tp_state == TOPO_USB_P_ACPI_PATH);
313672fc84aSRobert Mustacchi 	VERIFY3P(parse->tp_cport, !=, NULL);
314672fc84aSRobert Mustacchi 
315672fc84aSRobert Mustacchi 	if ((fspath = topo_mod_strdup(mod, line)) == NULL) {
316672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to duplicate path");
317672fc84aSRobert Mustacchi 		return (B_FALSE);
318672fc84aSRobert Mustacchi 	}
319672fc84aSRobert Mustacchi 
320672fc84aSRobert Mustacchi 	if ((path = topo_mod_zalloc(mod, sizeof (topo_usb_meta_port_path_t))) ==
321672fc84aSRobert Mustacchi 	    NULL) {
322672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to allocate meta port path "
323672fc84aSRobert Mustacchi 		    "structure");
324672fc84aSRobert Mustacchi 		topo_mod_strfree(mod, fspath);
325672fc84aSRobert Mustacchi 		return (B_FALSE);
326672fc84aSRobert Mustacchi 	}
327672fc84aSRobert Mustacchi 
328672fc84aSRobert Mustacchi 	path->tmpp_type = ptype;
329672fc84aSRobert Mustacchi 	path->tmpp_path = fspath;
330672fc84aSRobert Mustacchi 
331672fc84aSRobert Mustacchi 	topo_list_append(&parse->tp_cport->tmp_paths, path);
332672fc84aSRobert Mustacchi 
333672fc84aSRobert Mustacchi 	parse->tp_state = TOPO_USB_P_PORT;
334672fc84aSRobert Mustacchi 	return (B_TRUE);
335672fc84aSRobert Mustacchi }
336672fc84aSRobert Mustacchi 
337672fc84aSRobert Mustacchi 
338672fc84aSRobert Mustacchi void
topo_usb_free_metadata(topo_mod_t * mod,topo_list_t * metadata)339672fc84aSRobert Mustacchi topo_usb_free_metadata(topo_mod_t *mod, topo_list_t *metadata)
340672fc84aSRobert Mustacchi {
341672fc84aSRobert Mustacchi 	topo_usb_meta_port_t *mp;
342672fc84aSRobert Mustacchi 
343672fc84aSRobert Mustacchi 	while ((mp = topo_list_next(metadata)) != NULL) {
344672fc84aSRobert Mustacchi 		topo_usb_meta_port_path_t *path;
345672fc84aSRobert Mustacchi 
346672fc84aSRobert Mustacchi 		while ((path = topo_list_next((&mp->tmp_paths))) != NULL) {
347672fc84aSRobert Mustacchi 			topo_list_delete(&mp->tmp_paths, path);
348672fc84aSRobert Mustacchi 			topo_mod_strfree(mod, path->tmpp_path);
349672fc84aSRobert Mustacchi 			topo_mod_free(mod, path,
350672fc84aSRobert Mustacchi 			    sizeof (topo_usb_meta_port_path_t));
351672fc84aSRobert Mustacchi 		}
352672fc84aSRobert Mustacchi 
353672fc84aSRobert Mustacchi 		topo_list_delete(metadata, mp);
354672fc84aSRobert Mustacchi 		topo_mod_strfree(mod, mp->tmp_label);
355672fc84aSRobert Mustacchi 		topo_mod_free(mod, mp, sizeof (topo_usb_meta_port_t));
356672fc84aSRobert Mustacchi 	}
357672fc84aSRobert Mustacchi }
358672fc84aSRobert Mustacchi 
359672fc84aSRobert Mustacchi int
topo_usb_load_metadata(topo_mod_t * mod,tnode_t * pnode,topo_list_t * list,topo_usb_meta_flags_t * flagsp)360672fc84aSRobert Mustacchi topo_usb_load_metadata(topo_mod_t *mod, tnode_t *pnode, topo_list_t *list,
361672fc84aSRobert Mustacchi     topo_usb_meta_flags_t *flagsp)
362672fc84aSRobert Mustacchi {
363672fc84aSRobert Mustacchi 	int fd;
364672fc84aSRobert Mustacchi 	FILE *f = NULL;
365672fc84aSRobert Mustacchi 	char buf[TOPO_USB_META_LINE_MAX], *first, *prod;
366672fc84aSRobert Mustacchi 	int ret;
367672fc84aSRobert Mustacchi 	topo_usb_parse_t parse;
368672fc84aSRobert Mustacchi 	char pbuf[PATH_MAX];
369672fc84aSRobert Mustacchi 
370672fc84aSRobert Mustacchi 	*flagsp = USB_TOPO_META_DEFAULT_FLAGS;
371672fc84aSRobert Mustacchi 
372672fc84aSRobert Mustacchi 	/*
373672fc84aSRobert Mustacchi 	 * If no product string, just leave it as is and don't attempt to get
374672fc84aSRobert Mustacchi 	 * metadata.
375672fc84aSRobert Mustacchi 	 */
376672fc84aSRobert Mustacchi 	if ((topo_prop_get_string(pnode, FM_FMRI_AUTHORITY,
377672fc84aSRobert Mustacchi 	    FM_FMRI_AUTH_PRODUCT, &prod, &ret)) != 0) {
378672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "skipping metadata load: failed to get "
379672fc84aSRobert Mustacchi 		    "auth");
380672fc84aSRobert Mustacchi 		return (0);
381672fc84aSRobert Mustacchi 	}
382672fc84aSRobert Mustacchi 
383672fc84aSRobert Mustacchi 	if (snprintf(pbuf, sizeof (pbuf), "maps/%s-usb.usbtopo", prod) >=
384672fc84aSRobert Mustacchi 	    sizeof (pbuf)) {
385672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "skipping metadata load: product name "
386672fc84aSRobert Mustacchi 		    "too long");
387672fc84aSRobert Mustacchi 		topo_mod_strfree(mod, prod);
388672fc84aSRobert Mustacchi 		return (0);
389672fc84aSRobert Mustacchi 	}
390672fc84aSRobert Mustacchi 	topo_mod_strfree(mod, prod);
391672fc84aSRobert Mustacchi 
392672fc84aSRobert Mustacchi 	if ((fd = topo_mod_file_search(mod, pbuf, O_RDONLY)) < 0) {
393672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "skipping metadata load: couldn't find "
394672fc84aSRobert Mustacchi 		    "%s", pbuf);
395672fc84aSRobert Mustacchi 		return (0);
396672fc84aSRobert Mustacchi 	}
397672fc84aSRobert Mustacchi 
398672fc84aSRobert Mustacchi 
399672fc84aSRobert Mustacchi 	if ((f = fdopen(fd, "r")) == NULL) {
400672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to fdopen metadata file %s: %s",
401672fc84aSRobert Mustacchi 		    pbuf, strerror(errno));
402672fc84aSRobert Mustacchi 		VERIFY0(close(fd));
403672fc84aSRobert Mustacchi 		ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
404672fc84aSRobert Mustacchi 		goto err;
405672fc84aSRobert Mustacchi 	}
406672fc84aSRobert Mustacchi 
407672fc84aSRobert Mustacchi 	bzero(&parse, sizeof (parse));
408672fc84aSRobert Mustacchi 	parse.tp_ports = list;
409672fc84aSRobert Mustacchi 	parse.tp_state = TOPO_USB_P_START;
410672fc84aSRobert Mustacchi 
411672fc84aSRobert Mustacchi 	while ((ret = topo_usb_getline(mod, buf, sizeof (buf), f, &first)) !=
412672fc84aSRobert Mustacchi 	    0) {
413672fc84aSRobert Mustacchi 		if (ret == -1) {
414672fc84aSRobert Mustacchi 			ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
415672fc84aSRobert Mustacchi 			goto err;
416672fc84aSRobert Mustacchi 		}
417672fc84aSRobert Mustacchi 
418672fc84aSRobert Mustacchi 		switch (parse.tp_state) {
419672fc84aSRobert Mustacchi 		case TOPO_USB_P_START:
420672fc84aSRobert Mustacchi 			if (!topo_usb_parse_start(mod, &parse, first)) {
421672fc84aSRobert Mustacchi 				ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
422672fc84aSRobert Mustacchi 				goto err;
423672fc84aSRobert Mustacchi 			}
424672fc84aSRobert Mustacchi 			break;
425672fc84aSRobert Mustacchi 		case TOPO_USB_P_PORT:
426672fc84aSRobert Mustacchi 			if (!topo_usb_parse_port(mod, &parse, first)) {
427672fc84aSRobert Mustacchi 				ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
428672fc84aSRobert Mustacchi 				goto err;
429672fc84aSRobert Mustacchi 			}
430672fc84aSRobert Mustacchi 			break;
431672fc84aSRobert Mustacchi 		case TOPO_USB_P_LABEL:
432672fc84aSRobert Mustacchi 				if (!topo_usb_parse_label(mod, &parse, first)) {
433672fc84aSRobert Mustacchi 				ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
434672fc84aSRobert Mustacchi 				goto err;
435672fc84aSRobert Mustacchi 			}
436672fc84aSRobert Mustacchi 			break;
437672fc84aSRobert Mustacchi 		case TOPO_USB_P_PORT_TYPE:
438672fc84aSRobert Mustacchi 			if (!topo_usb_parse_port_type(mod, &parse, first)) {
439672fc84aSRobert Mustacchi 				ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
440672fc84aSRobert Mustacchi 				goto err;
441672fc84aSRobert Mustacchi 			}
442672fc84aSRobert Mustacchi 			break;
443672fc84aSRobert Mustacchi 
444672fc84aSRobert Mustacchi 		case TOPO_USB_P_ACPI_PATH:
445672fc84aSRobert Mustacchi 			if (!topo_usb_parse_path(mod, &parse, TOPO_USB_T_ACPI,
446672fc84aSRobert Mustacchi 			    first)) {
447672fc84aSRobert Mustacchi 				ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
448672fc84aSRobert Mustacchi 				goto err;
449672fc84aSRobert Mustacchi 			}
450672fc84aSRobert Mustacchi 			break;
451672fc84aSRobert Mustacchi 		}
452672fc84aSRobert Mustacchi 	}
453672fc84aSRobert Mustacchi 
454672fc84aSRobert Mustacchi 	if (parse.tp_state != TOPO_USB_P_START) {
455672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "metadata file didn't end in correct "
456672fc84aSRobert Mustacchi 		    "state, failing");
457672fc84aSRobert Mustacchi 		ret = topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM);
458672fc84aSRobert Mustacchi 		goto err;
459672fc84aSRobert Mustacchi 	}
460672fc84aSRobert Mustacchi 
461672fc84aSRobert Mustacchi 	topo_mod_dprintf(mod, "successfully loaded metadata %s", pbuf);
462672fc84aSRobert Mustacchi 	VERIFY0(fclose(f));
463672fc84aSRobert Mustacchi 	*flagsp = parse.tp_flags;
464672fc84aSRobert Mustacchi 	return (0);
465672fc84aSRobert Mustacchi 
466672fc84aSRobert Mustacchi err:
467672fc84aSRobert Mustacchi 	if (f != NULL)
468672fc84aSRobert Mustacchi 		VERIFY0(fclose(f));
469672fc84aSRobert Mustacchi 	topo_usb_free_metadata(mod, list);
470672fc84aSRobert Mustacchi 	return (ret);
471672fc84aSRobert Mustacchi }
472