xref: /openbmc/u-boot/drivers/input/i8042.c (revision 2a055ea53260ac8addeeb94eb671172844bc9106)
183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
216b195c8SJean-Christophe PLAGNIOL-VILLARD /*
316b195c8SJean-Christophe PLAGNIOL-VILLARD  * (C) Copyright 2002 ELTEC Elektronik AG
416b195c8SJean-Christophe PLAGNIOL-VILLARD  * Frank Gottschling <fgottschling@eltec.de>
516b195c8SJean-Christophe PLAGNIOL-VILLARD  */
616b195c8SJean-Christophe PLAGNIOL-VILLARD 
716b195c8SJean-Christophe PLAGNIOL-VILLARD /* i8042.c - Intel 8042 keyboard driver routines */
816b195c8SJean-Christophe PLAGNIOL-VILLARD 
916b195c8SJean-Christophe PLAGNIOL-VILLARD #include <common.h>
10dcbf8257SSimon Glass #include <dm.h>
11dcbf8257SSimon Glass #include <errno.h>
1216b195c8SJean-Christophe PLAGNIOL-VILLARD #include <i8042.h>
132ec739dbSSimon Glass #include <input.h>
14dcbf8257SSimon Glass #include <keyboard.h>
152ec739dbSSimon Glass #include <asm/io.h>
1616b195c8SJean-Christophe PLAGNIOL-VILLARD 
17011d89d6SSimon Glass DECLARE_GLOBAL_DATA_PTR;
18011d89d6SSimon Glass 
1916b195c8SJean-Christophe PLAGNIOL-VILLARD /* defines */
20835dd000SBin Meng #define in8(p)		inb(p)
21835dd000SBin Meng #define out8(p, v)	outb(v, p)
2216b195c8SJean-Christophe PLAGNIOL-VILLARD 
23011d89d6SSimon Glass enum {
24011d89d6SSimon Glass 	QUIRK_DUP_POR	= 1 << 0,
25011d89d6SSimon Glass };
26011d89d6SSimon Glass 
2716b195c8SJean-Christophe PLAGNIOL-VILLARD /* locals */
28dcbf8257SSimon Glass struct i8042_kbd_priv {
29dcbf8257SSimon Glass 	bool extended;	/* true if an extended keycode is expected next */
30011d89d6SSimon Glass 	int quirks;	/* quirks that we support */
31dcbf8257SSimon Glass };
3216b195c8SJean-Christophe PLAGNIOL-VILLARD 
33dd4a5b22SGabe Black static unsigned char ext_key_map[] = {
3416b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x1c, /* keypad enter */
3516b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x1d, /* right control */
3616b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x35, /* keypad slash */
3716b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x37, /* print screen */
3816b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x38, /* right alt */
3916b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x46, /* break */
4016b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x47, /* editpad home */
4116b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x48, /* editpad up */
4216b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x49, /* editpad pgup */
4316b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x4b, /* editpad left */
4416b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x4d, /* editpad right */
4516b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x4f, /* editpad end */
4616b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x50, /* editpad dn */
4716b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x51, /* editpad pgdn */
4816b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x52, /* editpad ins */
4916b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x53, /* editpad del */
5016b195c8SJean-Christophe PLAGNIOL-VILLARD 	0x00  /* map end */
5116b195c8SJean-Christophe PLAGNIOL-VILLARD 	};
5216b195c8SJean-Christophe PLAGNIOL-VILLARD 
kbd_input_empty(void)533928d66aSBin Meng static int kbd_input_empty(void)
543928d66aSBin Meng {
55835dd000SBin Meng 	int kbd_timeout = KBD_TIMEOUT * 1000;
563928d66aSBin Meng 
57835dd000SBin Meng 	while ((in8(I8042_STS_REG) & STATUS_IBF) && kbd_timeout--)
583928d66aSBin Meng 		udelay(1);
593928d66aSBin Meng 
60835dd000SBin Meng 	return kbd_timeout != -1;
613928d66aSBin Meng }
623928d66aSBin Meng 
kbd_output_full(void)63835dd000SBin Meng static int kbd_output_full(void)
643928d66aSBin Meng {
65835dd000SBin Meng 	int kbd_timeout = KBD_TIMEOUT * 1000;
663928d66aSBin Meng 
67835dd000SBin Meng 	while (((in8(I8042_STS_REG) & STATUS_OBF) == 0) && kbd_timeout--)
683928d66aSBin Meng 		udelay(1);
693928d66aSBin Meng 
70835dd000SBin Meng 	return kbd_timeout != -1;
713928d66aSBin Meng }
723928d66aSBin Meng 
73dcbf8257SSimon Glass /**
74dcbf8257SSimon Glass  * check_leds() - Check the keyboard LEDs and update them it needed
75dcbf8257SSimon Glass  *
76dcbf8257SSimon Glass  * @ret:	Value to return
77dcbf8257SSimon Glass  * @return value of @ret
78dcbf8257SSimon Glass  */
i8042_kbd_update_leds(struct udevice * dev,int leds)79dcbf8257SSimon Glass static int i8042_kbd_update_leds(struct udevice *dev, int leds)
803928d66aSBin Meng {
813928d66aSBin Meng 	kbd_input_empty();
82835dd000SBin Meng 	out8(I8042_DATA_REG, CMD_SET_KBD_LED);
833928d66aSBin Meng 	kbd_input_empty();
84dcbf8257SSimon Glass 	out8(I8042_DATA_REG, leds & 0x7);
85dcbf8257SSimon Glass 
86dcbf8257SSimon Glass 	return 0;
873928d66aSBin Meng }
883928d66aSBin Meng 
kbd_write(int reg,int value)8931d38ee6SSimon Glass static int kbd_write(int reg, int value)
9031d38ee6SSimon Glass {
9131d38ee6SSimon Glass 	if (!kbd_input_empty())
9231d38ee6SSimon Glass 		return -1;
9331d38ee6SSimon Glass 	out8(reg, value);
9431d38ee6SSimon Glass 
9531d38ee6SSimon Glass 	return 0;
9631d38ee6SSimon Glass }
9731d38ee6SSimon Glass 
kbd_read(int reg)9831d38ee6SSimon Glass static int kbd_read(int reg)
9931d38ee6SSimon Glass {
10031d38ee6SSimon Glass 	if (!kbd_output_full())
10131d38ee6SSimon Glass 		return -1;
10231d38ee6SSimon Glass 
10331d38ee6SSimon Glass 	return in8(reg);
10431d38ee6SSimon Glass }
10531d38ee6SSimon Glass 
kbd_cmd_read(int cmd)10631d38ee6SSimon Glass static int kbd_cmd_read(int cmd)
10731d38ee6SSimon Glass {
10831d38ee6SSimon Glass 	if (kbd_write(I8042_CMD_REG, cmd))
10931d38ee6SSimon Glass 		return -1;
11031d38ee6SSimon Glass 
11131d38ee6SSimon Glass 	return kbd_read(I8042_DATA_REG);
11231d38ee6SSimon Glass }
11331d38ee6SSimon Glass 
kbd_cmd_write(int cmd,int data)11431d38ee6SSimon Glass static int kbd_cmd_write(int cmd, int data)
11531d38ee6SSimon Glass {
11631d38ee6SSimon Glass 	if (kbd_write(I8042_CMD_REG, cmd))
11731d38ee6SSimon Glass 		return -1;
11831d38ee6SSimon Glass 
11931d38ee6SSimon Glass 	return kbd_write(I8042_DATA_REG, data);
12031d38ee6SSimon Glass }
12131d38ee6SSimon Glass 
kbd_reset(int quirk)122011d89d6SSimon Glass static int kbd_reset(int quirk)
1233928d66aSBin Meng {
12431d38ee6SSimon Glass 	int config;
1257d96166bSBin Meng 
1267d96166bSBin Meng 	/* controller self test */
12731d38ee6SSimon Glass 	if (kbd_cmd_read(CMD_SELF_TEST) != KBC_TEST_OK)
1284f087bacSSimon Glass 		goto err;
1293928d66aSBin Meng 
1307d96166bSBin Meng 	/* keyboard reset */
13131d38ee6SSimon Glass 	if (kbd_write(I8042_DATA_REG, CMD_RESET_KBD) ||
13231d38ee6SSimon Glass 	    kbd_read(I8042_DATA_REG) != KBD_ACK ||
13331d38ee6SSimon Glass 	    kbd_read(I8042_DATA_REG) != KBD_POR)
1344f087bacSSimon Glass 		goto err;
1353928d66aSBin Meng 
1368226a3e9SSimon Glass 	if (kbd_write(I8042_DATA_REG, CMD_DRAIN_OUTPUT) ||
1378226a3e9SSimon Glass 	    kbd_read(I8042_DATA_REG) != KBD_ACK)
1388226a3e9SSimon Glass 		goto err;
1398226a3e9SSimon Glass 
1407d96166bSBin Meng 	/* set AT translation and disable irq */
14131d38ee6SSimon Glass 	config = kbd_cmd_read(CMD_RD_CONFIG);
14231d38ee6SSimon Glass 	if (config == -1)
1434f087bacSSimon Glass 		goto err;
14431d38ee6SSimon Glass 
145011d89d6SSimon Glass 	/* Sometimes get a second byte */
146011d89d6SSimon Glass 	else if ((quirk & QUIRK_DUP_POR) && config == KBD_POR)
147011d89d6SSimon Glass 		config = kbd_cmd_read(CMD_RD_CONFIG);
148011d89d6SSimon Glass 
1497d96166bSBin Meng 	config |= CONFIG_AT_TRANS;
1507d96166bSBin Meng 	config &= ~(CONFIG_KIRQ_EN | CONFIG_MIRQ_EN);
15131d38ee6SSimon Glass 	if (kbd_cmd_write(CMD_WR_CONFIG, config))
1524f087bacSSimon Glass 		goto err;
1533928d66aSBin Meng 
1547d96166bSBin Meng 	/* enable keyboard */
15531d38ee6SSimon Glass 	if (kbd_write(I8042_CMD_REG, CMD_KBD_EN) ||
15631d38ee6SSimon Glass 	    !kbd_input_empty())
1574f087bacSSimon Glass 		goto err;
1583928d66aSBin Meng 
1593928d66aSBin Meng 	return 0;
1604f087bacSSimon Glass err:
1614f087bacSSimon Glass 	debug("%s: Keyboard failure\n", __func__);
1624f087bacSSimon Glass 	return -1;
1633928d66aSBin Meng }
1643928d66aSBin Meng 
kbd_controller_present(void)16522e0f5a9SGabe Black static int kbd_controller_present(void)
16622e0f5a9SGabe Black {
167835dd000SBin Meng 	return in8(I8042_STS_REG) != 0xff;
16822e0f5a9SGabe Black }
16922e0f5a9SGabe Black 
170*165be50fSSimon Glass /** Flush all buffer from keyboard controller to host*/
i8042_flush(void)171*165be50fSSimon Glass static void i8042_flush(void)
17245fe668fSLouis Yung-Chieh Lo {
17345fe668fSLouis Yung-Chieh Lo 	int timeout;
17445fe668fSLouis Yung-Chieh Lo 
17545fe668fSLouis Yung-Chieh Lo 	/*
176835dd000SBin Meng 	 * The delay is to give the keyboard controller some time
177835dd000SBin Meng 	 * to fill the next byte.
17845fe668fSLouis Yung-Chieh Lo 	 */
17945fe668fSLouis Yung-Chieh Lo 	while (1) {
18045fe668fSLouis Yung-Chieh Lo 		timeout = 100;	/* wait for no longer than 100us */
181835dd000SBin Meng 		while (timeout > 0 && !(in8(I8042_STS_REG) & STATUS_OBF)) {
18245fe668fSLouis Yung-Chieh Lo 			udelay(1);
18345fe668fSLouis Yung-Chieh Lo 			timeout--;
18445fe668fSLouis Yung-Chieh Lo 		}
18545fe668fSLouis Yung-Chieh Lo 
186835dd000SBin Meng 		/* Try to pull next byte if not timeout */
187835dd000SBin Meng 		if (in8(I8042_STS_REG) & STATUS_OBF)
18845fe668fSLouis Yung-Chieh Lo 			in8(I8042_DATA_REG);
18945fe668fSLouis Yung-Chieh Lo 		else
19045fe668fSLouis Yung-Chieh Lo 			break;
19145fe668fSLouis Yung-Chieh Lo 	}
19245fe668fSLouis Yung-Chieh Lo }
19345fe668fSLouis Yung-Chieh Lo 
194*165be50fSSimon Glass /**
195*165be50fSSimon Glass  * Disables the keyboard so that key strokes no longer generate scancodes to
196*165be50fSSimon Glass  * the host.
197*165be50fSSimon Glass  *
198*165be50fSSimon Glass  * @return 0 if ok, -1 if keyboard input was found while disabling
199*165be50fSSimon Glass  */
i8042_disable(void)200*165be50fSSimon Glass static int i8042_disable(void)
20145fe668fSLouis Yung-Chieh Lo {
20245fe668fSLouis Yung-Chieh Lo 	if (kbd_input_empty() == 0)
20345fe668fSLouis Yung-Chieh Lo 		return -1;
20445fe668fSLouis Yung-Chieh Lo 
20545fe668fSLouis Yung-Chieh Lo 	/* Disable keyboard */
206835dd000SBin Meng 	out8(I8042_CMD_REG, CMD_KBD_DIS);
20745fe668fSLouis Yung-Chieh Lo 
20845fe668fSLouis Yung-Chieh Lo 	if (kbd_input_empty() == 0)
20945fe668fSLouis Yung-Chieh Lo 		return -1;
21045fe668fSLouis Yung-Chieh Lo 
21145fe668fSLouis Yung-Chieh Lo 	return 0;
21245fe668fSLouis Yung-Chieh Lo }
21345fe668fSLouis Yung-Chieh Lo 
i8042_kbd_check(struct input_config * input)2142ec739dbSSimon Glass static int i8042_kbd_check(struct input_config *input)
2152ec739dbSSimon Glass {
216dcbf8257SSimon Glass 	struct i8042_kbd_priv *priv = dev_get_priv(input->dev);
217dcbf8257SSimon Glass 
2182ec739dbSSimon Glass 	if ((in8(I8042_STS_REG) & STATUS_OBF) == 0) {
2192ec739dbSSimon Glass 		return 0;
2202ec739dbSSimon Glass 	} else {
2212ec739dbSSimon Glass 		bool release = false;
2222ec739dbSSimon Glass 		int scan_code;
2232ec739dbSSimon Glass 		int i;
2242ec739dbSSimon Glass 
2252ec739dbSSimon Glass 		scan_code = in8(I8042_DATA_REG);
2262ec739dbSSimon Glass 		if (scan_code == 0xfa) {
2272ec739dbSSimon Glass 			return 0;
2282ec739dbSSimon Glass 		} else if (scan_code == 0xe0) {
229dcbf8257SSimon Glass 			priv->extended = true;
2302ec739dbSSimon Glass 			return 0;
2312ec739dbSSimon Glass 		}
2322ec739dbSSimon Glass 		if (scan_code & 0x80) {
2332ec739dbSSimon Glass 			scan_code &= 0x7f;
2342ec739dbSSimon Glass 			release = true;
2352ec739dbSSimon Glass 		}
236dcbf8257SSimon Glass 		if (priv->extended) {
237dcbf8257SSimon Glass 			priv->extended = false;
2382ec739dbSSimon Glass 			for (i = 0; ext_key_map[i]; i++) {
2392ec739dbSSimon Glass 				if (ext_key_map[i] == scan_code) {
2402ec739dbSSimon Glass 					scan_code = 0x60 + i;
2412ec739dbSSimon Glass 					break;
2422ec739dbSSimon Glass 				}
2432ec739dbSSimon Glass 			}
2442ec739dbSSimon Glass 			/* not found ? */
2452ec739dbSSimon Glass 			if (!ext_key_map[i])
2462ec739dbSSimon Glass 				return 0;
2472ec739dbSSimon Glass 		}
2482ec739dbSSimon Glass 
249dcbf8257SSimon Glass 		input_add_keycode(input, scan_code, release);
2502ec739dbSSimon Glass 		return 1;
2512ec739dbSSimon Glass 	}
2522ec739dbSSimon Glass }
2532ec739dbSSimon Glass 
254835dd000SBin Meng /* i8042_kbd_init - reset keyboard and init state flags */
i8042_start(struct udevice * dev)255dcbf8257SSimon Glass static int i8042_start(struct udevice *dev)
25616b195c8SJean-Christophe PLAGNIOL-VILLARD {
257dcbf8257SSimon Glass 	struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
258011d89d6SSimon Glass 	struct i8042_kbd_priv *priv = dev_get_priv(dev);
259dcbf8257SSimon Glass 	struct input_config *input = &uc_priv->input;
26016b195c8SJean-Christophe PLAGNIOL-VILLARD 	int keymap, try;
26116b195c8SJean-Christophe PLAGNIOL-VILLARD 	char *penv;
2622ec739dbSSimon Glass 	int ret;
26316b195c8SJean-Christophe PLAGNIOL-VILLARD 
264*165be50fSSimon Glass 	if (!kbd_controller_present()) {
265835dd000SBin Meng 		debug("i8042 keyboard controller is not present\n");
266dcbf8257SSimon Glass 		return -ENOENT;
267835dd000SBin Meng 	}
26822e0f5a9SGabe Black 
26916b195c8SJean-Christophe PLAGNIOL-VILLARD 	/* Init keyboard device (default US layout) */
27016b195c8SJean-Christophe PLAGNIOL-VILLARD 	keymap = KBD_US;
27100caae6dSSimon Glass 	penv = env_get("keymap");
272dd4a5b22SGabe Black 	if (penv != NULL) {
27316b195c8SJean-Christophe PLAGNIOL-VILLARD 		if (strncmp(penv, "de", 3) == 0)
27416b195c8SJean-Christophe PLAGNIOL-VILLARD 			keymap = KBD_GER;
27516b195c8SJean-Christophe PLAGNIOL-VILLARD 	}
27616b195c8SJean-Christophe PLAGNIOL-VILLARD 
277011d89d6SSimon Glass 	for (try = 0; kbd_reset(priv->quirks) != 0; try++) {
278c5d257f9SSimon Glass 		if (try >= KBD_RESET_TRIES)
279c5d257f9SSimon Glass 			return -1;
280c5d257f9SSimon Glass 	}
281c5d257f9SSimon Glass 
282dcbf8257SSimon Glass 	ret = input_add_tables(input, keymap == KBD_GER);
2832ec739dbSSimon Glass 	if (ret)
2842ec739dbSSimon Glass 		return ret;
2852ec739dbSSimon Glass 
286dcbf8257SSimon Glass 	i8042_kbd_update_leds(dev, NORMAL);
287dcbf8257SSimon Glass 	debug("%s: started\n", __func__);
288835dd000SBin Meng 
28916b195c8SJean-Christophe PLAGNIOL-VILLARD 	return 0;
29016b195c8SJean-Christophe PLAGNIOL-VILLARD }
29116b195c8SJean-Christophe PLAGNIOL-VILLARD 
i8042_kbd_remove(struct udevice * dev)292*165be50fSSimon Glass static int i8042_kbd_remove(struct udevice *dev)
293*165be50fSSimon Glass {
294*165be50fSSimon Glass 	if (i8042_disable())
295*165be50fSSimon Glass 		log_debug("i8042_disable() failed. fine, continue.\n");
296*165be50fSSimon Glass 	i8042_flush();
297*165be50fSSimon Glass 
298*165be50fSSimon Glass 	return 0;
299*165be50fSSimon Glass }
300*165be50fSSimon Glass 
3012ec739dbSSimon Glass /**
302dcbf8257SSimon Glass  * Set up the i8042 keyboard. This is called by the stdio device handler
303835dd000SBin Meng  *
304dcbf8257SSimon Glass  * We want to do this init when the keyboard is actually used rather than
305dcbf8257SSimon Glass  * at start-up, since keyboard input may not currently be selected.
306dcbf8257SSimon Glass  *
307dcbf8257SSimon Glass  * Once the keyboard starts there will be a period during which we must
308dcbf8257SSimon Glass  * wait for the keyboard to init. We do this only when a key is first
309dcbf8257SSimon Glass  * read - see kbd_wait_for_fifo_init().
310dcbf8257SSimon Glass  *
311dcbf8257SSimon Glass  * @return 0 if ok, -ve on error
31216b195c8SJean-Christophe PLAGNIOL-VILLARD  */
i8042_kbd_probe(struct udevice * dev)313dcbf8257SSimon Glass static int i8042_kbd_probe(struct udevice *dev)
3142ec739dbSSimon Glass {
315dcbf8257SSimon Glass 	struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
316011d89d6SSimon Glass 	struct i8042_kbd_priv *priv = dev_get_priv(dev);
317dcbf8257SSimon Glass 	struct stdio_dev *sdev = &uc_priv->sdev;
318dcbf8257SSimon Glass 	struct input_config *input = &uc_priv->input;
319dcbf8257SSimon Glass 	int ret;
3202ec739dbSSimon Glass 
321e160f7d4SSimon Glass 	if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
322011d89d6SSimon Glass 			    "intel,duplicate-por"))
323011d89d6SSimon Glass 		priv->quirks |= QUIRK_DUP_POR;
324011d89d6SSimon Glass 
325dcbf8257SSimon Glass 	/* Register the device. i8042_start() will be called soon */
326dcbf8257SSimon Glass 	input->dev = dev;
327dcbf8257SSimon Glass 	input->read_keys = i8042_kbd_check;
328dcbf8257SSimon Glass 	input_allow_repeats(input, true);
329dcbf8257SSimon Glass 	strcpy(sdev->name, "i8042-kbd");
330dcbf8257SSimon Glass 	ret = input_stdio_register(sdev);
331dcbf8257SSimon Glass 	if (ret) {
332dcbf8257SSimon Glass 		debug("%s: input_stdio_register() failed\n", __func__);
3332ec739dbSSimon Glass 		return ret;
3342ec739dbSSimon Glass 	}
335dcbf8257SSimon Glass 	debug("%s: ready\n", __func__);
3362ec739dbSSimon Glass 
337dcbf8257SSimon Glass 	return 0;
33816b195c8SJean-Christophe PLAGNIOL-VILLARD }
339835dd000SBin Meng 
340dcbf8257SSimon Glass static const struct keyboard_ops i8042_kbd_ops = {
341dcbf8257SSimon Glass 	.start	= i8042_start,
342dcbf8257SSimon Glass 	.update_leds	= i8042_kbd_update_leds,
343dcbf8257SSimon Glass };
344dcbf8257SSimon Glass 
345dcbf8257SSimon Glass static const struct udevice_id i8042_kbd_ids[] = {
346dcbf8257SSimon Glass 	{ .compatible = "intel,i8042-keyboard" },
347dcbf8257SSimon Glass 	{ }
348dcbf8257SSimon Glass };
349dcbf8257SSimon Glass 
350dcbf8257SSimon Glass U_BOOT_DRIVER(i8042_kbd) = {
351dcbf8257SSimon Glass 	.name	= "i8042_kbd",
352dcbf8257SSimon Glass 	.id	= UCLASS_KEYBOARD,
353dcbf8257SSimon Glass 	.of_match = i8042_kbd_ids,
354dcbf8257SSimon Glass 	.probe = i8042_kbd_probe,
355*165be50fSSimon Glass 	.remove = i8042_kbd_remove,
356dcbf8257SSimon Glass 	.ops	= &i8042_kbd_ops,
357dcbf8257SSimon Glass 	.priv_auto_alloc_size = sizeof(struct i8042_kbd_priv),
358dcbf8257SSimon Glass };
359