diff options
author | Alexander Shiyan <shc_work@mail.ru> | 2012-12-03 13:23:31 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-01-16 02:00:38 -0500 |
commit | ec063899b7b308019afa9f5eb32f0a58a6c6ee53 (patch) | |
tree | cf4ad8c5076dc75113677b1bbd053704308a58de /drivers/tty/serial/sccnxp.c | |
parent | 496c907740ff083499f5449d2907af442e79ceb0 (diff) |
serial: sccnxp: Implement polling mode
This patch adds support for polling work mode, i.e. system is perform
periodical check chip status when IRQ-line is not connected to CPU.
Signed-off-by: Alexander Shiyan <shc_work@mail.ru>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/serial/sccnxp.c')
-rw-r--r-- | drivers/tty/serial/sccnxp.c | 148 |
1 files changed, 103 insertions, 45 deletions
diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index 2ced871becff..3a4c57e6ea1e 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/io.h> | 23 | #include <linux/io.h> |
24 | #include <linux/tty.h> | 24 | #include <linux/tty.h> |
25 | #include <linux/tty_flip.h> | 25 | #include <linux/tty_flip.h> |
26 | #include <linux/spinlock.h> | ||
26 | #include <linux/platform_device.h> | 27 | #include <linux/platform_device.h> |
27 | #include <linux/platform_data/sccnxp.h> | 28 | #include <linux/platform_data/sccnxp.h> |
28 | 29 | ||
@@ -106,6 +107,7 @@ enum { | |||
106 | struct sccnxp_port { | 107 | struct sccnxp_port { |
107 | struct uart_driver uart; | 108 | struct uart_driver uart; |
108 | struct uart_port port[SCCNXP_MAX_UARTS]; | 109 | struct uart_port port[SCCNXP_MAX_UARTS]; |
110 | bool opened[SCCNXP_MAX_UARTS]; | ||
109 | 111 | ||
110 | const char *name; | 112 | const char *name; |
111 | int irq; | 113 | int irq; |
@@ -122,7 +124,10 @@ struct sccnxp_port { | |||
122 | struct console console; | 124 | struct console console; |
123 | #endif | 125 | #endif |
124 | 126 | ||
125 | struct mutex sccnxp_mutex; | 127 | spinlock_t lock; |
128 | |||
129 | bool poll; | ||
130 | struct timer_list timer; | ||
126 | 131 | ||
127 | struct sccnxp_pdata pdata; | 132 | struct sccnxp_pdata pdata; |
128 | }; | 133 | }; |
@@ -371,31 +376,48 @@ static void sccnxp_handle_tx(struct uart_port *port) | |||
371 | uart_write_wakeup(port); | 376 | uart_write_wakeup(port); |
372 | } | 377 | } |
373 | 378 | ||
374 | static irqreturn_t sccnxp_ist(int irq, void *dev_id) | 379 | static void sccnxp_handle_events(struct sccnxp_port *s) |
375 | { | 380 | { |
376 | int i; | 381 | int i; |
377 | u8 isr; | 382 | u8 isr; |
378 | struct sccnxp_port *s = (struct sccnxp_port *)dev_id; | ||
379 | |||
380 | mutex_lock(&s->sccnxp_mutex); | ||
381 | 383 | ||
382 | for (;;) { | 384 | do { |
383 | isr = sccnxp_read(&s->port[0], SCCNXP_ISR_REG); | 385 | isr = sccnxp_read(&s->port[0], SCCNXP_ISR_REG); |
384 | isr &= s->imr; | 386 | isr &= s->imr; |
385 | if (!isr) | 387 | if (!isr) |
386 | break; | 388 | break; |
387 | 389 | ||
388 | dev_dbg(s->port[0].dev, "IRQ status: 0x%02x\n", isr); | ||
389 | |||
390 | for (i = 0; i < s->uart.nr; i++) { | 390 | for (i = 0; i < s->uart.nr; i++) { |
391 | if (isr & ISR_RXRDY(i)) | 391 | if (s->opened[i] && (isr & ISR_RXRDY(i))) |
392 | sccnxp_handle_rx(&s->port[i]); | 392 | sccnxp_handle_rx(&s->port[i]); |
393 | if (isr & ISR_TXRDY(i)) | 393 | if (s->opened[i] && (isr & ISR_TXRDY(i))) |
394 | sccnxp_handle_tx(&s->port[i]); | 394 | sccnxp_handle_tx(&s->port[i]); |
395 | } | 395 | } |
396 | } | 396 | } while (1); |
397 | } | ||
398 | |||
399 | static void sccnxp_timer(unsigned long data) | ||
400 | { | ||
401 | struct sccnxp_port *s = (struct sccnxp_port *)data; | ||
402 | unsigned long flags; | ||
397 | 403 | ||
398 | mutex_unlock(&s->sccnxp_mutex); | 404 | spin_lock_irqsave(&s->lock, flags); |
405 | sccnxp_handle_events(s); | ||
406 | spin_unlock_irqrestore(&s->lock, flags); | ||
407 | |||
408 | if (!timer_pending(&s->timer)) | ||
409 | mod_timer(&s->timer, jiffies + | ||
410 | usecs_to_jiffies(s->pdata.poll_time_us)); | ||
411 | } | ||
412 | |||
413 | static irqreturn_t sccnxp_ist(int irq, void *dev_id) | ||
414 | { | ||
415 | struct sccnxp_port *s = (struct sccnxp_port *)dev_id; | ||
416 | unsigned long flags; | ||
417 | |||
418 | spin_lock_irqsave(&s->lock, flags); | ||
419 | sccnxp_handle_events(s); | ||
420 | spin_unlock_irqrestore(&s->lock, flags); | ||
399 | 421 | ||
400 | return IRQ_HANDLED; | 422 | return IRQ_HANDLED; |
401 | } | 423 | } |
@@ -403,8 +425,9 @@ static irqreturn_t sccnxp_ist(int irq, void *dev_id) | |||
403 | static void sccnxp_start_tx(struct uart_port *port) | 425 | static void sccnxp_start_tx(struct uart_port *port) |
404 | { | 426 | { |
405 | struct sccnxp_port *s = dev_get_drvdata(port->dev); | 427 | struct sccnxp_port *s = dev_get_drvdata(port->dev); |
428 | unsigned long flags; | ||
406 | 429 | ||
407 | mutex_lock(&s->sccnxp_mutex); | 430 | spin_lock_irqsave(&s->lock, flags); |
408 | 431 | ||
409 | /* Set direction to output */ | 432 | /* Set direction to output */ |
410 | if (s->flags & SCCNXP_HAVE_IO) | 433 | if (s->flags & SCCNXP_HAVE_IO) |
@@ -412,7 +435,7 @@ static void sccnxp_start_tx(struct uart_port *port) | |||
412 | 435 | ||
413 | sccnxp_enable_irq(port, IMR_TXRDY); | 436 | sccnxp_enable_irq(port, IMR_TXRDY); |
414 | 437 | ||
415 | mutex_unlock(&s->sccnxp_mutex); | 438 | spin_unlock_irqrestore(&s->lock, flags); |
416 | } | 439 | } |
417 | 440 | ||
418 | static void sccnxp_stop_tx(struct uart_port *port) | 441 | static void sccnxp_stop_tx(struct uart_port *port) |
@@ -423,20 +446,22 @@ static void sccnxp_stop_tx(struct uart_port *port) | |||
423 | static void sccnxp_stop_rx(struct uart_port *port) | 446 | static void sccnxp_stop_rx(struct uart_port *port) |
424 | { | 447 | { |
425 | struct sccnxp_port *s = dev_get_drvdata(port->dev); | 448 | struct sccnxp_port *s = dev_get_drvdata(port->dev); |
449 | unsigned long flags; | ||
426 | 450 | ||
427 | mutex_lock(&s->sccnxp_mutex); | 451 | spin_lock_irqsave(&s->lock, flags); |
428 | sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE); | 452 | sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE); |
429 | mutex_unlock(&s->sccnxp_mutex); | 453 | spin_unlock_irqrestore(&s->lock, flags); |
430 | } | 454 | } |
431 | 455 | ||
432 | static unsigned int sccnxp_tx_empty(struct uart_port *port) | 456 | static unsigned int sccnxp_tx_empty(struct uart_port *port) |
433 | { | 457 | { |
434 | u8 val; | 458 | u8 val; |
459 | unsigned long flags; | ||
435 | struct sccnxp_port *s = dev_get_drvdata(port->dev); | 460 | struct sccnxp_port *s = dev_get_drvdata(port->dev); |
436 | 461 | ||
437 | mutex_lock(&s->sccnxp_mutex); | 462 | spin_lock_irqsave(&s->lock, flags); |
438 | val = sccnxp_port_read(port, SCCNXP_SR_REG); | 463 | val = sccnxp_port_read(port, SCCNXP_SR_REG); |
439 | mutex_unlock(&s->sccnxp_mutex); | 464 | spin_unlock_irqrestore(&s->lock, flags); |
440 | 465 | ||
441 | return (val & SR_TXEMT) ? TIOCSER_TEMT : 0; | 466 | return (val & SR_TXEMT) ? TIOCSER_TEMT : 0; |
442 | } | 467 | } |
@@ -449,28 +474,30 @@ static void sccnxp_enable_ms(struct uart_port *port) | |||
449 | static void sccnxp_set_mctrl(struct uart_port *port, unsigned int mctrl) | 474 | static void sccnxp_set_mctrl(struct uart_port *port, unsigned int mctrl) |
450 | { | 475 | { |
451 | struct sccnxp_port *s = dev_get_drvdata(port->dev); | 476 | struct sccnxp_port *s = dev_get_drvdata(port->dev); |
477 | unsigned long flags; | ||
452 | 478 | ||
453 | if (!(s->flags & SCCNXP_HAVE_IO)) | 479 | if (!(s->flags & SCCNXP_HAVE_IO)) |
454 | return; | 480 | return; |
455 | 481 | ||
456 | mutex_lock(&s->sccnxp_mutex); | 482 | spin_lock_irqsave(&s->lock, flags); |
457 | 483 | ||
458 | sccnxp_set_bit(port, DTR_OP, mctrl & TIOCM_DTR); | 484 | sccnxp_set_bit(port, DTR_OP, mctrl & TIOCM_DTR); |
459 | sccnxp_set_bit(port, RTS_OP, mctrl & TIOCM_RTS); | 485 | sccnxp_set_bit(port, RTS_OP, mctrl & TIOCM_RTS); |
460 | 486 | ||
461 | mutex_unlock(&s->sccnxp_mutex); | 487 | spin_unlock_irqrestore(&s->lock, flags); |
462 | } | 488 | } |
463 | 489 | ||
464 | static unsigned int sccnxp_get_mctrl(struct uart_port *port) | 490 | static unsigned int sccnxp_get_mctrl(struct uart_port *port) |
465 | { | 491 | { |
466 | u8 bitmask, ipr; | 492 | u8 bitmask, ipr; |
493 | unsigned long flags; | ||
467 | struct sccnxp_port *s = dev_get_drvdata(port->dev); | 494 | struct sccnxp_port *s = dev_get_drvdata(port->dev); |
468 | unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; | 495 | unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; |
469 | 496 | ||
470 | if (!(s->flags & SCCNXP_HAVE_IO)) | 497 | if (!(s->flags & SCCNXP_HAVE_IO)) |
471 | return mctrl; | 498 | return mctrl; |
472 | 499 | ||
473 | mutex_lock(&s->sccnxp_mutex); | 500 | spin_lock_irqsave(&s->lock, flags); |
474 | 501 | ||
475 | ipr = ~sccnxp_read(port, SCCNXP_IPCR_REG); | 502 | ipr = ~sccnxp_read(port, SCCNXP_IPCR_REG); |
476 | 503 | ||
@@ -499,7 +526,7 @@ static unsigned int sccnxp_get_mctrl(struct uart_port *port) | |||
499 | mctrl |= (ipr & bitmask) ? TIOCM_RNG : 0; | 526 | mctrl |= (ipr & bitmask) ? TIOCM_RNG : 0; |
500 | } | 527 | } |
501 | 528 | ||
502 | mutex_unlock(&s->sccnxp_mutex); | 529 | spin_unlock_irqrestore(&s->lock, flags); |
503 | 530 | ||
504 | return mctrl; | 531 | return mctrl; |
505 | } | 532 | } |
@@ -507,21 +534,23 @@ static unsigned int sccnxp_get_mctrl(struct uart_port *port) | |||
507 | static void sccnxp_break_ctl(struct uart_port *port, int break_state) | 534 | static void sccnxp_break_ctl(struct uart_port *port, int break_state) |
508 | { | 535 | { |
509 | struct sccnxp_port *s = dev_get_drvdata(port->dev); | 536 | struct sccnxp_port *s = dev_get_drvdata(port->dev); |
537 | unsigned long flags; | ||
510 | 538 | ||
511 | mutex_lock(&s->sccnxp_mutex); | 539 | spin_lock_irqsave(&s->lock, flags); |
512 | sccnxp_port_write(port, SCCNXP_CR_REG, break_state ? | 540 | sccnxp_port_write(port, SCCNXP_CR_REG, break_state ? |
513 | CR_CMD_START_BREAK : CR_CMD_STOP_BREAK); | 541 | CR_CMD_START_BREAK : CR_CMD_STOP_BREAK); |
514 | mutex_unlock(&s->sccnxp_mutex); | 542 | spin_unlock_irqrestore(&s->lock, flags); |
515 | } | 543 | } |
516 | 544 | ||
517 | static void sccnxp_set_termios(struct uart_port *port, | 545 | static void sccnxp_set_termios(struct uart_port *port, |
518 | struct ktermios *termios, struct ktermios *old) | 546 | struct ktermios *termios, struct ktermios *old) |
519 | { | 547 | { |
520 | struct sccnxp_port *s = dev_get_drvdata(port->dev); | 548 | struct sccnxp_port *s = dev_get_drvdata(port->dev); |
549 | unsigned long flags; | ||
521 | u8 mr1, mr2; | 550 | u8 mr1, mr2; |
522 | int baud; | 551 | int baud; |
523 | 552 | ||
524 | mutex_lock(&s->sccnxp_mutex); | 553 | spin_lock_irqsave(&s->lock, flags); |
525 | 554 | ||
526 | /* Mask termios capabilities we don't support */ | 555 | /* Mask termios capabilities we don't support */ |
527 | termios->c_cflag &= ~CMSPAR; | 556 | termios->c_cflag &= ~CMSPAR; |
@@ -588,20 +617,22 @@ static void sccnxp_set_termios(struct uart_port *port, | |||
588 | /* Update timeout according to new baud rate */ | 617 | /* Update timeout according to new baud rate */ |
589 | uart_update_timeout(port, termios->c_cflag, baud); | 618 | uart_update_timeout(port, termios->c_cflag, baud); |
590 | 619 | ||
620 | /* Report actual baudrate back to core */ | ||
591 | if (tty_termios_baud_rate(termios)) | 621 | if (tty_termios_baud_rate(termios)) |
592 | tty_termios_encode_baud_rate(termios, baud, baud); | 622 | tty_termios_encode_baud_rate(termios, baud, baud); |
593 | 623 | ||
594 | /* Enable RX & TX */ | 624 | /* Enable RX & TX */ |
595 | sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE); | 625 | sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE); |
596 | 626 | ||
597 | mutex_unlock(&s->sccnxp_mutex); | 627 | spin_unlock_irqrestore(&s->lock, flags); |
598 | } | 628 | } |
599 | 629 | ||
600 | static int sccnxp_startup(struct uart_port *port) | 630 | static int sccnxp_startup(struct uart_port *port) |
601 | { | 631 | { |
602 | struct sccnxp_port *s = dev_get_drvdata(port->dev); | 632 | struct sccnxp_port *s = dev_get_drvdata(port->dev); |
633 | unsigned long flags; | ||
603 | 634 | ||
604 | mutex_lock(&s->sccnxp_mutex); | 635 | spin_lock_irqsave(&s->lock, flags); |
605 | 636 | ||
606 | if (s->flags & SCCNXP_HAVE_IO) { | 637 | if (s->flags & SCCNXP_HAVE_IO) { |
607 | /* Outputs are controlled manually */ | 638 | /* Outputs are controlled manually */ |
@@ -620,7 +651,9 @@ static int sccnxp_startup(struct uart_port *port) | |||
620 | /* Enable RX interrupt */ | 651 | /* Enable RX interrupt */ |
621 | sccnxp_enable_irq(port, IMR_RXRDY); | 652 | sccnxp_enable_irq(port, IMR_RXRDY); |
622 | 653 | ||
623 | mutex_unlock(&s->sccnxp_mutex); | 654 | s->opened[port->line] = 1; |
655 | |||
656 | spin_unlock_irqrestore(&s->lock, flags); | ||
624 | 657 | ||
625 | return 0; | 658 | return 0; |
626 | } | 659 | } |
@@ -628,8 +661,11 @@ static int sccnxp_startup(struct uart_port *port) | |||
628 | static void sccnxp_shutdown(struct uart_port *port) | 661 | static void sccnxp_shutdown(struct uart_port *port) |
629 | { | 662 | { |
630 | struct sccnxp_port *s = dev_get_drvdata(port->dev); | 663 | struct sccnxp_port *s = dev_get_drvdata(port->dev); |
664 | unsigned long flags; | ||
665 | |||
666 | spin_lock_irqsave(&s->lock, flags); | ||
631 | 667 | ||
632 | mutex_lock(&s->sccnxp_mutex); | 668 | s->opened[port->line] = 0; |
633 | 669 | ||
634 | /* Disable interrupts */ | 670 | /* Disable interrupts */ |
635 | sccnxp_disable_irq(port, IMR_TXRDY | IMR_RXRDY); | 671 | sccnxp_disable_irq(port, IMR_TXRDY | IMR_RXRDY); |
@@ -641,7 +677,7 @@ static void sccnxp_shutdown(struct uart_port *port) | |||
641 | if (s->flags & SCCNXP_HAVE_IO) | 677 | if (s->flags & SCCNXP_HAVE_IO) |
642 | sccnxp_set_bit(port, DIR_OP, 0); | 678 | sccnxp_set_bit(port, DIR_OP, 0); |
643 | 679 | ||
644 | mutex_unlock(&s->sccnxp_mutex); | 680 | spin_unlock_irqrestore(&s->lock, flags); |
645 | } | 681 | } |
646 | 682 | ||
647 | static const char *sccnxp_type(struct uart_port *port) | 683 | static const char *sccnxp_type(struct uart_port *port) |
@@ -715,10 +751,11 @@ static void sccnxp_console_write(struct console *co, const char *c, unsigned n) | |||
715 | { | 751 | { |
716 | struct sccnxp_port *s = (struct sccnxp_port *)co->data; | 752 | struct sccnxp_port *s = (struct sccnxp_port *)co->data; |
717 | struct uart_port *port = &s->port[co->index]; | 753 | struct uart_port *port = &s->port[co->index]; |
754 | unsigned long flags; | ||
718 | 755 | ||
719 | mutex_lock(&s->sccnxp_mutex); | 756 | spin_lock_irqsave(&s->lock, flags); |
720 | uart_console_write(port, c, n, sccnxp_console_putchar); | 757 | uart_console_write(port, c, n, sccnxp_console_putchar); |
721 | mutex_unlock(&s->sccnxp_mutex); | 758 | spin_unlock_irqrestore(&s->lock, flags); |
722 | } | 759 | } |
723 | 760 | ||
724 | static int sccnxp_console_setup(struct console *co, char *options) | 761 | static int sccnxp_console_setup(struct console *co, char *options) |
@@ -757,7 +794,7 @@ static int sccnxp_probe(struct platform_device *pdev) | |||
757 | } | 794 | } |
758 | platform_set_drvdata(pdev, s); | 795 | platform_set_drvdata(pdev, s); |
759 | 796 | ||
760 | mutex_init(&s->sccnxp_mutex); | 797 | spin_lock_init(&s->lock); |
761 | 798 | ||
762 | /* Individual chip settings */ | 799 | /* Individual chip settings */ |
763 | switch (chiptype) { | 800 | switch (chiptype) { |
@@ -854,11 +891,19 @@ static int sccnxp_probe(struct platform_device *pdev) | |||
854 | } else | 891 | } else |
855 | memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata)); | 892 | memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata)); |
856 | 893 | ||
857 | s->irq = platform_get_irq(pdev, 0); | 894 | if (pdata->poll_time_us) { |
858 | if (s->irq <= 0) { | 895 | dev_info(&pdev->dev, "Using poll mode, resolution %u usecs\n", |
859 | dev_err(&pdev->dev, "Missing irq resource data\n"); | 896 | pdata->poll_time_us); |
860 | ret = -ENXIO; | 897 | s->poll = 1; |
861 | goto err_out; | 898 | } |
899 | |||
900 | if (!s->poll) { | ||
901 | s->irq = platform_get_irq(pdev, 0); | ||
902 | if (s->irq < 0) { | ||
903 | dev_err(&pdev->dev, "Missing irq resource data\n"); | ||
904 | ret = -ENXIO; | ||
905 | goto err_out; | ||
906 | } | ||
862 | } | 907 | } |
863 | 908 | ||
864 | /* Check input frequency */ | 909 | /* Check input frequency */ |
@@ -923,13 +968,23 @@ static int sccnxp_probe(struct platform_device *pdev) | |||
923 | if (s->pdata.init) | 968 | if (s->pdata.init) |
924 | s->pdata.init(); | 969 | s->pdata.init(); |
925 | 970 | ||
926 | ret = devm_request_threaded_irq(&pdev->dev, s->irq, NULL, sccnxp_ist, | 971 | if (!s->poll) { |
927 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | 972 | ret = devm_request_threaded_irq(&pdev->dev, s->irq, NULL, |
928 | dev_name(&pdev->dev), s); | 973 | sccnxp_ist, |
929 | if (!ret) | 974 | IRQF_TRIGGER_FALLING | |
975 | IRQF_ONESHOT, | ||
976 | dev_name(&pdev->dev), s); | ||
977 | if (!ret) | ||
978 | return 0; | ||
979 | |||
980 | dev_err(&pdev->dev, "Unable to reguest IRQ %i\n", s->irq); | ||
981 | } else { | ||
982 | init_timer(&s->timer); | ||
983 | setup_timer(&s->timer, sccnxp_timer, (unsigned long)s); | ||
984 | mod_timer(&s->timer, jiffies + | ||
985 | usecs_to_jiffies(s->pdata.poll_time_us)); | ||
930 | return 0; | 986 | return 0; |
931 | 987 | } | |
932 | dev_err(&pdev->dev, "Unable to reguest IRQ %i\n", s->irq); | ||
933 | 988 | ||
934 | err_out: | 989 | err_out: |
935 | platform_set_drvdata(pdev, NULL); | 990 | platform_set_drvdata(pdev, NULL); |
@@ -942,7 +997,10 @@ static int sccnxp_remove(struct platform_device *pdev) | |||
942 | int i; | 997 | int i; |
943 | struct sccnxp_port *s = platform_get_drvdata(pdev); | 998 | struct sccnxp_port *s = platform_get_drvdata(pdev); |
944 | 999 | ||
945 | devm_free_irq(&pdev->dev, s->irq, s); | 1000 | if (!s->poll) |
1001 | devm_free_irq(&pdev->dev, s->irq, s); | ||
1002 | else | ||
1003 | del_timer_sync(&s->timer); | ||
946 | 1004 | ||
947 | for (i = 0; i < s->uart.nr; i++) | 1005 | for (i = 0; i < s->uart.nr; i++) |
948 | uart_remove_one_port(&s->uart, &s->port[i]); | 1006 | uart_remove_one_port(&s->uart, &s->port[i]); |