TTY/Serial driver updates for 5.17-rc1
Here is the big set of tty/serial driver updates for 5.17-rc1. Nothing major in here, just lots of good updates and fixes, including: - more tty core cleanups from Jiri as well as mxser driver cleanups. This is the majority of the core diffstat - tty documentation updates from Jiri - platform_get_irq() updates - various serial driver updates for new features and hardware - fifo usage for 8250 console, reducing cpu load a lot - LED fix for keyboards, long-time bugfix that went through many revisions - minor cleanups All have been in linux-next for a while with no reported problems. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCYd7Q0g8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+yn3FACgoFZEFY04TU+Cd9mrlRq/mazZm/IAniJfPxOF U0s57L5o1dlnmawh8mmV =HSOB -----END PGP SIGNATURE----- Merge tag 'tty-5.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty Pull tty/serial driver updates from Greg KH: "Here is the big set of tty/serial driver updates for 5.17-rc1. Nothing major in here, just lots of good updates and fixes, including: - more tty core cleanups from Jiri as well as mxser driver cleanups. This is the majority of the core diffstat - tty documentation updates from Jiri - platform_get_irq() updates - various serial driver updates for new features and hardware - fifo usage for 8250 console, reducing cpu load a lot - LED fix for keyboards, long-time bugfix that went through many revisions - minor cleanups All have been in linux-next for a while with no reported problems" * tag 'tty-5.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (119 commits) serial: core: Keep mctrl register state and cached copy in sync serial: stm32: correct loop for dma error handling serial: stm32: fix flow control transfer in DMA mode serial: stm32: rework TX DMA state condition serial: stm32: move tx dma terminate DMA to shutdown serial: pl011: Drop redundant DTR/RTS preservation on close/open serial: pl011: Drop CR register reset on set_termios serial: pl010: Drop CR register reset on set_termios serial: liteuart: fix MODULE_ALIAS serial: 8250_bcm7271: Fix return error code in case of dma_alloc_coherent() failure Revert "serdev: BREAK/FRAME/PARITY/OVERRUN notification prototype V2" tty: goldfish: Use platform_get_irq() to get the interrupt serdev: BREAK/FRAME/PARITY/OVERRUN notification prototype V2 tty: serial: meson: Drop the legacy compatible strings and clock code serial: pmac_zilog: Use platform_get_irq() to get the interrupt serial: bcm63xx: Use platform_get_irq() to get the interrupt serial: ar933x: Use platform_get_irq() to get the interrupt serial: vt8500: Use platform_get_irq() to get the interrupt serial: altera_jtaguart: Use platform_get_irq_optional() to get the interrupt serial: pxa: Use platform_get_irq() to get the interrupt ...
This commit is contained in:
commit
342465f533
86 changed files with 3329 additions and 3015 deletions
|
@ -29,6 +29,7 @@ properties:
|
||||||
- amlogic,meson8-uart
|
- amlogic,meson8-uart
|
||||||
- amlogic,meson8b-uart
|
- amlogic,meson8b-uart
|
||||||
- amlogic,meson-gx-uart
|
- amlogic,meson-gx-uart
|
||||||
|
- amlogic,meson-s4-uart
|
||||||
- const: amlogic,meson-ao-uart
|
- const: amlogic,meson-ao-uart
|
||||||
- description: Everything-Else power domain UART controller
|
- description: Everything-Else power domain UART controller
|
||||||
enum:
|
enum:
|
||||||
|
@ -36,6 +37,7 @@ properties:
|
||||||
- amlogic,meson8-uart
|
- amlogic,meson8-uart
|
||||||
- amlogic,meson8b-uart
|
- amlogic,meson8b-uart
|
||||||
- amlogic,meson-gx-uart
|
- amlogic,meson-gx-uart
|
||||||
|
- amlogic,meson-s4-uart
|
||||||
|
|
||||||
reg:
|
reg:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|
|
@ -21,9 +21,15 @@ properties:
|
||||||
- fsl,ls1028a-lpuart
|
- fsl,ls1028a-lpuart
|
||||||
- fsl,imx7ulp-lpuart
|
- fsl,imx7ulp-lpuart
|
||||||
- fsl,imx8qm-lpuart
|
- fsl,imx8qm-lpuart
|
||||||
|
- fsl,imxrt1050-lpuart
|
||||||
- items:
|
- items:
|
||||||
- const: fsl,imx8qxp-lpuart
|
- enum:
|
||||||
|
- fsl,imx8qxp-lpuart
|
||||||
|
- fsl,imx8ulp-lpuart
|
||||||
- const: fsl,imx7ulp-lpuart
|
- const: fsl,imx7ulp-lpuart
|
||||||
|
- items:
|
||||||
|
- const: fsl,imx8qm-lpuart
|
||||||
|
- const: fsl,imx8qxp-lpuart
|
||||||
|
|
||||||
reg:
|
reg:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|
|
@ -14,7 +14,15 @@ allOf:
|
||||||
|
|
||||||
properties:
|
properties:
|
||||||
compatible:
|
compatible:
|
||||||
const: renesas,sci
|
oneOf:
|
||||||
|
- items:
|
||||||
|
- enum:
|
||||||
|
- renesas,r9a07g044-sci # RZ/G2{L,LC}
|
||||||
|
- renesas,r9a07g054-sci # RZ/V2L
|
||||||
|
- const: renesas,sci # generic SCI compatible UART
|
||||||
|
|
||||||
|
- items:
|
||||||
|
- const: renesas,sci # generic SCI compatible UART
|
||||||
|
|
||||||
reg:
|
reg:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
@ -54,18 +62,46 @@ required:
|
||||||
- clocks
|
- clocks
|
||||||
- clock-names
|
- clock-names
|
||||||
|
|
||||||
|
if:
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
contains:
|
||||||
|
enum:
|
||||||
|
- renesas,r9a07g044-sci
|
||||||
|
- renesas,r9a07g054-sci
|
||||||
|
then:
|
||||||
|
properties:
|
||||||
|
resets:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
power-domains:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
required:
|
||||||
|
- resets
|
||||||
|
- power-domains
|
||||||
|
|
||||||
unevaluatedProperties: false
|
unevaluatedProperties: false
|
||||||
|
|
||||||
examples:
|
examples:
|
||||||
- |
|
- |
|
||||||
|
#include <dt-bindings/clock/r9a07g044-cpg.h>
|
||||||
|
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||||
|
|
||||||
aliases {
|
aliases {
|
||||||
serial0 = &sci0;
|
serial0 = &sci0;
|
||||||
};
|
};
|
||||||
|
|
||||||
sci0: serial@ffff78 {
|
sci0: serial@1004d000 {
|
||||||
compatible = "renesas,sci";
|
compatible = "renesas,r9a07g044-sci", "renesas,sci";
|
||||||
reg = <0xffff78 8>;
|
reg = <0x1004d000 0x400>;
|
||||||
interrupts = <88 0>, <89 0>, <90 0>, <91 0>;
|
interrupts = <GIC_SPI 405 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
clocks = <&fclk>;
|
<GIC_SPI 406 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 407 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<GIC_SPI 408 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
interrupt-names = "eri", "rxi", "txi", "tei";
|
||||||
|
clocks = <&cpg CPG_MOD R9A07G044_SCI0_CLKP>;
|
||||||
clock-names = "fck";
|
clock-names = "fck";
|
||||||
|
power-domains = <&cpg>;
|
||||||
|
resets = <&cpg R9A07G044_SCI0_RST>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -64,9 +64,21 @@ properties:
|
||||||
- const: renesas,rcar-gen3-scif # R-Car Gen3 and RZ/G2
|
- const: renesas,rcar-gen3-scif # R-Car Gen3 and RZ/G2
|
||||||
- const: renesas,scif # generic SCIF compatible UART
|
- const: renesas,scif # generic SCIF compatible UART
|
||||||
|
|
||||||
|
- items:
|
||||||
|
- enum:
|
||||||
|
- renesas,scif-r8a779f0 # R-Car S4-8
|
||||||
|
- const: renesas,rcar-gen4-scif # R-Car Gen4
|
||||||
|
- const: renesas,scif # generic SCIF compatible UART
|
||||||
|
|
||||||
- items:
|
- items:
|
||||||
- enum:
|
- enum:
|
||||||
- renesas,scif-r9a07g044 # RZ/G2{L,LC}
|
- renesas,scif-r9a07g044 # RZ/G2{L,LC}
|
||||||
|
- renesas,scif-r9a07g054 # RZ/V2L
|
||||||
|
|
||||||
|
- items:
|
||||||
|
- enum:
|
||||||
|
- renesas,scif-r9a07g054 # RZ/V2L
|
||||||
|
- const: renesas,scif-r9a07g044 # RZ/G2{L,LC} fallback for RZ/V2L
|
||||||
|
|
||||||
reg:
|
reg:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
@ -153,6 +165,9 @@ if:
|
||||||
enum:
|
enum:
|
||||||
- renesas,rcar-gen2-scif
|
- renesas,rcar-gen2-scif
|
||||||
- renesas,rcar-gen3-scif
|
- renesas,rcar-gen3-scif
|
||||||
|
- renesas,rcar-gen4-scif
|
||||||
|
- renesas,scif-r9a07g044
|
||||||
|
- renesas,scif-r9a07g054
|
||||||
then:
|
then:
|
||||||
required:
|
required:
|
||||||
- resets
|
- resets
|
||||||
|
|
|
@ -9,7 +9,6 @@ Support for Serial devices
|
||||||
|
|
||||||
|
|
||||||
driver
|
driver
|
||||||
tty
|
|
||||||
|
|
||||||
Serial drivers
|
Serial drivers
|
||||||
==============
|
==============
|
||||||
|
|
|
@ -18,9 +18,12 @@ How to use it
|
||||||
1.1 initialize the modem in 0710 mux mode (usually AT+CMUX= command) through
|
1.1 initialize the modem in 0710 mux mode (usually AT+CMUX= command) through
|
||||||
its serial port. Depending on the modem used, you can pass more or less
|
its serial port. Depending on the modem used, you can pass more or less
|
||||||
parameters to this command.
|
parameters to this command.
|
||||||
|
|
||||||
1.2 switch the serial line to using the n_gsm line discipline by using
|
1.2 switch the serial line to using the n_gsm line discipline by using
|
||||||
TIOCSETD ioctl.
|
TIOCSETD ioctl.
|
||||||
|
|
||||||
1.3 configure the mux using GSMIOC_GETCONF / GSMIOC_SETCONF ioctl.
|
1.3 configure the mux using GSMIOC_GETCONF / GSMIOC_SETCONF ioctl.
|
||||||
|
|
||||||
1.4 obtain base gsmtty number for the used serial port.
|
1.4 obtain base gsmtty number for the used serial port.
|
||||||
|
|
||||||
Major parts of the initialization program :
|
Major parts of the initialization program :
|
||||||
|
@ -95,10 +98,13 @@ Major parts of the initialization program :
|
||||||
|
|
||||||
2.1 receive string "AT+CMUX= command" through its serial port,initialize
|
2.1 receive string "AT+CMUX= command" through its serial port,initialize
|
||||||
mux mode config
|
mux mode config
|
||||||
|
|
||||||
2.2 switch the serial line to using the n_gsm line discipline by using
|
2.2 switch the serial line to using the n_gsm line discipline by using
|
||||||
TIOCSETD ioctl.
|
TIOCSETD ioctl.
|
||||||
|
|
||||||
2.3 configure the mux using GSMIOC_GETCONF / GSMIOC_SETCONF ioctl.
|
2.3 configure the mux using GSMIOC_GETCONF / GSMIOC_SETCONF ioctl.
|
||||||
2.4 obtain base gsmtty number for the used serial port,
|
|
||||||
|
2.4 obtain base gsmtty number for the used serial port::
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
|
@ -1,328 +0,0 @@
|
||||||
=================
|
|
||||||
The Lockronomicon
|
|
||||||
=================
|
|
||||||
|
|
||||||
Your guide to the ancient and twisted locking policies of the tty layer and
|
|
||||||
the warped logic behind them. Beware all ye who read on.
|
|
||||||
|
|
||||||
|
|
||||||
Line Discipline
|
|
||||||
---------------
|
|
||||||
|
|
||||||
Line disciplines are registered with tty_register_ldisc() passing the
|
|
||||||
discipline number and the ldisc structure. At the point of registration the
|
|
||||||
discipline must be ready to use and it is possible it will get used before
|
|
||||||
the call returns success. If the call returns an error then it won't get
|
|
||||||
called. Do not re-use ldisc numbers as they are part of the userspace ABI
|
|
||||||
and writing over an existing ldisc will cause demons to eat your computer.
|
|
||||||
After the return the ldisc data has been copied so you may free your own
|
|
||||||
copy of the structure. You must not re-register over the top of the line
|
|
||||||
discipline even with the same data or your computer again will be eaten by
|
|
||||||
demons.
|
|
||||||
|
|
||||||
In order to remove a line discipline call tty_unregister_ldisc().
|
|
||||||
In ancient times this always worked. In modern times the function will
|
|
||||||
return -EBUSY if the ldisc is currently in use. Since the ldisc referencing
|
|
||||||
code manages the module counts this should not usually be a concern.
|
|
||||||
|
|
||||||
Heed this warning: the reference count field of the registered copies of the
|
|
||||||
tty_ldisc structure in the ldisc table counts the number of lines using this
|
|
||||||
discipline. The reference count of the tty_ldisc structure within a tty
|
|
||||||
counts the number of active users of the ldisc at this instant. In effect it
|
|
||||||
counts the number of threads of execution within an ldisc method (plus those
|
|
||||||
about to enter and exit although this detail matters not).
|
|
||||||
|
|
||||||
Line Discipline Methods
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
TTY side interfaces
|
|
||||||
^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
======================= =======================================================
|
|
||||||
open() Called when the line discipline is attached to
|
|
||||||
the terminal. No other call into the line
|
|
||||||
discipline for this tty will occur until it
|
|
||||||
completes successfully. Should initialize any
|
|
||||||
state needed by the ldisc, and set receive_room
|
|
||||||
in the tty_struct to the maximum amount of data
|
|
||||||
the line discipline is willing to accept from the
|
|
||||||
driver with a single call to receive_buf().
|
|
||||||
Returning an error will prevent the ldisc from
|
|
||||||
being attached. Can sleep.
|
|
||||||
|
|
||||||
close() This is called on a terminal when the line
|
|
||||||
discipline is being unplugged. At the point of
|
|
||||||
execution no further users will enter the
|
|
||||||
ldisc code for this tty. Can sleep.
|
|
||||||
|
|
||||||
hangup() Called when the tty line is hung up.
|
|
||||||
The line discipline should cease I/O to the tty.
|
|
||||||
No further calls into the ldisc code will occur.
|
|
||||||
Can sleep.
|
|
||||||
|
|
||||||
read() (optional) A process requests reading data from
|
|
||||||
the line. Multiple read calls may occur in parallel
|
|
||||||
and the ldisc must deal with serialization issues.
|
|
||||||
If not defined, the process will receive an EIO
|
|
||||||
error. May sleep.
|
|
||||||
|
|
||||||
write() (optional) A process requests writing data to the
|
|
||||||
line. Multiple write calls are serialized by the
|
|
||||||
tty layer for the ldisc. If not defined, the
|
|
||||||
process will receive an EIO error. May sleep.
|
|
||||||
|
|
||||||
flush_buffer() (optional) May be called at any point between
|
|
||||||
open and close, and instructs the line discipline
|
|
||||||
to empty its input buffer.
|
|
||||||
|
|
||||||
set_termios() (optional) Called on termios structure changes.
|
|
||||||
The caller passes the old termios data and the
|
|
||||||
current data is in the tty. Called under the
|
|
||||||
termios semaphore so allowed to sleep. Serialized
|
|
||||||
against itself only.
|
|
||||||
|
|
||||||
poll() (optional) Check the status for the poll/select
|
|
||||||
calls. Multiple poll calls may occur in parallel.
|
|
||||||
May sleep.
|
|
||||||
|
|
||||||
ioctl() (optional) Called when an ioctl is handed to the
|
|
||||||
tty layer that might be for the ldisc. Multiple
|
|
||||||
ioctl calls may occur in parallel. May sleep.
|
|
||||||
|
|
||||||
compat_ioctl() (optional) Called when a 32 bit ioctl is handed
|
|
||||||
to the tty layer that might be for the ldisc.
|
|
||||||
Multiple ioctl calls may occur in parallel.
|
|
||||||
May sleep.
|
|
||||||
======================= =======================================================
|
|
||||||
|
|
||||||
Driver Side Interfaces
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
======================= =======================================================
|
|
||||||
receive_buf() (optional) Called by the low-level driver to hand
|
|
||||||
a buffer of received bytes to the ldisc for
|
|
||||||
processing. The number of bytes is guaranteed not
|
|
||||||
to exceed the current value of tty->receive_room.
|
|
||||||
All bytes must be processed.
|
|
||||||
|
|
||||||
receive_buf2() (optional) Called by the low-level driver to hand
|
|
||||||
a buffer of received bytes to the ldisc for
|
|
||||||
processing. Returns the number of bytes processed.
|
|
||||||
|
|
||||||
If both receive_buf() and receive_buf2() are
|
|
||||||
defined, receive_buf2() should be preferred.
|
|
||||||
|
|
||||||
write_wakeup() May be called at any point between open and close.
|
|
||||||
The TTY_DO_WRITE_WAKEUP flag indicates if a call
|
|
||||||
is needed but always races versus calls. Thus the
|
|
||||||
ldisc must be careful about setting order and to
|
|
||||||
handle unexpected calls. Must not sleep.
|
|
||||||
|
|
||||||
The driver is forbidden from calling this directly
|
|
||||||
from the ->write call from the ldisc as the ldisc
|
|
||||||
is permitted to call the driver write method from
|
|
||||||
this function. In such a situation defer it.
|
|
||||||
|
|
||||||
dcd_change() Report to the tty line the current DCD pin status
|
|
||||||
changes and the relative timestamp. The timestamp
|
|
||||||
cannot be NULL.
|
|
||||||
======================= =======================================================
|
|
||||||
|
|
||||||
|
|
||||||
Driver Access
|
|
||||||
^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Line discipline methods can call the following methods of the underlying
|
|
||||||
hardware driver through the function pointers within the tty->driver
|
|
||||||
structure:
|
|
||||||
|
|
||||||
======================= =======================================================
|
|
||||||
write() Write a block of characters to the tty device.
|
|
||||||
Returns the number of characters accepted. The
|
|
||||||
character buffer passed to this method is already
|
|
||||||
in kernel space.
|
|
||||||
|
|
||||||
put_char() Queues a character for writing to the tty device.
|
|
||||||
If there is no room in the queue, the character is
|
|
||||||
ignored.
|
|
||||||
|
|
||||||
flush_chars() (Optional) If defined, must be called after
|
|
||||||
queueing characters with put_char() in order to
|
|
||||||
start transmission.
|
|
||||||
|
|
||||||
write_room() Returns the numbers of characters the tty driver
|
|
||||||
will accept for queueing to be written.
|
|
||||||
|
|
||||||
ioctl() Invoke device specific ioctl.
|
|
||||||
Expects data pointers to refer to userspace.
|
|
||||||
Returns ENOIOCTLCMD for unrecognized ioctl numbers.
|
|
||||||
|
|
||||||
set_termios() Notify the tty driver that the device's termios
|
|
||||||
settings have changed. New settings are in
|
|
||||||
tty->termios. Previous settings should be passed in
|
|
||||||
the "old" argument.
|
|
||||||
|
|
||||||
The API is defined such that the driver should return
|
|
||||||
the actual modes selected. This means that the
|
|
||||||
driver function is responsible for modifying any
|
|
||||||
bits in the request it cannot fulfill to indicate
|
|
||||||
the actual modes being used. A device with no
|
|
||||||
hardware capability for change (e.g. a USB dongle or
|
|
||||||
virtual port) can provide NULL for this method.
|
|
||||||
|
|
||||||
throttle() Notify the tty driver that input buffers for the
|
|
||||||
line discipline are close to full, and it should
|
|
||||||
somehow signal that no more characters should be
|
|
||||||
sent to the tty.
|
|
||||||
|
|
||||||
unthrottle() Notify the tty driver that characters can now be
|
|
||||||
sent to the tty without fear of overrunning the
|
|
||||||
input buffers of the line disciplines.
|
|
||||||
|
|
||||||
stop() Ask the tty driver to stop outputting characters
|
|
||||||
to the tty device.
|
|
||||||
|
|
||||||
start() Ask the tty driver to resume sending characters
|
|
||||||
to the tty device.
|
|
||||||
|
|
||||||
hangup() Ask the tty driver to hang up the tty device.
|
|
||||||
|
|
||||||
break_ctl() (Optional) Ask the tty driver to turn on or off
|
|
||||||
BREAK status on the RS-232 port. If state is -1,
|
|
||||||
then the BREAK status should be turned on; if
|
|
||||||
state is 0, then BREAK should be turned off.
|
|
||||||
If this routine is not implemented, use ioctls
|
|
||||||
TIOCSBRK / TIOCCBRK instead.
|
|
||||||
|
|
||||||
wait_until_sent() Waits until the device has written out all of the
|
|
||||||
characters in its transmitter FIFO.
|
|
||||||
|
|
||||||
send_xchar() Send a high-priority XON/XOFF character to the device.
|
|
||||||
======================= =======================================================
|
|
||||||
|
|
||||||
|
|
||||||
Flags
|
|
||||||
^^^^^
|
|
||||||
|
|
||||||
Line discipline methods have access to tty->flags field containing the
|
|
||||||
following interesting flags:
|
|
||||||
|
|
||||||
======================= =======================================================
|
|
||||||
TTY_THROTTLED Driver input is throttled. The ldisc should call
|
|
||||||
tty->driver->unthrottle() in order to resume
|
|
||||||
reception when it is ready to process more data.
|
|
||||||
|
|
||||||
TTY_DO_WRITE_WAKEUP If set, causes the driver to call the ldisc's
|
|
||||||
write_wakeup() method in order to resume
|
|
||||||
transmission when it can accept more data
|
|
||||||
to transmit.
|
|
||||||
|
|
||||||
TTY_IO_ERROR If set, causes all subsequent userspace read/write
|
|
||||||
calls on the tty to fail, returning -EIO.
|
|
||||||
|
|
||||||
TTY_OTHER_CLOSED Device is a pty and the other side has closed.
|
|
||||||
|
|
||||||
TTY_NO_WRITE_SPLIT Prevent driver from splitting up writes into
|
|
||||||
smaller chunks.
|
|
||||||
======================= =======================================================
|
|
||||||
|
|
||||||
|
|
||||||
Locking
|
|
||||||
^^^^^^^
|
|
||||||
|
|
||||||
Callers to the line discipline functions from the tty layer are required to
|
|
||||||
take line discipline locks. The same is true of calls from the driver side
|
|
||||||
but not yet enforced.
|
|
||||||
|
|
||||||
Three calls are now provided::
|
|
||||||
|
|
||||||
ldisc = tty_ldisc_ref(tty);
|
|
||||||
|
|
||||||
takes a handle to the line discipline in the tty and returns it. If no ldisc
|
|
||||||
is currently attached or the ldisc is being closed and re-opened at this
|
|
||||||
point then NULL is returned. While this handle is held the ldisc will not
|
|
||||||
change or go away::
|
|
||||||
|
|
||||||
tty_ldisc_deref(ldisc)
|
|
||||||
|
|
||||||
Returns the ldisc reference and allows the ldisc to be closed. Returning the
|
|
||||||
reference takes away your right to call the ldisc functions until you take
|
|
||||||
a new reference::
|
|
||||||
|
|
||||||
ldisc = tty_ldisc_ref_wait(tty);
|
|
||||||
|
|
||||||
Performs the same function as tty_ldisc_ref except that it will wait for an
|
|
||||||
ldisc change to complete and then return a reference to the new ldisc.
|
|
||||||
|
|
||||||
While these functions are slightly slower than the old code they should have
|
|
||||||
minimal impact as most receive logic uses the flip buffers and they only
|
|
||||||
need to take a reference when they push bits up through the driver.
|
|
||||||
|
|
||||||
A caution: The ldisc->open(), ldisc->close() and driver->set_ldisc
|
|
||||||
functions are called with the ldisc unavailable. Thus tty_ldisc_ref will
|
|
||||||
fail in this situation if used within these functions. Ldisc and driver
|
|
||||||
code calling its own functions must be careful in this case.
|
|
||||||
|
|
||||||
|
|
||||||
Driver Interface
|
|
||||||
----------------
|
|
||||||
|
|
||||||
======================= =======================================================
|
|
||||||
open() Called when a device is opened. May sleep
|
|
||||||
|
|
||||||
close() Called when a device is closed. At the point of
|
|
||||||
return from this call the driver must make no
|
|
||||||
further ldisc calls of any kind. May sleep
|
|
||||||
|
|
||||||
write() Called to write bytes to the device. May not
|
|
||||||
sleep. May occur in parallel in special cases.
|
|
||||||
Because this includes panic paths drivers generally
|
|
||||||
shouldn't try and do clever locking here.
|
|
||||||
|
|
||||||
put_char() Stuff a single character onto the queue. The
|
|
||||||
driver is guaranteed following up calls to
|
|
||||||
flush_chars.
|
|
||||||
|
|
||||||
flush_chars() Ask the kernel to write put_char queue
|
|
||||||
|
|
||||||
write_room() Return the number of characters that can be stuffed
|
|
||||||
into the port buffers without overflow (or less).
|
|
||||||
The ldisc is responsible for being intelligent
|
|
||||||
about multi-threading of write_room/write calls
|
|
||||||
|
|
||||||
ioctl() Called when an ioctl may be for the driver
|
|
||||||
|
|
||||||
set_termios() Called on termios change, serialized against
|
|
||||||
itself by a semaphore. May sleep.
|
|
||||||
|
|
||||||
set_ldisc() Notifier for discipline change. At the point this
|
|
||||||
is done the discipline is not yet usable. Can now
|
|
||||||
sleep (I think)
|
|
||||||
|
|
||||||
throttle() Called by the ldisc to ask the driver to do flow
|
|
||||||
control. Serialization including with unthrottle
|
|
||||||
is the job of the ldisc layer.
|
|
||||||
|
|
||||||
unthrottle() Called by the ldisc to ask the driver to stop flow
|
|
||||||
control.
|
|
||||||
|
|
||||||
stop() Ldisc notifier to the driver to stop output. As with
|
|
||||||
throttle the serializations with start() are down
|
|
||||||
to the ldisc layer.
|
|
||||||
|
|
||||||
start() Ldisc notifier to the driver to start output.
|
|
||||||
|
|
||||||
hangup() Ask the tty driver to cause a hangup initiated
|
|
||||||
from the host side. [Can sleep ??]
|
|
||||||
|
|
||||||
break_ctl() Send RS232 break. Can sleep. Can get called in
|
|
||||||
parallel, driver must serialize (for now), and
|
|
||||||
with write calls.
|
|
||||||
|
|
||||||
wait_until_sent() Wait for characters to exit the hardware queue
|
|
||||||
of the driver. Can sleep
|
|
||||||
|
|
||||||
send_xchar() Send XON/XOFF and if possible jump the queue with
|
|
||||||
it in order to get fast flow control responses.
|
|
||||||
Cannot sleep ??
|
|
||||||
======================= =======================================================
|
|
|
@ -137,6 +137,7 @@ needed).
|
||||||
misc-devices/index
|
misc-devices/index
|
||||||
scheduler/index
|
scheduler/index
|
||||||
mhi/index
|
mhi/index
|
||||||
|
tty/index
|
||||||
|
|
||||||
Architecture-agnostic documentation
|
Architecture-agnostic documentation
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
63
Documentation/tty/index.rst
Normal file
63
Documentation/tty/index.rst
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
.. SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
===
|
||||||
|
TTY
|
||||||
|
===
|
||||||
|
|
||||||
|
Teletypewriter (TTY) layer takes care of all those serial devices. Including
|
||||||
|
the virtual ones like pseudoterminal (PTY).
|
||||||
|
|
||||||
|
TTY structures
|
||||||
|
==============
|
||||||
|
|
||||||
|
There are several major TTY structures. Every TTY device in a system has a
|
||||||
|
corresponding struct tty_port. These devices are maintained by a TTY driver
|
||||||
|
which is struct tty_driver. This structure describes the driver but also
|
||||||
|
contains a reference to operations which could be performed on the TTYs. It is
|
||||||
|
struct tty_operations. Then, upon open, a struct tty_struct is allocated and
|
||||||
|
lives until the final close. During this time, several callbacks from struct
|
||||||
|
tty_operations are invoked by the TTY layer.
|
||||||
|
|
||||||
|
Every character received by the kernel (both from devices and users) is passed
|
||||||
|
through a preselected :doc:`tty_ldisc` (in
|
||||||
|
short ldisc; in C, struct tty_ldisc_ops). Its task is to transform characters
|
||||||
|
as defined by a particular ldisc or by user too. The default one is n_tty,
|
||||||
|
implementing echoes, signal handling, jobs control, special characters
|
||||||
|
processing, and more. The transformed characters are passed further to
|
||||||
|
user/device, depending on the source.
|
||||||
|
|
||||||
|
In-detail description of the named TTY structures is in separate documents:
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
tty_driver
|
||||||
|
tty_port
|
||||||
|
tty_struct
|
||||||
|
tty_ldisc
|
||||||
|
tty_buffer
|
||||||
|
n_tty
|
||||||
|
tty_internals
|
||||||
|
|
||||||
|
Writing TTY Driver
|
||||||
|
==================
|
||||||
|
|
||||||
|
Before one starts writing a TTY driver, they must consider
|
||||||
|
:doc:`Serial <../driver-api/serial/driver>` and :doc:`USB Serial
|
||||||
|
<../usb/usb-serial>` layers
|
||||||
|
first. Drivers for serial devices can often use one of these specific layers to
|
||||||
|
implement a serial driver. Only special devices should be handled directly by
|
||||||
|
the TTY Layer. If you are about to write such a driver, read on.
|
||||||
|
|
||||||
|
A *typical* sequence a TTY driver performs is as follows:
|
||||||
|
|
||||||
|
#. Allocate and register a TTY driver (module init)
|
||||||
|
#. Create and register TTY devices as they are probed (probe function)
|
||||||
|
#. Handle TTY operations and events like interrupts (TTY core invokes the
|
||||||
|
former, the device the latter)
|
||||||
|
#. Remove devices as they are going away (remove function)
|
||||||
|
#. Unregister and free the TTY driver (module exit)
|
||||||
|
|
||||||
|
Steps regarding driver, i.e. 1., 3., and 5. are described in detail in
|
||||||
|
:doc:`tty_driver`. For the other two (devices handling), look into
|
||||||
|
:doc:`tty_port`.
|
22
Documentation/tty/n_tty.rst
Normal file
22
Documentation/tty/n_tty.rst
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
.. SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
=====
|
||||||
|
N_TTY
|
||||||
|
=====
|
||||||
|
|
||||||
|
.. contents:: :local:
|
||||||
|
|
||||||
|
The default (and fallback) :doc:`TTY line discipline <tty_ldisc>`. It tries to
|
||||||
|
handle characters as per POSIX.
|
||||||
|
|
||||||
|
External Functions
|
||||||
|
==================
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/n_tty.c
|
||||||
|
:export:
|
||||||
|
|
||||||
|
Internal Functions
|
||||||
|
==================
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/n_tty.c
|
||||||
|
:internal:
|
46
Documentation/tty/tty_buffer.rst
Normal file
46
Documentation/tty/tty_buffer.rst
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
.. SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
==========
|
||||||
|
TTY Buffer
|
||||||
|
==========
|
||||||
|
|
||||||
|
.. contents:: :local:
|
||||||
|
|
||||||
|
Here, we document functions for taking care of tty buffer and their flipping.
|
||||||
|
Drivers are supposed to fill the buffer by one of those functions below and
|
||||||
|
then flip the buffer, so that the data are passed to :doc:`line discipline
|
||||||
|
<tty_ldisc>` for further processing.
|
||||||
|
|
||||||
|
Flip Buffer Management
|
||||||
|
======================
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_buffer.c
|
||||||
|
:identifiers: tty_prepare_flip_string tty_insert_flip_string_fixed_flag
|
||||||
|
tty_insert_flip_string_flags __tty_insert_flip_char
|
||||||
|
tty_flip_buffer_push tty_ldisc_receive_buf
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Other Functions
|
||||||
|
===============
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_buffer.c
|
||||||
|
:identifiers: tty_buffer_space_avail tty_buffer_set_limit
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Buffer Locking
|
||||||
|
==============
|
||||||
|
|
||||||
|
These are used only in special circumstances. Avoid them.
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_buffer.c
|
||||||
|
:identifiers: tty_buffer_lock_exclusive tty_buffer_unlock_exclusive
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Internal Functions
|
||||||
|
==================
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_buffer.c
|
||||||
|
:internal:
|
128
Documentation/tty/tty_driver.rst
Normal file
128
Documentation/tty/tty_driver.rst
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
.. SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
=============================
|
||||||
|
TTY Driver and TTY Operations
|
||||||
|
=============================
|
||||||
|
|
||||||
|
.. contents:: :local:
|
||||||
|
|
||||||
|
Allocation
|
||||||
|
==========
|
||||||
|
|
||||||
|
The first thing a driver needs to do is to allocate a struct tty_driver. This
|
||||||
|
is done by tty_alloc_driver() (or __tty_alloc_driver()). Next, the newly
|
||||||
|
allocated structure is filled with information. See `TTY Driver Reference`_ at
|
||||||
|
the end of this document on what actually shall be filled in.
|
||||||
|
|
||||||
|
The allocation routines expect a number of devices the driver can handle at
|
||||||
|
most and flags. Flags are those starting ``TTY_DRIVER_`` listed and described
|
||||||
|
in `TTY Driver Flags`_ below.
|
||||||
|
|
||||||
|
When the driver is about to be freed, tty_driver_kref_put() is called on that.
|
||||||
|
It will decrements the reference count and if it reaches zero, the driver is
|
||||||
|
freed.
|
||||||
|
|
||||||
|
For reference, both allocation and deallocation functions are explained here in
|
||||||
|
detail:
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_io.c
|
||||||
|
:identifiers: __tty_alloc_driver tty_driver_kref_put
|
||||||
|
|
||||||
|
TTY Driver Flags
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Here comes the documentation of flags accepted by tty_alloc_driver() (or
|
||||||
|
__tty_alloc_driver()):
|
||||||
|
|
||||||
|
.. kernel-doc:: include/linux/tty_driver.h
|
||||||
|
:doc: TTY Driver Flags
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Registration
|
||||||
|
============
|
||||||
|
|
||||||
|
When a struct tty_driver is allocated and filled in, it can be registered using
|
||||||
|
tty_register_driver(). It is recommended to pass ``TTY_DRIVER_DYNAMIC_DEV`` in
|
||||||
|
flags of tty_alloc_driver(). If it is not passed, *all* devices are also
|
||||||
|
registered during tty_register_driver() and the following paragraph of
|
||||||
|
registering devices can be skipped for such drivers. However, the struct
|
||||||
|
tty_port part in `Registering Devices`_ is still relevant there.
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_io.c
|
||||||
|
:identifiers: tty_register_driver tty_unregister_driver
|
||||||
|
|
||||||
|
Registering Devices
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Every TTY device shall be backed by a struct tty_port. Usually, TTY drivers
|
||||||
|
embed tty_port into device's private structures. Further details about handling
|
||||||
|
tty_port can be found in :doc:`tty_port`. The driver is also recommended to use
|
||||||
|
tty_port's reference counting by tty_port_get() and tty_port_put(). The final
|
||||||
|
put is supposed to free the tty_port including the device's private struct.
|
||||||
|
|
||||||
|
Unless ``TTY_DRIVER_DYNAMIC_DEV`` was passed as flags to tty_alloc_driver(),
|
||||||
|
TTY driver is supposed to register every device discovered in the system
|
||||||
|
(the latter is preferred). This is performed by tty_register_device(). Or by
|
||||||
|
tty_register_device_attr() if the driver wants to expose some information
|
||||||
|
through struct attribute_group. Both of them register ``index``'th device and
|
||||||
|
upon return, the device can be opened. There are also preferred tty_port
|
||||||
|
variants described in `Linking Devices to Ports`_ later. It is up to driver to
|
||||||
|
manage free indices and choosing the right one. The TTY layer only refuses to
|
||||||
|
register more devices than passed to tty_alloc_driver().
|
||||||
|
|
||||||
|
When the device is opened, the TTY layer allocates struct tty_struct and starts
|
||||||
|
calling operations from :c:member:`tty_driver.ops`, see `TTY Operations
|
||||||
|
Reference`_.
|
||||||
|
|
||||||
|
The registration routines are documented as follows:
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_io.c
|
||||||
|
:identifiers: tty_register_device tty_register_device_attr
|
||||||
|
tty_unregister_device
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Linking Devices to Ports
|
||||||
|
------------------------
|
||||||
|
As stated earlier, every TTY device shall have a struct tty_port assigned to
|
||||||
|
it. It must be known to the TTY layer at :c:member:`tty_driver.ops.install()`
|
||||||
|
at latest. There are few helpers to *link* the two. Ideally, the driver uses
|
||||||
|
tty_port_register_device() or tty_port_register_device_attr() instead of
|
||||||
|
tty_register_device() and tty_register_device_attr() at the registration time.
|
||||||
|
This way, the driver needs not care about linking later on.
|
||||||
|
|
||||||
|
If that is not possible, the driver still can link the tty_port to a specific
|
||||||
|
index *before* the actual registration by tty_port_link_device(). If it still
|
||||||
|
does not fit, tty_port_install() can be used from the
|
||||||
|
:c:member:`tty_driver.ops.install` hook as a last resort. The last one is
|
||||||
|
dedicated mostly for in-memory devices like PTY where tty_ports are allocated
|
||||||
|
on demand.
|
||||||
|
|
||||||
|
The linking routines are documented here:
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_port.c
|
||||||
|
:identifiers: tty_port_link_device tty_port_register_device
|
||||||
|
tty_port_register_device_attr
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
TTY Driver Reference
|
||||||
|
====================
|
||||||
|
|
||||||
|
All members of struct tty_driver are documented here. The required members are
|
||||||
|
noted at the end. struct tty_operations are documented next.
|
||||||
|
|
||||||
|
.. kernel-doc:: include/linux/tty_driver.h
|
||||||
|
:identifiers: tty_driver
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
TTY Operations Reference
|
||||||
|
========================
|
||||||
|
|
||||||
|
When a TTY is registered, these driver hooks can be invoked by the TTY layer:
|
||||||
|
|
||||||
|
.. kernel-doc:: include/linux/tty_driver.h
|
||||||
|
:identifiers: tty_operations
|
||||||
|
|
31
Documentation/tty/tty_internals.rst
Normal file
31
Documentation/tty/tty_internals.rst
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
.. SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
=============
|
||||||
|
TTY Internals
|
||||||
|
=============
|
||||||
|
|
||||||
|
.. contents:: :local:
|
||||||
|
|
||||||
|
Kopen
|
||||||
|
=====
|
||||||
|
|
||||||
|
These functions serve for opening a TTY from the kernelspace:
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_io.c
|
||||||
|
:identifiers: tty_kopen_exclusive tty_kopen_shared tty_kclose
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Exported Internal Functions
|
||||||
|
===========================
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_io.c
|
||||||
|
:identifiers: tty_release_struct tty_dev_name_to_number tty_get_icount
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Internal Functions
|
||||||
|
==================
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_io.c
|
||||||
|
:internal:
|
85
Documentation/tty/tty_ldisc.rst
Normal file
85
Documentation/tty/tty_ldisc.rst
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
.. SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
===================
|
||||||
|
TTY Line Discipline
|
||||||
|
===================
|
||||||
|
|
||||||
|
.. contents:: :local:
|
||||||
|
|
||||||
|
TTY line discipline process all incoming and outgoing character from/to a tty
|
||||||
|
device. The default line discipline is :doc:`N_TTY <n_tty>`. It is also a
|
||||||
|
fallback if establishing any other discipline for a tty fails. If even N_TTY
|
||||||
|
fails, N_NULL takes over. That never fails, but also does not process any
|
||||||
|
characters -- it throws them away.
|
||||||
|
|
||||||
|
Registration
|
||||||
|
============
|
||||||
|
|
||||||
|
Line disciplines are registered with tty_register_ldisc() passing the ldisc
|
||||||
|
structure. At the point of registration the discipline must be ready to use and
|
||||||
|
it is possible it will get used before the call returns success. If the call
|
||||||
|
returns an error then it won’t get called. Do not re-use ldisc numbers as they
|
||||||
|
are part of the userspace ABI and writing over an existing ldisc will cause
|
||||||
|
demons to eat your computer. You must not re-register over the top of the line
|
||||||
|
discipline even with the same data or your computer again will be eaten by
|
||||||
|
demons. In order to remove a line discipline call tty_unregister_ldisc().
|
||||||
|
|
||||||
|
Heed this warning: the reference count field of the registered copies of the
|
||||||
|
tty_ldisc structure in the ldisc table counts the number of lines using this
|
||||||
|
discipline. The reference count of the tty_ldisc structure within a tty counts
|
||||||
|
the number of active users of the ldisc at this instant. In effect it counts
|
||||||
|
the number of threads of execution within an ldisc method (plus those about to
|
||||||
|
enter and exit although this detail matters not).
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_ldisc.c
|
||||||
|
:identifiers: tty_register_ldisc tty_unregister_ldisc
|
||||||
|
|
||||||
|
Other Functions
|
||||||
|
===============
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_ldisc.c
|
||||||
|
:identifiers: tty_set_ldisc tty_ldisc_flush
|
||||||
|
|
||||||
|
Line Discipline Operations Reference
|
||||||
|
====================================
|
||||||
|
|
||||||
|
.. kernel-doc:: include/linux/tty_ldisc.h
|
||||||
|
:identifiers: tty_ldisc_ops
|
||||||
|
|
||||||
|
Driver Access
|
||||||
|
=============
|
||||||
|
|
||||||
|
Line discipline methods can call the methods of the underlying hardware driver.
|
||||||
|
These are documented as a part of struct tty_operations.
|
||||||
|
|
||||||
|
TTY Flags
|
||||||
|
=========
|
||||||
|
|
||||||
|
Line discipline methods have access to :c:member:`tty_struct.flags` field. See
|
||||||
|
:doc:`tty_struct`.
|
||||||
|
|
||||||
|
Locking
|
||||||
|
=======
|
||||||
|
|
||||||
|
Callers to the line discipline functions from the tty layer are required to
|
||||||
|
take line discipline locks. The same is true of calls from the driver side
|
||||||
|
but not yet enforced.
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_ldisc.c
|
||||||
|
:identifiers: tty_ldisc_ref_wait tty_ldisc_ref tty_ldisc_deref
|
||||||
|
|
||||||
|
While these functions are slightly slower than the old code they should have
|
||||||
|
minimal impact as most receive logic uses the flip buffers and they only
|
||||||
|
need to take a reference when they push bits up through the driver.
|
||||||
|
|
||||||
|
A caution: The :c:member:`tty_ldisc_ops.open()`,
|
||||||
|
:c:member:`tty_ldisc_ops.close()` and :c:member:`tty_driver.set_ldisc()`
|
||||||
|
functions are called with the ldisc unavailable. Thus tty_ldisc_ref() will fail
|
||||||
|
in this situation if used within these functions. Ldisc and driver code
|
||||||
|
calling its own functions must be careful in this case.
|
||||||
|
|
||||||
|
Internal Functions
|
||||||
|
==================
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_ldisc.c
|
||||||
|
:internal:
|
70
Documentation/tty/tty_port.rst
Normal file
70
Documentation/tty/tty_port.rst
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
.. SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
========
|
||||||
|
TTY Port
|
||||||
|
========
|
||||||
|
|
||||||
|
.. contents:: :local:
|
||||||
|
|
||||||
|
The TTY drivers are advised to use struct tty_port helpers as much as possible.
|
||||||
|
If the drivers implement :c:member:`tty_port.ops.activate()` and
|
||||||
|
:c:member:`tty_port.ops.shutdown()`, they can use tty_port_open(),
|
||||||
|
tty_port_close(), and tty_port_hangup() in respective
|
||||||
|
:c:member:`tty_struct.ops` hooks.
|
||||||
|
|
||||||
|
The reference and details are contained in the `TTY Port Reference`_ and `TTY
|
||||||
|
Port Operations Reference`_ sections at the bottom.
|
||||||
|
|
||||||
|
TTY Port Functions
|
||||||
|
==================
|
||||||
|
|
||||||
|
Init & Destroy
|
||||||
|
--------------
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_port.c
|
||||||
|
:identifiers: tty_port_init tty_port_destroy
|
||||||
|
tty_port_get tty_port_put
|
||||||
|
|
||||||
|
Open/Close/Hangup Helpers
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_port.c
|
||||||
|
:identifiers: tty_port_install tty_port_open tty_port_block_til_ready
|
||||||
|
tty_port_close tty_port_close_start tty_port_close_end tty_port_hangup
|
||||||
|
tty_port_shutdown
|
||||||
|
|
||||||
|
TTY Refcounting
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_port.c
|
||||||
|
:identifiers: tty_port_tty_get tty_port_tty_set
|
||||||
|
|
||||||
|
TTY Helpers
|
||||||
|
-----------
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_port.c
|
||||||
|
:identifiers: tty_port_tty_hangup tty_port_tty_wakeup
|
||||||
|
|
||||||
|
|
||||||
|
Modem Signals
|
||||||
|
-------------
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_port.c
|
||||||
|
:identifiers: tty_port_carrier_raised tty_port_raise_dtr_rts
|
||||||
|
tty_port_lower_dtr_rts
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
TTY Port Reference
|
||||||
|
==================
|
||||||
|
|
||||||
|
.. kernel-doc:: include/linux/tty_port.h
|
||||||
|
:identifiers: tty_port
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
TTY Port Operations Reference
|
||||||
|
=============================
|
||||||
|
|
||||||
|
.. kernel-doc:: include/linux/tty_port.h
|
||||||
|
:identifiers: tty_port_operations
|
81
Documentation/tty/tty_struct.rst
Normal file
81
Documentation/tty/tty_struct.rst
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
.. SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
==========
|
||||||
|
TTY Struct
|
||||||
|
==========
|
||||||
|
|
||||||
|
.. contents:: :local:
|
||||||
|
|
||||||
|
struct tty_struct is allocated by the TTY layer upon the first open of the TTY
|
||||||
|
device and released after the last close. The TTY layer passes this structure
|
||||||
|
to most of struct tty_operation's hooks. Members of tty_struct are documented
|
||||||
|
in `TTY Struct Reference`_ at the bottom.
|
||||||
|
|
||||||
|
Initialization
|
||||||
|
==============
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_io.c
|
||||||
|
:identifiers: tty_init_termios
|
||||||
|
|
||||||
|
Name
|
||||||
|
====
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_io.c
|
||||||
|
:identifiers: tty_name
|
||||||
|
|
||||||
|
Reference counting
|
||||||
|
==================
|
||||||
|
|
||||||
|
.. kernel-doc:: include/linux/tty.h
|
||||||
|
:identifiers: tty_kref_get
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_io.c
|
||||||
|
:identifiers: tty_kref_put
|
||||||
|
|
||||||
|
Install
|
||||||
|
=======
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_io.c
|
||||||
|
:identifiers: tty_standard_install
|
||||||
|
|
||||||
|
Read & Write
|
||||||
|
============
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_io.c
|
||||||
|
:identifiers: tty_put_char
|
||||||
|
|
||||||
|
Start & Stop
|
||||||
|
============
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_io.c
|
||||||
|
:identifiers: start_tty stop_tty
|
||||||
|
|
||||||
|
Wakeup
|
||||||
|
======
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_io.c
|
||||||
|
:identifiers: tty_wakeup
|
||||||
|
|
||||||
|
Hangup
|
||||||
|
======
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_io.c
|
||||||
|
:identifiers: tty_hangup tty_vhangup tty_hung_up_p
|
||||||
|
|
||||||
|
Misc
|
||||||
|
====
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/tty/tty_io.c
|
||||||
|
:identifiers: tty_do_resize
|
||||||
|
|
||||||
|
TTY Struct Flags
|
||||||
|
================
|
||||||
|
|
||||||
|
.. kernel-doc:: include/linux/tty.h
|
||||||
|
:doc: TTY Struct Flags
|
||||||
|
|
||||||
|
TTY Struct Reference
|
||||||
|
====================
|
||||||
|
|
||||||
|
.. kernel-doc:: include/linux/tty.h
|
||||||
|
:identifiers: tty_struct
|
|
@ -59,7 +59,7 @@ srmcons_do_receive_chars(struct tty_port *port)
|
||||||
} while((result.bits.status & 1) && (++loops < 10));
|
} while((result.bits.status & 1) && (++loops < 10));
|
||||||
|
|
||||||
if (count)
|
if (count)
|
||||||
tty_schedule_flip(port);
|
tty_flip_buffer_push(port);
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,13 +8,6 @@
|
||||||
*/
|
*/
|
||||||
#include <linux/amba/serial.h>
|
#include <linux/amba/serial.h>
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_ZTE_ZX
|
|
||||||
#undef UART01x_DR
|
|
||||||
#undef UART01x_FR
|
|
||||||
#define UART01x_DR 0x04
|
|
||||||
#define UART01x_FR 0x14
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_UART_PHYS
|
#ifdef CONFIG_DEBUG_UART_PHYS
|
||||||
.macro addruart, rp, rv, tmp
|
.macro addruart, rp, rv, tmp
|
||||||
ldr \rp, =CONFIG_DEBUG_UART_PHYS
|
ldr \rp, =CONFIG_DEBUG_UART_PHYS
|
||||||
|
|
|
@ -88,7 +88,7 @@ static int spk_ttyio_receive_buf2(struct tty_struct *tty,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ldisc_data->buf_free)
|
if (!ldisc_data->buf_free)
|
||||||
/* ttyio_in will tty_schedule_flip */
|
/* ttyio_in will tty_flip_buffer_push */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Make sure the consumer has read buf before we have seen
|
/* Make sure the consumer has read buf before we have seen
|
||||||
|
@ -312,7 +312,7 @@ static unsigned char ttyio_in(struct spk_synth *in_synth, int timeout)
|
||||||
mb();
|
mb();
|
||||||
ldisc_data->buf_free = true;
|
ldisc_data->buf_free = true;
|
||||||
/* Let TTY push more characters */
|
/* Let TTY push more characters */
|
||||||
tty_schedule_flip(tty->port);
|
tty_flip_buffer_push(tty->port);
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
|
@ -739,14 +739,13 @@ static int hci_uart_set_flags(struct hci_uart *hu, unsigned long flags)
|
||||||
* Arguments:
|
* Arguments:
|
||||||
*
|
*
|
||||||
* tty pointer to tty instance data
|
* tty pointer to tty instance data
|
||||||
* file pointer to open file object for device
|
|
||||||
* cmd IOCTL command code
|
* cmd IOCTL command code
|
||||||
* arg argument for IOCTL call (cmd dependent)
|
* arg argument for IOCTL call (cmd dependent)
|
||||||
*
|
*
|
||||||
* Return Value: Command dependent
|
* Return Value: Command dependent
|
||||||
*/
|
*/
|
||||||
static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file *file,
|
static int hci_uart_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
|
||||||
unsigned int cmd, unsigned long arg)
|
unsigned long arg)
|
||||||
{
|
{
|
||||||
struct hci_uart *hu = tty->disc_data;
|
struct hci_uart *hu = tty->disc_data;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
|
@ -207,8 +207,8 @@ static void serport_set_type(struct tty_struct *tty, unsigned long type)
|
||||||
* serport_ldisc_ioctl() allows to set the port protocol, and device ID
|
* serport_ldisc_ioctl() allows to set the port protocol, and device ID
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int serport_ldisc_ioctl(struct tty_struct *tty, struct file *file,
|
static int serport_ldisc_ioctl(struct tty_struct *tty, unsigned int cmd,
|
||||||
unsigned int cmd, unsigned long arg)
|
unsigned long arg)
|
||||||
{
|
{
|
||||||
if (cmd == SPIOCSTYPE) {
|
if (cmd == SPIOCSTYPE) {
|
||||||
unsigned long type;
|
unsigned long type;
|
||||||
|
@ -226,7 +226,6 @@ static int serport_ldisc_ioctl(struct tty_struct *tty, struct file *file,
|
||||||
#ifdef CONFIG_COMPAT
|
#ifdef CONFIG_COMPAT
|
||||||
#define COMPAT_SPIOCSTYPE _IOW('q', 0x01, compat_ulong_t)
|
#define COMPAT_SPIOCSTYPE _IOW('q', 0x01, compat_ulong_t)
|
||||||
static int serport_ldisc_compat_ioctl(struct tty_struct *tty,
|
static int serport_ldisc_compat_ioctl(struct tty_struct *tty,
|
||||||
struct file *file,
|
|
||||||
unsigned int cmd, unsigned long arg)
|
unsigned int cmd, unsigned long arg)
|
||||||
{
|
{
|
||||||
if (cmd == COMPAT_SPIOCSTYPE) {
|
if (cmd == COMPAT_SPIOCSTYPE) {
|
||||||
|
|
|
@ -673,8 +673,8 @@ static void slcan_hangup(struct tty_struct *tty)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Perform I/O control on an active SLCAN channel. */
|
/* Perform I/O control on an active SLCAN channel. */
|
||||||
static int slcan_ioctl(struct tty_struct *tty, struct file *file,
|
static int slcan_ioctl(struct tty_struct *tty, unsigned int cmd,
|
||||||
unsigned int cmd, unsigned long arg)
|
unsigned long arg)
|
||||||
{
|
{
|
||||||
struct slcan *sl = (struct slcan *) tty->disc_data;
|
struct slcan *sl = (struct slcan *) tty->disc_data;
|
||||||
unsigned int tmp;
|
unsigned int tmp;
|
||||||
|
|
|
@ -681,8 +681,8 @@ static void sixpack_close(struct tty_struct *tty)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Perform I/O control on an active 6pack channel. */
|
/* Perform I/O control on an active 6pack channel. */
|
||||||
static int sixpack_ioctl(struct tty_struct *tty, struct file *file,
|
static int sixpack_ioctl(struct tty_struct *tty, unsigned int cmd,
|
||||||
unsigned int cmd, unsigned long arg)
|
unsigned long arg)
|
||||||
{
|
{
|
||||||
struct sixpack *sp = sp_get(tty);
|
struct sixpack *sp = sp_get(tty);
|
||||||
struct net_device *dev;
|
struct net_device *dev;
|
||||||
|
|
|
@ -806,8 +806,8 @@ static void mkiss_close(struct tty_struct *tty)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Perform I/O control on an active ax25 channel. */
|
/* Perform I/O control on an active ax25 channel. */
|
||||||
static int mkiss_ioctl(struct tty_struct *tty, struct file *file,
|
static int mkiss_ioctl(struct tty_struct *tty, unsigned int cmd,
|
||||||
unsigned int cmd, unsigned long arg)
|
unsigned long arg)
|
||||||
{
|
{
|
||||||
struct mkiss *ax = mkiss_get(tty);
|
struct mkiss *ax = mkiss_get(tty);
|
||||||
struct net_device *dev;
|
struct net_device *dev;
|
||||||
|
|
|
@ -281,8 +281,7 @@ ppp_asynctty_write(struct tty_struct *tty, struct file *file,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ppp_asynctty_ioctl(struct tty_struct *tty, struct file *file,
|
ppp_asynctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
|
||||||
unsigned int cmd, unsigned long arg)
|
|
||||||
{
|
{
|
||||||
struct asyncppp *ap = ap_get(tty);
|
struct asyncppp *ap = ap_get(tty);
|
||||||
int err, val;
|
int err, val;
|
||||||
|
|
|
@ -274,8 +274,7 @@ ppp_sync_write(struct tty_struct *tty, struct file *file,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ppp_synctty_ioctl(struct tty_struct *tty, struct file *file,
|
ppp_synctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
|
||||||
unsigned int cmd, unsigned long arg)
|
|
||||||
{
|
{
|
||||||
struct syncppp *ap = sp_get(tty);
|
struct syncppp *ap = sp_get(tty);
|
||||||
int __user *p = (int __user *)arg;
|
int __user *p = (int __user *)arg;
|
||||||
|
|
|
@ -1072,8 +1072,8 @@ static void slip_unesc6(struct slip *sl, unsigned char s)
|
||||||
#endif /* CONFIG_SLIP_MODE_SLIP6 */
|
#endif /* CONFIG_SLIP_MODE_SLIP6 */
|
||||||
|
|
||||||
/* Perform I/O control on an active SLIP channel. */
|
/* Perform I/O control on an active SLIP channel. */
|
||||||
static int slip_ioctl(struct tty_struct *tty, struct file *file,
|
static int slip_ioctl(struct tty_struct *tty, unsigned int cmd,
|
||||||
unsigned int cmd, unsigned long arg)
|
unsigned long arg)
|
||||||
{
|
{
|
||||||
struct slip *sl = tty->disc_data;
|
struct slip *sl = tty->disc_data;
|
||||||
unsigned int tmp;
|
unsigned int tmp;
|
||||||
|
|
|
@ -56,7 +56,7 @@ static inline void
|
||||||
kbd_put_queue(struct tty_port *port, int ch)
|
kbd_put_queue(struct tty_port *port, int ch)
|
||||||
{
|
{
|
||||||
tty_insert_flip_char(port, ch, 0);
|
tty_insert_flip_char(port, ch, 0);
|
||||||
tty_schedule_flip(port);
|
tty_flip_buffer_push(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
|
@ -64,5 +64,5 @@ kbd_puts_queue(struct tty_port *port, char *cp)
|
||||||
{
|
{
|
||||||
while (*cp)
|
while (*cp)
|
||||||
tty_insert_flip_char(port, *cp++, 0);
|
tty_insert_flip_char(port, *cp++, 0);
|
||||||
tty_schedule_flip(port);
|
tty_flip_buffer_push(port);
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,7 +151,7 @@ static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id)
|
||||||
address = (unsigned long)(void *)buf;
|
address = (unsigned long)(void *)buf;
|
||||||
goldfish_tty_rw(qtty, address, count, 0);
|
goldfish_tty_rw(qtty, address, count, 0);
|
||||||
|
|
||||||
tty_schedule_flip(&qtty->port);
|
tty_flip_buffer_push(&qtty->port);
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,7 +298,7 @@ static int goldfish_tty_probe(struct platform_device *pdev)
|
||||||
struct resource *r;
|
struct resource *r;
|
||||||
struct device *ttydev;
|
struct device *ttydev;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
u32 irq;
|
int irq;
|
||||||
unsigned int line;
|
unsigned int line;
|
||||||
|
|
||||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
@ -313,14 +313,12 @@ static int goldfish_tty_probe(struct platform_device *pdev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
if (!r) {
|
if (irq < 0) {
|
||||||
pr_err("goldfish_tty: No IRQ resource available!\n");
|
ret = irq;
|
||||||
goto err_unmap;
|
goto err_unmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
irq = r->start;
|
|
||||||
|
|
||||||
mutex_lock(&goldfish_tty_lock);
|
mutex_lock(&goldfish_tty_lock);
|
||||||
|
|
||||||
if (pdev->id == PLATFORM_DEVID_NONE)
|
if (pdev->id == PLATFORM_DEVID_NONE)
|
||||||
|
|
|
@ -955,19 +955,18 @@ static int mips_ejtag_fdc_tty_probe(struct mips_cdmm_device *dev)
|
||||||
mips_ejtag_fdc_con.tty_drv = driver;
|
mips_ejtag_fdc_con.tty_drv = driver;
|
||||||
|
|
||||||
init_waitqueue_head(&priv->waitqueue);
|
init_waitqueue_head(&priv->waitqueue);
|
||||||
priv->thread = kthread_create(mips_ejtag_fdc_put, priv, priv->fdc_name);
|
|
||||||
if (IS_ERR(priv->thread)) {
|
|
||||||
ret = PTR_ERR(priv->thread);
|
|
||||||
dev_err(priv->dev, "Couldn't create kthread (%d)\n", ret);
|
|
||||||
goto err_destroy_ports;
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
* Bind the writer thread to the right CPU so it can't migrate.
|
* Bind the writer thread to the right CPU so it can't migrate.
|
||||||
* The channels are per-CPU and we want all channel I/O to be on a
|
* The channels are per-CPU and we want all channel I/O to be on a
|
||||||
* single predictable CPU.
|
* single predictable CPU.
|
||||||
*/
|
*/
|
||||||
kthread_bind(priv->thread, dev->cpu);
|
priv->thread = kthread_run_on_cpu(mips_ejtag_fdc_put, priv,
|
||||||
wake_up_process(priv->thread);
|
dev->cpu, "ttyFDC/%u");
|
||||||
|
if (IS_ERR(priv->thread)) {
|
||||||
|
ret = PTR_ERR(priv->thread);
|
||||||
|
dev_err(priv->dev, "Couldn't create kthread (%d)\n", ret);
|
||||||
|
goto err_destroy_ports;
|
||||||
|
}
|
||||||
|
|
||||||
/* Look for an FDC IRQ */
|
/* Look for an FDC IRQ */
|
||||||
priv->irq = get_c0_fdc_int();
|
priv->irq = get_c0_fdc_int();
|
||||||
|
@ -1095,15 +1094,14 @@ static int mips_ejtag_fdc_tty_cpu_up(struct mips_cdmm_device *dev)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Restart the kthread */
|
/* Restart the kthread */
|
||||||
priv->thread = kthread_create(mips_ejtag_fdc_put, priv, priv->fdc_name);
|
/* Bind it back to the right CPU and set it off */
|
||||||
|
priv->thread = kthread_run_on_cpu(mips_ejtag_fdc_put, priv,
|
||||||
|
dev->cpu, "ttyFDC/%u");
|
||||||
if (IS_ERR(priv->thread)) {
|
if (IS_ERR(priv->thread)) {
|
||||||
ret = PTR_ERR(priv->thread);
|
ret = PTR_ERR(priv->thread);
|
||||||
dev_err(priv->dev, "Couldn't re-create kthread (%d)\n", ret);
|
dev_err(priv->dev, "Couldn't re-create kthread (%d)\n", ret);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
/* Bind it back to the right CPU and set it off */
|
|
||||||
kthread_bind(priv->thread, dev->cpu);
|
|
||||||
wake_up_process(priv->thread);
|
|
||||||
out:
|
out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1683,7 +1683,7 @@ static int moxa_poll_port(struct moxa_port *p, unsigned int handle,
|
||||||
if (inited && !tty_throttled(tty) &&
|
if (inited && !tty_throttled(tty) &&
|
||||||
MoxaPortRxQueue(p) > 0) { /* RX */
|
MoxaPortRxQueue(p) > 0) { /* RX */
|
||||||
MoxaPortReadData(p);
|
MoxaPortReadData(p);
|
||||||
tty_schedule_flip(&p->port);
|
tty_flip_buffer_push(&p->port);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
clear_bit(EMPTYWAIT, &p->statusflags);
|
clear_bit(EMPTYWAIT, &p->statusflags);
|
||||||
|
@ -1708,7 +1708,7 @@ static int moxa_poll_port(struct moxa_port *p, unsigned int handle,
|
||||||
|
|
||||||
if (tty && (intr & IntrBreak) && !I_IGNBRK(tty)) { /* BREAK */
|
if (tty && (intr & IntrBreak) && !I_IGNBRK(tty)) { /* BREAK */
|
||||||
tty_insert_flip_char(&p->port, 0, TTY_BREAK);
|
tty_insert_flip_char(&p->port, 0, TTY_BREAK);
|
||||||
tty_schedule_flip(&p->port);
|
tty_flip_buffer_push(&p->port);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (intr & IntrLine)
|
if (intr & IntrLine)
|
||||||
|
|
|
@ -159,14 +159,32 @@
|
||||||
#define MXSER_BAUD_BASE 921600
|
#define MXSER_BAUD_BASE 921600
|
||||||
#define MXSER_CUSTOM_DIVISOR (MXSER_BAUD_BASE * 16)
|
#define MXSER_CUSTOM_DIVISOR (MXSER_BAUD_BASE * 16)
|
||||||
|
|
||||||
#define PCI_DEVICE_ID_POS104UL 0x1044
|
#define PCI_DEVICE_ID_MOXA_RC7000 0x0001
|
||||||
#define PCI_DEVICE_ID_CB108 0x1080
|
#define PCI_DEVICE_ID_MOXA_CP102 0x1020
|
||||||
#define PCI_DEVICE_ID_CP102UF 0x1023
|
#define PCI_DEVICE_ID_MOXA_CP102UL 0x1021
|
||||||
#define PCI_DEVICE_ID_CP112UL 0x1120
|
#define PCI_DEVICE_ID_MOXA_CP102U 0x1022
|
||||||
#define PCI_DEVICE_ID_CB114 0x1142
|
#define PCI_DEVICE_ID_MOXA_CP102UF 0x1023
|
||||||
#define PCI_DEVICE_ID_CP114UL 0x1143
|
#define PCI_DEVICE_ID_MOXA_C104 0x1040
|
||||||
#define PCI_DEVICE_ID_CB134I 0x1341
|
#define PCI_DEVICE_ID_MOXA_CP104U 0x1041
|
||||||
#define PCI_DEVICE_ID_CP138U 0x1380
|
#define PCI_DEVICE_ID_MOXA_CP104JU 0x1042
|
||||||
|
#define PCI_DEVICE_ID_MOXA_CP104EL 0x1043
|
||||||
|
#define PCI_DEVICE_ID_MOXA_POS104UL 0x1044
|
||||||
|
#define PCI_DEVICE_ID_MOXA_CB108 0x1080
|
||||||
|
#define PCI_DEVICE_ID_MOXA_CP112UL 0x1120
|
||||||
|
#define PCI_DEVICE_ID_MOXA_CT114 0x1140
|
||||||
|
#define PCI_DEVICE_ID_MOXA_CP114 0x1141
|
||||||
|
#define PCI_DEVICE_ID_MOXA_CB114 0x1142
|
||||||
|
#define PCI_DEVICE_ID_MOXA_CP114UL 0x1143
|
||||||
|
#define PCI_DEVICE_ID_MOXA_CP118U 0x1180
|
||||||
|
#define PCI_DEVICE_ID_MOXA_CP118EL 0x1181
|
||||||
|
#define PCI_DEVICE_ID_MOXA_CP132 0x1320
|
||||||
|
#define PCI_DEVICE_ID_MOXA_CP132U 0x1321
|
||||||
|
#define PCI_DEVICE_ID_MOXA_CP134U 0x1340
|
||||||
|
#define PCI_DEVICE_ID_MOXA_CB134I 0x1341
|
||||||
|
#define PCI_DEVICE_ID_MOXA_CP138U 0x1380
|
||||||
|
#define PCI_DEVICE_ID_MOXA_C168 0x1680
|
||||||
|
#define PCI_DEVICE_ID_MOXA_CP168U 0x1681
|
||||||
|
#define PCI_DEVICE_ID_MOXA_CP168EL 0x1682
|
||||||
|
|
||||||
#define MXSER_NPORTS(ddata) ((ddata) & 0xffU)
|
#define MXSER_NPORTS(ddata) ((ddata) & 0xffU)
|
||||||
#define MXSER_HIGHBAUD 0x0100
|
#define MXSER_HIGHBAUD 0x0100
|
||||||
|
@ -194,32 +212,32 @@ static const struct {
|
||||||
/* driver_data correspond to the lines in the structure above
|
/* driver_data correspond to the lines in the structure above
|
||||||
see also ISA probe function before you change something */
|
see also ISA probe function before you change something */
|
||||||
static const struct pci_device_id mxser_pcibrds[] = {
|
static const struct pci_device_id mxser_pcibrds[] = {
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C168), .driver_data = 8 },
|
{ PCI_DEVICE_DATA(MOXA, C168, 8) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C104), .driver_data = 4 },
|
{ PCI_DEVICE_DATA(MOXA, C104, 4) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132), .driver_data = 2 },
|
{ PCI_DEVICE_DATA(MOXA, CP132, 2) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114), .driver_data = 4 },
|
{ PCI_DEVICE_DATA(MOXA, CP114, 4) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CT114), .driver_data = 4 },
|
{ PCI_DEVICE_DATA(MOXA, CT114, 4) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102), .driver_data = 2 | MXSER_HIGHBAUD },
|
{ PCI_DEVICE_DATA(MOXA, CP102, 2 | MXSER_HIGHBAUD) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104U), .driver_data = 4 },
|
{ PCI_DEVICE_DATA(MOXA, CP104U, 4) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168U), .driver_data = 8 },
|
{ PCI_DEVICE_DATA(MOXA, CP168U, 8) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132U), .driver_data = 2 },
|
{ PCI_DEVICE_DATA(MOXA, CP132U, 2) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134U), .driver_data = 4 },
|
{ PCI_DEVICE_DATA(MOXA, CP134U, 4) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104JU),.driver_data = 4 },
|
{ PCI_DEVICE_DATA(MOXA, CP104JU, 4) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_RC7000), .driver_data = 8 }, /* RC7000 */
|
{ PCI_DEVICE_DATA(MOXA, RC7000, 8) }, /* RC7000 */
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118U), .driver_data = 8 },
|
{ PCI_DEVICE_DATA(MOXA, CP118U, 8) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102UL),.driver_data = 2 },
|
{ PCI_DEVICE_DATA(MOXA, CP102UL, 2) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102U), .driver_data = 2 },
|
{ PCI_DEVICE_DATA(MOXA, CP102U, 2) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118EL),.driver_data = 8 },
|
{ PCI_DEVICE_DATA(MOXA, CP118EL, 8) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168EL),.driver_data = 8 },
|
{ PCI_DEVICE_DATA(MOXA, CP168EL, 8) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104EL),.driver_data = 4 },
|
{ PCI_DEVICE_DATA(MOXA, CP104EL, 4) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB108), .driver_data = 8 },
|
{ PCI_DEVICE_DATA(MOXA, CB108, 8) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB114), .driver_data = 4 },
|
{ PCI_DEVICE_DATA(MOXA, CB114, 4) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB134I), .driver_data = 4 },
|
{ PCI_DEVICE_DATA(MOXA, CB134I, 4) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP138U), .driver_data = 8 },
|
{ PCI_DEVICE_DATA(MOXA, CP138U, 8) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_POS104UL), .driver_data = 4 },
|
{ PCI_DEVICE_DATA(MOXA, POS104UL, 4) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP114UL), .driver_data = 4 },
|
{ PCI_DEVICE_DATA(MOXA, CP114UL, 4) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP102UF), .driver_data = 2 },
|
{ PCI_DEVICE_DATA(MOXA, CP102UF, 2) },
|
||||||
{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP112UL), .driver_data = 2 },
|
{ PCI_DEVICE_DATA(MOXA, CP112UL, 2) },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(pci, mxser_pcibrds);
|
MODULE_DEVICE_TABLE(pci, mxser_pcibrds);
|
||||||
|
@ -251,8 +269,6 @@ struct mxser_port {
|
||||||
u8 MCR; /* Modem control register */
|
u8 MCR; /* Modem control register */
|
||||||
u8 FCR; /* FIFO control register */
|
u8 FCR; /* FIFO control register */
|
||||||
|
|
||||||
bool ldisc_stop_rx;
|
|
||||||
|
|
||||||
struct async_icount icount; /* kernel counters for 4 input interrupts */
|
struct async_icount icount; /* kernel counters for 4 input interrupts */
|
||||||
unsigned int timeout;
|
unsigned int timeout;
|
||||||
|
|
||||||
|
@ -262,7 +278,6 @@ struct mxser_port {
|
||||||
unsigned int xmit_head;
|
unsigned int xmit_head;
|
||||||
unsigned int xmit_tail;
|
unsigned int xmit_tail;
|
||||||
unsigned int xmit_cnt;
|
unsigned int xmit_cnt;
|
||||||
int closing;
|
|
||||||
|
|
||||||
spinlock_t slock;
|
spinlock_t slock;
|
||||||
};
|
};
|
||||||
|
@ -684,27 +699,34 @@ static void mxser_change_speed(struct tty_struct *tty, struct ktermios *old_term
|
||||||
outb(cval, info->ioaddr + UART_LCR);
|
outb(cval, info->ioaddr + UART_LCR);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mxser_check_modem_status(struct tty_struct *tty,
|
static u8 mxser_check_modem_status(struct tty_struct *tty,
|
||||||
struct mxser_port *port, int status)
|
struct mxser_port *port)
|
||||||
{
|
{
|
||||||
|
u8 msr = inb(port->ioaddr + UART_MSR);
|
||||||
|
|
||||||
|
if (!(msr & UART_MSR_ANY_DELTA))
|
||||||
|
return msr;
|
||||||
|
|
||||||
/* update input line counters */
|
/* update input line counters */
|
||||||
if (status & UART_MSR_TERI)
|
if (msr & UART_MSR_TERI)
|
||||||
port->icount.rng++;
|
port->icount.rng++;
|
||||||
if (status & UART_MSR_DDSR)
|
if (msr & UART_MSR_DDSR)
|
||||||
port->icount.dsr++;
|
port->icount.dsr++;
|
||||||
if (status & UART_MSR_DDCD)
|
if (msr & UART_MSR_DDCD)
|
||||||
port->icount.dcd++;
|
port->icount.dcd++;
|
||||||
if (status & UART_MSR_DCTS)
|
if (msr & UART_MSR_DCTS)
|
||||||
port->icount.cts++;
|
port->icount.cts++;
|
||||||
wake_up_interruptible(&port->port.delta_msr_wait);
|
wake_up_interruptible(&port->port.delta_msr_wait);
|
||||||
|
|
||||||
if (tty_port_check_carrier(&port->port) && (status & UART_MSR_DDCD)) {
|
if (tty_port_check_carrier(&port->port) && (msr & UART_MSR_DDCD)) {
|
||||||
if (status & UART_MSR_DCD)
|
if (msr & UART_MSR_DCD)
|
||||||
wake_up_interruptible(&port->port.open_wait);
|
wake_up_interruptible(&port->port.open_wait);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tty_port_cts_enabled(&port->port))
|
if (tty_port_cts_enabled(&port->port))
|
||||||
mxser_handle_cts(tty, port, status);
|
mxser_handle_cts(tty, port, msr);
|
||||||
|
|
||||||
|
return msr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mxser_disable_and_clear_FIFO(struct mxser_port *info)
|
static void mxser_disable_and_clear_FIFO(struct mxser_port *info)
|
||||||
|
@ -801,6 +823,20 @@ static int mxser_activate(struct tty_port *port, struct tty_struct *tty)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To stop accepting input, we disable the receive line status interrupts, and
|
||||||
|
* tell the interrupt driver to stop checking the data ready bit in the line
|
||||||
|
* status register.
|
||||||
|
*/
|
||||||
|
static void mxser_stop_rx(struct mxser_port *info)
|
||||||
|
{
|
||||||
|
info->IER &= ~UART_IER_RLSI;
|
||||||
|
if (info->board->must_hwid)
|
||||||
|
info->IER &= ~MOXA_MUST_RECV_ISR;
|
||||||
|
|
||||||
|
outb(info->IER, info->ioaddr + UART_IER);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This routine will shutdown a serial port
|
* This routine will shutdown a serial port
|
||||||
*/
|
*/
|
||||||
|
@ -811,6 +847,8 @@ static void mxser_shutdown_port(struct tty_port *port)
|
||||||
|
|
||||||
spin_lock_irqsave(&info->slock, flags);
|
spin_lock_irqsave(&info->slock, flags);
|
||||||
|
|
||||||
|
mxser_stop_rx(info);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* clear delta_msr_wait queue to avoid mem leaks: we may free the irq
|
* clear delta_msr_wait queue to avoid mem leaks: we may free the irq
|
||||||
* here so the queue might never be waken up
|
* here so the queue might never be waken up
|
||||||
|
@ -874,64 +912,9 @@ static void mxser_flush_buffer(struct tty_struct *tty)
|
||||||
tty_wakeup(tty);
|
tty_wakeup(tty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void mxser_close_port(struct tty_port *port)
|
|
||||||
{
|
|
||||||
struct mxser_port *info = container_of(port, struct mxser_port, port);
|
|
||||||
unsigned long timeout;
|
|
||||||
/*
|
|
||||||
* At this point we stop accepting input. To do this, we
|
|
||||||
* disable the receive line status interrupts, and tell the
|
|
||||||
* interrupt driver to stop checking the data ready bit in the
|
|
||||||
* line status register.
|
|
||||||
*/
|
|
||||||
info->IER &= ~UART_IER_RLSI;
|
|
||||||
if (info->board->must_hwid)
|
|
||||||
info->IER &= ~MOXA_MUST_RECV_ISR;
|
|
||||||
|
|
||||||
outb(info->IER, info->ioaddr + UART_IER);
|
|
||||||
/*
|
|
||||||
* Before we drop DTR, make sure the UART transmitter
|
|
||||||
* has completely drained; this is especially
|
|
||||||
* important if there is a transmit FIFO!
|
|
||||||
*/
|
|
||||||
timeout = jiffies + HZ;
|
|
||||||
while (!(inb(info->ioaddr + UART_LSR) & UART_LSR_TEMT)) {
|
|
||||||
schedule_timeout_interruptible(5);
|
|
||||||
if (time_after(jiffies, timeout))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This routine is called when the serial port gets closed. First, we
|
|
||||||
* wait for the last remaining data to be sent. Then, we unlink its
|
|
||||||
* async structure from the interrupt chain if necessary, and we free
|
|
||||||
* that IRQ if nothing is left in the chain.
|
|
||||||
*/
|
|
||||||
static void mxser_close(struct tty_struct *tty, struct file *filp)
|
static void mxser_close(struct tty_struct *tty, struct file *filp)
|
||||||
{
|
{
|
||||||
struct mxser_port *info = tty->driver_data;
|
tty_port_close(tty->port, tty, filp);
|
||||||
struct tty_port *port = &info->port;
|
|
||||||
|
|
||||||
if (info == NULL)
|
|
||||||
return;
|
|
||||||
if (tty_port_close_start(port, tty, filp) == 0)
|
|
||||||
return;
|
|
||||||
info->closing = 1;
|
|
||||||
mutex_lock(&port->mutex);
|
|
||||||
mxser_close_port(port);
|
|
||||||
mxser_flush_buffer(tty);
|
|
||||||
if (tty_port_initialized(port) && C_HUPCL(tty))
|
|
||||||
tty_port_lower_dtr_rts(port);
|
|
||||||
mxser_shutdown_port(port);
|
|
||||||
tty_port_set_initialized(port, 0);
|
|
||||||
mutex_unlock(&port->mutex);
|
|
||||||
info->closing = 0;
|
|
||||||
/* Right now the tty_port set is done outside of the close_end helper
|
|
||||||
as we don't yet have everyone using refcounts */
|
|
||||||
tty_port_close_end(port, tty);
|
|
||||||
tty_port_tty_set(port, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mxser_write(struct tty_struct *tty, const unsigned char *buf, int count)
|
static int mxser_write(struct tty_struct *tty, const unsigned char *buf, int count)
|
||||||
|
@ -940,9 +923,6 @@ static int mxser_write(struct tty_struct *tty, const unsigned char *buf, int cou
|
||||||
struct mxser_port *info = tty->driver_data;
|
struct mxser_port *info = tty->driver_data;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
if (!info->port.xmit_buf)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
|
c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
|
||||||
SERIAL_XMIT_SIZE - info->xmit_head));
|
SERIAL_XMIT_SIZE - info->xmit_head));
|
||||||
|
@ -973,9 +953,6 @@ static int mxser_put_char(struct tty_struct *tty, unsigned char ch)
|
||||||
struct mxser_port *info = tty->driver_data;
|
struct mxser_port *info = tty->driver_data;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
if (!info->port.xmit_buf)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1)
|
if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -993,7 +970,7 @@ static void mxser_flush_chars(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct mxser_port *info = tty->driver_data;
|
struct mxser_port *info = tty->driver_data;
|
||||||
|
|
||||||
if (!info->xmit_cnt || tty->flow.stopped || !info->port.xmit_buf ||
|
if (!info->xmit_cnt || tty->flow.stopped ||
|
||||||
(tty->hw_stopped && !mxser_16550A_or_MUST(info)))
|
(tty->hw_stopped && !mxser_16550A_or_MUST(info)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1153,25 +1130,24 @@ static int mxser_get_lsr_info(struct mxser_port *info,
|
||||||
static int mxser_tiocmget(struct tty_struct *tty)
|
static int mxser_tiocmget(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct mxser_port *info = tty->driver_data;
|
struct mxser_port *info = tty->driver_data;
|
||||||
unsigned char control, status;
|
unsigned char control;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
u8 msr;
|
||||||
|
|
||||||
if (tty_io_error(tty))
|
if (tty_io_error(tty))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
spin_lock_irqsave(&info->slock, flags);
|
spin_lock_irqsave(&info->slock, flags);
|
||||||
control = info->MCR;
|
control = info->MCR;
|
||||||
status = inb(info->ioaddr + UART_MSR);
|
msr = mxser_check_modem_status(tty, info);
|
||||||
if (status & UART_MSR_ANY_DELTA)
|
|
||||||
mxser_check_modem_status(tty, info, status);
|
|
||||||
spin_unlock_irqrestore(&info->slock, flags);
|
spin_unlock_irqrestore(&info->slock, flags);
|
||||||
|
|
||||||
return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) |
|
return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) |
|
||||||
((control & UART_MCR_DTR) ? TIOCM_DTR : 0) |
|
((control & UART_MCR_DTR) ? TIOCM_DTR : 0) |
|
||||||
((status & UART_MSR_DCD) ? TIOCM_CAR : 0) |
|
((msr & UART_MSR_DCD) ? TIOCM_CAR : 0) |
|
||||||
((status & UART_MSR_RI) ? TIOCM_RNG : 0) |
|
((msr & UART_MSR_RI) ? TIOCM_RNG : 0) |
|
||||||
((status & UART_MSR_DSR) ? TIOCM_DSR : 0) |
|
((msr & UART_MSR_DSR) ? TIOCM_DSR : 0) |
|
||||||
((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
|
((msr & UART_MSR_CTS) ? TIOCM_CTS : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mxser_tiocmset(struct tty_struct *tty,
|
static int mxser_tiocmset(struct tty_struct *tty,
|
||||||
|
@ -1326,11 +1302,14 @@ static int mxser_get_icount(struct tty_struct *tty,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mxser_stoprx(struct tty_struct *tty)
|
/*
|
||||||
|
* This routine is called by the upper-layer tty layer to signal that
|
||||||
|
* incoming characters should be throttled.
|
||||||
|
*/
|
||||||
|
static void mxser_throttle(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct mxser_port *info = tty->driver_data;
|
struct mxser_port *info = tty->driver_data;
|
||||||
|
|
||||||
info->ldisc_stop_rx = true;
|
|
||||||
if (I_IXOFF(tty)) {
|
if (I_IXOFF(tty)) {
|
||||||
if (info->board->must_hwid) {
|
if (info->board->must_hwid) {
|
||||||
info->IER &= ~MOXA_MUST_RECV_ISR;
|
info->IER &= ~MOXA_MUST_RECV_ISR;
|
||||||
|
@ -1349,21 +1328,11 @@ static void mxser_stoprx(struct tty_struct *tty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* This routine is called by the upper-layer tty layer to signal that
|
|
||||||
* incoming characters should be throttled.
|
|
||||||
*/
|
|
||||||
static void mxser_throttle(struct tty_struct *tty)
|
|
||||||
{
|
|
||||||
mxser_stoprx(tty);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mxser_unthrottle(struct tty_struct *tty)
|
static void mxser_unthrottle(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct mxser_port *info = tty->driver_data;
|
struct mxser_port *info = tty->driver_data;
|
||||||
|
|
||||||
/* startrx */
|
/* startrx */
|
||||||
info->ldisc_stop_rx = false;
|
|
||||||
if (I_IXOFF(tty)) {
|
if (I_IXOFF(tty)) {
|
||||||
if (info->x_char)
|
if (info->x_char)
|
||||||
info->x_char = 0;
|
info->x_char = 0;
|
||||||
|
@ -1409,7 +1378,7 @@ static void mxser_start(struct tty_struct *tty)
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
spin_lock_irqsave(&info->slock, flags);
|
spin_lock_irqsave(&info->slock, flags);
|
||||||
if (info->xmit_cnt && info->port.xmit_buf)
|
if (info->xmit_cnt)
|
||||||
__mxser_start_tx(info);
|
__mxser_start_tx(info);
|
||||||
spin_unlock_irqrestore(&info->slock, flags);
|
spin_unlock_irqrestore(&info->slock, flags);
|
||||||
}
|
}
|
||||||
|
@ -1442,15 +1411,25 @@ static void mxser_set_termios(struct tty_struct *tty, struct ktermios *old_termi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool mxser_tx_empty(struct mxser_port *info)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
u8 lsr;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&info->slock, flags);
|
||||||
|
lsr = inb(info->ioaddr + UART_LSR);
|
||||||
|
spin_unlock_irqrestore(&info->slock, flags);
|
||||||
|
|
||||||
|
return !(lsr & UART_LSR_TEMT);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* mxser_wait_until_sent() --- wait until the transmitter is empty
|
* mxser_wait_until_sent() --- wait until the transmitter is empty
|
||||||
*/
|
*/
|
||||||
static void mxser_wait_until_sent(struct tty_struct *tty, int timeout)
|
static void mxser_wait_until_sent(struct tty_struct *tty, int timeout)
|
||||||
{
|
{
|
||||||
struct mxser_port *info = tty->driver_data;
|
struct mxser_port *info = tty->driver_data;
|
||||||
unsigned long orig_jiffies, char_time;
|
unsigned long expire, char_time;
|
||||||
unsigned long flags;
|
|
||||||
int lsr;
|
|
||||||
|
|
||||||
if (info->type == PORT_UNKNOWN)
|
if (info->type == PORT_UNKNOWN)
|
||||||
return;
|
return;
|
||||||
|
@ -1458,7 +1437,6 @@ static void mxser_wait_until_sent(struct tty_struct *tty, int timeout)
|
||||||
if (info->xmit_fifo_size == 0)
|
if (info->xmit_fifo_size == 0)
|
||||||
return; /* Just in case.... */
|
return; /* Just in case.... */
|
||||||
|
|
||||||
orig_jiffies = jiffies;
|
|
||||||
/*
|
/*
|
||||||
* Set the check interval to be 1/5 of the estimated time to
|
* Set the check interval to be 1/5 of the estimated time to
|
||||||
* send a single character, and make it at least 1. The check
|
* send a single character, and make it at least 1. The check
|
||||||
|
@ -1473,6 +1451,9 @@ static void mxser_wait_until_sent(struct tty_struct *tty, int timeout)
|
||||||
char_time = 1;
|
char_time = 1;
|
||||||
if (timeout && timeout < char_time)
|
if (timeout && timeout < char_time)
|
||||||
char_time = timeout;
|
char_time = timeout;
|
||||||
|
|
||||||
|
char_time = jiffies_to_msecs(char_time);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the transmitter hasn't cleared in twice the approximate
|
* If the transmitter hasn't cleared in twice the approximate
|
||||||
* amount of time to send the entire FIFO, it probably won't
|
* amount of time to send the entire FIFO, it probably won't
|
||||||
|
@ -1485,18 +1466,15 @@ static void mxser_wait_until_sent(struct tty_struct *tty, int timeout)
|
||||||
if (!timeout || timeout > 2 * info->timeout)
|
if (!timeout || timeout > 2 * info->timeout)
|
||||||
timeout = 2 * info->timeout;
|
timeout = 2 * info->timeout;
|
||||||
|
|
||||||
spin_lock_irqsave(&info->slock, flags);
|
expire = jiffies + timeout;
|
||||||
while (!((lsr = inb(info->ioaddr + UART_LSR)) & UART_LSR_TEMT)) {
|
|
||||||
spin_unlock_irqrestore(&info->slock, flags);
|
while (mxser_tx_empty(info)) {
|
||||||
schedule_timeout_interruptible(char_time);
|
msleep_interruptible(char_time);
|
||||||
spin_lock_irqsave(&info->slock, flags);
|
|
||||||
if (signal_pending(current))
|
if (signal_pending(current))
|
||||||
break;
|
break;
|
||||||
if (timeout && time_after(jiffies, orig_jiffies + timeout))
|
if (time_after(jiffies, expire))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&info->slock, flags);
|
|
||||||
set_current_state(TASK_RUNNING);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1531,8 +1509,7 @@ static int mxser_rs_break(struct tty_struct *tty, int break_state)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool mxser_receive_chars_new(struct tty_struct *tty,
|
static bool mxser_receive_chars_new(struct mxser_port *port, u8 status)
|
||||||
struct mxser_port *port, u8 status)
|
|
||||||
{
|
{
|
||||||
enum mxser_must_hwid hwid = port->board->must_hwid;
|
enum mxser_must_hwid hwid = port->board->must_hwid;
|
||||||
u8 gdl;
|
u8 gdl;
|
||||||
|
@ -1546,12 +1523,10 @@ static bool mxser_receive_chars_new(struct tty_struct *tty,
|
||||||
if (hwid == MOXA_MUST_MU150_HWID)
|
if (hwid == MOXA_MUST_MU150_HWID)
|
||||||
gdl &= MOXA_MUST_GDL_MASK;
|
gdl &= MOXA_MUST_GDL_MASK;
|
||||||
|
|
||||||
if (gdl >= tty->receive_room && !port->ldisc_stop_rx)
|
|
||||||
mxser_stoprx(tty);
|
|
||||||
|
|
||||||
while (gdl--) {
|
while (gdl--) {
|
||||||
u8 ch = inb(port->ioaddr + UART_RX);
|
u8 ch = inb(port->ioaddr + UART_RX);
|
||||||
tty_insert_flip_char(&port->port, ch, 0);
|
if (!tty_insert_flip_char(&port->port, ch, 0))
|
||||||
|
port->icount.buf_overrun++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1561,10 +1536,8 @@ static u8 mxser_receive_chars_old(struct tty_struct *tty,
|
||||||
struct mxser_port *port, u8 status)
|
struct mxser_port *port, u8 status)
|
||||||
{
|
{
|
||||||
enum mxser_must_hwid hwid = port->board->must_hwid;
|
enum mxser_must_hwid hwid = port->board->must_hwid;
|
||||||
int recv_room = tty->receive_room;
|
|
||||||
int ignored = 0;
|
int ignored = 0;
|
||||||
int max = 256;
|
int max = 256;
|
||||||
int cnt = 0;
|
|
||||||
u8 ch;
|
u8 ch;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
@ -1599,14 +1572,10 @@ static u8 mxser_receive_chars_old(struct tty_struct *tty,
|
||||||
port->icount.overrun++;
|
port->icount.overrun++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tty_insert_flip_char(&port->port, ch, flag);
|
if (!tty_insert_flip_char(&port->port, ch, flag)) {
|
||||||
cnt++;
|
port->icount.buf_overrun++;
|
||||||
if (cnt >= recv_room) {
|
|
||||||
if (!port->ldisc_stop_rx)
|
|
||||||
mxser_stoprx(tty);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hwid)
|
if (hwid)
|
||||||
|
@ -1621,10 +1590,7 @@ static u8 mxser_receive_chars_old(struct tty_struct *tty,
|
||||||
static u8 mxser_receive_chars(struct tty_struct *tty,
|
static u8 mxser_receive_chars(struct tty_struct *tty,
|
||||||
struct mxser_port *port, u8 status)
|
struct mxser_port *port, u8 status)
|
||||||
{
|
{
|
||||||
if (tty->receive_room == 0 && !port->ldisc_stop_rx)
|
if (!mxser_receive_chars_new(port, status))
|
||||||
mxser_stoprx(tty);
|
|
||||||
|
|
||||||
if (!mxser_receive_chars_new(tty, port, status))
|
|
||||||
status = mxser_receive_chars_old(tty, port, status);
|
status = mxser_receive_chars_old(tty, port, status);
|
||||||
|
|
||||||
tty_flip_buffer_push(&port->port);
|
tty_flip_buffer_push(&port->port);
|
||||||
|
@ -1634,7 +1600,7 @@ static u8 mxser_receive_chars(struct tty_struct *tty,
|
||||||
|
|
||||||
static void mxser_transmit_chars(struct tty_struct *tty, struct mxser_port *port)
|
static void mxser_transmit_chars(struct tty_struct *tty, struct mxser_port *port)
|
||||||
{
|
{
|
||||||
int count, cnt;
|
int count;
|
||||||
|
|
||||||
if (port->x_char) {
|
if (port->x_char) {
|
||||||
outb(port->x_char, port->ioaddr + UART_TX);
|
outb(port->x_char, port->ioaddr + UART_TX);
|
||||||
|
@ -1643,27 +1609,22 @@ static void mxser_transmit_chars(struct tty_struct *tty, struct mxser_port *port
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (port->port.xmit_buf == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!port->xmit_cnt || tty->flow.stopped ||
|
if (!port->xmit_cnt || tty->flow.stopped ||
|
||||||
(tty->hw_stopped && !mxser_16550A_or_MUST(port))) {
|
(tty->hw_stopped && !mxser_16550A_or_MUST(port))) {
|
||||||
__mxser_stop_tx(port);
|
__mxser_stop_tx(port);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cnt = port->xmit_cnt;
|
|
||||||
count = port->xmit_fifo_size;
|
count = port->xmit_fifo_size;
|
||||||
do {
|
do {
|
||||||
outb(port->port.xmit_buf[port->xmit_tail++],
|
outb(port->port.xmit_buf[port->xmit_tail++],
|
||||||
port->ioaddr + UART_TX);
|
port->ioaddr + UART_TX);
|
||||||
port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE - 1);
|
port->xmit_tail &= SERIAL_XMIT_SIZE - 1;
|
||||||
|
port->icount.tx++;
|
||||||
if (!--port->xmit_cnt)
|
if (!--port->xmit_cnt)
|
||||||
break;
|
break;
|
||||||
} while (--count > 0);
|
} while (--count > 0);
|
||||||
|
|
||||||
port->icount.tx += (cnt - port->xmit_cnt);
|
|
||||||
|
|
||||||
if (port->xmit_cnt < WAKEUP_CHARS)
|
if (port->xmit_cnt < WAKEUP_CHARS)
|
||||||
tty_wakeup(tty);
|
tty_wakeup(tty);
|
||||||
|
|
||||||
|
@ -1674,7 +1635,7 @@ static void mxser_transmit_chars(struct tty_struct *tty, struct mxser_port *port
|
||||||
static bool mxser_port_isr(struct mxser_port *port)
|
static bool mxser_port_isr(struct mxser_port *port)
|
||||||
{
|
{
|
||||||
struct tty_struct *tty;
|
struct tty_struct *tty;
|
||||||
u8 iir, msr, status;
|
u8 iir, status;
|
||||||
bool error = false;
|
bool error = false;
|
||||||
|
|
||||||
iir = inb(port->ioaddr + UART_IIR);
|
iir = inb(port->ioaddr + UART_IIR);
|
||||||
|
@ -1683,7 +1644,7 @@ static bool mxser_port_isr(struct mxser_port *port)
|
||||||
|
|
||||||
iir &= MOXA_MUST_IIR_MASK;
|
iir &= MOXA_MUST_IIR_MASK;
|
||||||
tty = tty_port_tty_get(&port->port);
|
tty = tty_port_tty_get(&port->port);
|
||||||
if (!tty || port->closing || !tty_port_initialized(&port->port)) {
|
if (!tty) {
|
||||||
status = inb(port->ioaddr + UART_LSR);
|
status = inb(port->ioaddr + UART_LSR);
|
||||||
outb(port->FCR | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,
|
outb(port->FCR | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,
|
||||||
port->ioaddr + UART_FCR);
|
port->ioaddr + UART_FCR);
|
||||||
|
@ -1707,9 +1668,7 @@ static bool mxser_port_isr(struct mxser_port *port)
|
||||||
status = mxser_receive_chars(tty, port, status);
|
status = mxser_receive_chars(tty, port, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
msr = inb(port->ioaddr + UART_MSR);
|
mxser_check_modem_status(tty, port);
|
||||||
if (msr & UART_MSR_ANY_DELTA)
|
|
||||||
mxser_check_modem_status(tty, port, msr);
|
|
||||||
|
|
||||||
if (port->board->must_hwid) {
|
if (port->board->must_hwid) {
|
||||||
if (iir == 0x02 && (status & UART_LSR_THRE))
|
if (iir == 0x02 && (status & UART_LSR_THRE))
|
||||||
|
@ -1836,7 +1795,6 @@ static void mxser_initbrd(struct mxser_board *brd, bool high_baud)
|
||||||
tty_port_init(&info->port);
|
tty_port_init(&info->port);
|
||||||
info->port.ops = &mxser_port_ops;
|
info->port.ops = &mxser_port_ops;
|
||||||
info->board = brd;
|
info->board = brd;
|
||||||
info->ldisc_stop_rx = false;
|
|
||||||
|
|
||||||
/* Enhance mode enabled here */
|
/* Enhance mode enabled here */
|
||||||
if (brd->must_hwid != MOXA_OTHER_UART)
|
if (brd->must_hwid != MOXA_OTHER_UART)
|
||||||
|
|
|
@ -2074,8 +2074,6 @@ static void gsm1_receive(struct gsm_mux *gsm, unsigned char c)
|
||||||
/**
|
/**
|
||||||
* gsm_error - handle tty error
|
* gsm_error - handle tty error
|
||||||
* @gsm: ldisc data
|
* @gsm: ldisc data
|
||||||
* @data: byte received (may be invalid)
|
|
||||||
* @flag: error received
|
|
||||||
*
|
*
|
||||||
* Handle an error in the receipt of data for a frame. Currently we just
|
* Handle an error in the receipt of data for a frame. Currently we just
|
||||||
* go back to hunting for a SOF.
|
* go back to hunting for a SOF.
|
||||||
|
@ -2083,8 +2081,7 @@ static void gsm1_receive(struct gsm_mux *gsm, unsigned char c)
|
||||||
* FIXME: better diagnostics ?
|
* FIXME: better diagnostics ?
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void gsm_error(struct gsm_mux *gsm,
|
static void gsm_error(struct gsm_mux *gsm)
|
||||||
unsigned char data, unsigned char flag)
|
|
||||||
{
|
{
|
||||||
gsm->state = GSM_SEARCH;
|
gsm->state = GSM_SEARCH;
|
||||||
gsm->io_error++;
|
gsm->io_error++;
|
||||||
|
@ -2504,7 +2501,7 @@ static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp,
|
||||||
case TTY_BREAK:
|
case TTY_BREAK:
|
||||||
case TTY_PARITY:
|
case TTY_PARITY:
|
||||||
case TTY_FRAME:
|
case TTY_FRAME:
|
||||||
gsm_error(gsm, *cp, flags);
|
gsm_error(gsm);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
WARN_ONCE(1, "%s: unknown flag %d\n",
|
WARN_ONCE(1, "%s: unknown flag %d\n",
|
||||||
|
@ -2690,8 +2687,8 @@ static __poll_t gsmld_poll(struct tty_struct *tty, struct file *file,
|
||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gsmld_ioctl(struct tty_struct *tty, struct file *file,
|
static int gsmld_ioctl(struct tty_struct *tty, unsigned int cmd,
|
||||||
unsigned int cmd, unsigned long arg)
|
unsigned long arg)
|
||||||
{
|
{
|
||||||
struct gsm_config c;
|
struct gsm_config c;
|
||||||
struct gsm_mux *gsm = tty->disc_data;
|
struct gsm_mux *gsm = tty->disc_data;
|
||||||
|
|
|
@ -593,14 +593,13 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,
|
||||||
/**
|
/**
|
||||||
* n_hdlc_tty_ioctl - process IOCTL system call for the tty device.
|
* n_hdlc_tty_ioctl - process IOCTL system call for the tty device.
|
||||||
* @tty: pointer to tty instance data
|
* @tty: pointer to tty instance data
|
||||||
* @file: pointer to open file object for device
|
|
||||||
* @cmd: IOCTL command code
|
* @cmd: IOCTL command code
|
||||||
* @arg: argument for IOCTL call (cmd dependent)
|
* @arg: argument for IOCTL call (cmd dependent)
|
||||||
*
|
*
|
||||||
* Returns command dependent result.
|
* Returns command dependent result.
|
||||||
*/
|
*/
|
||||||
static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file,
|
static int n_hdlc_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
|
||||||
unsigned int cmd, unsigned long arg)
|
unsigned long arg)
|
||||||
{
|
{
|
||||||
struct n_hdlc *n_hdlc = tty->disc_data;
|
struct n_hdlc *n_hdlc = tty->disc_data;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
|
@ -189,14 +189,13 @@ static void tty_copy(struct tty_struct *tty, void *to, size_t tail, size_t n)
|
||||||
* n_tty_kick_worker - start input worker (if required)
|
* n_tty_kick_worker - start input worker (if required)
|
||||||
* @tty: terminal
|
* @tty: terminal
|
||||||
*
|
*
|
||||||
* Re-schedules the flip buffer work if it may have stopped
|
* Re-schedules the flip buffer work if it may have stopped.
|
||||||
*
|
*
|
||||||
* Caller holds exclusive termios_rwsem
|
* Locking:
|
||||||
* or
|
* * Caller holds exclusive %termios_rwsem, or
|
||||||
* n_tty_read()/consumer path:
|
* * n_tty_read()/consumer path:
|
||||||
* holds non-exclusive termios_rwsem
|
* holds non-exclusive %termios_rwsem
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void n_tty_kick_worker(struct tty_struct *tty)
|
static void n_tty_kick_worker(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct n_tty_data *ldata = tty->disc_data;
|
struct n_tty_data *ldata = tty->disc_data;
|
||||||
|
@ -233,11 +232,9 @@ static ssize_t chars_in_buffer(struct tty_struct *tty)
|
||||||
* n_tty_write_wakeup - asynchronous I/O notifier
|
* n_tty_write_wakeup - asynchronous I/O notifier
|
||||||
* @tty: tty device
|
* @tty: tty device
|
||||||
*
|
*
|
||||||
* Required for the ptys, serial driver etc. since processes
|
* Required for the ptys, serial driver etc. since processes that attach
|
||||||
* that attach themselves to the master and rely on ASYNC
|
* themselves to the master and rely on ASYNC IO must be woken up.
|
||||||
* IO must be woken up
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void n_tty_write_wakeup(struct tty_struct *tty)
|
static void n_tty_write_wakeup(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||||
|
@ -306,10 +303,10 @@ static void n_tty_check_unthrottle(struct tty_struct *tty)
|
||||||
*
|
*
|
||||||
* Add a character to the tty read_buf queue.
|
* Add a character to the tty read_buf queue.
|
||||||
*
|
*
|
||||||
* n_tty_receive_buf()/producer path:
|
* Locking:
|
||||||
* caller holds non-exclusive termios_rwsem
|
* * n_tty_receive_buf()/producer path:
|
||||||
|
* caller holds non-exclusive %termios_rwsem
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static inline void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
|
static inline void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
|
||||||
{
|
{
|
||||||
*read_buf_addr(ldata, ldata->read_head) = c;
|
*read_buf_addr(ldata, ldata->read_head) = c;
|
||||||
|
@ -320,13 +317,13 @@ static inline void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
|
||||||
* reset_buffer_flags - reset buffer state
|
* reset_buffer_flags - reset buffer state
|
||||||
* @ldata: line disc data to reset
|
* @ldata: line disc data to reset
|
||||||
*
|
*
|
||||||
* Reset the read buffer counters and clear the flags.
|
* Reset the read buffer counters and clear the flags. Called from
|
||||||
* Called from n_tty_open() and n_tty_flush_buffer().
|
* n_tty_open() and n_tty_flush_buffer().
|
||||||
*
|
*
|
||||||
* Locking: caller holds exclusive termios_rwsem
|
* Locking:
|
||||||
* (or locking is not required)
|
* * caller holds exclusive %termios_rwsem, or
|
||||||
|
* * (locking is not required)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void reset_buffer_flags(struct n_tty_data *ldata)
|
static void reset_buffer_flags(struct n_tty_data *ldata)
|
||||||
{
|
{
|
||||||
ldata->read_head = ldata->canon_head = ldata->read_tail = 0;
|
ldata->read_head = ldata->canon_head = ldata->read_tail = 0;
|
||||||
|
@ -354,16 +351,15 @@ static void n_tty_packet_mode_flush(struct tty_struct *tty)
|
||||||
* n_tty_flush_buffer - clean input queue
|
* n_tty_flush_buffer - clean input queue
|
||||||
* @tty: terminal device
|
* @tty: terminal device
|
||||||
*
|
*
|
||||||
* Flush the input buffer. Called when the tty layer wants the
|
* Flush the input buffer. Called when the tty layer wants the buffer flushed
|
||||||
* buffer flushed (eg at hangup) or when the N_TTY line discipline
|
* (eg at hangup) or when the %N_TTY line discipline internally has to clean
|
||||||
* internally has to clean the pending queue (for example some signals).
|
* the pending queue (for example some signals).
|
||||||
*
|
*
|
||||||
* Holds termios_rwsem to exclude producer/consumer while
|
* Holds %termios_rwsem to exclude producer/consumer while buffer indices are
|
||||||
* buffer indices are reset.
|
* reset.
|
||||||
*
|
*
|
||||||
* Locking: ctrl.lock, exclusive termios_rwsem
|
* Locking: %ctrl.lock, exclusive %termios_rwsem
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void n_tty_flush_buffer(struct tty_struct *tty)
|
static void n_tty_flush_buffer(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
down_write(&tty->termios_rwsem);
|
down_write(&tty->termios_rwsem);
|
||||||
|
@ -379,11 +375,10 @@ static void n_tty_flush_buffer(struct tty_struct *tty)
|
||||||
* is_utf8_continuation - utf8 multibyte check
|
* is_utf8_continuation - utf8 multibyte check
|
||||||
* @c: byte to check
|
* @c: byte to check
|
||||||
*
|
*
|
||||||
* Returns true if the utf8 character 'c' is a multibyte continuation
|
* Returns: true if the utf8 character @c is a multibyte continuation
|
||||||
* character. We use this to correctly compute the on screen size
|
* character. We use this to correctly compute the on-screen size of the
|
||||||
* of the character when printing
|
* character when printing.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static inline int is_utf8_continuation(unsigned char c)
|
static inline int is_utf8_continuation(unsigned char c)
|
||||||
{
|
{
|
||||||
return (c & 0xc0) == 0x80;
|
return (c & 0xc0) == 0x80;
|
||||||
|
@ -394,10 +389,9 @@ static inline int is_utf8_continuation(unsigned char c)
|
||||||
* @c: byte to check
|
* @c: byte to check
|
||||||
* @tty: terminal device
|
* @tty: terminal device
|
||||||
*
|
*
|
||||||
* Returns true if the utf8 character 'c' is a multibyte continuation
|
* Returns: true if the utf8 character @c is a multibyte continuation character
|
||||||
* character and the terminal is in unicode mode.
|
* and the terminal is in unicode mode.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static inline int is_continuation(unsigned char c, struct tty_struct *tty)
|
static inline int is_continuation(unsigned char c, struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
return I_IUTF8(tty) && is_utf8_continuation(c);
|
return I_IUTF8(tty) && is_utf8_continuation(c);
|
||||||
|
@ -409,22 +403,19 @@ static inline int is_continuation(unsigned char c, struct tty_struct *tty)
|
||||||
* @tty: terminal device
|
* @tty: terminal device
|
||||||
* @space: space available in tty driver write buffer
|
* @space: space available in tty driver write buffer
|
||||||
*
|
*
|
||||||
* This is a helper function that handles one output character
|
* This is a helper function that handles one output character (including
|
||||||
* (including special characters like TAB, CR, LF, etc.),
|
* special characters like TAB, CR, LF, etc.), doing OPOST processing and
|
||||||
* doing OPOST processing and putting the results in the
|
* putting the results in the tty driver's write buffer.
|
||||||
* tty driver's write buffer.
|
|
||||||
*
|
*
|
||||||
* Note that Linux currently ignores TABDLY, CRDLY, VTDLY, FFDLY
|
* Note that Linux currently ignores TABDLY, CRDLY, VTDLY, FFDLY and NLDLY.
|
||||||
* and NLDLY. They simply aren't relevant in the world today.
|
* They simply aren't relevant in the world today. If you ever need them, add
|
||||||
* If you ever need them, add them here.
|
* them here.
|
||||||
*
|
*
|
||||||
* Returns the number of bytes of buffer space used or -1 if
|
* Returns: the number of bytes of buffer space used or -1 if no space left.
|
||||||
* no space left.
|
|
||||||
*
|
*
|
||||||
* Locking: should be called under the output_lock to protect
|
* Locking: should be called under the %output_lock to protect the column state
|
||||||
* the column state and space left in the buffer
|
* and space left in the buffer.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int do_output_char(unsigned char c, struct tty_struct *tty, int space)
|
static int do_output_char(unsigned char c, struct tty_struct *tty, int space)
|
||||||
{
|
{
|
||||||
struct n_tty_data *ldata = tty->disc_data;
|
struct n_tty_data *ldata = tty->disc_data;
|
||||||
|
@ -492,14 +483,13 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space)
|
||||||
* @tty: terminal device
|
* @tty: terminal device
|
||||||
*
|
*
|
||||||
* Output one character with OPOST processing.
|
* Output one character with OPOST processing.
|
||||||
* Returns -1 when the output device is full and the character
|
|
||||||
* must be retried.
|
|
||||||
*
|
*
|
||||||
* Locking: output_lock to protect column state and space left
|
* Returns: -1 when the output device is full and the character must be
|
||||||
* (also, this is called from n_tty_write under the
|
* retried.
|
||||||
* tty layer write lock)
|
*
|
||||||
|
* Locking: %output_lock to protect column state and space left (also, this is
|
||||||
|
*called from n_tty_write() under the tty layer write lock).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int process_output(unsigned char c, struct tty_struct *tty)
|
static int process_output(unsigned char c, struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct n_tty_data *ldata = tty->disc_data;
|
struct n_tty_data *ldata = tty->disc_data;
|
||||||
|
@ -524,18 +514,17 @@ static int process_output(unsigned char c, struct tty_struct *tty)
|
||||||
* @nr: number of bytes to output
|
* @nr: number of bytes to output
|
||||||
*
|
*
|
||||||
* Output a block of characters with OPOST processing.
|
* Output a block of characters with OPOST processing.
|
||||||
* Returns the number of characters output.
|
|
||||||
*
|
*
|
||||||
* This path is used to speed up block console writes, among other
|
* This path is used to speed up block console writes, among other things when
|
||||||
* things when processing blocks of output data. It handles only
|
* processing blocks of output data. It handles only the simple cases normally
|
||||||
* the simple cases normally found and helps to generate blocks of
|
* found and helps to generate blocks of symbols for the console driver and
|
||||||
* symbols for the console driver and thus improve performance.
|
* thus improve performance.
|
||||||
*
|
*
|
||||||
* Locking: output_lock to protect column state and space left
|
* Returns: the number of characters output.
|
||||||
* (also, this is called from n_tty_write under the
|
*
|
||||||
* tty layer write lock)
|
* Locking: %output_lock to protect column state and space left (also, this is
|
||||||
|
* called from n_tty_write() under the tty layer write lock).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static ssize_t process_output_block(struct tty_struct *tty,
|
static ssize_t process_output_block(struct tty_struct *tty,
|
||||||
const unsigned char *buf, unsigned int nr)
|
const unsigned char *buf, unsigned int nr)
|
||||||
{
|
{
|
||||||
|
@ -596,30 +585,27 @@ break_out:
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* process_echoes - write pending echo characters
|
* __process_echoes - write pending echo characters
|
||||||
* @tty: terminal device
|
* @tty: terminal device
|
||||||
*
|
*
|
||||||
* Write previously buffered echo (and other ldisc-generated)
|
* Write previously buffered echo (and other ldisc-generated) characters to the
|
||||||
* characters to the tty.
|
* tty.
|
||||||
*
|
*
|
||||||
* Characters generated by the ldisc (including echoes) need to
|
* Characters generated by the ldisc (including echoes) need to be buffered
|
||||||
* be buffered because the driver's write buffer can fill during
|
* because the driver's write buffer can fill during heavy program output.
|
||||||
* heavy program output. Echoing straight to the driver will
|
* Echoing straight to the driver will often fail under these conditions,
|
||||||
* often fail under these conditions, causing lost characters and
|
* causing lost characters and resulting mismatches of ldisc state information.
|
||||||
* resulting mismatches of ldisc state information.
|
|
||||||
*
|
*
|
||||||
* Since the ldisc state must represent the characters actually sent
|
* Since the ldisc state must represent the characters actually sent to the
|
||||||
* to the driver at the time of the write, operations like certain
|
* driver at the time of the write, operations like certain changes in column
|
||||||
* changes in column state are also saved in the buffer and executed
|
* state are also saved in the buffer and executed here.
|
||||||
* here.
|
|
||||||
*
|
*
|
||||||
* A circular fifo buffer is used so that the most recent characters
|
* A circular fifo buffer is used so that the most recent characters are
|
||||||
* are prioritized. Also, when control characters are echoed with a
|
* prioritized. Also, when control characters are echoed with a prefixed "^",
|
||||||
* prefixed "^", the pair is treated atomically and thus not separated.
|
* the pair is treated atomically and thus not separated.
|
||||||
*
|
*
|
||||||
* Locking: callers must hold output_lock
|
* Locking: callers must hold %output_lock.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static size_t __process_echoes(struct tty_struct *tty)
|
static size_t __process_echoes(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct n_tty_data *ldata = tty->disc_data;
|
struct n_tty_data *ldata = tty->disc_data;
|
||||||
|
@ -834,7 +820,6 @@ static void flush_echoes(struct tty_struct *tty)
|
||||||
*
|
*
|
||||||
* Add a character or operation byte to the echo buffer.
|
* Add a character or operation byte to the echo buffer.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static inline void add_echo_byte(unsigned char c, struct n_tty_data *ldata)
|
static inline void add_echo_byte(unsigned char c, struct n_tty_data *ldata)
|
||||||
{
|
{
|
||||||
*echo_buf_addr(ldata, ldata->echo_head) = c;
|
*echo_buf_addr(ldata, ldata->echo_head) = c;
|
||||||
|
@ -848,7 +833,6 @@ static inline void add_echo_byte(unsigned char c, struct n_tty_data *ldata)
|
||||||
*
|
*
|
||||||
* Add an operation to the echo buffer to move back one column.
|
* Add an operation to the echo buffer to move back one column.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void echo_move_back_col(struct n_tty_data *ldata)
|
static void echo_move_back_col(struct n_tty_data *ldata)
|
||||||
{
|
{
|
||||||
add_echo_byte(ECHO_OP_START, ldata);
|
add_echo_byte(ECHO_OP_START, ldata);
|
||||||
|
@ -859,10 +843,9 @@ static void echo_move_back_col(struct n_tty_data *ldata)
|
||||||
* echo_set_canon_col - add operation to set the canon column
|
* echo_set_canon_col - add operation to set the canon column
|
||||||
* @ldata: n_tty data
|
* @ldata: n_tty data
|
||||||
*
|
*
|
||||||
* Add an operation to the echo buffer to set the canon column
|
* Add an operation to the echo buffer to set the canon column to the current
|
||||||
* to the current column.
|
* column.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void echo_set_canon_col(struct n_tty_data *ldata)
|
static void echo_set_canon_col(struct n_tty_data *ldata)
|
||||||
{
|
{
|
||||||
add_echo_byte(ECHO_OP_START, ldata);
|
add_echo_byte(ECHO_OP_START, ldata);
|
||||||
|
@ -877,13 +860,11 @@ static void echo_set_canon_col(struct n_tty_data *ldata)
|
||||||
*
|
*
|
||||||
* Add an operation to the echo buffer to erase a tab.
|
* Add an operation to the echo buffer to erase a tab.
|
||||||
*
|
*
|
||||||
* Called by the eraser function, which knows how many character
|
* Called by the eraser function, which knows how many character columns have
|
||||||
* columns have been used since either a previous tab or the start
|
* been used since either a previous tab or the start of input. This
|
||||||
* of input. This information will be used later, along with
|
* information will be used later, along with canon column (if applicable), to
|
||||||
* canon column (if applicable), to go back the correct number
|
* go back the correct number of columns.
|
||||||
* of columns.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void echo_erase_tab(unsigned int num_chars, int after_tab,
|
static void echo_erase_tab(unsigned int num_chars, int after_tab,
|
||||||
struct n_tty_data *ldata)
|
struct n_tty_data *ldata)
|
||||||
{
|
{
|
||||||
|
@ -906,11 +887,10 @@ static void echo_erase_tab(unsigned int num_chars, int after_tab,
|
||||||
* @ldata: line disc data
|
* @ldata: line disc data
|
||||||
*
|
*
|
||||||
* Echo user input back onto the screen. This must be called only when
|
* Echo user input back onto the screen. This must be called only when
|
||||||
* L_ECHO(tty) is true. Called from the driver receive_buf path.
|
* L_ECHO(tty) is true. Called from the &tty_driver.receive_buf() path.
|
||||||
*
|
*
|
||||||
* This variant does not treat control characters specially.
|
* This variant does not treat control characters specially.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void echo_char_raw(unsigned char c, struct n_tty_data *ldata)
|
static void echo_char_raw(unsigned char c, struct n_tty_data *ldata)
|
||||||
{
|
{
|
||||||
if (c == ECHO_OP_START) {
|
if (c == ECHO_OP_START) {
|
||||||
|
@ -927,12 +907,11 @@ static void echo_char_raw(unsigned char c, struct n_tty_data *ldata)
|
||||||
* @tty: terminal device
|
* @tty: terminal device
|
||||||
*
|
*
|
||||||
* Echo user input back onto the screen. This must be called only when
|
* Echo user input back onto the screen. This must be called only when
|
||||||
* L_ECHO(tty) is true. Called from the driver receive_buf path.
|
* L_ECHO(tty) is true. Called from the &tty_driver.receive_buf() path.
|
||||||
*
|
*
|
||||||
* This variant tags control characters to be echoed as "^X"
|
* This variant tags control characters to be echoed as "^X" (where X is the
|
||||||
* (where X is the letter representing the control char).
|
* letter representing the control char).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void echo_char(unsigned char c, struct tty_struct *tty)
|
static void echo_char(unsigned char c, struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct n_tty_data *ldata = tty->disc_data;
|
struct n_tty_data *ldata = tty->disc_data;
|
||||||
|
@ -951,7 +930,6 @@ static void echo_char(unsigned char c, struct tty_struct *tty)
|
||||||
* finish_erasing - complete erase
|
* finish_erasing - complete erase
|
||||||
* @ldata: n_tty data
|
* @ldata: n_tty data
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static inline void finish_erasing(struct n_tty_data *ldata)
|
static inline void finish_erasing(struct n_tty_data *ldata)
|
||||||
{
|
{
|
||||||
if (ldata->erasing) {
|
if (ldata->erasing) {
|
||||||
|
@ -965,14 +943,13 @@ static inline void finish_erasing(struct n_tty_data *ldata)
|
||||||
* @c: character input
|
* @c: character input
|
||||||
* @tty: terminal device
|
* @tty: terminal device
|
||||||
*
|
*
|
||||||
* Perform erase and necessary output when an erase character is
|
* Perform erase and necessary output when an erase character is present in the
|
||||||
* present in the stream from the driver layer. Handles the complexities
|
* stream from the driver layer. Handles the complexities of UTF-8 multibyte
|
||||||
* of UTF-8 multibyte symbols.
|
* symbols.
|
||||||
*
|
*
|
||||||
* n_tty_receive_buf()/producer path:
|
* Locking: n_tty_receive_buf()/producer path:
|
||||||
* caller holds non-exclusive termios_rwsem
|
* caller holds non-exclusive %termios_rwsem
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void eraser(unsigned char c, struct tty_struct *tty)
|
static void eraser(unsigned char c, struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct n_tty_data *ldata = tty->disc_data;
|
struct n_tty_data *ldata = tty->disc_data;
|
||||||
|
@ -1091,20 +1068,6 @@ static void eraser(unsigned char c, struct tty_struct *tty)
|
||||||
finish_erasing(ldata);
|
finish_erasing(ldata);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* isig - handle the ISIG optio
|
|
||||||
* @sig: signal
|
|
||||||
* @tty: terminal
|
|
||||||
*
|
|
||||||
* Called when a signal is being sent due to terminal input.
|
|
||||||
* Called from the driver receive_buf path so serialized.
|
|
||||||
*
|
|
||||||
* Performs input and output flush if !NOFLSH. In this context, the echo
|
|
||||||
* buffer is 'output'. The signal is processed first to alert any current
|
|
||||||
* readers or writers to discontinue and exit their i/o loops.
|
|
||||||
*
|
|
||||||
* Locking: ctrl.lock
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void __isig(int sig, struct tty_struct *tty)
|
static void __isig(int sig, struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
|
@ -1115,6 +1078,20 @@ static void __isig(int sig, struct tty_struct *tty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* isig - handle the ISIG optio
|
||||||
|
* @sig: signal
|
||||||
|
* @tty: terminal
|
||||||
|
*
|
||||||
|
* Called when a signal is being sent due to terminal input. Called from the
|
||||||
|
* &tty_driver.receive_buf() path, so serialized.
|
||||||
|
*
|
||||||
|
* Performs input and output flush if !NOFLSH. In this context, the echo
|
||||||
|
* buffer is 'output'. The signal is processed first to alert any current
|
||||||
|
* readers or writers to discontinue and exit their i/o loops.
|
||||||
|
*
|
||||||
|
* Locking: %ctrl.lock
|
||||||
|
*/
|
||||||
static void isig(int sig, struct tty_struct *tty)
|
static void isig(int sig, struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct n_tty_data *ldata = tty->disc_data;
|
struct n_tty_data *ldata = tty->disc_data;
|
||||||
|
@ -1154,15 +1131,14 @@ static void isig(int sig, struct tty_struct *tty)
|
||||||
* n_tty_receive_break - handle break
|
* n_tty_receive_break - handle break
|
||||||
* @tty: terminal
|
* @tty: terminal
|
||||||
*
|
*
|
||||||
* An RS232 break event has been hit in the incoming bitstream. This
|
* An RS232 break event has been hit in the incoming bitstream. This can cause
|
||||||
* can cause a variety of events depending upon the termios settings.
|
* a variety of events depending upon the termios settings.
|
||||||
*
|
*
|
||||||
* n_tty_receive_buf()/producer path:
|
* Locking: n_tty_receive_buf()/producer path:
|
||||||
* caller holds non-exclusive termios_rwsem
|
* caller holds non-exclusive termios_rwsem
|
||||||
*
|
*
|
||||||
* Note: may get exclusive termios_rwsem if flushing input buffer
|
* Note: may get exclusive %termios_rwsem if flushing input buffer
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void n_tty_receive_break(struct tty_struct *tty)
|
static void n_tty_receive_break(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct n_tty_data *ldata = tty->disc_data;
|
struct n_tty_data *ldata = tty->disc_data;
|
||||||
|
@ -1184,15 +1160,12 @@ static void n_tty_receive_break(struct tty_struct *tty)
|
||||||
* n_tty_receive_overrun - handle overrun reporting
|
* n_tty_receive_overrun - handle overrun reporting
|
||||||
* @tty: terminal
|
* @tty: terminal
|
||||||
*
|
*
|
||||||
* Data arrived faster than we could process it. While the tty
|
* Data arrived faster than we could process it. While the tty driver has
|
||||||
* driver has flagged this the bits that were missed are gone
|
* flagged this the bits that were missed are gone forever.
|
||||||
* forever.
|
|
||||||
*
|
*
|
||||||
* Called from the receive_buf path so single threaded. Does not
|
* Called from the receive_buf path so single threaded. Does not need locking
|
||||||
* need locking as num_overrun and overrun_time are function
|
* as num_overrun and overrun_time are function private.
|
||||||
* private.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void n_tty_receive_overrun(struct tty_struct *tty)
|
static void n_tty_receive_overrun(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct n_tty_data *ldata = tty->disc_data;
|
struct n_tty_data *ldata = tty->disc_data;
|
||||||
|
@ -1211,11 +1184,11 @@ static void n_tty_receive_overrun(struct tty_struct *tty)
|
||||||
* @tty: terminal device
|
* @tty: terminal device
|
||||||
* @c: character
|
* @c: character
|
||||||
*
|
*
|
||||||
* Process a parity error and queue the right data to indicate
|
* Process a parity error and queue the right data to indicate the error case
|
||||||
* the error case if necessary.
|
* if necessary.
|
||||||
*
|
*
|
||||||
* n_tty_receive_buf()/producer path:
|
* Locking: n_tty_receive_buf()/producer path:
|
||||||
* caller holds non-exclusive termios_rwsem
|
* caller holds non-exclusive %termios_rwsem
|
||||||
*/
|
*/
|
||||||
static void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c)
|
static void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c)
|
||||||
{
|
{
|
||||||
|
@ -1247,19 +1220,6 @@ n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c)
|
||||||
process_echoes(tty);
|
process_echoes(tty);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* n_tty_receive_char - perform processing
|
|
||||||
* @tty: terminal device
|
|
||||||
* @c: character
|
|
||||||
*
|
|
||||||
* Process an individual character of input received from the driver.
|
|
||||||
* This is serialized with respect to itself by the rules for the
|
|
||||||
* driver above.
|
|
||||||
*
|
|
||||||
* n_tty_receive_buf()/producer path:
|
|
||||||
* caller holds non-exclusive termios_rwsem
|
|
||||||
* publishes canon_head if canonical mode is active
|
|
||||||
*/
|
|
||||||
static void n_tty_receive_char_special(struct tty_struct *tty, unsigned char c)
|
static void n_tty_receive_char_special(struct tty_struct *tty, unsigned char c)
|
||||||
{
|
{
|
||||||
struct n_tty_data *ldata = tty->disc_data;
|
struct n_tty_data *ldata = tty->disc_data;
|
||||||
|
@ -1394,6 +1354,18 @@ handle_newline:
|
||||||
put_tty_queue(c, ldata);
|
put_tty_queue(c, ldata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* n_tty_receive_char - perform processing
|
||||||
|
* @tty: terminal device
|
||||||
|
* @c: character
|
||||||
|
*
|
||||||
|
* Process an individual character of input received from the driver. This is
|
||||||
|
* serialized with respect to itself by the rules for the driver above.
|
||||||
|
*
|
||||||
|
* Locking: n_tty_receive_buf()/producer path:
|
||||||
|
* caller holds non-exclusive %termios_rwsem
|
||||||
|
* publishes canon_head if canonical mode is active
|
||||||
|
*/
|
||||||
static void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
|
static void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
|
||||||
{
|
{
|
||||||
struct n_tty_data *ldata = tty->disc_data;
|
struct n_tty_data *ldata = tty->disc_data;
|
||||||
|
@ -1597,34 +1569,33 @@ static void __receive_buf(struct tty_struct *tty, const unsigned char *cp,
|
||||||
* n_tty_receive_buf_common - process input
|
* n_tty_receive_buf_common - process input
|
||||||
* @tty: device to receive input
|
* @tty: device to receive input
|
||||||
* @cp: input chars
|
* @cp: input chars
|
||||||
* @fp: flags for each char (if NULL, all chars are TTY_NORMAL)
|
* @fp: flags for each char (if %NULL, all chars are %TTY_NORMAL)
|
||||||
* @count: number of input chars in @cp
|
* @count: number of input chars in @cp
|
||||||
* @flow: enable flow control
|
* @flow: enable flow control
|
||||||
*
|
*
|
||||||
* Called by the terminal driver when a block of characters has
|
* Called by the terminal driver when a block of characters has been received.
|
||||||
* been received. This function must be called from soft contexts
|
* This function must be called from soft contexts not from interrupt context.
|
||||||
* not from interrupt context. The driver is responsible for making
|
* The driver is responsible for making calls one at a time and in order (or
|
||||||
* calls one at a time and in order (or using flush_to_ldisc)
|
* using flush_to_ldisc()).
|
||||||
*
|
*
|
||||||
* Returns the # of input chars from @cp which were processed.
|
* Returns: the # of input chars from @cp which were processed.
|
||||||
*
|
*
|
||||||
* In canonical mode, the maximum line length is 4096 chars (including
|
* In canonical mode, the maximum line length is 4096 chars (including the line
|
||||||
* the line termination char); lines longer than 4096 chars are
|
* termination char); lines longer than 4096 chars are truncated. After 4095
|
||||||
* truncated. After 4095 chars, input data is still processed but
|
* chars, input data is still processed but not stored. Overflow processing
|
||||||
* not stored. Overflow processing ensures the tty can always
|
* ensures the tty can always receive more input until at least one line can be
|
||||||
* receive more input until at least one line can be read.
|
* read.
|
||||||
*
|
*
|
||||||
* In non-canonical mode, the read buffer will only accept 4095 chars;
|
* In non-canonical mode, the read buffer will only accept 4095 chars; this
|
||||||
* this provides the necessary space for a newline char if the input
|
* provides the necessary space for a newline char if the input mode is
|
||||||
* mode is switched to canonical.
|
* switched to canonical.
|
||||||
*
|
*
|
||||||
* Note it is possible for the read buffer to _contain_ 4096 chars
|
* Note it is possible for the read buffer to _contain_ 4096 chars in
|
||||||
* in non-canonical mode: the read buffer could already contain the
|
* non-canonical mode: the read buffer could already contain the maximum canon
|
||||||
* maximum canon line of 4096 chars when the mode is switched to
|
* line of 4096 chars when the mode is switched to non-canonical.
|
||||||
* non-canonical.
|
|
||||||
*
|
*
|
||||||
* n_tty_receive_buf()/producer path:
|
* Locking: n_tty_receive_buf()/producer path:
|
||||||
* claims non-exclusive termios_rwsem
|
* claims non-exclusive %termios_rwsem
|
||||||
* publishes commit_head or canon_head
|
* publishes commit_head or canon_head
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
|
@ -1714,15 +1685,13 @@ static int n_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp,
|
||||||
* @tty: terminal
|
* @tty: terminal
|
||||||
* @old: previous data
|
* @old: previous data
|
||||||
*
|
*
|
||||||
* Called by the tty layer when the user changes termios flags so
|
* Called by the tty layer when the user changes termios flags so that the line
|
||||||
* that the line discipline can plan ahead. This function cannot sleep
|
* discipline can plan ahead. This function cannot sleep and is protected from
|
||||||
* and is protected from re-entry by the tty layer. The user is
|
* re-entry by the tty layer. The user is guaranteed that this function will
|
||||||
* guaranteed that this function will not be re-entered or in progress
|
* not be re-entered or in progress when the ldisc is closed.
|
||||||
* when the ldisc is closed.
|
|
||||||
*
|
*
|
||||||
* Locking: Caller holds tty->termios_rwsem
|
* Locking: Caller holds @tty->termios_rwsem
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
|
static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
|
||||||
{
|
{
|
||||||
struct n_tty_data *ldata = tty->disc_data;
|
struct n_tty_data *ldata = tty->disc_data;
|
||||||
|
@ -1811,12 +1780,10 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
|
||||||
* n_tty_close - close the ldisc for this tty
|
* n_tty_close - close the ldisc for this tty
|
||||||
* @tty: device
|
* @tty: device
|
||||||
*
|
*
|
||||||
* Called from the terminal layer when this line discipline is
|
* Called from the terminal layer when this line discipline is being shut down,
|
||||||
* being shut down, either because of a close or becsuse of a
|
* either because of a close or becsuse of a discipline change. The function
|
||||||
* discipline change. The function will not be called while other
|
* will not be called while other ldisc methods are in progress.
|
||||||
* ldisc methods are in progress.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void n_tty_close(struct tty_struct *tty)
|
static void n_tty_close(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct n_tty_data *ldata = tty->disc_data;
|
struct n_tty_data *ldata = tty->disc_data;
|
||||||
|
@ -1834,12 +1801,10 @@ static void n_tty_close(struct tty_struct *tty)
|
||||||
* n_tty_open - open an ldisc
|
* n_tty_open - open an ldisc
|
||||||
* @tty: terminal to open
|
* @tty: terminal to open
|
||||||
*
|
*
|
||||||
* Called when this line discipline is being attached to the
|
* Called when this line discipline is being attached to the terminal device.
|
||||||
* terminal device. Can sleep. Called serialized so that no
|
* Can sleep. Called serialized so that no other events will occur in parallel.
|
||||||
* other events will occur in parallel. No further open will occur
|
* No further open will occur until a close.
|
||||||
* until a close.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int n_tty_open(struct tty_struct *tty)
|
static int n_tty_open(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct n_tty_data *ldata;
|
struct n_tty_data *ldata;
|
||||||
|
@ -1879,19 +1844,18 @@ static inline int input_available_p(struct tty_struct *tty, int poll)
|
||||||
* @kbp: data
|
* @kbp: data
|
||||||
* @nr: size of data
|
* @nr: size of data
|
||||||
*
|
*
|
||||||
* Helper function to speed up n_tty_read. It is only called when
|
* Helper function to speed up n_tty_read(). It is only called when %ICANON is
|
||||||
* ICANON is off; it copies characters straight from the tty queue.
|
* off; it copies characters straight from the tty queue.
|
||||||
*
|
*
|
||||||
* Called under the ldata->atomic_read_lock sem
|
* Returns: true if it successfully copied data, but there is still more data
|
||||||
|
* to be had.
|
||||||
*
|
*
|
||||||
* Returns true if it successfully copied data, but there is still
|
* Locking:
|
||||||
* more data to be had.
|
* * called under the @ldata->atomic_read_lock sem
|
||||||
*
|
* * n_tty_read()/consumer path:
|
||||||
* n_tty_read()/consumer path:
|
* caller holds non-exclusive %termios_rwsem;
|
||||||
* caller holds non-exclusive termios_rwsem
|
|
||||||
* read_tail published
|
* read_tail published
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static bool copy_from_read_buf(struct tty_struct *tty,
|
static bool copy_from_read_buf(struct tty_struct *tty,
|
||||||
unsigned char **kbp,
|
unsigned char **kbp,
|
||||||
size_t *nr)
|
size_t *nr)
|
||||||
|
@ -1931,23 +1895,22 @@ static bool copy_from_read_buf(struct tty_struct *tty,
|
||||||
* @kbp: data
|
* @kbp: data
|
||||||
* @nr: size of data
|
* @nr: size of data
|
||||||
*
|
*
|
||||||
* Helper function for n_tty_read. It is only called when ICANON is on;
|
* Helper function for n_tty_read(). It is only called when %ICANON is on; it
|
||||||
* it copies one line of input up to and including the line-delimiting
|
* copies one line of input up to and including the line-delimiting character
|
||||||
* character into the result buffer.
|
* into the result buffer.
|
||||||
*
|
*
|
||||||
* NB: When termios is changed from non-canonical to canonical mode and
|
* Note: When termios is changed from non-canonical to canonical mode and the
|
||||||
* the read buffer contains data, n_tty_set_termios() simulates an EOF
|
* read buffer contains data, n_tty_set_termios() simulates an EOF push (as if
|
||||||
* push (as if C-d were input) _without_ the DISABLED_CHAR in the buffer.
|
* C-d were input) _without_ the %DISABLED_CHAR in the buffer. This causes data
|
||||||
* This causes data already processed as input to be immediately available
|
* already processed as input to be immediately available as input although a
|
||||||
* as input although a newline has not been received.
|
* newline has not been received.
|
||||||
*
|
*
|
||||||
* Called under the atomic_read_lock mutex
|
* Locking:
|
||||||
*
|
* * called under the %atomic_read_lock mutex
|
||||||
* n_tty_read()/consumer path:
|
* * n_tty_read()/consumer path:
|
||||||
* caller holds non-exclusive termios_rwsem
|
* caller holds non-exclusive %termios_rwsem;
|
||||||
* read_tail published
|
* read_tail published
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static bool canon_copy_from_read_buf(struct tty_struct *tty,
|
static bool canon_copy_from_read_buf(struct tty_struct *tty,
|
||||||
unsigned char **kbp,
|
unsigned char **kbp,
|
||||||
size_t *nr)
|
size_t *nr)
|
||||||
|
@ -2019,15 +1982,15 @@ static bool canon_copy_from_read_buf(struct tty_struct *tty,
|
||||||
* @tty: tty
|
* @tty: tty
|
||||||
* @file: file handle
|
* @file: file handle
|
||||||
*
|
*
|
||||||
* Perform job control management checks on this file/tty descriptor
|
* Perform job control management checks on this @file/@tty descriptor and if
|
||||||
* and if appropriate send any needed signals and return a negative
|
* appropriate send any needed signals and return a negative error code if
|
||||||
* error code if action should be taken.
|
* action should be taken.
|
||||||
*
|
*
|
||||||
* Locking: redirected write test is safe
|
* Locking:
|
||||||
* current->signal->tty check is safe
|
* * redirected write test is safe
|
||||||
* ctrl.lock to safely reference tty->ctrl.pgrp
|
* * current->signal->tty check is safe
|
||||||
|
* * ctrl.lock to safely reference @tty->ctrl.pgrp
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int job_control(struct tty_struct *tty, struct file *file)
|
static int job_control(struct tty_struct *tty, struct file *file)
|
||||||
{
|
{
|
||||||
/* Job control check -- must be done at start and after
|
/* Job control check -- must be done at start and after
|
||||||
|
@ -2046,21 +2009,22 @@ static int job_control(struct tty_struct *tty, struct file *file)
|
||||||
* n_tty_read - read function for tty
|
* n_tty_read - read function for tty
|
||||||
* @tty: tty device
|
* @tty: tty device
|
||||||
* @file: file object
|
* @file: file object
|
||||||
* @buf: userspace buffer pointer
|
* @kbuf: kernelspace buffer pointer
|
||||||
* @nr: size of I/O
|
* @nr: size of I/O
|
||||||
|
* @cookie: if non-%NULL, this is a continuation read
|
||||||
|
* @offset: where to continue reading from (unused in n_tty)
|
||||||
*
|
*
|
||||||
* Perform reads for the line discipline. We are guaranteed that the
|
* Perform reads for the line discipline. We are guaranteed that the line
|
||||||
* line discipline will not be closed under us but we may get multiple
|
* discipline will not be closed under us but we may get multiple parallel
|
||||||
* parallel readers and must handle this ourselves. We may also get
|
* readers and must handle this ourselves. We may also get a hangup. Always
|
||||||
* a hangup. Always called in user context, may sleep.
|
* called in user context, may sleep.
|
||||||
*
|
*
|
||||||
* This code must be sure never to sleep through a hangup.
|
* This code must be sure never to sleep through a hangup.
|
||||||
*
|
*
|
||||||
* n_tty_read()/consumer path:
|
* Locking: n_tty_read()/consumer path:
|
||||||
* claims non-exclusive termios_rwsem
|
* claims non-exclusive termios_rwsem;
|
||||||
* publishes read_tail
|
* publishes read_tail
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
|
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
|
||||||
unsigned char *kbuf, size_t nr,
|
unsigned char *kbuf, size_t nr,
|
||||||
void **cookie, unsigned long offset)
|
void **cookie, unsigned long offset)
|
||||||
|
@ -2238,19 +2202,17 @@ more_to_be_read:
|
||||||
* @buf: userspace buffer pointer
|
* @buf: userspace buffer pointer
|
||||||
* @nr: size of I/O
|
* @nr: size of I/O
|
||||||
*
|
*
|
||||||
* Write function of the terminal device. This is serialized with
|
* Write function of the terminal device. This is serialized with respect to
|
||||||
* respect to other write callers but not to termios changes, reads
|
* other write callers but not to termios changes, reads and other such events.
|
||||||
* and other such events. Since the receive code will echo characters,
|
* Since the receive code will echo characters, thus calling driver write
|
||||||
* thus calling driver write methods, the output_lock is used in
|
* methods, the %output_lock is used in the output processing functions called
|
||||||
* the output processing functions called here as well as in the
|
* here as well as in the echo processing function to protect the column state
|
||||||
* echo processing function to protect the column state and space
|
* and space left in the buffer.
|
||||||
* left in the buffer.
|
|
||||||
*
|
*
|
||||||
* This code must be sure never to sleep through a hangup.
|
* This code must be sure never to sleep through a hangup.
|
||||||
*
|
*
|
||||||
* Locking: output_lock to protect column state and space left
|
* Locking: output_lock to protect column state and space left
|
||||||
* (note that the process_output*() functions take this
|
* (note that the process_output*() functions take this lock themselves)
|
||||||
* lock themselves)
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
|
static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
|
||||||
|
@ -2346,14 +2308,14 @@ break_out:
|
||||||
* @file: file accessing it
|
* @file: file accessing it
|
||||||
* @wait: poll table
|
* @wait: poll table
|
||||||
*
|
*
|
||||||
* Called when the line discipline is asked to poll() for data or
|
* Called when the line discipline is asked to poll() for data or for special
|
||||||
* for special events. This code is not serialized with respect to
|
* events. This code is not serialized with respect to other events save
|
||||||
* other events save open/close.
|
* open/close.
|
||||||
*
|
*
|
||||||
* This code must be sure never to sleep through a hangup.
|
* This code must be sure never to sleep through a hangup.
|
||||||
* Called without the kernel lock held - fine
|
*
|
||||||
|
* Locking: called without the kernel lock held -- fine.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static __poll_t n_tty_poll(struct tty_struct *tty, struct file *file,
|
static __poll_t n_tty_poll(struct tty_struct *tty, struct file *file,
|
||||||
poll_table *wait)
|
poll_table *wait)
|
||||||
{
|
{
|
||||||
|
@ -2400,8 +2362,8 @@ static unsigned long inq_canon(struct n_tty_data *ldata)
|
||||||
return nr;
|
return nr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int n_tty_ioctl(struct tty_struct *tty, struct file *file,
|
static int n_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
|
||||||
unsigned int cmd, unsigned long arg)
|
unsigned long arg)
|
||||||
{
|
{
|
||||||
struct n_tty_data *ldata = tty->disc_data;
|
struct n_tty_data *ldata = tty->disc_data;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
|
@ -241,16 +241,8 @@ static inline int serial8250_in_MCR(struct uart_8250_port *up)
|
||||||
return mctrl;
|
return mctrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__alpha__) && !defined(CONFIG_PCI)
|
bool alpha_jensen(void);
|
||||||
/*
|
void alpha_jensen_set_mctrl(struct uart_port *port, unsigned int mctrl);
|
||||||
* Digital did something really horribly wrong with the OUT1 and OUT2
|
|
||||||
* lines on at least some ALPHA's. The failure mode is that if either
|
|
||||||
* is cleared, the machine locks up with endless interrupts.
|
|
||||||
*/
|
|
||||||
#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2 | UART_MCR_OUT1)
|
|
||||||
#else
|
|
||||||
#define ALPHA_KLUDGE_MCR 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_SERIAL_8250_PNP
|
#ifdef CONFIG_SERIAL_8250_PNP
|
||||||
int serial8250_pnp_init(void);
|
int serial8250_pnp_init(void);
|
||||||
|
|
21
drivers/tty/serial/8250/8250_alpha.c
Normal file
21
drivers/tty/serial/8250/8250_alpha.c
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
|
||||||
|
#include <asm/machvec.h>
|
||||||
|
#include "8250.h"
|
||||||
|
|
||||||
|
bool alpha_jensen(void)
|
||||||
|
{
|
||||||
|
return !strcmp(alpha_mv.vector_name, "Jensen");
|
||||||
|
}
|
||||||
|
|
||||||
|
void alpha_jensen_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Digital did something really horribly wrong with the OUT1 and OUT2
|
||||||
|
* lines on Alpha Jensen. The failure mode is that if either is
|
||||||
|
* cleared, the machine locks up with endless interrupts.
|
||||||
|
*/
|
||||||
|
mctrl |= TIOCM_OUT1 | TIOCM_OUT2;
|
||||||
|
|
||||||
|
serial8250_do_set_mctrl(port, mctrl);
|
||||||
|
}
|
|
@ -941,7 +941,7 @@ static int brcmuart_probe(struct platform_device *pdev)
|
||||||
struct brcmuart_priv *priv;
|
struct brcmuart_priv *priv;
|
||||||
struct clk *baud_mux_clk;
|
struct clk *baud_mux_clk;
|
||||||
struct uart_8250_port up;
|
struct uart_8250_port up;
|
||||||
struct resource *irq;
|
int irq;
|
||||||
void __iomem *membase = NULL;
|
void __iomem *membase = NULL;
|
||||||
resource_size_t mapbase = 0;
|
resource_size_t mapbase = 0;
|
||||||
u32 clk_rate = 0;
|
u32 clk_rate = 0;
|
||||||
|
@ -952,11 +952,9 @@ static int brcmuart_probe(struct platform_device *pdev)
|
||||||
"uart", "dma_rx", "dma_tx", "dma_intr2", "dma_arb"
|
"uart", "dma_rx", "dma_tx", "dma_intr2", "dma_arb"
|
||||||
};
|
};
|
||||||
|
|
||||||
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
if (!irq) {
|
if (irq < 0)
|
||||||
dev_err(dev, "missing irq\n");
|
return irq;
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
priv = devm_kzalloc(dev, sizeof(struct brcmuart_priv),
|
priv = devm_kzalloc(dev, sizeof(struct brcmuart_priv),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!priv)
|
if (!priv)
|
||||||
|
@ -1044,7 +1042,7 @@ static int brcmuart_probe(struct platform_device *pdev)
|
||||||
up.port.dev = dev;
|
up.port.dev = dev;
|
||||||
up.port.mapbase = mapbase;
|
up.port.mapbase = mapbase;
|
||||||
up.port.membase = membase;
|
up.port.membase = membase;
|
||||||
up.port.irq = irq->start;
|
up.port.irq = irq;
|
||||||
up.port.handle_irq = brcmuart_handle_irq;
|
up.port.handle_irq = brcmuart_handle_irq;
|
||||||
up.port.regshift = 2;
|
up.port.regshift = 2;
|
||||||
up.port.iotype = of_device_is_big_endian(np) ?
|
up.port.iotype = of_device_is_big_endian(np) ?
|
||||||
|
@ -1076,15 +1074,19 @@ static int brcmuart_probe(struct platform_device *pdev)
|
||||||
priv->rx_bufs = dma_alloc_coherent(dev,
|
priv->rx_bufs = dma_alloc_coherent(dev,
|
||||||
priv->rx_size,
|
priv->rx_size,
|
||||||
&priv->rx_addr, GFP_KERNEL);
|
&priv->rx_addr, GFP_KERNEL);
|
||||||
if (!priv->rx_bufs)
|
if (!priv->rx_bufs) {
|
||||||
|
ret = -ENOMEM;
|
||||||
goto err;
|
goto err;
|
||||||
|
}
|
||||||
priv->tx_size = UART_XMIT_SIZE;
|
priv->tx_size = UART_XMIT_SIZE;
|
||||||
priv->tx_buf = dma_alloc_coherent(dev,
|
priv->tx_buf = dma_alloc_coherent(dev,
|
||||||
priv->tx_size,
|
priv->tx_size,
|
||||||
&priv->tx_addr, GFP_KERNEL);
|
&priv->tx_addr, GFP_KERNEL);
|
||||||
if (!priv->tx_buf)
|
if (!priv->tx_buf) {
|
||||||
|
ret = -ENOMEM;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ret = serial8250_register_8250_port(&up);
|
ret = serial8250_register_8250_port(&up);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -1097,6 +1099,7 @@ static int brcmuart_probe(struct platform_device *pdev)
|
||||||
if (priv->dma_enabled) {
|
if (priv->dma_enabled) {
|
||||||
dma_irq = platform_get_irq_byname(pdev, "dma");
|
dma_irq = platform_get_irq_byname(pdev, "dma");
|
||||||
if (dma_irq < 0) {
|
if (dma_irq < 0) {
|
||||||
|
ret = dma_irq;
|
||||||
dev_err(dev, "no IRQ resource info\n");
|
dev_err(dev, "no IRQ resource info\n");
|
||||||
goto err1;
|
goto err1;
|
||||||
}
|
}
|
||||||
|
@ -1116,7 +1119,7 @@ err1:
|
||||||
err:
|
err:
|
||||||
brcmuart_free_bufs(dev, priv);
|
brcmuart_free_bufs(dev, priv);
|
||||||
brcmuart_arbitration(priv, 0);
|
brcmuart_arbitration(priv, 0);
|
||||||
return -ENODEV;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int brcmuart_remove(struct platform_device *pdev)
|
static int brcmuart_remove(struct platform_device *pdev)
|
||||||
|
|
|
@ -509,11 +509,10 @@ static void __init serial8250_isa_init_ports(void)
|
||||||
|
|
||||||
up->ops = &univ8250_driver_ops;
|
up->ops = &univ8250_driver_ops;
|
||||||
|
|
||||||
/*
|
if (IS_ENABLED(CONFIG_ALPHA_JENSEN) ||
|
||||||
* ALPHA_KLUDGE_MCR needs to be killed.
|
(IS_ENABLED(CONFIG_ALPHA_GENERIC) && alpha_jensen()))
|
||||||
*/
|
port->set_mctrl = alpha_jensen_set_mctrl;
|
||||||
up->mcr_mask = ~ALPHA_KLUDGE_MCR;
|
|
||||||
up->mcr_force = ALPHA_KLUDGE_MCR;
|
|
||||||
serial8250_set_defaults(up);
|
serial8250_set_defaults(up);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1278,7 +1278,7 @@ static int pci_quatech_init(struct pci_dev *dev)
|
||||||
outl(inl(base + 0x38) | 0x00002000, base + 0x38);
|
outl(inl(base + 0x38) | 0x00002000, base + 0x38);
|
||||||
tmp = inl(base + 0x3c);
|
tmp = inl(base + 0x3c);
|
||||||
outl(tmp | 0x01000000, base + 0x3c);
|
outl(tmp | 0x01000000, base + 0x3c);
|
||||||
outl(tmp &= ~0x01000000, base + 0x3c);
|
outl(tmp & ~0x01000000, base + 0x3c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1318,89 +1318,6 @@ static int pci_default_setup(struct serial_private *priv,
|
||||||
|
|
||||||
return setup_port(priv, port, bar, offset, board->reg_shift);
|
return setup_port(priv, port, bar, offset, board->reg_shift);
|
||||||
}
|
}
|
||||||
static void
|
|
||||||
pericom_do_set_divisor(struct uart_port *port, unsigned int baud,
|
|
||||||
unsigned int quot, unsigned int quot_frac)
|
|
||||||
{
|
|
||||||
int scr;
|
|
||||||
int lcr;
|
|
||||||
|
|
||||||
for (scr = 16; scr > 4; scr--) {
|
|
||||||
unsigned int maxrate = port->uartclk / scr;
|
|
||||||
unsigned int divisor = max(maxrate / baud, 1U);
|
|
||||||
int delta = maxrate / divisor - baud;
|
|
||||||
|
|
||||||
if (baud > maxrate + baud / 50)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (delta > baud / 50)
|
|
||||||
divisor++;
|
|
||||||
|
|
||||||
if (divisor > 0xffff)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Update delta due to possible divisor change */
|
|
||||||
delta = maxrate / divisor - baud;
|
|
||||||
if (abs(delta) < baud / 50) {
|
|
||||||
lcr = serial_port_in(port, UART_LCR);
|
|
||||||
serial_port_out(port, UART_LCR, lcr | 0x80);
|
|
||||||
serial_port_out(port, UART_DLL, divisor & 0xff);
|
|
||||||
serial_port_out(port, UART_DLM, divisor >> 8 & 0xff);
|
|
||||||
serial_port_out(port, 2, 16 - scr);
|
|
||||||
serial_port_out(port, UART_LCR, lcr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static int pci_pericom_setup(struct serial_private *priv,
|
|
||||||
const struct pciserial_board *board,
|
|
||||||
struct uart_8250_port *port, int idx)
|
|
||||||
{
|
|
||||||
unsigned int bar, offset = board->first_offset, maxnr;
|
|
||||||
|
|
||||||
bar = FL_GET_BASE(board->flags);
|
|
||||||
if (board->flags & FL_BASE_BARS)
|
|
||||||
bar += idx;
|
|
||||||
else
|
|
||||||
offset += idx * board->uart_offset;
|
|
||||||
|
|
||||||
|
|
||||||
maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) >>
|
|
||||||
(board->reg_shift + 3);
|
|
||||||
|
|
||||||
if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
port->port.set_divisor = pericom_do_set_divisor;
|
|
||||||
|
|
||||||
return setup_port(priv, port, bar, offset, board->reg_shift);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pci_pericom_setup_four_at_eight(struct serial_private *priv,
|
|
||||||
const struct pciserial_board *board,
|
|
||||||
struct uart_8250_port *port, int idx)
|
|
||||||
{
|
|
||||||
unsigned int bar, offset = board->first_offset, maxnr;
|
|
||||||
|
|
||||||
bar = FL_GET_BASE(board->flags);
|
|
||||||
if (board->flags & FL_BASE_BARS)
|
|
||||||
bar += idx;
|
|
||||||
else
|
|
||||||
offset += idx * board->uart_offset;
|
|
||||||
|
|
||||||
if (idx==3)
|
|
||||||
offset = 0x38;
|
|
||||||
|
|
||||||
maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) >>
|
|
||||||
(board->reg_shift + 3);
|
|
||||||
|
|
||||||
if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
port->port.set_divisor = pericom_do_set_divisor;
|
|
||||||
|
|
||||||
return setup_port(priv, port, bar, offset, board->reg_shift);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ce4100_serial_setup(struct serial_private *priv,
|
ce4100_serial_setup(struct serial_private *priv,
|
||||||
|
@ -1886,42 +1803,6 @@ pci_moxa_setup(struct serial_private *priv,
|
||||||
#define PCIE_DEVICE_ID_WCH_CH384_8S 0x3853
|
#define PCIE_DEVICE_ID_WCH_CH384_8S 0x3853
|
||||||
#define PCIE_DEVICE_ID_WCH_CH382_2S 0x3253
|
#define PCIE_DEVICE_ID_WCH_CH382_2S 0x3253
|
||||||
|
|
||||||
#define PCI_VENDOR_ID_ACCESIO 0x494f
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SDB 0x1051
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2S 0x1053
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SDB 0x105C
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4S 0x105E
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_COM232_2DB 0x1091
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_2 0x1093
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4DB 0x1099
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_4 0x109B
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SMDB 0x10D1
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2SM 0x10D3
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SMDB 0x10DA
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4SM 0x10DC
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_1 0x1108
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_2 0x1110
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_2 0x1111
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_4 0x1118
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_4 0x1119
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2S 0x1152
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4S 0x115A
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_2 0x1190
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_2 0x1191
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_4 0x1198
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_4 0x1199
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2SM 0x11D0
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_COM422_4 0x105A
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_COM485_4 0x105B
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_COM422_8 0x106A
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_COM485_8 0x106B
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4 0x1098
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_COM232_8 0x10A9
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SM 0x10D9
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_8SM 0x10E9
|
|
||||||
#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4SM 0x11D8
|
|
||||||
|
|
||||||
|
|
||||||
#define PCI_DEVICE_ID_MOXA_CP102E 0x1024
|
#define PCI_DEVICE_ID_MOXA_CP102E 0x1024
|
||||||
#define PCI_DEVICE_ID_MOXA_CP102EL 0x1025
|
#define PCI_DEVICE_ID_MOXA_CP102EL 0x1025
|
||||||
#define PCI_DEVICE_ID_MOXA_CP104EL_A 0x1045
|
#define PCI_DEVICE_ID_MOXA_CP104EL_A 0x1045
|
||||||
|
@ -2198,16 +2079,6 @@ static struct pci_serial_quirk pci_serial_quirks[] = {
|
||||||
.setup = pci_default_setup,
|
.setup = pci_default_setup,
|
||||||
.exit = pci_plx9050_exit,
|
.exit = pci_plx9050_exit,
|
||||||
},
|
},
|
||||||
/*
|
|
||||||
* Pericom (Only 7954 - It have a offset jump for port 4)
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
.vendor = PCI_VENDOR_ID_PERICOM,
|
|
||||||
.device = PCI_DEVICE_ID_PERICOM_PI7C9X7954,
|
|
||||||
.subvendor = PCI_ANY_ID,
|
|
||||||
.subdevice = PCI_ANY_ID,
|
|
||||||
.setup = pci_pericom_setup_four_at_eight,
|
|
||||||
},
|
|
||||||
/*
|
/*
|
||||||
* PLX
|
* PLX
|
||||||
*/
|
*/
|
||||||
|
@ -2238,125 +2109,7 @@ static struct pci_serial_quirk pci_serial_quirks[] = {
|
||||||
.setup = pci_default_setup,
|
.setup = pci_default_setup,
|
||||||
.exit = pci_plx9050_exit,
|
.exit = pci_plx9050_exit,
|
||||||
},
|
},
|
||||||
{
|
/*
|
||||||
.vendor = PCI_VENDOR_ID_ACCESIO,
|
|
||||||
.device = PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SDB,
|
|
||||||
.subvendor = PCI_ANY_ID,
|
|
||||||
.subdevice = PCI_ANY_ID,
|
|
||||||
.setup = pci_pericom_setup_four_at_eight,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.vendor = PCI_VENDOR_ID_ACCESIO,
|
|
||||||
.device = PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4S,
|
|
||||||
.subvendor = PCI_ANY_ID,
|
|
||||||
.subdevice = PCI_ANY_ID,
|
|
||||||
.setup = pci_pericom_setup_four_at_eight,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.vendor = PCI_VENDOR_ID_ACCESIO,
|
|
||||||
.device = PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4DB,
|
|
||||||
.subvendor = PCI_ANY_ID,
|
|
||||||
.subdevice = PCI_ANY_ID,
|
|
||||||
.setup = pci_pericom_setup_four_at_eight,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.vendor = PCI_VENDOR_ID_ACCESIO,
|
|
||||||
.device = PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_4,
|
|
||||||
.subvendor = PCI_ANY_ID,
|
|
||||||
.subdevice = PCI_ANY_ID,
|
|
||||||
.setup = pci_pericom_setup_four_at_eight,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.vendor = PCI_VENDOR_ID_ACCESIO,
|
|
||||||
.device = PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SMDB,
|
|
||||||
.subvendor = PCI_ANY_ID,
|
|
||||||
.subdevice = PCI_ANY_ID,
|
|
||||||
.setup = pci_pericom_setup_four_at_eight,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.vendor = PCI_VENDOR_ID_ACCESIO,
|
|
||||||
.device = PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4SM,
|
|
||||||
.subvendor = PCI_ANY_ID,
|
|
||||||
.subdevice = PCI_ANY_ID,
|
|
||||||
.setup = pci_pericom_setup_four_at_eight,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.vendor = PCI_VENDOR_ID_ACCESIO,
|
|
||||||
.device = PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_4,
|
|
||||||
.subvendor = PCI_ANY_ID,
|
|
||||||
.subdevice = PCI_ANY_ID,
|
|
||||||
.setup = pci_pericom_setup_four_at_eight,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.vendor = PCI_VENDOR_ID_ACCESIO,
|
|
||||||
.device = PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_4,
|
|
||||||
.subvendor = PCI_ANY_ID,
|
|
||||||
.subdevice = PCI_ANY_ID,
|
|
||||||
.setup = pci_pericom_setup_four_at_eight,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.vendor = PCI_VENDOR_ID_ACCESIO,
|
|
||||||
.device = PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_4,
|
|
||||||
.subvendor = PCI_ANY_ID,
|
|
||||||
.subdevice = PCI_ANY_ID,
|
|
||||||
.setup = pci_pericom_setup_four_at_eight,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.vendor = PCI_VENDOR_ID_ACCESIO,
|
|
||||||
.device = PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4S,
|
|
||||||
.subvendor = PCI_ANY_ID,
|
|
||||||
.subdevice = PCI_ANY_ID,
|
|
||||||
.setup = pci_pericom_setup_four_at_eight,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.vendor = PCI_VENDOR_ID_ACCESIO,
|
|
||||||
.device = PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_4,
|
|
||||||
.subvendor = PCI_ANY_ID,
|
|
||||||
.subdevice = PCI_ANY_ID,
|
|
||||||
.setup = pci_pericom_setup_four_at_eight,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.vendor = PCI_VENDOR_ID_ACCESIO,
|
|
||||||
.device = PCI_DEVICE_ID_ACCESIO_PCIE_COM422_4,
|
|
||||||
.subvendor = PCI_ANY_ID,
|
|
||||||
.subdevice = PCI_ANY_ID,
|
|
||||||
.setup = pci_pericom_setup_four_at_eight,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.vendor = PCI_VENDOR_ID_ACCESIO,
|
|
||||||
.device = PCI_DEVICE_ID_ACCESIO_PCIE_COM485_4,
|
|
||||||
.subvendor = PCI_ANY_ID,
|
|
||||||
.subdevice = PCI_ANY_ID,
|
|
||||||
.setup = pci_pericom_setup_four_at_eight,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.vendor = PCI_VENDOR_ID_ACCESIO,
|
|
||||||
.device = PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4,
|
|
||||||
.subvendor = PCI_ANY_ID,
|
|
||||||
.subdevice = PCI_ANY_ID,
|
|
||||||
.setup = pci_pericom_setup_four_at_eight,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.vendor = PCI_VENDOR_ID_ACCESIO,
|
|
||||||
.device = PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SM,
|
|
||||||
.subvendor = PCI_ANY_ID,
|
|
||||||
.subdevice = PCI_ANY_ID,
|
|
||||||
.setup = pci_pericom_setup_four_at_eight,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.vendor = PCI_VENDOR_ID_ACCESIO,
|
|
||||||
.device = PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4SM,
|
|
||||||
.subvendor = PCI_ANY_ID,
|
|
||||||
.subdevice = PCI_ANY_ID,
|
|
||||||
.setup = pci_pericom_setup_four_at_eight,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.vendor = PCI_VENDOR_ID_ACCESIO,
|
|
||||||
.device = PCI_ANY_ID,
|
|
||||||
.subvendor = PCI_ANY_ID,
|
|
||||||
.subdevice = PCI_ANY_ID,
|
|
||||||
.setup = pci_pericom_setup,
|
|
||||||
}, /*
|
|
||||||
* SBS Technologies, Inc., PMC-OCTALPRO 232
|
* SBS Technologies, Inc., PMC-OCTALPRO 232
|
||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
|
@ -2948,10 +2701,6 @@ enum pci_board_num_t {
|
||||||
pbn_wch382_2,
|
pbn_wch382_2,
|
||||||
pbn_wch384_4,
|
pbn_wch384_4,
|
||||||
pbn_wch384_8,
|
pbn_wch384_8,
|
||||||
pbn_pericom_PI7C9X7951,
|
|
||||||
pbn_pericom_PI7C9X7952,
|
|
||||||
pbn_pericom_PI7C9X7954,
|
|
||||||
pbn_pericom_PI7C9X7958,
|
|
||||||
pbn_sunix_pci_1s,
|
pbn_sunix_pci_1s,
|
||||||
pbn_sunix_pci_2s,
|
pbn_sunix_pci_2s,
|
||||||
pbn_sunix_pci_4s,
|
pbn_sunix_pci_4s,
|
||||||
|
@ -3696,33 +3445,6 @@ static struct pciserial_board pci_boards[] = {
|
||||||
.uart_offset = 8,
|
.uart_offset = 8,
|
||||||
.first_offset = 0x00,
|
.first_offset = 0x00,
|
||||||
},
|
},
|
||||||
/*
|
|
||||||
* Pericom PI7C9X795[1248] Uno/Dual/Quad/Octal UART
|
|
||||||
*/
|
|
||||||
[pbn_pericom_PI7C9X7951] = {
|
|
||||||
.flags = FL_BASE0,
|
|
||||||
.num_ports = 1,
|
|
||||||
.base_baud = 921600,
|
|
||||||
.uart_offset = 0x8,
|
|
||||||
},
|
|
||||||
[pbn_pericom_PI7C9X7952] = {
|
|
||||||
.flags = FL_BASE0,
|
|
||||||
.num_ports = 2,
|
|
||||||
.base_baud = 921600,
|
|
||||||
.uart_offset = 0x8,
|
|
||||||
},
|
|
||||||
[pbn_pericom_PI7C9X7954] = {
|
|
||||||
.flags = FL_BASE0,
|
|
||||||
.num_ports = 4,
|
|
||||||
.base_baud = 921600,
|
|
||||||
.uart_offset = 0x8,
|
|
||||||
},
|
|
||||||
[pbn_pericom_PI7C9X7958] = {
|
|
||||||
.flags = FL_BASE0,
|
|
||||||
.num_ports = 8,
|
|
||||||
.base_baud = 921600,
|
|
||||||
.uart_offset = 0x8,
|
|
||||||
},
|
|
||||||
[pbn_sunix_pci_1s] = {
|
[pbn_sunix_pci_1s] = {
|
||||||
.num_ports = 1,
|
.num_ports = 1,
|
||||||
.base_baud = 921600,
|
.base_baud = 921600,
|
||||||
|
@ -3834,6 +3556,10 @@ static const struct pci_device_id blacklist[] = {
|
||||||
{ PCI_VDEVICE(EXAR, PCI_ANY_ID), },
|
{ PCI_VDEVICE(EXAR, PCI_ANY_ID), },
|
||||||
{ PCI_VDEVICE(COMMTECH, PCI_ANY_ID), },
|
{ PCI_VDEVICE(COMMTECH, PCI_ANY_ID), },
|
||||||
|
|
||||||
|
/* Pericom devices */
|
||||||
|
{ PCI_VDEVICE(PERICOM, PCI_ANY_ID), },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_ANY_ID), },
|
||||||
|
|
||||||
/* End of the black list */
|
/* End of the black list */
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
@ -5027,127 +4753,6 @@ static const struct pci_device_id serial_pci_tbl[] = {
|
||||||
{ PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8,
|
{ PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8,
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
||||||
pbn_b3_8_115200 },
|
pbn_b3_8_115200 },
|
||||||
/*
|
|
||||||
* Pericom PI7C9X795[1248] Uno/Dual/Quad/Octal UART
|
|
||||||
*/
|
|
||||||
{ PCI_VENDOR_ID_PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7951,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID,
|
|
||||||
0,
|
|
||||||
0, pbn_pericom_PI7C9X7951 },
|
|
||||||
{ PCI_VENDOR_ID_PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7952,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID,
|
|
||||||
0,
|
|
||||||
0, pbn_pericom_PI7C9X7952 },
|
|
||||||
{ PCI_VENDOR_ID_PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7954,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID,
|
|
||||||
0,
|
|
||||||
0, pbn_pericom_PI7C9X7954 },
|
|
||||||
{ PCI_VENDOR_ID_PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7958,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID,
|
|
||||||
0,
|
|
||||||
0, pbn_pericom_PI7C9X7958 },
|
|
||||||
/*
|
|
||||||
* ACCES I/O Products quad
|
|
||||||
*/
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SDB,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7952 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2S,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7952 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SDB,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7954 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4S,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7954 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_2DB,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7952 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_2,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7952 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4DB,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7954 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_4,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7954 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SMDB,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7952 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2SM,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7952 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SMDB,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7954 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4SM,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7954 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_1,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7951 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_2,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7952 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_2,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7952 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_4,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7954 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_4,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7954 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2S,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7952 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4S,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7954 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_2,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7952 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_2,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7952 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_4,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7954 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_4,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7954 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2SM,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7952 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM422_4,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7954 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM485_4,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7954 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM422_8,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7958 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM485_8,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7958 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7954 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_8,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7958 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SM,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7954 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_8SM,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7958 },
|
|
||||||
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4SM,
|
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
|
|
||||||
pbn_pericom_PI7C9X7954 },
|
|
||||||
/*
|
/*
|
||||||
* Topic TP560 Data/Fax/Voice 56k modem (reported by Evan Clarke)
|
* Topic TP560 Data/Fax/Voice 56k modem (reported by Evan Clarke)
|
||||||
*/
|
*/
|
||||||
|
|
214
drivers/tty/serial/8250/8250_pericom.c
Normal file
214
drivers/tty/serial/8250/8250_pericom.c
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/* Driver for Pericom UART */
|
||||||
|
|
||||||
|
#include <linux/bits.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/overflow.h>
|
||||||
|
#include <linux/pci.h>
|
||||||
|
|
||||||
|
#include "8250.h"
|
||||||
|
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM_2SDB 0x1051
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_2S 0x1053
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM422_4 0x105a
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM485_4 0x105b
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM_4SDB 0x105c
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_4S 0x105e
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM422_8 0x106a
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM485_8 0x106b
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_2DB 0x1091
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_MPCIE_COM232_2 0x1093
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_4 0x1098
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_4DB 0x1099
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_MPCIE_COM232_4 0x109b
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_8 0x10a9
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM_2SMDB 0x10d1
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_2SM 0x10d3
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM_4SM 0x10d9
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM_4SMDB 0x10da
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_4SM 0x10dc
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_PCIE_COM_8SM 0x10e9
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM485_1 0x1108
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM422_2 0x1110
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM485_2 0x1111
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM422_4 0x1118
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM485_4 0x1119
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_2S 0x1152
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_4S 0x115a
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_PCIE_ICM232_2 0x1190
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM232_2 0x1191
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_PCIE_ICM232_4 0x1198
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM232_4 0x1199
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_2SM 0x11d0
|
||||||
|
#define PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_4SM 0x11d8
|
||||||
|
|
||||||
|
struct pericom8250 {
|
||||||
|
void __iomem *virt;
|
||||||
|
unsigned int nr;
|
||||||
|
int line[];
|
||||||
|
};
|
||||||
|
|
||||||
|
static void pericom_do_set_divisor(struct uart_port *port, unsigned int baud,
|
||||||
|
unsigned int quot, unsigned int quot_frac)
|
||||||
|
{
|
||||||
|
int scr;
|
||||||
|
|
||||||
|
for (scr = 16; scr > 4; scr--) {
|
||||||
|
unsigned int maxrate = port->uartclk / scr;
|
||||||
|
unsigned int divisor = max(maxrate / baud, 1U);
|
||||||
|
int delta = maxrate / divisor - baud;
|
||||||
|
|
||||||
|
if (baud > maxrate + baud / 50)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (delta > baud / 50)
|
||||||
|
divisor++;
|
||||||
|
|
||||||
|
if (divisor > 0xffff)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Update delta due to possible divisor change */
|
||||||
|
delta = maxrate / divisor - baud;
|
||||||
|
if (abs(delta) < baud / 50) {
|
||||||
|
struct uart_8250_port *up = up_to_u8250p(port);
|
||||||
|
int lcr = serial_port_in(port, UART_LCR);
|
||||||
|
|
||||||
|
serial_port_out(port, UART_LCR, lcr | 0x80);
|
||||||
|
serial_dl_write(up, divisor);
|
||||||
|
serial_port_out(port, 2, 16 - scr);
|
||||||
|
serial_port_out(port, UART_LCR, lcr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pericom8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||||
|
{
|
||||||
|
unsigned int nr, i, bar = 0, maxnr;
|
||||||
|
struct pericom8250 *pericom;
|
||||||
|
struct uart_8250_port uart;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = pcim_enable_device(pdev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
maxnr = pci_resource_len(pdev, bar) >> 3;
|
||||||
|
|
||||||
|
if (pdev->vendor == PCI_VENDOR_ID_PERICOM)
|
||||||
|
nr = pdev->device & 0x0f;
|
||||||
|
else if (pdev->vendor == PCI_VENDOR_ID_ACCESSIO)
|
||||||
|
nr = BIT(((pdev->device & 0x38) >> 3) - 1);
|
||||||
|
else
|
||||||
|
nr = 1;
|
||||||
|
|
||||||
|
pericom = devm_kzalloc(&pdev->dev, struct_size(pericom, line, nr), GFP_KERNEL);
|
||||||
|
if (!pericom)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
pericom->virt = pcim_iomap(pdev, bar, 0);
|
||||||
|
if (!pericom->virt)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
memset(&uart, 0, sizeof(uart));
|
||||||
|
|
||||||
|
uart.port.dev = &pdev->dev;
|
||||||
|
uart.port.irq = pdev->irq;
|
||||||
|
uart.port.private_data = pericom;
|
||||||
|
uart.port.iotype = UPIO_PORT;
|
||||||
|
uart.port.uartclk = 921600 * 16;
|
||||||
|
uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ | UPF_MAGIC_MULTIPLIER;
|
||||||
|
uart.port.set_divisor = pericom_do_set_divisor;
|
||||||
|
for (i = 0; i < nr && i < maxnr; i++) {
|
||||||
|
unsigned int offset = (i == 3 && nr == 4) ? 0x38 : i * 0x8;
|
||||||
|
|
||||||
|
uart.port.iobase = pci_resource_start(pdev, bar) + offset;
|
||||||
|
|
||||||
|
dev_dbg(&pdev->dev, "Setup PCI port: port %lx, irq %d, type %d\n",
|
||||||
|
uart.port.iobase, uart.port.irq, uart.port.iotype);
|
||||||
|
|
||||||
|
pericom->line[i] = serial8250_register_8250_port(&uart);
|
||||||
|
if (pericom->line[i] < 0) {
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"Couldn't register serial port %lx, irq %d, type %d, error %d\n",
|
||||||
|
uart.port.iobase, uart.port.irq,
|
||||||
|
uart.port.iotype, pericom->line[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pericom->nr = i;
|
||||||
|
|
||||||
|
pci_set_drvdata(pdev, pericom);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pericom8250_remove(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
struct pericom8250 *pericom = pci_get_drvdata(pdev);
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < pericom->nr; i++)
|
||||||
|
serial8250_unregister_port(pericom->line[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct pci_device_id pericom8250_pci_ids[] = {
|
||||||
|
/*
|
||||||
|
* Pericom PI7C9X795[1248] Uno/Dual/Quad/Octal UART
|
||||||
|
* (Only 7954 has an offset jump for port 4)
|
||||||
|
*/
|
||||||
|
{ PCI_VDEVICE(PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7951) },
|
||||||
|
{ PCI_VDEVICE(PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7952) },
|
||||||
|
{ PCI_VDEVICE(PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7954) },
|
||||||
|
{ PCI_VDEVICE(PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7958) },
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ACCES I/O Products quad
|
||||||
|
* (Only 7954 has an offset jump for port 4)
|
||||||
|
*/
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM_2SDB) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_2S) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM422_4) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM485_4) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM_4SDB) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_4S) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM422_8) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM485_8) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_2DB) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_COM232_2) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_4) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_4DB) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_COM232_4) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM232_8) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM_2SMDB) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_2SM) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM_4SM) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM_4SMDB) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_COM_4SM) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_COM_8SM) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM485_1) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM422_2) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM485_2) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM422_4) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM485_4) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_2S) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_4S) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_ICM232_2) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM232_2) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_ICM232_4) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_MPCIE_ICM232_4) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_2SM) },
|
||||||
|
{ PCI_VDEVICE(ACCESSIO, PCI_DEVICE_ID_ACCESSIO_PCIE_ICM_4SM) },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(pci, pericom8250_pci_ids);
|
||||||
|
|
||||||
|
static struct pci_driver pericom8250_pci_driver = {
|
||||||
|
.name = "8250_pericom",
|
||||||
|
.id_table = pericom8250_pci_ids,
|
||||||
|
.probe = pericom8250_probe,
|
||||||
|
.remove = pericom8250_remove,
|
||||||
|
};
|
||||||
|
module_pci_driver(pericom8250_pci_driver);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_DESCRIPTION("Pericom UART driver");
|
|
@ -2026,7 +2026,7 @@ void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||||
|
|
||||||
mcr = serial8250_TIOCM_to_MCR(mctrl);
|
mcr = serial8250_TIOCM_to_MCR(mctrl);
|
||||||
|
|
||||||
mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr;
|
mcr |= up->mcr;
|
||||||
|
|
||||||
serial8250_out_MCR(up, mcr);
|
serial8250_out_MCR(up, mcr);
|
||||||
}
|
}
|
||||||
|
@ -2056,10 +2056,7 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state)
|
||||||
serial8250_rpm_put(up);
|
serial8250_rpm_put(up);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static void wait_for_lsr(struct uart_8250_port *up, int bits)
|
||||||
* Wait for transmitter & holding register to empty
|
|
||||||
*/
|
|
||||||
static void wait_for_xmitr(struct uart_8250_port *up, int bits)
|
|
||||||
{
|
{
|
||||||
unsigned int status, tmout = 10000;
|
unsigned int status, tmout = 10000;
|
||||||
|
|
||||||
|
@ -2076,6 +2073,16 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits)
|
||||||
udelay(1);
|
udelay(1);
|
||||||
touch_nmi_watchdog();
|
touch_nmi_watchdog();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait for transmitter & holding register to empty
|
||||||
|
*/
|
||||||
|
static void wait_for_xmitr(struct uart_8250_port *up, int bits)
|
||||||
|
{
|
||||||
|
unsigned int tmout;
|
||||||
|
|
||||||
|
wait_for_lsr(up, bits);
|
||||||
|
|
||||||
/* Wait up to 1s for flow control if necessary */
|
/* Wait up to 1s for flow control if necessary */
|
||||||
if (up->port.flags & UPF_CONS_FLOW) {
|
if (up->port.flags & UPF_CONS_FLOW) {
|
||||||
|
@ -3092,7 +3099,7 @@ static ssize_t rx_trig_bytes_show(struct device *dev,
|
||||||
if (rxtrig_bytes < 0)
|
if (rxtrig_bytes < 0)
|
||||||
return rxtrig_bytes;
|
return rxtrig_bytes;
|
||||||
|
|
||||||
return snprintf(buf, PAGE_SIZE, "%d\n", rxtrig_bytes);
|
return sysfs_emit(buf, "%d\n", rxtrig_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_set_rxtrig(struct tty_port *port, unsigned char bytes)
|
static int do_set_rxtrig(struct tty_port *port, unsigned char bytes)
|
||||||
|
@ -3325,6 +3332,35 @@ static void serial8250_console_restore(struct uart_8250_port *up)
|
||||||
serial8250_out_MCR(up, UART_MCR_DTR | UART_MCR_RTS);
|
serial8250_out_MCR(up, UART_MCR_DTR | UART_MCR_RTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print a string to the serial port using the device FIFO
|
||||||
|
*
|
||||||
|
* It sends fifosize bytes and then waits for the fifo
|
||||||
|
* to get empty.
|
||||||
|
*/
|
||||||
|
static void serial8250_console_fifo_write(struct uart_8250_port *up,
|
||||||
|
const char *s, unsigned int count)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
const char *end = s + count;
|
||||||
|
unsigned int fifosize = up->port.fifosize;
|
||||||
|
bool cr_sent = false;
|
||||||
|
|
||||||
|
while (s != end) {
|
||||||
|
wait_for_lsr(up, UART_LSR_THRE);
|
||||||
|
|
||||||
|
for (i = 0; i < fifosize && s != end; ++i) {
|
||||||
|
if (*s == '\n' && !cr_sent) {
|
||||||
|
serial_out(up, UART_TX, '\r');
|
||||||
|
cr_sent = true;
|
||||||
|
} else {
|
||||||
|
serial_out(up, UART_TX, *s++);
|
||||||
|
cr_sent = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Print a string to the serial port trying not to disturb
|
* Print a string to the serial port trying not to disturb
|
||||||
* any possible real use of the port...
|
* any possible real use of the port...
|
||||||
|
@ -3340,7 +3376,7 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s,
|
||||||
struct uart_8250_em485 *em485 = up->em485;
|
struct uart_8250_em485 *em485 = up->em485;
|
||||||
struct uart_port *port = &up->port;
|
struct uart_port *port = &up->port;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned int ier;
|
unsigned int ier, use_fifo;
|
||||||
int locked = 1;
|
int locked = 1;
|
||||||
|
|
||||||
touch_nmi_watchdog();
|
touch_nmi_watchdog();
|
||||||
|
@ -3372,6 +3408,19 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s,
|
||||||
mdelay(port->rs485.delay_rts_before_send);
|
mdelay(port->rs485.delay_rts_before_send);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use_fifo = (up->capabilities & UART_CAP_FIFO) &&
|
||||||
|
port->fifosize > 1 &&
|
||||||
|
(serial_port_in(port, UART_FCR) & UART_FCR_ENABLE_FIFO) &&
|
||||||
|
/*
|
||||||
|
* After we put a data in the fifo, the controller will send
|
||||||
|
* it regardless of the CTS state. Therefore, only use fifo
|
||||||
|
* if we don't use control flow.
|
||||||
|
*/
|
||||||
|
!(up->port.flags & UPF_CONS_FLOW);
|
||||||
|
|
||||||
|
if (likely(use_fifo))
|
||||||
|
serial8250_console_fifo_write(up, s, count);
|
||||||
|
else
|
||||||
uart_console_write(port, s, count, serial8250_console_putchar);
|
uart_console_write(port, s, count, serial8250_console_putchar);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -498,6 +498,14 @@ config SERIAL_8250_MID
|
||||||
present on the UART found on Intel Medfield SOC and various other
|
present on the UART found on Intel Medfield SOC and various other
|
||||||
Intel platforms.
|
Intel platforms.
|
||||||
|
|
||||||
|
config SERIAL_8250_PERICOM
|
||||||
|
tristate "Support for Pericom and Acces I/O serial ports"
|
||||||
|
default SERIAL_8250
|
||||||
|
depends on SERIAL_8250 && PCI
|
||||||
|
help
|
||||||
|
Selecting this option will enable handling of the extra features
|
||||||
|
present on the Pericom and Acces I/O UARTs.
|
||||||
|
|
||||||
config SERIAL_8250_PXA
|
config SERIAL_8250_PXA
|
||||||
tristate "PXA serial port support"
|
tristate "PXA serial port support"
|
||||||
depends on SERIAL_8250
|
depends on SERIAL_8250
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
obj-$(CONFIG_SERIAL_8250) += 8250.o 8250_base.o
|
obj-$(CONFIG_SERIAL_8250) += 8250.o 8250_base.o
|
||||||
8250-y := 8250_core.o
|
8250-y := 8250_core.o
|
||||||
|
8250-$(CONFIG_ALPHA_GENERIC) += 8250_alpha.o
|
||||||
|
8250-$(CONFIG_ALPHA_JENSEN) += 8250_alpha.o
|
||||||
8250-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o
|
8250-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o
|
||||||
8250_base-y := 8250_port.o
|
8250_base-y := 8250_port.o
|
||||||
8250_base-$(CONFIG_SERIAL_8250_DMA) += 8250_dma.o
|
8250_base-$(CONFIG_SERIAL_8250_DMA) += 8250_dma.o
|
||||||
|
@ -36,6 +38,7 @@ obj-$(CONFIG_SERIAL_8250_UNIPHIER) += 8250_uniphier.o
|
||||||
obj-$(CONFIG_SERIAL_8250_INGENIC) += 8250_ingenic.o
|
obj-$(CONFIG_SERIAL_8250_INGENIC) += 8250_ingenic.o
|
||||||
obj-$(CONFIG_SERIAL_8250_LPSS) += 8250_lpss.o
|
obj-$(CONFIG_SERIAL_8250_LPSS) += 8250_lpss.o
|
||||||
obj-$(CONFIG_SERIAL_8250_MID) += 8250_mid.o
|
obj-$(CONFIG_SERIAL_8250_MID) += 8250_mid.o
|
||||||
|
obj-$(CONFIG_SERIAL_8250_PERICOM) += 8250_pericom.o
|
||||||
obj-$(CONFIG_SERIAL_8250_PXA) += 8250_pxa.o
|
obj-$(CONFIG_SERIAL_8250_PXA) += 8250_pxa.o
|
||||||
obj-$(CONFIG_SERIAL_8250_TEGRA) += 8250_tegra.o
|
obj-$(CONFIG_SERIAL_8250_TEGRA) += 8250_tegra.o
|
||||||
obj-$(CONFIG_SERIAL_8250_BCM7271) += 8250_bcm7271.o
|
obj-$(CONFIG_SERIAL_8250_BCM7271) += 8250_bcm7271.o
|
||||||
|
|
|
@ -263,7 +263,7 @@ config SERIAL_SAMSUNG_UARTS
|
||||||
|
|
||||||
config SERIAL_SAMSUNG_CONSOLE
|
config SERIAL_SAMSUNG_CONSOLE
|
||||||
bool "Support for console on Samsung SoC serial port"
|
bool "Support for console on Samsung SoC serial port"
|
||||||
depends on SERIAL_SAMSUNG=y
|
depends on SERIAL_SAMSUNG
|
||||||
select SERIAL_CORE_CONSOLE
|
select SERIAL_CORE_CONSOLE
|
||||||
select SERIAL_EARLYCON
|
select SERIAL_EARLYCON
|
||||||
help
|
help
|
||||||
|
|
|
@ -418,8 +418,9 @@ static int altera_jtaguart_probe(struct platform_device *pdev)
|
||||||
struct altera_jtaguart_platform_uart *platp =
|
struct altera_jtaguart_platform_uart *platp =
|
||||||
dev_get_platdata(&pdev->dev);
|
dev_get_platdata(&pdev->dev);
|
||||||
struct uart_port *port;
|
struct uart_port *port;
|
||||||
struct resource *res_irq, *res_mem;
|
struct resource *res_mem;
|
||||||
int i = pdev->id;
|
int i = pdev->id;
|
||||||
|
int irq;
|
||||||
|
|
||||||
/* -1 emphasizes that the platform must have one port, no .N suffix */
|
/* -1 emphasizes that the platform must have one port, no .N suffix */
|
||||||
if (i == -1)
|
if (i == -1)
|
||||||
|
@ -438,9 +439,11 @@ static int altera_jtaguart_probe(struct platform_device *pdev)
|
||||||
else
|
else
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
irq = platform_get_irq_optional(pdev, 0);
|
||||||
if (res_irq)
|
if (irq < 0 && irq != -ENXIO)
|
||||||
port->irq = res_irq->start;
|
return irq;
|
||||||
|
if (irq > 0)
|
||||||
|
port->irq = irq;
|
||||||
else if (platp)
|
else if (platp)
|
||||||
port->irq = platp->irq;
|
port->irq = platp->irq;
|
||||||
else
|
else
|
||||||
|
|
|
@ -553,7 +553,6 @@ static int altera_uart_probe(struct platform_device *pdev)
|
||||||
struct altera_uart_platform_uart *platp = dev_get_platdata(&pdev->dev);
|
struct altera_uart_platform_uart *platp = dev_get_platdata(&pdev->dev);
|
||||||
struct uart_port *port;
|
struct uart_port *port;
|
||||||
struct resource *res_mem;
|
struct resource *res_mem;
|
||||||
struct resource *res_irq;
|
|
||||||
int i = pdev->id;
|
int i = pdev->id;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -577,9 +576,11 @@ static int altera_uart_probe(struct platform_device *pdev)
|
||||||
else
|
else
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
ret = platform_get_irq_optional(pdev, 0);
|
||||||
if (res_irq)
|
if (ret < 0 && ret != -ENXIO)
|
||||||
port->irq = res_irq->start;
|
return ret;
|
||||||
|
if (ret > 0)
|
||||||
|
port->irq = ret;
|
||||||
else if (platp)
|
else if (platp)
|
||||||
port->irq = platp->irq;
|
port->irq = platp->irq;
|
||||||
|
|
||||||
|
|
|
@ -446,14 +446,11 @@ pl010_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||||
if ((termios->c_cflag & CREAD) == 0)
|
if ((termios->c_cflag & CREAD) == 0)
|
||||||
uap->port.ignore_status_mask |= UART_DUMMY_RSR_RX;
|
uap->port.ignore_status_mask |= UART_DUMMY_RSR_RX;
|
||||||
|
|
||||||
/* first, disable everything */
|
|
||||||
old_cr = readb(uap->port.membase + UART010_CR) & ~UART010_CR_MSIE;
|
old_cr = readb(uap->port.membase + UART010_CR) & ~UART010_CR_MSIE;
|
||||||
|
|
||||||
if (UART_ENABLE_MS(port, termios->c_cflag))
|
if (UART_ENABLE_MS(port, termios->c_cflag))
|
||||||
old_cr |= UART010_CR_MSIE;
|
old_cr |= UART010_CR_MSIE;
|
||||||
|
|
||||||
writel(0, uap->port.membase + UART010_CR);
|
|
||||||
|
|
||||||
/* Set baud rate */
|
/* Set baud rate */
|
||||||
quot -= 1;
|
quot -= 1;
|
||||||
writel((quot & 0xf00) >> 8, uap->port.membase + UART010_LCRM);
|
writel((quot & 0xf00) >> 8, uap->port.membase + UART010_LCRM);
|
||||||
|
|
|
@ -188,38 +188,6 @@ static struct vendor_data vendor_st = {
|
||||||
.get_fifosize = get_fifosize_st,
|
.get_fifosize = get_fifosize_st,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const u16 pl011_zte_offsets[REG_ARRAY_SIZE] = {
|
|
||||||
[REG_DR] = ZX_UART011_DR,
|
|
||||||
[REG_FR] = ZX_UART011_FR,
|
|
||||||
[REG_LCRH_RX] = ZX_UART011_LCRH,
|
|
||||||
[REG_LCRH_TX] = ZX_UART011_LCRH,
|
|
||||||
[REG_IBRD] = ZX_UART011_IBRD,
|
|
||||||
[REG_FBRD] = ZX_UART011_FBRD,
|
|
||||||
[REG_CR] = ZX_UART011_CR,
|
|
||||||
[REG_IFLS] = ZX_UART011_IFLS,
|
|
||||||
[REG_IMSC] = ZX_UART011_IMSC,
|
|
||||||
[REG_RIS] = ZX_UART011_RIS,
|
|
||||||
[REG_MIS] = ZX_UART011_MIS,
|
|
||||||
[REG_ICR] = ZX_UART011_ICR,
|
|
||||||
[REG_DMACR] = ZX_UART011_DMACR,
|
|
||||||
};
|
|
||||||
|
|
||||||
static unsigned int get_fifosize_zte(struct amba_device *dev)
|
|
||||||
{
|
|
||||||
return 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct vendor_data vendor_zte = {
|
|
||||||
.reg_offset = pl011_zte_offsets,
|
|
||||||
.access_32b = true,
|
|
||||||
.ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8,
|
|
||||||
.fr_busy = ZX_UART01x_FR_BUSY,
|
|
||||||
.fr_dsr = ZX_UART01x_FR_DSR,
|
|
||||||
.fr_cts = ZX_UART01x_FR_CTS,
|
|
||||||
.fr_ri = ZX_UART011_FR_RI,
|
|
||||||
.get_fifosize = get_fifosize_zte,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Deals with DMA transactions */
|
/* Deals with DMA transactions */
|
||||||
|
|
||||||
struct pl011_sgbuf {
|
struct pl011_sgbuf {
|
||||||
|
@ -262,7 +230,6 @@ struct uart_amba_port {
|
||||||
unsigned int im; /* interrupt mask */
|
unsigned int im; /* interrupt mask */
|
||||||
unsigned int old_status;
|
unsigned int old_status;
|
||||||
unsigned int fifosize; /* vendor-specific */
|
unsigned int fifosize; /* vendor-specific */
|
||||||
unsigned int old_cr; /* state during shutdown */
|
|
||||||
unsigned int fixed_baud; /* vendor-set fixed baud rate */
|
unsigned int fixed_baud; /* vendor-set fixed baud rate */
|
||||||
char type[12];
|
char type[12];
|
||||||
bool rs485_tx_started;
|
bool rs485_tx_started;
|
||||||
|
@ -1837,8 +1804,8 @@ static int pl011_startup(struct uart_port *port)
|
||||||
|
|
||||||
spin_lock_irq(&uap->port.lock);
|
spin_lock_irq(&uap->port.lock);
|
||||||
|
|
||||||
/* restore RTS and DTR */
|
cr = pl011_read(uap, REG_CR);
|
||||||
cr = uap->old_cr & (UART011_CR_RTS | UART011_CR_DTR);
|
cr &= UART011_CR_RTS | UART011_CR_DTR;
|
||||||
cr |= UART01x_CR_UARTEN | UART011_CR_RXE;
|
cr |= UART01x_CR_UARTEN | UART011_CR_RXE;
|
||||||
|
|
||||||
if (port->rs485.flags & SER_RS485_ENABLED) {
|
if (port->rs485.flags & SER_RS485_ENABLED) {
|
||||||
|
@ -1915,7 +1882,6 @@ static void pl011_disable_uart(struct uart_amba_port *uap)
|
||||||
uap->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS);
|
uap->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS);
|
||||||
spin_lock_irq(&uap->port.lock);
|
spin_lock_irq(&uap->port.lock);
|
||||||
cr = pl011_read(uap, REG_CR);
|
cr = pl011_read(uap, REG_CR);
|
||||||
uap->old_cr = cr;
|
|
||||||
cr &= UART011_CR_RTS | UART011_CR_DTR;
|
cr &= UART011_CR_RTS | UART011_CR_DTR;
|
||||||
cr |= UART01x_CR_UARTEN | UART011_CR_TXE;
|
cr |= UART01x_CR_UARTEN | UART011_CR_TXE;
|
||||||
pl011_write(cr, uap, REG_CR);
|
pl011_write(cr, uap, REG_CR);
|
||||||
|
@ -2105,9 +2071,7 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||||
if (port->rs485.flags & SER_RS485_ENABLED)
|
if (port->rs485.flags & SER_RS485_ENABLED)
|
||||||
termios->c_cflag &= ~CRTSCTS;
|
termios->c_cflag &= ~CRTSCTS;
|
||||||
|
|
||||||
/* first, disable everything */
|
|
||||||
old_cr = pl011_read(uap, REG_CR);
|
old_cr = pl011_read(uap, REG_CR);
|
||||||
pl011_write(0, uap, REG_CR);
|
|
||||||
|
|
||||||
if (termios->c_cflag & CRTSCTS) {
|
if (termios->c_cflag & CRTSCTS) {
|
||||||
if (old_cr & UART011_CR_RTS)
|
if (old_cr & UART011_CR_RTS)
|
||||||
|
@ -2183,32 +2147,13 @@ static const char *pl011_type(struct uart_port *port)
|
||||||
return uap->port.type == PORT_AMBA ? uap->type : NULL;
|
return uap->port.type == PORT_AMBA ? uap->type : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Release the memory region(s) being used by 'port'
|
|
||||||
*/
|
|
||||||
static void pl011_release_port(struct uart_port *port)
|
|
||||||
{
|
|
||||||
release_mem_region(port->mapbase, SZ_4K);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Request the memory region(s) being used by 'port'
|
|
||||||
*/
|
|
||||||
static int pl011_request_port(struct uart_port *port)
|
|
||||||
{
|
|
||||||
return request_mem_region(port->mapbase, SZ_4K, "uart-pl011")
|
|
||||||
!= NULL ? 0 : -EBUSY;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Configure/autoconfigure the port.
|
* Configure/autoconfigure the port.
|
||||||
*/
|
*/
|
||||||
static void pl011_config_port(struct uart_port *port, int flags)
|
static void pl011_config_port(struct uart_port *port, int flags)
|
||||||
{
|
{
|
||||||
if (flags & UART_CONFIG_TYPE) {
|
if (flags & UART_CONFIG_TYPE)
|
||||||
port->type = PORT_AMBA;
|
port->type = PORT_AMBA;
|
||||||
pl011_request_port(port);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2223,6 +2168,8 @@ static int pl011_verify_port(struct uart_port *port, struct serial_struct *ser)
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
if (ser->baud_base < 9600)
|
if (ser->baud_base < 9600)
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
if (port->mapbase != (unsigned long) ser->iomem_base)
|
||||||
|
ret = -EINVAL;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2275,8 +2222,6 @@ static const struct uart_ops amba_pl011_pops = {
|
||||||
.flush_buffer = pl011_dma_flush_buffer,
|
.flush_buffer = pl011_dma_flush_buffer,
|
||||||
.set_termios = pl011_set_termios,
|
.set_termios = pl011_set_termios,
|
||||||
.type = pl011_type,
|
.type = pl011_type,
|
||||||
.release_port = pl011_release_port,
|
|
||||||
.request_port = pl011_request_port,
|
|
||||||
.config_port = pl011_config_port,
|
.config_port = pl011_config_port,
|
||||||
.verify_port = pl011_verify_port,
|
.verify_port = pl011_verify_port,
|
||||||
#ifdef CONFIG_CONSOLE_POLL
|
#ifdef CONFIG_CONSOLE_POLL
|
||||||
|
@ -2306,8 +2251,6 @@ static const struct uart_ops sbsa_uart_pops = {
|
||||||
.shutdown = sbsa_uart_shutdown,
|
.shutdown = sbsa_uart_shutdown,
|
||||||
.set_termios = sbsa_uart_set_termios,
|
.set_termios = sbsa_uart_set_termios,
|
||||||
.type = pl011_type,
|
.type = pl011_type,
|
||||||
.release_port = pl011_release_port,
|
|
||||||
.request_port = pl011_request_port,
|
|
||||||
.config_port = pl011_config_port,
|
.config_port = pl011_config_port,
|
||||||
.verify_port = pl011_verify_port,
|
.verify_port = pl011_verify_port,
|
||||||
#ifdef CONFIG_CONSOLE_POLL
|
#ifdef CONFIG_CONSOLE_POLL
|
||||||
|
@ -2754,7 +2697,6 @@ static int pl011_setup_port(struct device *dev, struct uart_amba_port *uap,
|
||||||
|
|
||||||
index = pl011_probe_dt_alias(index, dev);
|
index = pl011_probe_dt_alias(index, dev);
|
||||||
|
|
||||||
uap->old_cr = 0;
|
|
||||||
uap->port.dev = dev;
|
uap->port.dev = dev;
|
||||||
uap->port.mapbase = mmiobase->start;
|
uap->port.mapbase = mmiobase->start;
|
||||||
uap->port.membase = base;
|
uap->port.membase = base;
|
||||||
|
@ -2975,11 +2917,6 @@ static const struct amba_id pl011_ids[] = {
|
||||||
.mask = 0x00ffffff,
|
.mask = 0x00ffffff,
|
||||||
.data = &vendor_st,
|
.data = &vendor_st,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
.id = AMBA_LINUX_ID(0x00, 0x1, 0xffe),
|
|
||||||
.mask = 0x00ffffff,
|
|
||||||
.data = &vendor_zte,
|
|
||||||
},
|
|
||||||
{ 0, 0 },
|
{ 0, 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -707,11 +707,11 @@ static int ar933x_uart_probe(struct platform_device *pdev)
|
||||||
struct ar933x_uart_port *up;
|
struct ar933x_uart_port *up;
|
||||||
struct uart_port *port;
|
struct uart_port *port;
|
||||||
struct resource *mem_res;
|
struct resource *mem_res;
|
||||||
struct resource *irq_res;
|
|
||||||
struct device_node *np;
|
struct device_node *np;
|
||||||
unsigned int baud;
|
unsigned int baud;
|
||||||
int id;
|
int id;
|
||||||
int ret;
|
int ret;
|
||||||
|
int irq;
|
||||||
|
|
||||||
np = pdev->dev.of_node;
|
np = pdev->dev.of_node;
|
||||||
if (IS_ENABLED(CONFIG_OF) && np) {
|
if (IS_ENABLED(CONFIG_OF) && np) {
|
||||||
|
@ -730,11 +730,9 @@ static int ar933x_uart_probe(struct platform_device *pdev)
|
||||||
if (id >= CONFIG_SERIAL_AR933X_NR_UARTS)
|
if (id >= CONFIG_SERIAL_AR933X_NR_UARTS)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
if (!irq_res) {
|
if (irq < 0)
|
||||||
dev_err(&pdev->dev, "no IRQ resource\n");
|
return irq;
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
up = devm_kzalloc(&pdev->dev, sizeof(struct ar933x_uart_port),
|
up = devm_kzalloc(&pdev->dev, sizeof(struct ar933x_uart_port),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
|
@ -766,7 +764,7 @@ static int ar933x_uart_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
port->mapbase = mem_res->start;
|
port->mapbase = mem_res->start;
|
||||||
port->line = id;
|
port->line = id;
|
||||||
port->irq = irq_res->start;
|
port->irq = irq;
|
||||||
port->dev = &pdev->dev;
|
port->dev = &pdev->dev;
|
||||||
port->type = PORT_AR933X;
|
port->type = PORT_AR933X;
|
||||||
port->iotype = UPIO_MEM32;
|
port->iotype = UPIO_MEM32;
|
||||||
|
|
|
@ -1004,6 +1004,13 @@ static void atmel_tx_dma(struct uart_port *port)
|
||||||
desc->callback = atmel_complete_tx_dma;
|
desc->callback = atmel_complete_tx_dma;
|
||||||
desc->callback_param = atmel_port;
|
desc->callback_param = atmel_port;
|
||||||
atmel_port->cookie_tx = dmaengine_submit(desc);
|
atmel_port->cookie_tx = dmaengine_submit(desc);
|
||||||
|
if (dma_submit_error(atmel_port->cookie_tx)) {
|
||||||
|
dev_err(port->dev, "dma_submit_error %d\n",
|
||||||
|
atmel_port->cookie_tx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_async_issue_pending(chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
||||||
|
@ -1258,6 +1265,13 @@ static int atmel_prepare_rx_dma(struct uart_port *port)
|
||||||
desc->callback_param = port;
|
desc->callback_param = port;
|
||||||
atmel_port->desc_rx = desc;
|
atmel_port->desc_rx = desc;
|
||||||
atmel_port->cookie_rx = dmaengine_submit(desc);
|
atmel_port->cookie_rx = dmaengine_submit(desc);
|
||||||
|
if (dma_submit_error(atmel_port->cookie_rx)) {
|
||||||
|
dev_err(port->dev, "dma_submit_error %d\n",
|
||||||
|
atmel_port->cookie_rx);
|
||||||
|
goto chan_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_async_issue_pending(atmel_port->chan_rx);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -2479,7 +2493,7 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
|
||||||
port->fifosize = 1;
|
port->fifosize = 1;
|
||||||
port->dev = &pdev->dev;
|
port->dev = &pdev->dev;
|
||||||
port->mapbase = mpdev->resource[0].start;
|
port->mapbase = mpdev->resource[0].start;
|
||||||
port->irq = mpdev->resource[1].start;
|
port->irq = platform_get_irq(mpdev, 0);
|
||||||
port->rs485_config = atmel_config_rs485;
|
port->rs485_config = atmel_config_rs485;
|
||||||
port->iso7816_config = atmel_config_iso7816;
|
port->iso7816_config = atmel_config_iso7816;
|
||||||
port->membase = NULL;
|
port->membase = NULL;
|
||||||
|
|
|
@ -804,7 +804,7 @@ static struct uart_driver bcm_uart_driver = {
|
||||||
*/
|
*/
|
||||||
static int bcm_uart_probe(struct platform_device *pdev)
|
static int bcm_uart_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct resource *res_mem, *res_irq;
|
struct resource *res_mem;
|
||||||
struct uart_port *port;
|
struct uart_port *port;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -833,9 +833,10 @@ static int bcm_uart_probe(struct platform_device *pdev)
|
||||||
if (IS_ERR(port->membase))
|
if (IS_ERR(port->membase))
|
||||||
return PTR_ERR(port->membase);
|
return PTR_ERR(port->membase);
|
||||||
|
|
||||||
res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
ret = platform_get_irq(pdev, 0);
|
||||||
if (!res_irq)
|
if (ret < 0)
|
||||||
return -ENODEV;
|
return ret;
|
||||||
|
port->irq = ret;
|
||||||
|
|
||||||
clk = clk_get(&pdev->dev, "refclk");
|
clk = clk_get(&pdev->dev, "refclk");
|
||||||
if (IS_ERR(clk) && pdev->dev.of_node)
|
if (IS_ERR(clk) && pdev->dev.of_node)
|
||||||
|
@ -845,7 +846,6 @@ static int bcm_uart_probe(struct platform_device *pdev)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
port->iotype = UPIO_MEM;
|
port->iotype = UPIO_MEM;
|
||||||
port->irq = res_irq->start;
|
|
||||||
port->ops = &bcm_uart_ops;
|
port->ops = &bcm_uart_ops;
|
||||||
port->flags = UPF_BOOT_AUTOCONF;
|
port->flags = UPF_BOOT_AUTOCONF;
|
||||||
port->dev = &pdev->dev;
|
port->dev = &pdev->dev;
|
||||||
|
|
|
@ -247,6 +247,7 @@ enum lpuart_type {
|
||||||
LS1028A_LPUART,
|
LS1028A_LPUART,
|
||||||
IMX7ULP_LPUART,
|
IMX7ULP_LPUART,
|
||||||
IMX8QXP_LPUART,
|
IMX8QXP_LPUART,
|
||||||
|
IMXRT1050_LPUART,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct lpuart_port {
|
struct lpuart_port {
|
||||||
|
@ -310,6 +311,11 @@ static struct lpuart_soc_data imx8qxp_data = {
|
||||||
.iotype = UPIO_MEM32,
|
.iotype = UPIO_MEM32,
|
||||||
.reg_off = IMX_REG_OFF,
|
.reg_off = IMX_REG_OFF,
|
||||||
};
|
};
|
||||||
|
static struct lpuart_soc_data imxrt1050_data = {
|
||||||
|
.devtype = IMXRT1050_LPUART,
|
||||||
|
.iotype = UPIO_MEM32,
|
||||||
|
.reg_off = IMX_REG_OFF,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct of_device_id lpuart_dt_ids[] = {
|
static const struct of_device_id lpuart_dt_ids[] = {
|
||||||
{ .compatible = "fsl,vf610-lpuart", .data = &vf_data, },
|
{ .compatible = "fsl,vf610-lpuart", .data = &vf_data, },
|
||||||
|
@ -317,6 +323,7 @@ static const struct of_device_id lpuart_dt_ids[] = {
|
||||||
{ .compatible = "fsl,ls1028a-lpuart", .data = &ls1028a_data, },
|
{ .compatible = "fsl,ls1028a-lpuart", .data = &ls1028a_data, },
|
||||||
{ .compatible = "fsl,imx7ulp-lpuart", .data = &imx7ulp_data, },
|
{ .compatible = "fsl,imx7ulp-lpuart", .data = &imx7ulp_data, },
|
||||||
{ .compatible = "fsl,imx8qxp-lpuart", .data = &imx8qxp_data, },
|
{ .compatible = "fsl,imx8qxp-lpuart", .data = &imx8qxp_data, },
|
||||||
|
{ .compatible = "fsl,imxrt1050-lpuart", .data = &imxrt1050_data},
|
||||||
{ /* sentinel */ }
|
{ /* sentinel */ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
|
MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
|
||||||
|
@ -1793,8 +1800,8 @@ static void lpuart_dma_shutdown(struct lpuart_port *sport)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sport->lpuart_dma_tx_use) {
|
if (sport->lpuart_dma_tx_use) {
|
||||||
if (wait_event_interruptible(sport->dma_wait,
|
if (wait_event_interruptible_timeout(sport->dma_wait,
|
||||||
!sport->dma_tx_in_progress) != false) {
|
!sport->dma_tx_in_progress, msecs_to_jiffies(300)) <= 0) {
|
||||||
sport->dma_tx_in_progress = false;
|
sport->dma_tx_in_progress = false;
|
||||||
dmaengine_terminate_all(sport->dma_tx_chan);
|
dmaengine_terminate_all(sport->dma_tx_chan);
|
||||||
}
|
}
|
||||||
|
@ -2626,6 +2633,7 @@ OF_EARLYCON_DECLARE(lpuart32, "fsl,ls1021a-lpuart", lpuart32_early_console_setup
|
||||||
OF_EARLYCON_DECLARE(lpuart32, "fsl,ls1028a-lpuart", ls1028a_early_console_setup);
|
OF_EARLYCON_DECLARE(lpuart32, "fsl,ls1028a-lpuart", ls1028a_early_console_setup);
|
||||||
OF_EARLYCON_DECLARE(lpuart32, "fsl,imx7ulp-lpuart", lpuart32_imx_early_console_setup);
|
OF_EARLYCON_DECLARE(lpuart32, "fsl,imx7ulp-lpuart", lpuart32_imx_early_console_setup);
|
||||||
OF_EARLYCON_DECLARE(lpuart32, "fsl,imx8qxp-lpuart", lpuart32_imx_early_console_setup);
|
OF_EARLYCON_DECLARE(lpuart32, "fsl,imx8qxp-lpuart", lpuart32_imx_early_console_setup);
|
||||||
|
OF_EARLYCON_DECLARE(lpuart32, "fsl,imxrt1050-lpuart", lpuart32_imx_early_console_setup);
|
||||||
EARLYCON_DECLARE(lpuart, lpuart_early_console_setup);
|
EARLYCON_DECLARE(lpuart, lpuart_early_console_setup);
|
||||||
EARLYCON_DECLARE(lpuart32, lpuart32_early_console_setup);
|
EARLYCON_DECLARE(lpuart32, lpuart32_early_console_setup);
|
||||||
|
|
||||||
|
|
|
@ -486,18 +486,21 @@ static void imx_uart_stop_tx(struct uart_port *port)
|
||||||
static void imx_uart_stop_rx(struct uart_port *port)
|
static void imx_uart_stop_rx(struct uart_port *port)
|
||||||
{
|
{
|
||||||
struct imx_port *sport = (struct imx_port *)port;
|
struct imx_port *sport = (struct imx_port *)port;
|
||||||
u32 ucr1, ucr2;
|
u32 ucr1, ucr2, ucr4;
|
||||||
|
|
||||||
ucr1 = imx_uart_readl(sport, UCR1);
|
ucr1 = imx_uart_readl(sport, UCR1);
|
||||||
ucr2 = imx_uart_readl(sport, UCR2);
|
ucr2 = imx_uart_readl(sport, UCR2);
|
||||||
|
ucr4 = imx_uart_readl(sport, UCR4);
|
||||||
|
|
||||||
if (sport->dma_is_enabled) {
|
if (sport->dma_is_enabled) {
|
||||||
ucr1 &= ~(UCR1_RXDMAEN | UCR1_ATDMAEN);
|
ucr1 &= ~(UCR1_RXDMAEN | UCR1_ATDMAEN);
|
||||||
} else {
|
} else {
|
||||||
ucr1 &= ~UCR1_RRDYEN;
|
ucr1 &= ~UCR1_RRDYEN;
|
||||||
ucr2 &= ~UCR2_ATEN;
|
ucr2 &= ~UCR2_ATEN;
|
||||||
|
ucr4 &= ~UCR4_OREN;
|
||||||
}
|
}
|
||||||
imx_uart_writel(sport, ucr1, UCR1);
|
imx_uart_writel(sport, ucr1, UCR1);
|
||||||
|
imx_uart_writel(sport, ucr4, UCR4);
|
||||||
|
|
||||||
ucr2 &= ~UCR2_RXEN;
|
ucr2 &= ~UCR2_RXEN;
|
||||||
imx_uart_writel(sport, ucr2, UCR2);
|
imx_uart_writel(sport, ucr2, UCR2);
|
||||||
|
@ -1544,7 +1547,7 @@ static void imx_uart_shutdown(struct uart_port *port)
|
||||||
imx_uart_writel(sport, ucr1, UCR1);
|
imx_uart_writel(sport, ucr1, UCR1);
|
||||||
|
|
||||||
ucr4 = imx_uart_readl(sport, UCR4);
|
ucr4 = imx_uart_readl(sport, UCR4);
|
||||||
ucr4 &= ~(UCR4_OREN | UCR4_TCEN);
|
ucr4 &= ~UCR4_TCEN;
|
||||||
imx_uart_writel(sport, ucr4, UCR4);
|
imx_uart_writel(sport, ucr4, UCR4);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&sport->port.lock, flags);
|
spin_unlock_irqrestore(&sport->port.lock, flags);
|
||||||
|
@ -2482,10 +2485,12 @@ static void imx_uart_enable_wakeup(struct imx_port *sport, bool on)
|
||||||
|
|
||||||
if (sport->have_rtscts) {
|
if (sport->have_rtscts) {
|
||||||
u32 ucr1 = imx_uart_readl(sport, UCR1);
|
u32 ucr1 = imx_uart_readl(sport, UCR1);
|
||||||
if (on)
|
if (on) {
|
||||||
|
imx_uart_writel(sport, USR1_RTSD, USR1);
|
||||||
ucr1 |= UCR1_RTSDEN;
|
ucr1 |= UCR1_RTSDEN;
|
||||||
else
|
} else {
|
||||||
ucr1 &= ~UCR1_RTSDEN;
|
ucr1 &= ~UCR1_RTSDEN;
|
||||||
|
}
|
||||||
imx_uart_writel(sport, ucr1, UCR1);
|
imx_uart_writel(sport, ucr1, UCR1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,6 @@
|
||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
#include <linux/lantiq.h>
|
#include <linux/lantiq.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of_address.h>
|
|
||||||
#include <linux/of_irq.h>
|
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
#include <linux/serial.h>
|
#include <linux/serial.h>
|
||||||
#include <linux/serial_core.h>
|
#include <linux/serial_core.h>
|
||||||
|
@ -728,19 +726,23 @@ static struct uart_driver lqasc_reg = {
|
||||||
static int fetch_irq_lantiq(struct device *dev, struct ltq_uart_port *ltq_port)
|
static int fetch_irq_lantiq(struct device *dev, struct ltq_uart_port *ltq_port)
|
||||||
{
|
{
|
||||||
struct uart_port *port = <q_port->port;
|
struct uart_port *port = <q_port->port;
|
||||||
struct resource irqres[3];
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
int ret;
|
int irq;
|
||||||
|
|
||||||
ret = of_irq_to_resource_table(dev->of_node, irqres, 3);
|
irq = platform_get_irq(pdev, 0);
|
||||||
if (ret != 3) {
|
if (irq < 0)
|
||||||
dev_err(dev,
|
return irq;
|
||||||
"failed to get IRQs for serial port\n");
|
ltq_port->tx_irq = irq;
|
||||||
return -ENODEV;
|
irq = platform_get_irq(pdev, 1);
|
||||||
}
|
if (irq < 0)
|
||||||
ltq_port->tx_irq = irqres[0].start;
|
return irq;
|
||||||
ltq_port->rx_irq = irqres[1].start;
|
ltq_port->rx_irq = irq;
|
||||||
ltq_port->err_irq = irqres[2].start;
|
irq = platform_get_irq(pdev, 2);
|
||||||
port->irq = irqres[0].start;
|
if (irq < 0)
|
||||||
|
return irq;
|
||||||
|
ltq_port->err_irq = irq;
|
||||||
|
|
||||||
|
port->irq = ltq_port->tx_irq;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -793,7 +795,7 @@ static int fetch_irq_intel(struct device *dev, struct ltq_uart_port *ltq_port)
|
||||||
struct uart_port *port = <q_port->port;
|
struct uart_port *port = <q_port->port;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = of_irq_get(dev->of_node, 0);
|
ret = platform_get_irq(to_platform_device(dev), 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "failed to fetch IRQ for serial port\n");
|
dev_err(dev, "failed to fetch IRQ for serial port\n");
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -436,4 +436,4 @@ module_exit(liteuart_exit);
|
||||||
MODULE_AUTHOR("Antmicro <www.antmicro.com>");
|
MODULE_AUTHOR("Antmicro <www.antmicro.com>");
|
||||||
MODULE_DESCRIPTION("LiteUART serial driver");
|
MODULE_DESCRIPTION("LiteUART serial driver");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
MODULE_ALIAS("platform: liteuart");
|
MODULE_ALIAS("platform:liteuart");
|
||||||
|
|
|
@ -341,7 +341,7 @@ static irqreturn_t serial_lpc32xx_interrupt(int irq, void *dev_id)
|
||||||
LPC32XX_HSUART_IIR(port->membase));
|
LPC32XX_HSUART_IIR(port->membase));
|
||||||
port->icount.overrun++;
|
port->icount.overrun++;
|
||||||
tty_insert_flip_char(tport, 0, TTY_OVERRUN);
|
tty_insert_flip_char(tport, 0, TTY_OVERRUN);
|
||||||
tty_schedule_flip(tport);
|
tty_flip_buffer_push(tport);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Data received? */
|
/* Data received? */
|
||||||
|
|
|
@ -622,10 +622,7 @@ meson_serial_early_console_setup(struct earlycon_device *device, const char *opt
|
||||||
device->con->write = meson_serial_early_console_write;
|
device->con->write = meson_serial_early_console_write;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/* Legacy bindings, should be removed when no more used */
|
|
||||||
OF_EARLYCON_DECLARE(meson, "amlogic,meson-uart",
|
|
||||||
meson_serial_early_console_setup);
|
|
||||||
/* Stable bindings */
|
|
||||||
OF_EARLYCON_DECLARE(meson, "amlogic,meson-ao-uart",
|
OF_EARLYCON_DECLARE(meson, "amlogic,meson-ao-uart",
|
||||||
meson_serial_early_console_setup);
|
meson_serial_early_console_setup);
|
||||||
|
|
||||||
|
@ -668,25 +665,6 @@ static inline struct clk *meson_uart_probe_clock(struct device *dev,
|
||||||
return clk;
|
return clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* This function gets clocks in the legacy non-stable DT bindings.
|
|
||||||
* This code will be remove once all the platforms switch to the
|
|
||||||
* new DT bindings.
|
|
||||||
*/
|
|
||||||
static int meson_uart_probe_clocks_legacy(struct platform_device *pdev,
|
|
||||||
struct uart_port *port)
|
|
||||||
{
|
|
||||||
struct clk *clk = NULL;
|
|
||||||
|
|
||||||
clk = meson_uart_probe_clock(&pdev->dev, NULL);
|
|
||||||
if (IS_ERR(clk))
|
|
||||||
return PTR_ERR(clk);
|
|
||||||
|
|
||||||
port->uartclk = clk_get_rate(clk);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int meson_uart_probe_clocks(struct platform_device *pdev,
|
static int meson_uart_probe_clocks(struct platform_device *pdev,
|
||||||
struct uart_port *port)
|
struct uart_port *port)
|
||||||
{
|
{
|
||||||
|
@ -713,10 +691,11 @@ static int meson_uart_probe_clocks(struct platform_device *pdev,
|
||||||
|
|
||||||
static int meson_uart_probe(struct platform_device *pdev)
|
static int meson_uart_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct resource *res_mem, *res_irq;
|
struct resource *res_mem;
|
||||||
struct uart_port *port;
|
struct uart_port *port;
|
||||||
u32 fifosize = 64; /* Default is 64, 128 for EE UART_0 */
|
u32 fifosize = 64; /* Default is 64, 128 for EE UART_0 */
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
int irq;
|
||||||
|
|
||||||
if (pdev->dev.of_node)
|
if (pdev->dev.of_node)
|
||||||
pdev->id = of_alias_get_id(pdev->dev.of_node, "serial");
|
pdev->id = of_alias_get_id(pdev->dev.of_node, "serial");
|
||||||
|
@ -739,9 +718,9 @@ static int meson_uart_probe(struct platform_device *pdev)
|
||||||
if (!res_mem)
|
if (!res_mem)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
if (!res_irq)
|
if (irq < 0)
|
||||||
return -ENODEV;
|
return irq;
|
||||||
|
|
||||||
of_property_read_u32(pdev->dev.of_node, "fifo-size", &fifosize);
|
of_property_read_u32(pdev->dev.of_node, "fifo-size", &fifosize);
|
||||||
|
|
||||||
|
@ -754,19 +733,14 @@ static int meson_uart_probe(struct platform_device *pdev)
|
||||||
if (!port)
|
if (!port)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
/* Use legacy way until all platforms switch to new bindings */
|
|
||||||
if (of_device_is_compatible(pdev->dev.of_node, "amlogic,meson-uart"))
|
|
||||||
ret = meson_uart_probe_clocks_legacy(pdev, port);
|
|
||||||
else
|
|
||||||
ret = meson_uart_probe_clocks(pdev, port);
|
ret = meson_uart_probe_clocks(pdev, port);
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
port->iotype = UPIO_MEM;
|
port->iotype = UPIO_MEM;
|
||||||
port->mapbase = res_mem->start;
|
port->mapbase = res_mem->start;
|
||||||
port->mapsize = resource_size(res_mem);
|
port->mapsize = resource_size(res_mem);
|
||||||
port->irq = res_irq->start;
|
port->irq = irq;
|
||||||
port->flags = UPF_BOOT_AUTOCONF | UPF_LOW_LATENCY;
|
port->flags = UPF_BOOT_AUTOCONF | UPF_LOW_LATENCY;
|
||||||
port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_MESON_CONSOLE);
|
port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_MESON_CONSOLE);
|
||||||
port->dev = &pdev->dev;
|
port->dev = &pdev->dev;
|
||||||
|
@ -804,9 +778,6 @@ static int meson_uart_remove(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id meson_uart_dt_match[] = {
|
static const struct of_device_id meson_uart_dt_match[] = {
|
||||||
/* Legacy bindings, should be removed when no more used */
|
|
||||||
{ .compatible = "amlogic,meson-uart" },
|
|
||||||
/* Stable bindings */
|
|
||||||
{ .compatible = "amlogic,meson6-uart" },
|
{ .compatible = "amlogic,meson6-uart" },
|
||||||
{ .compatible = "amlogic,meson8-uart" },
|
{ .compatible = "amlogic,meson8-uart" },
|
||||||
{ .compatible = "amlogic,meson8b-uart" },
|
{ .compatible = "amlogic,meson8b-uart" },
|
||||||
|
|
|
@ -1702,17 +1702,21 @@ extern struct platform_device scc_a_pdev, scc_b_pdev;
|
||||||
|
|
||||||
static int __init pmz_init_port(struct uart_pmac_port *uap)
|
static int __init pmz_init_port(struct uart_pmac_port *uap)
|
||||||
{
|
{
|
||||||
struct resource *r_ports, *r_irq;
|
struct resource *r_ports;
|
||||||
|
int irq;
|
||||||
|
|
||||||
r_ports = platform_get_resource(uap->pdev, IORESOURCE_MEM, 0);
|
r_ports = platform_get_resource(uap->pdev, IORESOURCE_MEM, 0);
|
||||||
r_irq = platform_get_resource(uap->pdev, IORESOURCE_IRQ, 0);
|
if (!r_ports)
|
||||||
if (!r_ports || !r_irq)
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
irq = platform_get_irq(uap->pdev, 0);
|
||||||
|
if (irq < 0)
|
||||||
|
return irq;
|
||||||
|
|
||||||
uap->port.mapbase = r_ports->start;
|
uap->port.mapbase = r_ports->start;
|
||||||
uap->port.membase = (unsigned char __iomem *) r_ports->start;
|
uap->port.membase = (unsigned char __iomem *) r_ports->start;
|
||||||
uap->port.iotype = UPIO_MEM;
|
uap->port.iotype = UPIO_MEM;
|
||||||
uap->port.irq = r_irq->start;
|
uap->port.irq = irq;
|
||||||
uap->port.uartclk = ZS_CLOCK;
|
uap->port.uartclk = ZS_CLOCK;
|
||||||
uap->port.fifosize = 1;
|
uap->port.fifosize = 1;
|
||||||
uap->port.ops = &pmz_pops;
|
uap->port.ops = &pmz_pops;
|
||||||
|
|
|
@ -842,14 +842,18 @@ static int serial_pxa_probe_dt(struct platform_device *pdev,
|
||||||
static int serial_pxa_probe(struct platform_device *dev)
|
static int serial_pxa_probe(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
struct uart_pxa_port *sport;
|
struct uart_pxa_port *sport;
|
||||||
struct resource *mmres, *irqres;
|
struct resource *mmres;
|
||||||
int ret;
|
int ret;
|
||||||
|
int irq;
|
||||||
|
|
||||||
mmres = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
mmres = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
||||||
irqres = platform_get_resource(dev, IORESOURCE_IRQ, 0);
|
if (!mmres)
|
||||||
if (!mmres || !irqres)
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
irq = platform_get_irq(dev, 0);
|
||||||
|
if (irq < 0)
|
||||||
|
return irq;
|
||||||
|
|
||||||
sport = kzalloc(sizeof(struct uart_pxa_port), GFP_KERNEL);
|
sport = kzalloc(sizeof(struct uart_pxa_port), GFP_KERNEL);
|
||||||
if (!sport)
|
if (!sport)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -869,7 +873,7 @@ static int serial_pxa_probe(struct platform_device *dev)
|
||||||
sport->port.type = PORT_PXA;
|
sport->port.type = PORT_PXA;
|
||||||
sport->port.iotype = UPIO_MEM;
|
sport->port.iotype = UPIO_MEM;
|
||||||
sport->port.mapbase = mmres->start;
|
sport->port.mapbase = mmres->start;
|
||||||
sport->port.irq = irqres->start;
|
sport->port.irq = irq;
|
||||||
sport->port.fifosize = 64;
|
sport->port.fifosize = 64;
|
||||||
sport->port.ops = &serial_pxa_pops;
|
sport->port.ops = &serial_pxa_pops;
|
||||||
sport->port.dev = &dev->dev;
|
sport->port.dev = &dev->dev;
|
||||||
|
|
|
@ -65,7 +65,6 @@ enum s3c24xx_port_type {
|
||||||
struct s3c24xx_uart_info {
|
struct s3c24xx_uart_info {
|
||||||
char *name;
|
char *name;
|
||||||
enum s3c24xx_port_type type;
|
enum s3c24xx_port_type type;
|
||||||
bool has_usi;
|
|
||||||
unsigned int port_type;
|
unsigned int port_type;
|
||||||
unsigned int fifosize;
|
unsigned int fifosize;
|
||||||
unsigned long rx_fifomask;
|
unsigned long rx_fifomask;
|
||||||
|
@ -1357,28 +1356,6 @@ static int apple_s5l_serial_startup(struct uart_port *port)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void exynos_usi_init(struct uart_port *port)
|
|
||||||
{
|
|
||||||
struct s3c24xx_uart_port *ourport = to_ourport(port);
|
|
||||||
struct s3c24xx_uart_info *info = ourport->info;
|
|
||||||
unsigned int val;
|
|
||||||
|
|
||||||
if (!info->has_usi)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Clear the software reset of USI block (it's set at startup) */
|
|
||||||
val = rd_regl(port, USI_CON);
|
|
||||||
val &= ~USI_CON_RESET_MASK;
|
|
||||||
wr_regl(port, USI_CON, val);
|
|
||||||
udelay(1);
|
|
||||||
|
|
||||||
/* Continuously provide the clock to USI IP w/o gating (for Rx mode) */
|
|
||||||
val = rd_regl(port, USI_OPTION);
|
|
||||||
val &= ~USI_OPTION_HWACG_MASK;
|
|
||||||
val |= USI_OPTION_HWACG_CLKREQ_ON;
|
|
||||||
wr_regl(port, USI_OPTION, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* power power management control */
|
/* power power management control */
|
||||||
|
|
||||||
static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level,
|
static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level,
|
||||||
|
@ -1405,8 +1382,6 @@ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level,
|
||||||
|
|
||||||
if (!IS_ERR(ourport->baudclk))
|
if (!IS_ERR(ourport->baudclk))
|
||||||
clk_prepare_enable(ourport->baudclk);
|
clk_prepare_enable(ourport->baudclk);
|
||||||
|
|
||||||
exynos_usi_init(port);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_err(port->dev, "s3c24xx_serial: unknown pm %d\n", level);
|
dev_err(port->dev, "s3c24xx_serial: unknown pm %d\n", level);
|
||||||
|
@ -1740,15 +1715,21 @@ s3c24xx_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
|
||||||
|
|
||||||
static struct console s3c24xx_serial_console;
|
static struct console s3c24xx_serial_console;
|
||||||
|
|
||||||
static int __init s3c24xx_serial_console_init(void)
|
static void __init s3c24xx_serial_register_console(void)
|
||||||
{
|
{
|
||||||
register_console(&s3c24xx_serial_console);
|
register_console(&s3c24xx_serial_console);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
console_initcall(s3c24xx_serial_console_init);
|
|
||||||
|
static void s3c24xx_serial_unregister_console(void)
|
||||||
|
{
|
||||||
|
if (s3c24xx_serial_console.flags & CON_ENABLED)
|
||||||
|
unregister_console(&s3c24xx_serial_console);
|
||||||
|
}
|
||||||
|
|
||||||
#define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console
|
#define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console
|
||||||
#else
|
#else
|
||||||
|
static inline void s3c24xx_serial_register_console(void) { }
|
||||||
|
static inline void s3c24xx_serial_unregister_console(void) { }
|
||||||
#define S3C24XX_SERIAL_CONSOLE NULL
|
#define S3C24XX_SERIAL_CONSOLE NULL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -2130,8 +2111,6 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
|
||||||
if (ret)
|
if (ret)
|
||||||
pr_warn("uart: failed to enable baudclk\n");
|
pr_warn("uart: failed to enable baudclk\n");
|
||||||
|
|
||||||
exynos_usi_init(port);
|
|
||||||
|
|
||||||
/* Keep all interrupts masked and cleared */
|
/* Keep all interrupts masked and cleared */
|
||||||
switch (ourport->info->type) {
|
switch (ourport->info->type) {
|
||||||
case TYPE_S3C6400:
|
case TYPE_S3C6400:
|
||||||
|
@ -2521,7 +2500,8 @@ s3c24xx_serial_console_write(struct console *co, const char *s,
|
||||||
uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar);
|
uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __init
|
/* Shouldn't be __init, as it can be instantiated from other module */
|
||||||
|
static void
|
||||||
s3c24xx_serial_get_options(struct uart_port *port, int *baud,
|
s3c24xx_serial_get_options(struct uart_port *port, int *baud,
|
||||||
int *parity, int *bits)
|
int *parity, int *bits)
|
||||||
{
|
{
|
||||||
|
@ -2584,7 +2564,8 @@ s3c24xx_serial_get_options(struct uart_port *port, int *baud,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init
|
/* Shouldn't be __init, as it can be instantiated from other module */
|
||||||
|
static int
|
||||||
s3c24xx_serial_console_setup(struct console *co, char *options)
|
s3c24xx_serial_console_setup(struct console *co, char *options)
|
||||||
{
|
{
|
||||||
struct uart_port *port;
|
struct uart_port *port;
|
||||||
|
@ -2780,11 +2761,10 @@ static struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(CONFIG_ARCH_EXYNOS)
|
#if defined(CONFIG_ARCH_EXYNOS)
|
||||||
#define EXYNOS_COMMON_SERIAL_DRV_DATA(_has_usi) \
|
#define EXYNOS_COMMON_SERIAL_DRV_DATA() \
|
||||||
.info = &(struct s3c24xx_uart_info) { \
|
.info = &(struct s3c24xx_uart_info) { \
|
||||||
.name = "Samsung Exynos UART", \
|
.name = "Samsung Exynos UART", \
|
||||||
.type = TYPE_S3C6400, \
|
.type = TYPE_S3C6400, \
|
||||||
.has_usi = _has_usi, \
|
|
||||||
.port_type = PORT_S3C6400, \
|
.port_type = PORT_S3C6400, \
|
||||||
.has_divslot = 1, \
|
.has_divslot = 1, \
|
||||||
.rx_fifomask = S5PV210_UFSTAT_RXMASK, \
|
.rx_fifomask = S5PV210_UFSTAT_RXMASK, \
|
||||||
|
@ -2805,17 +2785,17 @@ static struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = {
|
||||||
} \
|
} \
|
||||||
|
|
||||||
static struct s3c24xx_serial_drv_data exynos4210_serial_drv_data = {
|
static struct s3c24xx_serial_drv_data exynos4210_serial_drv_data = {
|
||||||
EXYNOS_COMMON_SERIAL_DRV_DATA(false),
|
EXYNOS_COMMON_SERIAL_DRV_DATA(),
|
||||||
.fifosize = { 256, 64, 16, 16 },
|
.fifosize = { 256, 64, 16, 16 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct s3c24xx_serial_drv_data exynos5433_serial_drv_data = {
|
static struct s3c24xx_serial_drv_data exynos5433_serial_drv_data = {
|
||||||
EXYNOS_COMMON_SERIAL_DRV_DATA(false),
|
EXYNOS_COMMON_SERIAL_DRV_DATA(),
|
||||||
.fifosize = { 64, 256, 16, 256 },
|
.fifosize = { 64, 256, 16, 256 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct s3c24xx_serial_drv_data exynos850_serial_drv_data = {
|
static struct s3c24xx_serial_drv_data exynos850_serial_drv_data = {
|
||||||
EXYNOS_COMMON_SERIAL_DRV_DATA(true),
|
EXYNOS_COMMON_SERIAL_DRV_DATA(),
|
||||||
.fifosize = { 256, 64, 64, 64 },
|
.fifosize = { 256, 64, 64, 64 },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2926,7 +2906,29 @@ static struct platform_driver samsung_serial_driver = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
module_platform_driver(samsung_serial_driver);
|
static int __init samsung_serial_init(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
s3c24xx_serial_register_console();
|
||||||
|
|
||||||
|
ret = platform_driver_register(&samsung_serial_driver);
|
||||||
|
if (ret) {
|
||||||
|
s3c24xx_serial_unregister_console();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit samsung_serial_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&samsung_serial_driver);
|
||||||
|
s3c24xx_serial_unregister_console();
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(samsung_serial_init);
|
||||||
|
module_exit(samsung_serial_exit);
|
||||||
|
|
||||||
#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE
|
#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -162,7 +162,7 @@ static void uart_port_dtr_rts(struct uart_port *uport, int raise)
|
||||||
int RTS_after_send = !!(uport->rs485.flags & SER_RS485_RTS_AFTER_SEND);
|
int RTS_after_send = !!(uport->rs485.flags & SER_RS485_RTS_AFTER_SEND);
|
||||||
|
|
||||||
if (raise) {
|
if (raise) {
|
||||||
if (rs485_on && !RTS_after_send) {
|
if (rs485_on && RTS_after_send) {
|
||||||
uart_set_mctrl(uport, TIOCM_DTR);
|
uart_set_mctrl(uport, TIOCM_DTR);
|
||||||
uart_clear_mctrl(uport, TIOCM_RTS);
|
uart_clear_mctrl(uport, TIOCM_RTS);
|
||||||
} else {
|
} else {
|
||||||
|
@ -171,7 +171,7 @@ static void uart_port_dtr_rts(struct uart_port *uport, int raise)
|
||||||
} else {
|
} else {
|
||||||
unsigned int clear = TIOCM_DTR;
|
unsigned int clear = TIOCM_DTR;
|
||||||
|
|
||||||
clear |= (!rs485_on || !RTS_after_send) ? TIOCM_RTS : 0;
|
clear |= (!rs485_on || RTS_after_send) ? TIOCM_RTS : 0;
|
||||||
uart_clear_mctrl(uport, clear);
|
uart_clear_mctrl(uport, clear);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1701,17 +1701,13 @@ static void uart_port_shutdown(struct tty_port *port)
|
||||||
*/
|
*/
|
||||||
wake_up_interruptible(&port->delta_msr_wait);
|
wake_up_interruptible(&port->delta_msr_wait);
|
||||||
|
|
||||||
/*
|
if (uport) {
|
||||||
* Free the IRQ and disable the port.
|
/* Free the IRQ and disable the port. */
|
||||||
*/
|
|
||||||
if (uport)
|
|
||||||
uport->ops->shutdown(uport);
|
uport->ops->shutdown(uport);
|
||||||
|
|
||||||
/*
|
/* Ensure that the IRQ handler isn't running on another CPU. */
|
||||||
* Ensure that the IRQ handler isn't running on another CPU.
|
|
||||||
*/
|
|
||||||
if (uport)
|
|
||||||
synchronize_irq(uport->irq);
|
synchronize_irq(uport->irq);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int uart_carrier_raised(struct tty_port *port)
|
static int uart_carrier_raised(struct tty_port *port)
|
||||||
|
@ -2393,7 +2389,8 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state,
|
||||||
* We probably don't need a spinlock around this, but
|
* We probably don't need a spinlock around this, but
|
||||||
*/
|
*/
|
||||||
spin_lock_irqsave(&port->lock, flags);
|
spin_lock_irqsave(&port->lock, flags);
|
||||||
port->ops->set_mctrl(port, port->mctrl & TIOCM_DTR);
|
port->mctrl &= TIOCM_DTR;
|
||||||
|
port->ops->set_mctrl(port, port->mctrl);
|
||||||
spin_unlock_irqrestore(&port->lock, flags);
|
spin_unlock_irqrestore(&port->lock, flags);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/reset.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
#include <linux/serial.h>
|
#include <linux/serial.h>
|
||||||
#include <linux/serial_sci.h>
|
#include <linux/serial_sci.h>
|
||||||
|
@ -895,11 +896,9 @@ static void sci_receive_chars(struct uart_port *port)
|
||||||
if (status & SCxSR_FER(port)) {
|
if (status & SCxSR_FER(port)) {
|
||||||
flag = TTY_FRAME;
|
flag = TTY_FRAME;
|
||||||
port->icount.frame++;
|
port->icount.frame++;
|
||||||
dev_notice(port->dev, "frame error\n");
|
|
||||||
} else if (status & SCxSR_PER(port)) {
|
} else if (status & SCxSR_PER(port)) {
|
||||||
flag = TTY_PARITY;
|
flag = TTY_PARITY;
|
||||||
port->icount.parity++;
|
port->icount.parity++;
|
||||||
dev_notice(port->dev, "parity error\n");
|
|
||||||
} else
|
} else
|
||||||
flag = TTY_NORMAL;
|
flag = TTY_NORMAL;
|
||||||
|
|
||||||
|
@ -939,8 +938,6 @@ static int sci_handle_errors(struct uart_port *port)
|
||||||
/* overrun error */
|
/* overrun error */
|
||||||
if (tty_insert_flip_char(tport, 0, TTY_OVERRUN))
|
if (tty_insert_flip_char(tport, 0, TTY_OVERRUN))
|
||||||
copied++;
|
copied++;
|
||||||
|
|
||||||
dev_notice(port->dev, "overrun error\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status & SCxSR_FER(port)) {
|
if (status & SCxSR_FER(port)) {
|
||||||
|
@ -949,8 +946,6 @@ static int sci_handle_errors(struct uart_port *port)
|
||||||
|
|
||||||
if (tty_insert_flip_char(tport, 0, TTY_FRAME))
|
if (tty_insert_flip_char(tport, 0, TTY_FRAME))
|
||||||
copied++;
|
copied++;
|
||||||
|
|
||||||
dev_notice(port->dev, "frame error\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status & SCxSR_PER(port)) {
|
if (status & SCxSR_PER(port)) {
|
||||||
|
@ -959,8 +954,6 @@ static int sci_handle_errors(struct uart_port *port)
|
||||||
|
|
||||||
if (tty_insert_flip_char(tport, 0, TTY_PARITY))
|
if (tty_insert_flip_char(tport, 0, TTY_PARITY))
|
||||||
copied++;
|
copied++;
|
||||||
|
|
||||||
dev_notice(port->dev, "parity error\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (copied)
|
if (copied)
|
||||||
|
@ -990,8 +983,6 @@ static int sci_handle_fifo_overrun(struct uart_port *port)
|
||||||
|
|
||||||
tty_insert_flip_char(tport, 0, TTY_OVERRUN);
|
tty_insert_flip_char(tport, 0, TTY_OVERRUN);
|
||||||
tty_flip_buffer_push(tport);
|
tty_flip_buffer_push(tport);
|
||||||
|
|
||||||
dev_dbg(port->dev, "overrun error\n");
|
|
||||||
copied++;
|
copied++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1013,8 +1004,6 @@ static int sci_handle_breaks(struct uart_port *port)
|
||||||
/* Notify of BREAK */
|
/* Notify of BREAK */
|
||||||
if (tty_insert_flip_char(tport, 0, TTY_BREAK))
|
if (tty_insert_flip_char(tport, 0, TTY_BREAK))
|
||||||
copied++;
|
copied++;
|
||||||
|
|
||||||
dev_dbg(port->dev, "BREAK detected\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (copied)
|
if (copied)
|
||||||
|
@ -2778,44 +2767,29 @@ static int sci_init_clocks(struct sci_port *sci_port, struct device *dev)
|
||||||
clk_names[SCI_SCK] = "hsck";
|
clk_names[SCI_SCK] = "hsck";
|
||||||
|
|
||||||
for (i = 0; i < SCI_NUM_CLKS; i++) {
|
for (i = 0; i < SCI_NUM_CLKS; i++) {
|
||||||
clk = devm_clk_get(dev, clk_names[i]);
|
clk = devm_clk_get_optional(dev, clk_names[i]);
|
||||||
if (PTR_ERR(clk) == -EPROBE_DEFER)
|
if (IS_ERR(clk))
|
||||||
return -EPROBE_DEFER;
|
return PTR_ERR(clk);
|
||||||
|
|
||||||
if (IS_ERR(clk) && i == SCI_FCK) {
|
|
||||||
/*
|
|
||||||
* "fck" used to be called "sci_ick", and we need to
|
|
||||||
* maintain DT backward compatibility.
|
|
||||||
*/
|
|
||||||
clk = devm_clk_get(dev, "sci_ick");
|
|
||||||
if (PTR_ERR(clk) == -EPROBE_DEFER)
|
|
||||||
return -EPROBE_DEFER;
|
|
||||||
|
|
||||||
if (!IS_ERR(clk))
|
|
||||||
goto found;
|
|
||||||
|
|
||||||
|
if (!clk && i == SCI_FCK) {
|
||||||
/*
|
/*
|
||||||
* Not all SH platforms declare a clock lookup entry
|
* Not all SH platforms declare a clock lookup entry
|
||||||
* for SCI devices, in which case we need to get the
|
* for SCI devices, in which case we need to get the
|
||||||
* global "peripheral_clk" clock.
|
* global "peripheral_clk" clock.
|
||||||
*/
|
*/
|
||||||
clk = devm_clk_get(dev, "peripheral_clk");
|
clk = devm_clk_get(dev, "peripheral_clk");
|
||||||
if (!IS_ERR(clk))
|
if (IS_ERR(clk))
|
||||||
goto found;
|
return dev_err_probe(dev, PTR_ERR(clk),
|
||||||
|
"failed to get %s\n",
|
||||||
dev_err(dev, "failed to get %s (%ld)\n", clk_names[i],
|
clk_names[i]);
|
||||||
PTR_ERR(clk));
|
|
||||||
return PTR_ERR(clk);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
found:
|
if (!clk)
|
||||||
if (IS_ERR(clk))
|
dev_dbg(dev, "failed to get %s\n", clk_names[i]);
|
||||||
dev_dbg(dev, "failed to get %s (%ld)\n", clk_names[i],
|
|
||||||
PTR_ERR(clk));
|
|
||||||
else
|
else
|
||||||
dev_dbg(dev, "clk %s is %pC rate %lu\n", clk_names[i],
|
dev_dbg(dev, "clk %s is %pC rate %lu\n", clk_names[i],
|
||||||
clk, clk_get_rate(clk));
|
clk, clk_get_rate(clk));
|
||||||
sci_port->clks[i] = IS_ERR(clk) ? NULL : clk;
|
sci_port->clks[i] = clk;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -3180,6 +3154,9 @@ static const struct of_device_id of_sci_match[] = {
|
||||||
}, {
|
}, {
|
||||||
.compatible = "renesas,rcar-gen3-scif",
|
.compatible = "renesas,rcar-gen3-scif",
|
||||||
.data = SCI_OF_DATA(PORT_SCIF, SCIx_SH4_SCIF_BRG_REGTYPE),
|
.data = SCI_OF_DATA(PORT_SCIF, SCIx_SH4_SCIF_BRG_REGTYPE),
|
||||||
|
}, {
|
||||||
|
.compatible = "renesas,rcar-gen4-scif",
|
||||||
|
.data = SCI_OF_DATA(PORT_SCIF, SCIx_SH4_SCIF_BRG_REGTYPE),
|
||||||
},
|
},
|
||||||
/* Generic types */
|
/* Generic types */
|
||||||
{
|
{
|
||||||
|
@ -3203,23 +3180,47 @@ static const struct of_device_id of_sci_match[] = {
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, of_sci_match);
|
MODULE_DEVICE_TABLE(of, of_sci_match);
|
||||||
|
|
||||||
|
static void sci_reset_control_assert(void *data)
|
||||||
|
{
|
||||||
|
reset_control_assert(data);
|
||||||
|
}
|
||||||
|
|
||||||
static struct plat_sci_port *sci_parse_dt(struct platform_device *pdev,
|
static struct plat_sci_port *sci_parse_dt(struct platform_device *pdev,
|
||||||
unsigned int *dev_id)
|
unsigned int *dev_id)
|
||||||
{
|
{
|
||||||
struct device_node *np = pdev->dev.of_node;
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
struct reset_control *rstc;
|
||||||
struct plat_sci_port *p;
|
struct plat_sci_port *p;
|
||||||
struct sci_port *sp;
|
struct sci_port *sp;
|
||||||
const void *data;
|
const void *data;
|
||||||
int id;
|
int id, ret;
|
||||||
|
|
||||||
if (!IS_ENABLED(CONFIG_OF) || !np)
|
if (!IS_ENABLED(CONFIG_OF) || !np)
|
||||||
return NULL;
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
data = of_device_get_match_data(&pdev->dev);
|
data = of_device_get_match_data(&pdev->dev);
|
||||||
|
|
||||||
|
rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
|
||||||
|
if (IS_ERR(rstc))
|
||||||
|
return ERR_PTR(dev_err_probe(&pdev->dev, PTR_ERR(rstc),
|
||||||
|
"failed to get reset ctrl\n"));
|
||||||
|
|
||||||
|
ret = reset_control_deassert(rstc);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "failed to deassert reset %d\n", ret);
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = devm_add_action_or_reset(&pdev->dev, sci_reset_control_assert, rstc);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "failed to register assert devm action, %d\n",
|
||||||
|
ret);
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
}
|
||||||
|
|
||||||
p = devm_kzalloc(&pdev->dev, sizeof(struct plat_sci_port), GFP_KERNEL);
|
p = devm_kzalloc(&pdev->dev, sizeof(struct plat_sci_port), GFP_KERNEL);
|
||||||
if (!p)
|
if (!p)
|
||||||
return NULL;
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
/* Get the line number from the aliases node. */
|
/* Get the line number from the aliases node. */
|
||||||
id = of_alias_get_id(np, "serial");
|
id = of_alias_get_id(np, "serial");
|
||||||
|
@ -3227,11 +3228,11 @@ static struct plat_sci_port *sci_parse_dt(struct platform_device *pdev,
|
||||||
id = ffz(sci_ports_in_use);
|
id = ffz(sci_ports_in_use);
|
||||||
if (id < 0) {
|
if (id < 0) {
|
||||||
dev_err(&pdev->dev, "failed to get alias id (%d)\n", id);
|
dev_err(&pdev->dev, "failed to get alias id (%d)\n", id);
|
||||||
return NULL;
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
if (id >= ARRAY_SIZE(sci_ports)) {
|
if (id >= ARRAY_SIZE(sci_ports)) {
|
||||||
dev_err(&pdev->dev, "serial%d out of range\n", id);
|
dev_err(&pdev->dev, "serial%d out of range\n", id);
|
||||||
return NULL;
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
sp = &sci_ports[id];
|
sp = &sci_ports[id];
|
||||||
|
@ -3318,8 +3319,8 @@ static int sci_probe(struct platform_device *dev)
|
||||||
|
|
||||||
if (dev->dev.of_node) {
|
if (dev->dev.of_node) {
|
||||||
p = sci_parse_dt(dev, &dev_id);
|
p = sci_parse_dt(dev, &dev_id);
|
||||||
if (p == NULL)
|
if (IS_ERR(p))
|
||||||
return -EINVAL;
|
return PTR_ERR(p);
|
||||||
} else {
|
} else {
|
||||||
p = dev->dev.platform_data;
|
p = dev->dev.platform_data;
|
||||||
if (p == NULL) {
|
if (p == NULL) {
|
||||||
|
|
|
@ -365,6 +365,31 @@ static unsigned int stm32_usart_receive_chars(struct uart_port *port, bool force
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void stm32_usart_tx_dma_terminate(struct stm32_port *stm32_port)
|
||||||
|
{
|
||||||
|
dmaengine_terminate_async(stm32_port->tx_ch);
|
||||||
|
stm32_port->tx_dma_busy = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool stm32_usart_tx_dma_started(struct stm32_port *stm32_port)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We cannot use the function "dmaengine_tx_status" to know the
|
||||||
|
* status of DMA. This function does not show if the "dma complete"
|
||||||
|
* callback of the DMA transaction has been called. So we prefer
|
||||||
|
* to use "tx_dma_busy" flag to prevent dual DMA transaction at the
|
||||||
|
* same time.
|
||||||
|
*/
|
||||||
|
return stm32_port->tx_dma_busy;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool stm32_usart_tx_dma_enabled(struct stm32_port *stm32_port)
|
||||||
|
{
|
||||||
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
||||||
|
|
||||||
|
return !!(readl_relaxed(stm32_port->port.membase + ofs->cr3) & USART_CR3_DMAT);
|
||||||
|
}
|
||||||
|
|
||||||
static void stm32_usart_tx_dma_complete(void *arg)
|
static void stm32_usart_tx_dma_complete(void *arg)
|
||||||
{
|
{
|
||||||
struct uart_port *port = arg;
|
struct uart_port *port = arg;
|
||||||
|
@ -372,9 +397,8 @@ static void stm32_usart_tx_dma_complete(void *arg)
|
||||||
const struct stm32_usart_offsets *ofs = &stm32port->info->ofs;
|
const struct stm32_usart_offsets *ofs = &stm32port->info->ofs;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
dmaengine_terminate_async(stm32port->tx_ch);
|
|
||||||
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
|
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
|
||||||
stm32port->tx_dma_busy = false;
|
stm32_usart_tx_dma_terminate(stm32port);
|
||||||
|
|
||||||
/* Let's see if we have pending data to send */
|
/* Let's see if we have pending data to send */
|
||||||
spin_lock_irqsave(&port->lock, flags);
|
spin_lock_irqsave(&port->lock, flags);
|
||||||
|
@ -428,10 +452,8 @@ static void stm32_usart_transmit_chars_pio(struct uart_port *port)
|
||||||
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
||||||
struct circ_buf *xmit = &port->state->xmit;
|
struct circ_buf *xmit = &port->state->xmit;
|
||||||
|
|
||||||
if (stm32_port->tx_dma_busy) {
|
if (stm32_usart_tx_dma_enabled(stm32_port))
|
||||||
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
|
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
|
||||||
stm32_port->tx_dma_busy = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!uart_circ_empty(xmit)) {
|
while (!uart_circ_empty(xmit)) {
|
||||||
/* Check that TDR is empty before filling FIFO */
|
/* Check that TDR is empty before filling FIFO */
|
||||||
|
@ -455,12 +477,13 @@ static void stm32_usart_transmit_chars_dma(struct uart_port *port)
|
||||||
const struct stm32_usart_offsets *ofs = &stm32port->info->ofs;
|
const struct stm32_usart_offsets *ofs = &stm32port->info->ofs;
|
||||||
struct circ_buf *xmit = &port->state->xmit;
|
struct circ_buf *xmit = &port->state->xmit;
|
||||||
struct dma_async_tx_descriptor *desc = NULL;
|
struct dma_async_tx_descriptor *desc = NULL;
|
||||||
unsigned int count, i;
|
unsigned int count;
|
||||||
|
|
||||||
if (stm32port->tx_dma_busy)
|
if (stm32_usart_tx_dma_started(stm32port)) {
|
||||||
|
if (!stm32_usart_tx_dma_enabled(stm32port))
|
||||||
|
stm32_usart_set_bits(port, ofs->cr3, USART_CR3_DMAT);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
stm32port->tx_dma_busy = true;
|
|
||||||
|
|
||||||
count = uart_circ_chars_pending(xmit);
|
count = uart_circ_chars_pending(xmit);
|
||||||
|
|
||||||
|
@ -491,13 +514,21 @@ static void stm32_usart_transmit_chars_dma(struct uart_port *port)
|
||||||
if (!desc)
|
if (!desc)
|
||||||
goto fallback_err;
|
goto fallback_err;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set "tx_dma_busy" flag. This flag will be released when
|
||||||
|
* dmaengine_terminate_async will be called. This flag helps
|
||||||
|
* transmit_chars_dma not to start another DMA transaction
|
||||||
|
* if the callback of the previous is not yet called.
|
||||||
|
*/
|
||||||
|
stm32port->tx_dma_busy = true;
|
||||||
|
|
||||||
desc->callback = stm32_usart_tx_dma_complete;
|
desc->callback = stm32_usart_tx_dma_complete;
|
||||||
desc->callback_param = port;
|
desc->callback_param = port;
|
||||||
|
|
||||||
/* Push current DMA TX transaction in the pending queue */
|
/* Push current DMA TX transaction in the pending queue */
|
||||||
if (dma_submit_error(dmaengine_submit(desc))) {
|
if (dma_submit_error(dmaengine_submit(desc))) {
|
||||||
/* dma no yet started, safe to free resources */
|
/* dma no yet started, safe to free resources */
|
||||||
dmaengine_terminate_async(stm32port->tx_ch);
|
stm32_usart_tx_dma_terminate(stm32port);
|
||||||
goto fallback_err;
|
goto fallback_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,7 +542,6 @@ static void stm32_usart_transmit_chars_dma(struct uart_port *port)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fallback_err:
|
fallback_err:
|
||||||
for (i = count; i > 0; i--)
|
|
||||||
stm32_usart_transmit_chars_pio(port);
|
stm32_usart_transmit_chars_pio(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -522,12 +552,13 @@ static void stm32_usart_transmit_chars(struct uart_port *port)
|
||||||
struct circ_buf *xmit = &port->state->xmit;
|
struct circ_buf *xmit = &port->state->xmit;
|
||||||
|
|
||||||
if (port->x_char) {
|
if (port->x_char) {
|
||||||
if (stm32_port->tx_dma_busy)
|
if (stm32_usart_tx_dma_started(stm32_port) &&
|
||||||
|
stm32_usart_tx_dma_enabled(stm32_port))
|
||||||
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
|
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
|
||||||
writel_relaxed(port->x_char, port->membase + ofs->tdr);
|
writel_relaxed(port->x_char, port->membase + ofs->tdr);
|
||||||
port->x_char = 0;
|
port->x_char = 0;
|
||||||
port->icount.tx++;
|
port->icount.tx++;
|
||||||
if (stm32_port->tx_dma_busy)
|
if (stm32_usart_tx_dma_started(stm32_port))
|
||||||
stm32_usart_set_bits(port, ofs->cr3, USART_CR3_DMAT);
|
stm32_usart_set_bits(port, ofs->cr3, USART_CR3_DMAT);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -675,8 +706,11 @@ static void stm32_usart_stop_tx(struct uart_port *port)
|
||||||
{
|
{
|
||||||
struct stm32_port *stm32_port = to_stm32_port(port);
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
||||||
struct serial_rs485 *rs485conf = &port->rs485;
|
struct serial_rs485 *rs485conf = &port->rs485;
|
||||||
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
||||||
|
|
||||||
stm32_usart_tx_interrupt_disable(port);
|
stm32_usart_tx_interrupt_disable(port);
|
||||||
|
if (stm32_usart_tx_dma_started(stm32_port) && stm32_usart_tx_dma_enabled(stm32_port))
|
||||||
|
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
|
||||||
|
|
||||||
if (rs485conf->flags & SER_RS485_ENABLED) {
|
if (rs485conf->flags & SER_RS485_ENABLED) {
|
||||||
if (rs485conf->flags & SER_RS485_RTS_ON_SEND) {
|
if (rs485conf->flags & SER_RS485_RTS_ON_SEND) {
|
||||||
|
@ -719,9 +753,8 @@ static void stm32_usart_flush_buffer(struct uart_port *port)
|
||||||
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
||||||
|
|
||||||
if (stm32_port->tx_ch) {
|
if (stm32_port->tx_ch) {
|
||||||
dmaengine_terminate_async(stm32_port->tx_ch);
|
stm32_usart_tx_dma_terminate(stm32_port);
|
||||||
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
|
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
|
||||||
stm32_port->tx_dma_busy = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -883,6 +916,12 @@ static void stm32_usart_shutdown(struct uart_port *port)
|
||||||
u32 val, isr;
|
u32 val, isr;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (stm32_usart_tx_dma_enabled(stm32_port))
|
||||||
|
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
|
||||||
|
|
||||||
|
if (stm32_usart_tx_dma_started(stm32_port))
|
||||||
|
stm32_usart_tx_dma_terminate(stm32_port);
|
||||||
|
|
||||||
/* Disable modem control interrupts */
|
/* Disable modem control interrupts */
|
||||||
stm32_usart_disable_ms(port);
|
stm32_usart_disable_ms(port);
|
||||||
|
|
||||||
|
@ -1419,8 +1458,6 @@ static int stm32_usart_of_dma_tx_probe(struct stm32_port *stm32port,
|
||||||
struct dma_slave_config config;
|
struct dma_slave_config config;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
stm32port->tx_dma_busy = false;
|
|
||||||
|
|
||||||
stm32port->tx_buf = dma_alloc_coherent(dev, TX_BUF_L,
|
stm32port->tx_buf = dma_alloc_coherent(dev, TX_BUF_L,
|
||||||
&stm32port->tx_dma_buf,
|
&stm32port->tx_dma_buf,
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
|
@ -1570,7 +1607,6 @@ static int stm32_usart_serial_remove(struct platform_device *pdev)
|
||||||
writel_relaxed(cr3, port->membase + ofs->cr3);
|
writel_relaxed(cr3, port->membase + ofs->cr3);
|
||||||
|
|
||||||
if (stm32_port->tx_ch) {
|
if (stm32_port->tx_ch) {
|
||||||
dmaengine_terminate_async(stm32_port->tx_ch);
|
|
||||||
stm32_usart_of_dma_tx_remove(stm32_port, pdev);
|
stm32_usart_of_dma_tx_remove(stm32_port, pdev);
|
||||||
dma_release_channel(stm32_port->tx_ch);
|
dma_release_channel(stm32_port->tx_ch);
|
||||||
}
|
}
|
||||||
|
|
|
@ -264,7 +264,7 @@ struct stm32_port {
|
||||||
u32 cr1_irq; /* USART_CR1_RXNEIE or RTOIE */
|
u32 cr1_irq; /* USART_CR1_RXNEIE or RTOIE */
|
||||||
u32 cr3_irq; /* USART_CR3_RXFTIE */
|
u32 cr3_irq; /* USART_CR3_RXFTIE */
|
||||||
int last_res;
|
int last_res;
|
||||||
bool tx_dma_busy; /* dma tx busy */
|
bool tx_dma_busy; /* dma tx transaction in progress */
|
||||||
bool throttled; /* port throttled */
|
bool throttled; /* port throttled */
|
||||||
bool hw_flow_control;
|
bool hw_flow_control;
|
||||||
bool swap; /* swap RX & TX pins */
|
bool swap; /* swap RX & TX pins */
|
||||||
|
|
|
@ -127,7 +127,8 @@ static void serial_out(struct uart_sunsu_port *up, int offset, int value)
|
||||||
* gate outputs a logical one. Since we use level triggered interrupts
|
* gate outputs a logical one. Since we use level triggered interrupts
|
||||||
* we have lockup and watchdog reset. We cannot mask IRQ because
|
* we have lockup and watchdog reset. We cannot mask IRQ because
|
||||||
* keyboard shares IRQ with us (Word has it as Bob Smelik's design).
|
* keyboard shares IRQ with us (Word has it as Bob Smelik's design).
|
||||||
* This problem is similar to what Alpha people suffer, see serial.c.
|
* This problem is similar to what Alpha people suffer, see
|
||||||
|
* 8250_alpha.c.
|
||||||
*/
|
*/
|
||||||
if (offset == UART_MCR)
|
if (offset == UART_MCR)
|
||||||
value |= UART_MCR_OUT2;
|
value |= UART_MCR_OUT2;
|
||||||
|
|
|
@ -626,7 +626,7 @@ static struct uart_driver ulite_uart_driver = {
|
||||||
*
|
*
|
||||||
* Returns: 0 on success, <0 otherwise
|
* Returns: 0 on success, <0 otherwise
|
||||||
*/
|
*/
|
||||||
static int ulite_assign(struct device *dev, int id, u32 base, int irq,
|
static int ulite_assign(struct device *dev, int id, phys_addr_t base, int irq,
|
||||||
struct uartlite_data *pdata)
|
struct uartlite_data *pdata)
|
||||||
{
|
{
|
||||||
struct uart_port *port;
|
struct uart_port *port;
|
||||||
|
|
|
@ -621,21 +621,25 @@ static const struct of_device_id wmt_dt_ids[] = {
|
||||||
static int vt8500_serial_probe(struct platform_device *pdev)
|
static int vt8500_serial_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct vt8500_port *vt8500_port;
|
struct vt8500_port *vt8500_port;
|
||||||
struct resource *mmres, *irqres;
|
struct resource *mmres;
|
||||||
struct device_node *np = pdev->dev.of_node;
|
struct device_node *np = pdev->dev.of_node;
|
||||||
const unsigned int *flags;
|
const unsigned int *flags;
|
||||||
int ret;
|
int ret;
|
||||||
int port;
|
int port;
|
||||||
|
int irq;
|
||||||
|
|
||||||
flags = of_device_get_match_data(&pdev->dev);
|
flags = of_device_get_match_data(&pdev->dev);
|
||||||
if (!flags)
|
if (!flags)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
if (!mmres)
|
||||||
if (!mmres || !irqres)
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
if (irq < 0)
|
||||||
|
return irq;
|
||||||
|
|
||||||
if (np) {
|
if (np) {
|
||||||
port = of_alias_get_id(np, "serial");
|
port = of_alias_get_id(np, "serial");
|
||||||
if (port >= VT8500_MAX_PORTS)
|
if (port >= VT8500_MAX_PORTS)
|
||||||
|
@ -688,7 +692,7 @@ static int vt8500_serial_probe(struct platform_device *pdev)
|
||||||
vt8500_port->uart.type = PORT_VT8500;
|
vt8500_port->uart.type = PORT_VT8500;
|
||||||
vt8500_port->uart.iotype = UPIO_MEM;
|
vt8500_port->uart.iotype = UPIO_MEM;
|
||||||
vt8500_port->uart.mapbase = mmres->start;
|
vt8500_port->uart.mapbase = mmres->start;
|
||||||
vt8500_port->uart.irq = irqres->start;
|
vt8500_port->uart.irq = irq;
|
||||||
vt8500_port->uart.fifosize = 16;
|
vt8500_port->uart.fifosize = 16;
|
||||||
vt8500_port->uart.ops = &vt8500_uart_pops;
|
vt8500_port->uart.ops = &vt8500_uart_pops;
|
||||||
vt8500_port->uart.line = port;
|
vt8500_port->uart.line = port;
|
||||||
|
|
|
@ -40,19 +40,14 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tty_buffer_lock_exclusive - gain exclusive access to buffer
|
* tty_buffer_lock_exclusive - gain exclusive access to buffer
|
||||||
* tty_buffer_unlock_exclusive - release exclusive access
|
|
||||||
*
|
|
||||||
* @port: tty port owning the flip buffer
|
* @port: tty port owning the flip buffer
|
||||||
*
|
*
|
||||||
* Guarantees safe use of the line discipline's receive_buf() method by
|
* Guarantees safe use of the &tty_ldisc_ops.receive_buf() method by excluding
|
||||||
* excluding the buffer work and any pending flush from using the flip
|
* the buffer work and any pending flush from using the flip buffer. Data can
|
||||||
* buffer. Data can continue to be added concurrently to the flip buffer
|
* continue to be added concurrently to the flip buffer from the driver side.
|
||||||
* from the driver side.
|
|
||||||
*
|
*
|
||||||
* On release, the buffer work is restarted if there is data in the
|
* See also tty_buffer_unlock_exclusive().
|
||||||
* flip buffer
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void tty_buffer_lock_exclusive(struct tty_port *port)
|
void tty_buffer_lock_exclusive(struct tty_port *port)
|
||||||
{
|
{
|
||||||
struct tty_bufhead *buf = &port->buf;
|
struct tty_bufhead *buf = &port->buf;
|
||||||
|
@ -62,6 +57,14 @@ void tty_buffer_lock_exclusive(struct tty_port *port)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(tty_buffer_lock_exclusive);
|
EXPORT_SYMBOL_GPL(tty_buffer_lock_exclusive);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tty_buffer_unlock_exclusive - release exclusive access
|
||||||
|
* @port: tty port owning the flip buffer
|
||||||
|
*
|
||||||
|
* The buffer work is restarted if there is data in the flip buffer.
|
||||||
|
*
|
||||||
|
* See also tty_buffer_lock_exclusive().
|
||||||
|
*/
|
||||||
void tty_buffer_unlock_exclusive(struct tty_port *port)
|
void tty_buffer_unlock_exclusive(struct tty_port *port)
|
||||||
{
|
{
|
||||||
struct tty_bufhead *buf = &port->buf;
|
struct tty_bufhead *buf = &port->buf;
|
||||||
|
@ -80,14 +83,13 @@ EXPORT_SYMBOL_GPL(tty_buffer_unlock_exclusive);
|
||||||
* tty_buffer_space_avail - return unused buffer space
|
* tty_buffer_space_avail - return unused buffer space
|
||||||
* @port: tty port owning the flip buffer
|
* @port: tty port owning the flip buffer
|
||||||
*
|
*
|
||||||
* Returns the # of bytes which can be written by the driver without
|
* Returns: the # of bytes which can be written by the driver without reaching
|
||||||
* reaching the buffer limit.
|
* the buffer limit.
|
||||||
*
|
*
|
||||||
* Note: this does not guarantee that memory is available to write
|
* Note: this does not guarantee that memory is available to write the returned
|
||||||
* the returned # of bytes (use tty_prepare_flip_string_xxx() to
|
* # of bytes (use tty_prepare_flip_string() to pre-allocate if memory
|
||||||
* pre-allocate if memory guarantee is required).
|
* guarantee is required).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
unsigned int tty_buffer_space_avail(struct tty_port *port)
|
unsigned int tty_buffer_space_avail(struct tty_port *port)
|
||||||
{
|
{
|
||||||
int space = port->buf.mem_limit - atomic_read(&port->buf.mem_used);
|
int space = port->buf.mem_limit - atomic_read(&port->buf.mem_used);
|
||||||
|
@ -110,10 +112,9 @@ static void tty_buffer_reset(struct tty_buffer *p, size_t size)
|
||||||
* tty_buffer_free_all - free buffers used by a tty
|
* tty_buffer_free_all - free buffers used by a tty
|
||||||
* @port: tty port to free from
|
* @port: tty port to free from
|
||||||
*
|
*
|
||||||
* Remove all the buffers pending on a tty whether queued with data
|
* Remove all the buffers pending on a tty whether queued with data or in the
|
||||||
* or in the free ring. Must be called when the tty is no longer in use
|
* free ring. Must be called when the tty is no longer in use.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void tty_buffer_free_all(struct tty_port *port)
|
void tty_buffer_free_all(struct tty_port *port)
|
||||||
{
|
{
|
||||||
struct tty_bufhead *buf = &port->buf;
|
struct tty_bufhead *buf = &port->buf;
|
||||||
|
@ -146,13 +147,13 @@ void tty_buffer_free_all(struct tty_port *port)
|
||||||
* @port: tty port
|
* @port: tty port
|
||||||
* @size: desired size (characters)
|
* @size: desired size (characters)
|
||||||
*
|
*
|
||||||
* Allocate a new tty buffer to hold the desired number of characters.
|
* Allocate a new tty buffer to hold the desired number of characters. We
|
||||||
* We round our buffers off in 256 character chunks to get better
|
* round our buffers off in 256 character chunks to get better allocation
|
||||||
* allocation behaviour.
|
* behaviour.
|
||||||
* Return NULL if out of memory or the allocation would exceed the
|
*
|
||||||
* per device queue
|
* Returns: %NULL if out of memory or the allocation would exceed the per
|
||||||
|
* device queue.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size)
|
static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size)
|
||||||
{
|
{
|
||||||
struct llist_node *free;
|
struct llist_node *free;
|
||||||
|
@ -189,10 +190,9 @@ found:
|
||||||
* @port: tty port owning the buffer
|
* @port: tty port owning the buffer
|
||||||
* @b: the buffer to free
|
* @b: the buffer to free
|
||||||
*
|
*
|
||||||
* Free a tty buffer, or add it to the free list according to our
|
* Free a tty buffer, or add it to the free list according to our internal
|
||||||
* internal strategy
|
* strategy.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b)
|
static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b)
|
||||||
{
|
{
|
||||||
struct tty_bufhead *buf = &port->buf;
|
struct tty_bufhead *buf = &port->buf;
|
||||||
|
@ -211,13 +211,11 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b)
|
||||||
* @tty: tty to flush
|
* @tty: tty to flush
|
||||||
* @ld: optional ldisc ptr (must be referenced)
|
* @ld: optional ldisc ptr (must be referenced)
|
||||||
*
|
*
|
||||||
* flush all the buffers containing receive data. If ld != NULL,
|
* Flush all the buffers containing receive data. If @ld != %NULL, flush the
|
||||||
* flush the ldisc input buffer.
|
* ldisc input buffer.
|
||||||
*
|
*
|
||||||
* Locking: takes buffer lock to ensure single-threaded flip buffer
|
* Locking: takes buffer lock to ensure single-threaded flip buffer 'consumer'.
|
||||||
* 'consumer'
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld)
|
void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld)
|
||||||
{
|
{
|
||||||
struct tty_port *port = tty->port;
|
struct tty_port *port = tty->port;
|
||||||
|
@ -249,12 +247,13 @@ void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld)
|
||||||
* @size: size desired
|
* @size: size desired
|
||||||
* @flags: buffer flags if new buffer allocated (default = 0)
|
* @flags: buffer flags if new buffer allocated (default = 0)
|
||||||
*
|
*
|
||||||
* Make at least size bytes of linear space available for the tty
|
* Make at least @size bytes of linear space available for the tty buffer.
|
||||||
* buffer. If we fail return the size we managed to find.
|
|
||||||
*
|
*
|
||||||
* Will change over to a new buffer if the current buffer is encoded as
|
* Will change over to a new buffer if the current buffer is encoded as
|
||||||
* TTY_NORMAL (so has no flags buffer) and the new buffer requires
|
* %TTY_NORMAL (so has no flags buffer) and the new buffer requires a flags
|
||||||
* a flags buffer.
|
* buffer.
|
||||||
|
*
|
||||||
|
* Returns: the size we managed to find.
|
||||||
*/
|
*/
|
||||||
static int __tty_buffer_request_room(struct tty_port *port, size_t size,
|
static int __tty_buffer_request_room(struct tty_port *port, size_t size,
|
||||||
int flags)
|
int flags)
|
||||||
|
@ -300,16 +299,17 @@ int tty_buffer_request_room(struct tty_port *port, size_t size)
|
||||||
EXPORT_SYMBOL_GPL(tty_buffer_request_room);
|
EXPORT_SYMBOL_GPL(tty_buffer_request_room);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tty_insert_flip_string_fixed_flag - Add characters to the tty buffer
|
* tty_insert_flip_string_fixed_flag - add characters to the tty buffer
|
||||||
* @port: tty port
|
* @port: tty port
|
||||||
* @chars: characters
|
* @chars: characters
|
||||||
* @flag: flag value for each character
|
* @flag: flag value for each character
|
||||||
* @size: size
|
* @size: size
|
||||||
*
|
*
|
||||||
* Queue a series of bytes to the tty buffering. All the characters
|
* Queue a series of bytes to the tty buffering. All the characters passed are
|
||||||
* passed are marked with the supplied flag. Returns the number added.
|
* marked with the supplied flag.
|
||||||
|
*
|
||||||
|
* Returns: the number added.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int tty_insert_flip_string_fixed_flag(struct tty_port *port,
|
int tty_insert_flip_string_fixed_flag(struct tty_port *port,
|
||||||
const unsigned char *chars, char flag, size_t size)
|
const unsigned char *chars, char flag, size_t size)
|
||||||
{
|
{
|
||||||
|
@ -338,17 +338,17 @@ int tty_insert_flip_string_fixed_flag(struct tty_port *port,
|
||||||
EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag);
|
EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tty_insert_flip_string_flags - Add characters to the tty buffer
|
* tty_insert_flip_string_flags - add characters to the tty buffer
|
||||||
* @port: tty port
|
* @port: tty port
|
||||||
* @chars: characters
|
* @chars: characters
|
||||||
* @flags: flag bytes
|
* @flags: flag bytes
|
||||||
* @size: size
|
* @size: size
|
||||||
*
|
*
|
||||||
* Queue a series of bytes to the tty buffering. For each character
|
* Queue a series of bytes to the tty buffering. For each character the flags
|
||||||
* the flags array indicates the status of the character. Returns the
|
* array indicates the status of the character.
|
||||||
* number added.
|
*
|
||||||
|
* Returns: the number added.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int tty_insert_flip_string_flags(struct tty_port *port,
|
int tty_insert_flip_string_flags(struct tty_port *port,
|
||||||
const unsigned char *chars, const char *flags, size_t size)
|
const unsigned char *chars, const char *flags, size_t size)
|
||||||
{
|
{
|
||||||
|
@ -376,13 +376,13 @@ int tty_insert_flip_string_flags(struct tty_port *port,
|
||||||
EXPORT_SYMBOL(tty_insert_flip_string_flags);
|
EXPORT_SYMBOL(tty_insert_flip_string_flags);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __tty_insert_flip_char - Add one character to the tty buffer
|
* __tty_insert_flip_char - add one character to the tty buffer
|
||||||
* @port: tty port
|
* @port: tty port
|
||||||
* @ch: character
|
* @ch: character
|
||||||
* @flag: flag byte
|
* @flag: flag byte
|
||||||
*
|
*
|
||||||
* Queue a single byte to the tty buffering, with an optional flag.
|
* Queue a single byte @ch to the tty buffering, with an optional flag. This is
|
||||||
* This is the slow path of tty_insert_flip_char.
|
* the slow path of tty_insert_flip_char().
|
||||||
*/
|
*/
|
||||||
int __tty_insert_flip_char(struct tty_port *port, unsigned char ch, char flag)
|
int __tty_insert_flip_char(struct tty_port *port, unsigned char ch, char flag)
|
||||||
{
|
{
|
||||||
|
@ -401,40 +401,20 @@ int __tty_insert_flip_char(struct tty_port *port, unsigned char ch, char flag)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(__tty_insert_flip_char);
|
EXPORT_SYMBOL(__tty_insert_flip_char);
|
||||||
|
|
||||||
/**
|
|
||||||
* tty_schedule_flip - push characters to ldisc
|
|
||||||
* @port: tty port to push from
|
|
||||||
*
|
|
||||||
* Takes any pending buffers and transfers their ownership to the
|
|
||||||
* ldisc side of the queue. It then schedules those characters for
|
|
||||||
* processing by the line discipline.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void tty_schedule_flip(struct tty_port *port)
|
|
||||||
{
|
|
||||||
struct tty_bufhead *buf = &port->buf;
|
|
||||||
|
|
||||||
/* paired w/ acquire in flush_to_ldisc(); ensures
|
|
||||||
* flush_to_ldisc() sees buffer data.
|
|
||||||
*/
|
|
||||||
smp_store_release(&buf->tail->commit, buf->tail->used);
|
|
||||||
queue_work(system_unbound_wq, &buf->work);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(tty_schedule_flip);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tty_prepare_flip_string - make room for characters
|
* tty_prepare_flip_string - make room for characters
|
||||||
* @port: tty port
|
* @port: tty port
|
||||||
* @chars: return pointer for character write area
|
* @chars: return pointer for character write area
|
||||||
* @size: desired size
|
* @size: desired size
|
||||||
*
|
*
|
||||||
* Prepare a block of space in the buffer for data. Returns the length
|
* Prepare a block of space in the buffer for data.
|
||||||
* available and buffer pointer to the space which is now allocated and
|
*
|
||||||
* accounted for as ready for normal characters. This is used for drivers
|
* This is used for drivers that need their own block copy routines into the
|
||||||
* that need their own block copy routines into the buffer. There is no
|
* buffer. There is no guarantee the buffer is a DMA target!
|
||||||
* guarantee the buffer is a DMA target!
|
*
|
||||||
|
* Returns: the length available and buffer pointer (@chars) to the space which
|
||||||
|
* is now allocated and accounted for as ready for normal characters.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars,
|
int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars,
|
||||||
size_t size)
|
size_t size)
|
||||||
{
|
{
|
||||||
|
@ -456,13 +436,13 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string);
|
||||||
* tty_ldisc_receive_buf - forward data to line discipline
|
* tty_ldisc_receive_buf - forward data to line discipline
|
||||||
* @ld: line discipline to process input
|
* @ld: line discipline to process input
|
||||||
* @p: char buffer
|
* @p: char buffer
|
||||||
* @f: TTY_* flags buffer
|
* @f: %TTY_NORMAL, %TTY_BREAK, etc. flags buffer
|
||||||
* @count: number of bytes to process
|
* @count: number of bytes to process
|
||||||
*
|
*
|
||||||
* Callers other than flush_to_ldisc() need to exclude the kworker
|
* Callers other than flush_to_ldisc() need to exclude the kworker from
|
||||||
* from concurrent use of the line discipline, see paste_selection().
|
* concurrent use of the line discipline, see paste_selection().
|
||||||
*
|
*
|
||||||
* Returns the number of bytes processed
|
* Returns: the number of bytes processed.
|
||||||
*/
|
*/
|
||||||
int tty_ldisc_receive_buf(struct tty_ldisc *ld, const unsigned char *p,
|
int tty_ldisc_receive_buf(struct tty_ldisc *ld, const unsigned char *p,
|
||||||
const char *f, int count)
|
const char *f, int count)
|
||||||
|
@ -495,18 +475,16 @@ receive_buf(struct tty_port *port, struct tty_buffer *head, int count)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* flush_to_ldisc
|
* flush_to_ldisc - flush data from buffer to ldisc
|
||||||
* @work: tty structure passed from work queue.
|
* @work: tty structure passed from work queue.
|
||||||
*
|
*
|
||||||
* This routine is called out of the software interrupt to flush data
|
* This routine is called out of the software interrupt to flush data from the
|
||||||
* from the buffer chain to the line discipline.
|
* buffer chain to the line discipline.
|
||||||
*
|
*
|
||||||
* The receive_buf method is single threaded for each tty instance.
|
* The receive_buf() method is single threaded for each tty instance.
|
||||||
*
|
*
|
||||||
* Locking: takes buffer lock to ensure single-threaded flip buffer
|
* Locking: takes buffer lock to ensure single-threaded flip buffer 'consumer'.
|
||||||
* 'consumer'
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void flush_to_ldisc(struct work_struct *work)
|
static void flush_to_ldisc(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct tty_port *port = container_of(work, struct tty_port, buf.work);
|
struct tty_port *port = container_of(work, struct tty_port, buf.work);
|
||||||
|
@ -554,19 +532,25 @@ static void flush_to_ldisc(struct work_struct *work)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tty_flip_buffer_push - terminal
|
* tty_flip_buffer_push - push terminal buffers
|
||||||
* @port: tty port to push
|
* @port: tty port to push
|
||||||
*
|
*
|
||||||
* Queue a push of the terminal flip buffers to the line discipline.
|
* Queue a push of the terminal flip buffers to the line discipline. Can be
|
||||||
* Can be called from IRQ/atomic context.
|
* called from IRQ/atomic context.
|
||||||
*
|
*
|
||||||
* In the event of the queue being busy for flipping the work will be
|
* In the event of the queue being busy for flipping the work will be held off
|
||||||
* held off and retried later.
|
* and retried later.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void tty_flip_buffer_push(struct tty_port *port)
|
void tty_flip_buffer_push(struct tty_port *port)
|
||||||
{
|
{
|
||||||
tty_schedule_flip(port);
|
struct tty_bufhead *buf = &port->buf;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Paired w/ acquire in flush_to_ldisc(); ensures flush_to_ldisc() sees
|
||||||
|
* buffer data.
|
||||||
|
*/
|
||||||
|
smp_store_release(&buf->tail->commit, buf->tail->used);
|
||||||
|
queue_work(system_unbound_wq, &buf->work);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(tty_flip_buffer_push);
|
EXPORT_SYMBOL(tty_flip_buffer_push);
|
||||||
|
|
||||||
|
@ -574,10 +558,9 @@ EXPORT_SYMBOL(tty_flip_buffer_push);
|
||||||
* tty_buffer_init - prepare a tty buffer structure
|
* tty_buffer_init - prepare a tty buffer structure
|
||||||
* @port: tty port to initialise
|
* @port: tty port to initialise
|
||||||
*
|
*
|
||||||
* Set up the initial state of the buffer management for a tty device.
|
* Set up the initial state of the buffer management for a tty device. Must be
|
||||||
* Must be called before the other tty buffer functions are used.
|
* called before the other tty buffer functions are used.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void tty_buffer_init(struct tty_port *port)
|
void tty_buffer_init(struct tty_port *port)
|
||||||
{
|
{
|
||||||
struct tty_bufhead *buf = &port->buf;
|
struct tty_bufhead *buf = &port->buf;
|
||||||
|
@ -599,9 +582,9 @@ void tty_buffer_init(struct tty_port *port)
|
||||||
* @limit: memory limit to set
|
* @limit: memory limit to set
|
||||||
*
|
*
|
||||||
* Change the tty buffer memory limit.
|
* Change the tty buffer memory limit.
|
||||||
|
*
|
||||||
* Must be called before the other tty buffer functions are used.
|
* Must be called before the other tty buffer functions are used.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int tty_buffer_set_limit(struct tty_port *port, int limit)
|
int tty_buffer_set_limit(struct tty_port *port, int limit)
|
||||||
{
|
{
|
||||||
if (limit < MIN_TTYB_SIZE)
|
if (limit < MIN_TTYB_SIZE)
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -50,14 +50,11 @@ static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS];
|
||||||
* tty_register_ldisc - install a line discipline
|
* tty_register_ldisc - install a line discipline
|
||||||
* @new_ldisc: pointer to the ldisc object
|
* @new_ldisc: pointer to the ldisc object
|
||||||
*
|
*
|
||||||
* Installs a new line discipline into the kernel. The discipline
|
* Installs a new line discipline into the kernel. The discipline is set up as
|
||||||
* is set up as unreferenced and then made available to the kernel
|
* unreferenced and then made available to the kernel from this point onwards.
|
||||||
* from this point onwards.
|
|
||||||
*
|
*
|
||||||
* Locking:
|
* Locking: takes %tty_ldiscs_lock to guard against ldisc races
|
||||||
* takes tty_ldiscs_lock to guard against ldisc races
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int tty_register_ldisc(struct tty_ldisc_ops *new_ldisc)
|
int tty_register_ldisc(struct tty_ldisc_ops *new_ldisc)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
@ -78,11 +75,10 @@ EXPORT_SYMBOL(tty_register_ldisc);
|
||||||
* tty_unregister_ldisc - unload a line discipline
|
* tty_unregister_ldisc - unload a line discipline
|
||||||
* @ldisc: ldisc number
|
* @ldisc: ldisc number
|
||||||
*
|
*
|
||||||
* Remove a line discipline from the kernel providing it is not
|
* Remove a line discipline from the kernel providing it is not currently in
|
||||||
* currently in use.
|
* use.
|
||||||
*
|
*
|
||||||
* Locking:
|
* Locking: takes %tty_ldiscs_lock to guard against ldisc races
|
||||||
* takes tty_ldiscs_lock to guard against ldisc races
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void tty_unregister_ldisc(struct tty_ldisc_ops *ldisc)
|
void tty_unregister_ldisc(struct tty_ldisc_ops *ldisc)
|
||||||
|
@ -122,27 +118,25 @@ static void put_ldops(struct tty_ldisc_ops *ldops)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tty_ldisc_autoload = IS_BUILTIN(CONFIG_LDISC_AUTOLOAD);
|
static int tty_ldisc_autoload = IS_BUILTIN(CONFIG_LDISC_AUTOLOAD);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tty_ldisc_get - take a reference to an ldisc
|
* tty_ldisc_get - take a reference to an ldisc
|
||||||
* @tty: tty device
|
* @tty: tty device
|
||||||
* @disc: ldisc number
|
* @disc: ldisc number
|
||||||
*
|
*
|
||||||
* Takes a reference to a line discipline. Deals with refcounts and
|
* Takes a reference to a line discipline. Deals with refcounts and module
|
||||||
* module locking counts.
|
* locking counts. If the discipline is not available, its module loaded, if
|
||||||
|
* possible.
|
||||||
*
|
*
|
||||||
* Returns: -EINVAL if the discipline index is not [N_TTY..NR_LDISCS] or
|
* Returns:
|
||||||
* if the discipline is not registered
|
* * -%EINVAL if the discipline index is not [%N_TTY .. %NR_LDISCS] or if the
|
||||||
* -EAGAIN if request_module() failed to load or register the
|
* discipline is not registered
|
||||||
* discipline
|
* * -%EAGAIN if request_module() failed to load or register the discipline
|
||||||
* -ENOMEM if allocation failure
|
* * -%ENOMEM if allocation failure
|
||||||
|
* * Otherwise, returns a pointer to the discipline and bumps the ref count
|
||||||
*
|
*
|
||||||
* Otherwise, returns a pointer to the discipline and bumps the
|
* Locking: takes %tty_ldiscs_lock to guard against ldisc races
|
||||||
* ref count
|
|
||||||
*
|
|
||||||
* Locking:
|
|
||||||
* takes tty_ldiscs_lock to guard against ldisc races
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc)
|
static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc)
|
||||||
{
|
{
|
||||||
struct tty_ldisc *ld;
|
struct tty_ldisc *ld;
|
||||||
|
@ -176,8 +170,9 @@ static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc)
|
||||||
return ld;
|
return ld;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* tty_ldisc_put - release the ldisc
|
* tty_ldisc_put - release the ldisc
|
||||||
|
* @ld: lisdsc to release
|
||||||
*
|
*
|
||||||
* Complement of tty_ldisc_get().
|
* Complement of tty_ldisc_get().
|
||||||
*/
|
*/
|
||||||
|
@ -229,22 +224,19 @@ const struct seq_operations tty_ldiscs_seq_ops = {
|
||||||
* tty_ldisc_ref_wait - wait for the tty ldisc
|
* tty_ldisc_ref_wait - wait for the tty ldisc
|
||||||
* @tty: tty device
|
* @tty: tty device
|
||||||
*
|
*
|
||||||
* Dereference the line discipline for the terminal and take a
|
* Dereference the line discipline for the terminal and take a reference to it.
|
||||||
* reference to it. If the line discipline is in flux then
|
* If the line discipline is in flux then wait patiently until it changes.
|
||||||
* wait patiently until it changes.
|
|
||||||
*
|
*
|
||||||
* Returns: NULL if the tty has been hungup and not re-opened with
|
* Returns: %NULL if the tty has been hungup and not re-opened with a new file
|
||||||
* a new file descriptor, otherwise valid ldisc reference
|
* descriptor, otherwise valid ldisc reference
|
||||||
*
|
*
|
||||||
* Note 1: Must not be called from an IRQ/timer context. The caller
|
* Note 1: Must not be called from an IRQ/timer context. The caller must also
|
||||||
* must also be careful not to hold other locks that will deadlock
|
* be careful not to hold other locks that will deadlock against a discipline
|
||||||
* against a discipline change, such as an existing ldisc reference
|
* change, such as an existing ldisc reference (which we check for).
|
||||||
* (which we check for)
|
|
||||||
*
|
*
|
||||||
* Note 2: a file_operations routine (read/poll/write) should use this
|
* Note 2: a file_operations routine (read/poll/write) should use this function
|
||||||
* function to wait for any ldisc lifetime events to finish.
|
* to wait for any ldisc lifetime events to finish.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
|
struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct tty_ldisc *ld;
|
struct tty_ldisc *ld;
|
||||||
|
@ -261,11 +253,10 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
|
||||||
* tty_ldisc_ref - get the tty ldisc
|
* tty_ldisc_ref - get the tty ldisc
|
||||||
* @tty: tty device
|
* @tty: tty device
|
||||||
*
|
*
|
||||||
* Dereference the line discipline for the terminal and take a
|
* Dereference the line discipline for the terminal and take a reference to it.
|
||||||
* reference to it. If the line discipline is in flux then
|
* If the line discipline is in flux then return %NULL. Can be called from IRQ
|
||||||
* return NULL. Can be called from IRQ and timer functions.
|
* and timer functions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty)
|
struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct tty_ldisc *ld = NULL;
|
struct tty_ldisc *ld = NULL;
|
||||||
|
@ -283,10 +274,9 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref);
|
||||||
* tty_ldisc_deref - free a tty ldisc reference
|
* tty_ldisc_deref - free a tty ldisc reference
|
||||||
* @ld: reference to free up
|
* @ld: reference to free up
|
||||||
*
|
*
|
||||||
* Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May
|
* Undoes the effect of tty_ldisc_ref() or tty_ldisc_ref_wait(). May be called
|
||||||
* be called in IRQ context.
|
* in IRQ context.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void tty_ldisc_deref(struct tty_ldisc *ld)
|
void tty_ldisc_deref(struct tty_ldisc *ld)
|
||||||
{
|
{
|
||||||
ldsem_up_read(&ld->tty->ldisc_sem);
|
ldsem_up_read(&ld->tty->ldisc_sem);
|
||||||
|
@ -387,12 +377,11 @@ static void tty_ldisc_unlock_pair(struct tty_struct *tty,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tty_ldisc_flush - flush line discipline queue
|
* tty_ldisc_flush - flush line discipline queue
|
||||||
* @tty: tty
|
* @tty: tty to flush ldisc for
|
||||||
*
|
*
|
||||||
* Flush the line discipline queue (if any) and the tty flip buffers
|
* Flush the line discipline queue (if any) and the tty flip buffers for this
|
||||||
* for this tty.
|
* @tty.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void tty_ldisc_flush(struct tty_struct *tty)
|
void tty_ldisc_flush(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct tty_ldisc *ld = tty_ldisc_ref(tty);
|
struct tty_ldisc *ld = tty_ldisc_ref(tty);
|
||||||
|
@ -408,17 +397,14 @@ EXPORT_SYMBOL_GPL(tty_ldisc_flush);
|
||||||
* @tty: tty structure
|
* @tty: tty structure
|
||||||
* @disc: line discipline number
|
* @disc: line discipline number
|
||||||
*
|
*
|
||||||
* This is probably overkill for real world processors but
|
* This is probably overkill for real world processors but they are not on hot
|
||||||
* they are not on hot paths so a little discipline won't do
|
* paths so a little discipline won't do any harm.
|
||||||
* any harm.
|
|
||||||
*
|
*
|
||||||
* The line discipline-related tty_struct fields are reset to
|
* The line discipline-related tty_struct fields are reset to prevent the ldisc
|
||||||
* prevent the ldisc driver from re-using stale information for
|
* driver from re-using stale information for the new ldisc instance.
|
||||||
* the new ldisc instance.
|
|
||||||
*
|
*
|
||||||
* Locking: takes termios_rwsem
|
* Locking: takes termios_rwsem
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void tty_set_termios_ldisc(struct tty_struct *tty, int disc)
|
static void tty_set_termios_ldisc(struct tty_struct *tty, int disc)
|
||||||
{
|
{
|
||||||
down_write(&tty->termios_rwsem);
|
down_write(&tty->termios_rwsem);
|
||||||
|
@ -434,12 +420,10 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int disc)
|
||||||
* @tty: tty we are opening the ldisc on
|
* @tty: tty we are opening the ldisc on
|
||||||
* @ld: discipline to open
|
* @ld: discipline to open
|
||||||
*
|
*
|
||||||
* A helper opening method. Also a convenient debugging and check
|
* A helper opening method. Also a convenient debugging and check point.
|
||||||
* point.
|
|
||||||
*
|
*
|
||||||
* Locking: always called with BTM already held.
|
* Locking: always called with BTM already held.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
|
static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
|
||||||
{
|
{
|
||||||
WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags));
|
WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags));
|
||||||
|
@ -461,10 +445,8 @@ static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
|
||||||
* @tty: tty we are opening the ldisc on
|
* @tty: tty we are opening the ldisc on
|
||||||
* @ld: discipline to close
|
* @ld: discipline to close
|
||||||
*
|
*
|
||||||
* A helper close method. Also a convenient debugging and check
|
* A helper close method. Also a convenient debugging and check point.
|
||||||
* point.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
|
static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
|
||||||
{
|
{
|
||||||
lockdep_assert_held_write(&tty->ldisc_sem);
|
lockdep_assert_held_write(&tty->ldisc_sem);
|
||||||
|
@ -480,10 +462,9 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
|
||||||
* @tty: tty to open the ldisc on
|
* @tty: tty to open the ldisc on
|
||||||
* @ld: ldisc we are trying to fail back to
|
* @ld: ldisc we are trying to fail back to
|
||||||
*
|
*
|
||||||
* Helper to try and recover a tty when switching back to the old
|
* Helper to try and recover a tty when switching back to the old ldisc fails
|
||||||
* ldisc fails and we need something attached.
|
* and we need something attached.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int tty_ldisc_failto(struct tty_struct *tty, int ld)
|
static int tty_ldisc_failto(struct tty_struct *tty, int ld)
|
||||||
{
|
{
|
||||||
struct tty_ldisc *disc = tty_ldisc_get(tty, ld);
|
struct tty_ldisc *disc = tty_ldisc_get(tty, ld);
|
||||||
|
@ -505,10 +486,9 @@ static int tty_ldisc_failto(struct tty_struct *tty, int ld)
|
||||||
* @tty: tty to recover
|
* @tty: tty to recover
|
||||||
* @old: previous ldisc
|
* @old: previous ldisc
|
||||||
*
|
*
|
||||||
* Restore the previous line discipline or N_TTY when a line discipline
|
* Restore the previous line discipline or %N_TTY when a line discipline change
|
||||||
* change fails due to an open error
|
* fails due to an open error
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
|
static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
|
||||||
{
|
{
|
||||||
/* There is an outstanding reference here so this is safe */
|
/* There is an outstanding reference here so this is safe */
|
||||||
|
@ -532,12 +512,11 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
|
||||||
* @tty: the terminal to set
|
* @tty: the terminal to set
|
||||||
* @disc: the line discipline number
|
* @disc: the line discipline number
|
||||||
*
|
*
|
||||||
* Set the discipline of a tty line. Must be called from a process
|
* Set the discipline of a tty line. Must be called from a process context. The
|
||||||
* context. The ldisc change logic has to protect itself against any
|
* ldisc change logic has to protect itself against any overlapping ldisc
|
||||||
* overlapping ldisc change (including on the other end of pty pairs),
|
* change (including on the other end of pty pairs), the close of one side of a
|
||||||
* the close of one side of a tty/pty pair, and eventually hangup.
|
* tty/pty pair, and eventually hangup.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int tty_set_ldisc(struct tty_struct *tty, int disc)
|
int tty_set_ldisc(struct tty_struct *tty, int disc)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
|
@ -616,7 +595,7 @@ EXPORT_SYMBOL_GPL(tty_set_ldisc);
|
||||||
* tty_ldisc_kill - teardown ldisc
|
* tty_ldisc_kill - teardown ldisc
|
||||||
* @tty: tty being released
|
* @tty: tty being released
|
||||||
*
|
*
|
||||||
* Perform final close of the ldisc and reset tty->ldisc
|
* Perform final close of the ldisc and reset @tty->ldisc
|
||||||
*/
|
*/
|
||||||
static void tty_ldisc_kill(struct tty_struct *tty)
|
static void tty_ldisc_kill(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
|
@ -638,7 +617,6 @@ static void tty_ldisc_kill(struct tty_struct *tty)
|
||||||
*
|
*
|
||||||
* Restore a terminal to the driver default state.
|
* Restore a terminal to the driver default state.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void tty_reset_termios(struct tty_struct *tty)
|
static void tty_reset_termios(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
down_write(&tty->termios_rwsem);
|
down_write(&tty->termios_rwsem);
|
||||||
|
@ -654,15 +632,13 @@ static void tty_reset_termios(struct tty_struct *tty)
|
||||||
* @tty: tty to reinit
|
* @tty: tty to reinit
|
||||||
* @disc: line discipline to reinitialize
|
* @disc: line discipline to reinitialize
|
||||||
*
|
*
|
||||||
* Completely reinitialize the line discipline state, by closing the
|
* Completely reinitialize the line discipline state, by closing the current
|
||||||
* current instance, if there is one, and opening a new instance. If
|
* instance, if there is one, and opening a new instance. If an error occurs
|
||||||
* an error occurs opening the new non-N_TTY instance, the instance
|
* opening the new non-%N_TTY instance, the instance is dropped and @tty->ldisc
|
||||||
* is dropped and tty->ldisc reset to NULL. The caller can then retry
|
* reset to %NULL. The caller can then retry with %N_TTY instead.
|
||||||
* with N_TTY instead.
|
|
||||||
*
|
*
|
||||||
* Returns 0 if successful, otherwise error code < 0
|
* Returns: 0 if successful, otherwise error code < 0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int tty_ldisc_reinit(struct tty_struct *tty, int disc)
|
int tty_ldisc_reinit(struct tty_struct *tty, int disc)
|
||||||
{
|
{
|
||||||
struct tty_ldisc *ld;
|
struct tty_ldisc *ld;
|
||||||
|
@ -696,17 +672,16 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc)
|
||||||
* @tty: tty being hung up
|
* @tty: tty being hung up
|
||||||
* @reinit: whether to re-initialise the tty
|
* @reinit: whether to re-initialise the tty
|
||||||
*
|
*
|
||||||
* Some tty devices reset their termios when they receive a hangup
|
* Some tty devices reset their termios when they receive a hangup event. In
|
||||||
* event. In that situation we must also switch back to N_TTY properly
|
* that situation we must also switch back to %N_TTY properly before we reset
|
||||||
* before we reset the termios data.
|
* the termios data.
|
||||||
*
|
*
|
||||||
* Locking: We can take the ldisc mutex as the rest of the code is
|
* Locking: We can take the ldisc mutex as the rest of the code is careful to
|
||||||
* careful to allow for this.
|
* allow for this.
|
||||||
*
|
*
|
||||||
* In the pty pair case this occurs in the close() path of the
|
* In the pty pair case this occurs in the close() path of the tty itself so we
|
||||||
* tty itself so we must be careful about locking rules.
|
* must be careful about locking rules.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void tty_ldisc_hangup(struct tty_struct *tty, bool reinit)
|
void tty_ldisc_hangup(struct tty_struct *tty, bool reinit)
|
||||||
{
|
{
|
||||||
struct tty_ldisc *ld;
|
struct tty_ldisc *ld;
|
||||||
|
@ -756,11 +731,10 @@ void tty_ldisc_hangup(struct tty_struct *tty, bool reinit)
|
||||||
* @tty: tty being shut down
|
* @tty: tty being shut down
|
||||||
* @o_tty: pair tty for pty/tty pairs
|
* @o_tty: pair tty for pty/tty pairs
|
||||||
*
|
*
|
||||||
* Called during the initial open of a tty/pty pair in order to set up the
|
* Called during the initial open of a tty/pty pair in order to set up the line
|
||||||
* line disciplines and bind them to the tty. This has no locking issues
|
* disciplines and bind them to the @tty. This has no locking issues as the
|
||||||
* as the device isn't yet active.
|
* device isn't yet active.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
|
int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
|
||||||
{
|
{
|
||||||
int retval = tty_ldisc_open(tty, tty->ldisc);
|
int retval = tty_ldisc_open(tty, tty->ldisc);
|
||||||
|
@ -786,10 +760,9 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
|
||||||
* tty_ldisc_release - release line discipline
|
* tty_ldisc_release - release line discipline
|
||||||
* @tty: tty being shut down (or one end of pty pair)
|
* @tty: tty being shut down (or one end of pty pair)
|
||||||
*
|
*
|
||||||
* Called during the final close of a tty or a pty pair in order to shut
|
* Called during the final close of a tty or a pty pair in order to shut down
|
||||||
* down the line discpline layer. On exit, each tty's ldisc is NULL.
|
* the line discpline layer. On exit, each tty's ldisc is %NULL.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void tty_ldisc_release(struct tty_struct *tty)
|
void tty_ldisc_release(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct tty_struct *o_tty = tty->link;
|
struct tty_struct *o_tty = tty->link;
|
||||||
|
@ -817,10 +790,9 @@ void tty_ldisc_release(struct tty_struct *tty)
|
||||||
* tty_ldisc_init - ldisc setup for new tty
|
* tty_ldisc_init - ldisc setup for new tty
|
||||||
* @tty: tty being allocated
|
* @tty: tty being allocated
|
||||||
*
|
*
|
||||||
* Set up the line discipline objects for a newly allocated tty. Note that
|
* Set up the line discipline objects for a newly allocated tty. Note that the
|
||||||
* the tty structure is not completely set up when this call is made.
|
* tty structure is not completely set up when this call is made.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int tty_ldisc_init(struct tty_struct *tty)
|
int tty_ldisc_init(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);
|
struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);
|
||||||
|
@ -835,7 +807,7 @@ int tty_ldisc_init(struct tty_struct *tty)
|
||||||
* tty_ldisc_deinit - ldisc cleanup for new tty
|
* tty_ldisc_deinit - ldisc cleanup for new tty
|
||||||
* @tty: tty that was allocated recently
|
* @tty: tty that was allocated recently
|
||||||
*
|
*
|
||||||
* The tty structure must not becompletely set up (tty_ldisc_setup) when
|
* The tty structure must not be completely set up (tty_ldisc_setup()) when
|
||||||
* this call is made.
|
* this call is made.
|
||||||
*/
|
*/
|
||||||
void tty_ldisc_deinit(struct tty_struct *tty)
|
void tty_ldisc_deinit(struct tty_struct *tty)
|
||||||
|
|
|
@ -163,7 +163,7 @@ down_read_failed(struct ld_semaphore *sem, long count, long timeout)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try to reverse the lock attempt but if the count has changed
|
* Try to reverse the lock attempt but if the count has changed
|
||||||
* so that reversing fails, check if there are are no waiters,
|
* so that reversing fails, check if there are no waiters,
|
||||||
* and early-out if not
|
* and early-out if not
|
||||||
*/
|
*/
|
||||||
do {
|
do {
|
||||||
|
|
|
@ -59,6 +59,15 @@ const struct tty_port_client_operations tty_port_default_client_ops = {
|
||||||
};
|
};
|
||||||
EXPORT_SYMBOL_GPL(tty_port_default_client_ops);
|
EXPORT_SYMBOL_GPL(tty_port_default_client_ops);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tty_port_init -- initialize tty_port
|
||||||
|
* @port: tty_port to initialize
|
||||||
|
*
|
||||||
|
* Initializes the state of struct tty_port. When a port was initialized using
|
||||||
|
* this function, one has to destroy the port by tty_port_destroy(). Either
|
||||||
|
* indirectly by using &tty_port refcounting (tty_port_put()) or directly if
|
||||||
|
* refcounting is not used.
|
||||||
|
*/
|
||||||
void tty_port_init(struct tty_port *port)
|
void tty_port_init(struct tty_port *port)
|
||||||
{
|
{
|
||||||
memset(port, 0, sizeof(*port));
|
memset(port, 0, sizeof(*port));
|
||||||
|
@ -82,9 +91,9 @@ EXPORT_SYMBOL(tty_port_init);
|
||||||
* @index: index of the tty
|
* @index: index of the tty
|
||||||
*
|
*
|
||||||
* Provide the tty layer with a link from a tty (specified by @index) to a
|
* Provide the tty layer with a link from a tty (specified by @index) to a
|
||||||
* tty_port (@port). Use this only if neither tty_port_register_device nor
|
* tty_port (@port). Use this only if neither tty_port_register_device() nor
|
||||||
* tty_port_install is used in the driver. If used, this has to be called before
|
* tty_port_install() is used in the driver. If used, this has to be called
|
||||||
* tty_register_driver.
|
* before tty_register_driver().
|
||||||
*/
|
*/
|
||||||
void tty_port_link_device(struct tty_port *port,
|
void tty_port_link_device(struct tty_port *port,
|
||||||
struct tty_driver *driver, unsigned index)
|
struct tty_driver *driver, unsigned index)
|
||||||
|
@ -102,9 +111,9 @@ EXPORT_SYMBOL_GPL(tty_port_link_device);
|
||||||
* @index: index of the tty
|
* @index: index of the tty
|
||||||
* @device: parent if exists, otherwise NULL
|
* @device: parent if exists, otherwise NULL
|
||||||
*
|
*
|
||||||
* It is the same as tty_register_device except the provided @port is linked to
|
* It is the same as tty_register_device() except the provided @port is linked
|
||||||
* a concrete tty specified by @index. Use this or tty_port_install (or both).
|
* to a concrete tty specified by @index. Use this or tty_port_install() (or
|
||||||
* Call tty_port_link_device as a last resort.
|
* both). Call tty_port_link_device() as a last resort.
|
||||||
*/
|
*/
|
||||||
struct device *tty_port_register_device(struct tty_port *port,
|
struct device *tty_port_register_device(struct tty_port *port,
|
||||||
struct tty_driver *driver, unsigned index,
|
struct tty_driver *driver, unsigned index,
|
||||||
|
@ -123,9 +132,9 @@ EXPORT_SYMBOL_GPL(tty_port_register_device);
|
||||||
* @drvdata: Driver data to be set to device.
|
* @drvdata: Driver data to be set to device.
|
||||||
* @attr_grp: Attribute group to be set on device.
|
* @attr_grp: Attribute group to be set on device.
|
||||||
*
|
*
|
||||||
* It is the same as tty_register_device_attr except the provided @port is
|
* It is the same as tty_register_device_attr() except the provided @port is
|
||||||
* linked to a concrete tty specified by @index. Use this or tty_port_install
|
* linked to a concrete tty specified by @index. Use this or tty_port_install()
|
||||||
* (or both). Call tty_port_link_device as a last resort.
|
* (or both). Call tty_port_link_device() as a last resort.
|
||||||
*/
|
*/
|
||||||
struct device *tty_port_register_device_attr(struct tty_port *port,
|
struct device *tty_port_register_device_attr(struct tty_port *port,
|
||||||
struct tty_driver *driver, unsigned index,
|
struct tty_driver *driver, unsigned index,
|
||||||
|
@ -240,9 +249,9 @@ EXPORT_SYMBOL(tty_port_free_xmit_buf);
|
||||||
* tty_port_destroy -- destroy inited port
|
* tty_port_destroy -- destroy inited port
|
||||||
* @port: tty port to be destroyed
|
* @port: tty port to be destroyed
|
||||||
*
|
*
|
||||||
* When a port was initialized using tty_port_init, one has to destroy the
|
* When a port was initialized using tty_port_init(), one has to destroy the
|
||||||
* port by this function. Either indirectly by using tty_port refcounting
|
* port by this function. Either indirectly by using &tty_port refcounting
|
||||||
* (tty_port_put) or directly if refcounting is not used.
|
* (tty_port_put()) or directly if refcounting is not used.
|
||||||
*/
|
*/
|
||||||
void tty_port_destroy(struct tty_port *port)
|
void tty_port_destroy(struct tty_port *port)
|
||||||
{
|
{
|
||||||
|
@ -267,6 +276,13 @@ static void tty_port_destructor(struct kref *kref)
|
||||||
kfree(port);
|
kfree(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tty_port_put -- drop a reference to tty_port
|
||||||
|
* @port: port to drop a reference of (can be NULL)
|
||||||
|
*
|
||||||
|
* The final put will destroy and free up the @port using
|
||||||
|
* @port->ops->destruct() hook, or using kfree() if not provided.
|
||||||
|
*/
|
||||||
void tty_port_put(struct tty_port *port)
|
void tty_port_put(struct tty_port *port)
|
||||||
{
|
{
|
||||||
if (port)
|
if (port)
|
||||||
|
@ -278,8 +294,8 @@ EXPORT_SYMBOL(tty_port_put);
|
||||||
* tty_port_tty_get - get a tty reference
|
* tty_port_tty_get - get a tty reference
|
||||||
* @port: tty port
|
* @port: tty port
|
||||||
*
|
*
|
||||||
* Return a refcount protected tty instance or NULL if the port is not
|
* Return a refcount protected tty instance or %NULL if the port is not
|
||||||
* associated with a tty (eg due to close or hangup)
|
* associated with a tty (eg due to close or hangup).
|
||||||
*/
|
*/
|
||||||
struct tty_struct *tty_port_tty_get(struct tty_port *port)
|
struct tty_struct *tty_port_tty_get(struct tty_port *port)
|
||||||
{
|
{
|
||||||
|
@ -298,8 +314,8 @@ EXPORT_SYMBOL(tty_port_tty_get);
|
||||||
* @port: tty port
|
* @port: tty port
|
||||||
* @tty: the tty
|
* @tty: the tty
|
||||||
*
|
*
|
||||||
* Associate the port and tty pair. Manages any internal refcounts.
|
* Associate the port and tty pair. Manages any internal refcounts. Pass %NULL
|
||||||
* Pass NULL to deassociate a port
|
* to deassociate a port.
|
||||||
*/
|
*/
|
||||||
void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty)
|
void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
|
@ -312,6 +328,16 @@ void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(tty_port_tty_set);
|
EXPORT_SYMBOL(tty_port_tty_set);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tty_port_shutdown - internal helper to shutdown the device
|
||||||
|
* @port: tty port to be shut down
|
||||||
|
* @tty: the associated tty
|
||||||
|
*
|
||||||
|
* It is used by tty_port_hangup() and tty_port_close(). Its task is to
|
||||||
|
* shutdown the device if it was initialized (note consoles remain
|
||||||
|
* functioning). It lowers DTR/RTS (if @tty has HUPCL set) and invokes
|
||||||
|
* @port->ops->shutdown().
|
||||||
|
*/
|
||||||
static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty)
|
static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
mutex_lock(&port->mutex);
|
mutex_lock(&port->mutex);
|
||||||
|
@ -365,9 +391,8 @@ EXPORT_SYMBOL(tty_port_hangup);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tty_port_tty_hangup - helper to hang up a tty
|
* tty_port_tty_hangup - helper to hang up a tty
|
||||||
*
|
|
||||||
* @port: tty port
|
* @port: tty port
|
||||||
* @check_clocal: hang only ttys with CLOCAL unset?
|
* @check_clocal: hang only ttys with %CLOCAL unset?
|
||||||
*/
|
*/
|
||||||
void tty_port_tty_hangup(struct tty_port *port, bool check_clocal)
|
void tty_port_tty_hangup(struct tty_port *port, bool check_clocal)
|
||||||
{
|
{
|
||||||
|
@ -381,7 +406,6 @@ EXPORT_SYMBOL_GPL(tty_port_tty_hangup);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tty_port_tty_wakeup - helper to wake up a tty
|
* tty_port_tty_wakeup - helper to wake up a tty
|
||||||
*
|
|
||||||
* @port: tty port
|
* @port: tty port
|
||||||
*/
|
*/
|
||||||
void tty_port_tty_wakeup(struct tty_port *port)
|
void tty_port_tty_wakeup(struct tty_port *port)
|
||||||
|
@ -410,9 +434,9 @@ EXPORT_SYMBOL(tty_port_carrier_raised);
|
||||||
* tty_port_raise_dtr_rts - Raise DTR/RTS
|
* tty_port_raise_dtr_rts - Raise DTR/RTS
|
||||||
* @port: tty port
|
* @port: tty port
|
||||||
*
|
*
|
||||||
* Wrapper for the DTR/RTS raise logic. For the moment this is used
|
* Wrapper for the DTR/RTS raise logic. For the moment this is used to hide
|
||||||
* to hide some internal details. This will eventually become entirely
|
* some internal details. This will eventually become entirely internal to the
|
||||||
* internal to the tty port.
|
* tty port.
|
||||||
*/
|
*/
|
||||||
void tty_port_raise_dtr_rts(struct tty_port *port)
|
void tty_port_raise_dtr_rts(struct tty_port *port)
|
||||||
{
|
{
|
||||||
|
@ -425,9 +449,9 @@ EXPORT_SYMBOL(tty_port_raise_dtr_rts);
|
||||||
* tty_port_lower_dtr_rts - Lower DTR/RTS
|
* tty_port_lower_dtr_rts - Lower DTR/RTS
|
||||||
* @port: tty port
|
* @port: tty port
|
||||||
*
|
*
|
||||||
* Wrapper for the DTR/RTS raise logic. For the moment this is used
|
* Wrapper for the DTR/RTS raise logic. For the moment this is used to hide
|
||||||
* to hide some internal details. This will eventually become entirely
|
* some internal details. This will eventually become entirely internal to the
|
||||||
* internal to the tty port.
|
* tty port.
|
||||||
*/
|
*/
|
||||||
void tty_port_lower_dtr_rts(struct tty_port *port)
|
void tty_port_lower_dtr_rts(struct tty_port *port)
|
||||||
{
|
{
|
||||||
|
@ -440,25 +464,26 @@ EXPORT_SYMBOL(tty_port_lower_dtr_rts);
|
||||||
* tty_port_block_til_ready - Waiting logic for tty open
|
* tty_port_block_til_ready - Waiting logic for tty open
|
||||||
* @port: the tty port being opened
|
* @port: the tty port being opened
|
||||||
* @tty: the tty device being bound
|
* @tty: the tty device being bound
|
||||||
* @filp: the file pointer of the opener or NULL
|
* @filp: the file pointer of the opener or %NULL
|
||||||
*
|
*
|
||||||
* Implement the core POSIX/SuS tty behaviour when opening a tty device.
|
* Implement the core POSIX/SuS tty behaviour when opening a tty device.
|
||||||
* Handles:
|
* Handles:
|
||||||
|
*
|
||||||
* - hangup (both before and during)
|
* - hangup (both before and during)
|
||||||
* - non blocking open
|
* - non blocking open
|
||||||
* - rts/dtr/dcd
|
* - rts/dtr/dcd
|
||||||
* - signals
|
* - signals
|
||||||
* - port flags and counts
|
* - port flags and counts
|
||||||
*
|
*
|
||||||
* The passed tty_port must implement the carrier_raised method if it can
|
* The passed @port must implement the @port->ops->carrier_raised method if it
|
||||||
* do carrier detect and the dtr_rts method if it supports software
|
* can do carrier detect and the @port->ops->dtr_rts method if it supports
|
||||||
* management of these lines. Note that the dtr/rts raise is done each
|
* software management of these lines. Note that the dtr/rts raise is done each
|
||||||
* iteration as a hangup may have previously dropped them while we wait.
|
* iteration as a hangup may have previously dropped them while we wait.
|
||||||
*
|
*
|
||||||
* Caller holds tty lock.
|
* Caller holds tty lock.
|
||||||
*
|
*
|
||||||
* NB: May drop and reacquire tty lock when blocking, so tty and tty_port
|
* Note: May drop and reacquire tty lock when blocking, so @tty and @port may
|
||||||
* may have changed state (eg., may have been hung up).
|
* have changed state (eg., may have been hung up).
|
||||||
*/
|
*/
|
||||||
int tty_port_block_til_ready(struct tty_port *port,
|
int tty_port_block_til_ready(struct tty_port *port,
|
||||||
struct tty_struct *tty, struct file *filp)
|
struct tty_struct *tty, struct file *filp)
|
||||||
|
@ -560,7 +585,21 @@ static void tty_port_drain_delay(struct tty_port *port, struct tty_struct *tty)
|
||||||
schedule_timeout_interruptible(timeout);
|
schedule_timeout_interruptible(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Caller holds tty lock. */
|
/**
|
||||||
|
* tty_port_close_start - helper for tty->ops->close, part 1/2
|
||||||
|
* @port: tty_port of the device
|
||||||
|
* @tty: tty being closed
|
||||||
|
* @filp: passed file pointer
|
||||||
|
*
|
||||||
|
* Decrements and checks open count. Flushes the port if this is the last
|
||||||
|
* close. That means, dropping the data from the outpu buffer on the device and
|
||||||
|
* waiting for sending logic to finish. The rest of close handling is performed
|
||||||
|
* in tty_port_close_end().
|
||||||
|
*
|
||||||
|
* Locking: Caller holds tty lock.
|
||||||
|
*
|
||||||
|
* Return: 1 if this is the last close, otherwise 0
|
||||||
|
*/
|
||||||
int tty_port_close_start(struct tty_port *port,
|
int tty_port_close_start(struct tty_port *port,
|
||||||
struct tty_struct *tty, struct file *filp)
|
struct tty_struct *tty, struct file *filp)
|
||||||
{
|
{
|
||||||
|
@ -606,7 +645,17 @@ int tty_port_close_start(struct tty_port *port,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(tty_port_close_start);
|
EXPORT_SYMBOL(tty_port_close_start);
|
||||||
|
|
||||||
/* Caller holds tty lock */
|
/**
|
||||||
|
* tty_port_close_end - helper for tty->ops->close, part 2/2
|
||||||
|
* @port: tty_port of the device
|
||||||
|
* @tty: tty being closed
|
||||||
|
*
|
||||||
|
* This is a continuation of the first part: tty_port_close_start(). This
|
||||||
|
* should be called after turning off the device. It flushes the data from the
|
||||||
|
* line discipline and delays the close by @port->close_delay.
|
||||||
|
*
|
||||||
|
* Locking: Caller holds tty lock.
|
||||||
|
*/
|
||||||
void tty_port_close_end(struct tty_port *port, struct tty_struct *tty)
|
void tty_port_close_end(struct tty_port *port, struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
@ -628,10 +677,18 @@ void tty_port_close_end(struct tty_port *port, struct tty_struct *tty)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(tty_port_close_end);
|
EXPORT_SYMBOL(tty_port_close_end);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* tty_port_close
|
* tty_port_close - generic tty->ops->close handler
|
||||||
|
* @port: tty_port of the device
|
||||||
|
* @tty: tty being closed
|
||||||
|
* @filp: passed file pointer
|
||||||
*
|
*
|
||||||
* Caller holds tty lock
|
* It is a generic helper to be used in driver's @tty->ops->close. It wraps a
|
||||||
|
* sequence of tty_port_close_start(), tty_port_shutdown(), and
|
||||||
|
* tty_port_close_end(). The latter two are called only if this is the last
|
||||||
|
* close. See the respective functions for the details.
|
||||||
|
*
|
||||||
|
* Locking: Caller holds tty lock
|
||||||
*/
|
*/
|
||||||
void tty_port_close(struct tty_port *port, struct tty_struct *tty,
|
void tty_port_close(struct tty_port *port, struct tty_struct *tty,
|
||||||
struct file *filp)
|
struct file *filp)
|
||||||
|
@ -652,9 +709,9 @@ EXPORT_SYMBOL(tty_port_close);
|
||||||
* @driver: tty_driver for this device
|
* @driver: tty_driver for this device
|
||||||
* @tty: tty to be installed
|
* @tty: tty to be installed
|
||||||
*
|
*
|
||||||
* It is the same as tty_standard_install except the provided @port is linked
|
* It is the same as tty_standard_install() except the provided @port is linked
|
||||||
* to a concrete tty specified by @tty. Use this or tty_port_register_device
|
* to a concrete tty specified by @tty. Use this or tty_port_register_device()
|
||||||
* (or both). Call tty_port_link_device as a last resort.
|
* (or both). Call tty_port_link_device() as a last resort.
|
||||||
*/
|
*/
|
||||||
int tty_port_install(struct tty_port *port, struct tty_driver *driver,
|
int tty_port_install(struct tty_port *port, struct tty_driver *driver,
|
||||||
struct tty_struct *tty)
|
struct tty_struct *tty)
|
||||||
|
@ -664,13 +721,21 @@ int tty_port_install(struct tty_port *port, struct tty_driver *driver,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(tty_port_install);
|
EXPORT_SYMBOL_GPL(tty_port_install);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* tty_port_open
|
* tty_port_open - generic tty->ops->open handler
|
||||||
|
* @port: tty_port of the device
|
||||||
|
* @tty: tty to be opened
|
||||||
|
* @filp: passed file pointer
|
||||||
*
|
*
|
||||||
* Caller holds tty lock.
|
* It is a generic helper to be used in driver's @tty->ops->open. It activates
|
||||||
|
* the devices using @port->ops->activate if not active already. And waits for
|
||||||
|
* the device to be ready using tty_port_block_til_ready() (e.g. raises
|
||||||
|
* DTR/CTS and waits for carrier).
|
||||||
*
|
*
|
||||||
* NB: may drop and reacquire tty lock (in tty_port_block_til_ready()) so
|
* Locking: Caller holds tty lock.
|
||||||
* tty and tty_port may have changed state (eg., may be hung up now)
|
*
|
||||||
|
* Note: may drop and reacquire tty lock (in tty_port_block_til_ready()) so
|
||||||
|
* @tty and @port may have changed state (eg., may be hung up now).
|
||||||
*/
|
*/
|
||||||
int tty_port_open(struct tty_port *port, struct tty_struct *tty,
|
int tty_port_open(struct tty_port *port, struct tty_struct *tty,
|
||||||
struct file *filp)
|
struct file *filp)
|
||||||
|
|
|
@ -153,6 +153,7 @@ static int shift_state = 0;
|
||||||
|
|
||||||
static unsigned int ledstate = -1U; /* undefined */
|
static unsigned int ledstate = -1U; /* undefined */
|
||||||
static unsigned char ledioctl;
|
static unsigned char ledioctl;
|
||||||
|
static bool vt_switch;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Notifier list for console keyboard events
|
* Notifier list for console keyboard events
|
||||||
|
@ -324,13 +325,13 @@ int kbd_rate(struct kbd_repeat *rpt)
|
||||||
static void put_queue(struct vc_data *vc, int ch)
|
static void put_queue(struct vc_data *vc, int ch)
|
||||||
{
|
{
|
||||||
tty_insert_flip_char(&vc->port, ch, 0);
|
tty_insert_flip_char(&vc->port, ch, 0);
|
||||||
tty_schedule_flip(&vc->port);
|
tty_flip_buffer_push(&vc->port);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void puts_queue(struct vc_data *vc, const char *cp)
|
static void puts_queue(struct vc_data *vc, const char *cp)
|
||||||
{
|
{
|
||||||
tty_insert_flip_string(&vc->port, cp, strlen(cp));
|
tty_insert_flip_string(&vc->port, cp, strlen(cp));
|
||||||
tty_schedule_flip(&vc->port);
|
tty_flip_buffer_push(&vc->port);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void applkey(struct vc_data *vc, int key, char mode)
|
static void applkey(struct vc_data *vc, int key, char mode)
|
||||||
|
@ -414,6 +415,12 @@ void vt_set_leds_compute_shiftstate(void)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When VT is switched, the keyboard led needs to be set once.
|
||||||
|
* Ensure that after the switch is completed, the state of the
|
||||||
|
* keyboard LED is consistent with the state of the keyboard lock.
|
||||||
|
*/
|
||||||
|
vt_switch = true;
|
||||||
set_leds();
|
set_leds();
|
||||||
|
|
||||||
spin_lock_irqsave(&kbd_event_lock, flags);
|
spin_lock_irqsave(&kbd_event_lock, flags);
|
||||||
|
@ -584,7 +591,7 @@ static void fn_inc_console(struct vc_data *vc)
|
||||||
static void fn_send_intr(struct vc_data *vc)
|
static void fn_send_intr(struct vc_data *vc)
|
||||||
{
|
{
|
||||||
tty_insert_flip_char(&vc->port, 0, TTY_BREAK);
|
tty_insert_flip_char(&vc->port, 0, TTY_BREAK);
|
||||||
tty_schedule_flip(&vc->port);
|
tty_flip_buffer_push(&vc->port);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fn_scroll_forw(struct vc_data *vc)
|
static void fn_scroll_forw(struct vc_data *vc)
|
||||||
|
@ -1255,6 +1262,11 @@ static void kbd_bh(struct tasklet_struct *unused)
|
||||||
leds |= (unsigned int)kbd->lockstate << 8;
|
leds |= (unsigned int)kbd->lockstate << 8;
|
||||||
spin_unlock_irqrestore(&led_lock, flags);
|
spin_unlock_irqrestore(&led_lock, flags);
|
||||||
|
|
||||||
|
if (vt_switch) {
|
||||||
|
ledstate = ~leds;
|
||||||
|
vt_switch = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (leds != ledstate) {
|
if (leds != ledstate) {
|
||||||
kbd_propagate_led_state(ledstate, leds);
|
kbd_propagate_led_state(ledstate, leds);
|
||||||
ledstate = leds;
|
ledstate = leds;
|
||||||
|
|
|
@ -1833,7 +1833,7 @@ static void csi_m(struct vc_data *vc)
|
||||||
static void respond_string(const char *p, size_t len, struct tty_port *port)
|
static void respond_string(const char *p, size_t len, struct tty_port *port)
|
||||||
{
|
{
|
||||||
tty_insert_flip_string(port, p, len);
|
tty_insert_flip_string(port, p, len);
|
||||||
tty_schedule_flip(port);
|
tty_flip_buffer_push(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cursor_report(struct vc_data *vc, struct tty_struct *tty)
|
static void cursor_report(struct vc_data *vc, struct tty_struct *tty)
|
||||||
|
|
|
@ -685,10 +685,6 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
|
||||||
if (retval)
|
if (retval)
|
||||||
goto error_get_interface;
|
goto error_get_interface;
|
||||||
|
|
||||||
/*
|
|
||||||
* FIXME: Why do we need this? Allocating 64K of physically contiguous
|
|
||||||
* memory is really nasty...
|
|
||||||
*/
|
|
||||||
set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
|
set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
|
||||||
acm->control->needs_remote_wakeup = 1;
|
acm->control->needs_remote_wakeup = 1;
|
||||||
|
|
||||||
|
|
|
@ -90,14 +90,8 @@ enum amba_vendor {
|
||||||
AMBA_VENDOR_ST = 0x80,
|
AMBA_VENDOR_ST = 0x80,
|
||||||
AMBA_VENDOR_QCOM = 0x51,
|
AMBA_VENDOR_QCOM = 0x51,
|
||||||
AMBA_VENDOR_LSI = 0xb6,
|
AMBA_VENDOR_LSI = 0xb6,
|
||||||
AMBA_VENDOR_LINUX = 0xfe, /* This value is not official */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* This is used to generate pseudo-ID for AMBA device */
|
|
||||||
#define AMBA_LINUX_ID(conf, rev, part) \
|
|
||||||
(((conf) & 0xff) << 24 | ((rev) & 0xf) << 20 | \
|
|
||||||
AMBA_VENDOR_LINUX << 12 | ((part) & 0xfff))
|
|
||||||
|
|
||||||
extern struct bus_type amba_bustype;
|
extern struct bus_type amba_bustype;
|
||||||
|
|
||||||
#define to_amba_device(d) container_of(d, struct amba_device, dev)
|
#define to_amba_device(d) container_of(d, struct amba_device, dev)
|
||||||
|
|
|
@ -1965,24 +1965,6 @@
|
||||||
#define PCI_DEVICE_ID_APPLICOM_PCI2000PFB 0x0003
|
#define PCI_DEVICE_ID_APPLICOM_PCI2000PFB 0x0003
|
||||||
|
|
||||||
#define PCI_VENDOR_ID_MOXA 0x1393
|
#define PCI_VENDOR_ID_MOXA 0x1393
|
||||||
#define PCI_DEVICE_ID_MOXA_RC7000 0x0001
|
|
||||||
#define PCI_DEVICE_ID_MOXA_CP102 0x1020
|
|
||||||
#define PCI_DEVICE_ID_MOXA_CP102UL 0x1021
|
|
||||||
#define PCI_DEVICE_ID_MOXA_CP102U 0x1022
|
|
||||||
#define PCI_DEVICE_ID_MOXA_C104 0x1040
|
|
||||||
#define PCI_DEVICE_ID_MOXA_CP104U 0x1041
|
|
||||||
#define PCI_DEVICE_ID_MOXA_CP104JU 0x1042
|
|
||||||
#define PCI_DEVICE_ID_MOXA_CP104EL 0x1043
|
|
||||||
#define PCI_DEVICE_ID_MOXA_CT114 0x1140
|
|
||||||
#define PCI_DEVICE_ID_MOXA_CP114 0x1141
|
|
||||||
#define PCI_DEVICE_ID_MOXA_CP118U 0x1180
|
|
||||||
#define PCI_DEVICE_ID_MOXA_CP118EL 0x1181
|
|
||||||
#define PCI_DEVICE_ID_MOXA_CP132 0x1320
|
|
||||||
#define PCI_DEVICE_ID_MOXA_CP132U 0x1321
|
|
||||||
#define PCI_DEVICE_ID_MOXA_CP134U 0x1340
|
|
||||||
#define PCI_DEVICE_ID_MOXA_C168 0x1680
|
|
||||||
#define PCI_DEVICE_ID_MOXA_CP168U 0x1681
|
|
||||||
#define PCI_DEVICE_ID_MOXA_CP168EL 0x1682
|
|
||||||
#define PCI_DEVICE_ID_MOXA_CP204J 0x2040
|
#define PCI_DEVICE_ID_MOXA_CP204J 0x2040
|
||||||
#define PCI_DEVICE_ID_MOXA_C218 0x2180
|
#define PCI_DEVICE_ID_MOXA_C218 0x2180
|
||||||
#define PCI_DEVICE_ID_MOXA_C320 0x3200
|
#define PCI_DEVICE_ID_MOXA_C320 0x3200
|
||||||
|
|
|
@ -104,8 +104,6 @@ struct uart_8250_port {
|
||||||
unsigned char ier;
|
unsigned char ier;
|
||||||
unsigned char lcr;
|
unsigned char lcr;
|
||||||
unsigned char mcr;
|
unsigned char mcr;
|
||||||
unsigned char mcr_mask; /* mask of user bits */
|
|
||||||
unsigned char mcr_force; /* mask of forced bits */
|
|
||||||
unsigned char cur_iotype; /* Running I/O type */
|
unsigned char cur_iotype; /* Running I/O type */
|
||||||
unsigned int rpm_tx_active;
|
unsigned int rpm_tx_active;
|
||||||
unsigned char canary; /* non-zero during system sleep
|
unsigned char canary; /* non-zero during system sleep
|
||||||
|
|
|
@ -27,15 +27,6 @@
|
||||||
#define S3C2410_UERSTAT (0x14)
|
#define S3C2410_UERSTAT (0x14)
|
||||||
#define S3C2410_UFSTAT (0x18)
|
#define S3C2410_UFSTAT (0x18)
|
||||||
#define S3C2410_UMSTAT (0x1C)
|
#define S3C2410_UMSTAT (0x1C)
|
||||||
#define USI_CON (0xC4)
|
|
||||||
#define USI_OPTION (0xC8)
|
|
||||||
|
|
||||||
#define USI_CON_RESET (1<<0)
|
|
||||||
#define USI_CON_RESET_MASK (1<<0)
|
|
||||||
|
|
||||||
#define USI_OPTION_HWACG_CLKREQ_ON (1<<1)
|
|
||||||
#define USI_OPTION_HWACG_CLKSTOP_ON (1<<2)
|
|
||||||
#define USI_OPTION_HWACG_MASK (3<<1)
|
|
||||||
|
|
||||||
#define S3C2410_LCON_CFGMASK ((0xF<<3)|(0x3))
|
#define S3C2410_LCON_CFGMASK ((0xF<<3)|(0x3))
|
||||||
|
|
||||||
|
|
|
@ -122,33 +122,84 @@ struct tty_operations;
|
||||||
/**
|
/**
|
||||||
* struct tty_struct - state associated with a tty while open
|
* struct tty_struct - state associated with a tty while open
|
||||||
*
|
*
|
||||||
* @flow.lock: lock for flow members
|
* @magic: magic value set early in @alloc_tty_struct to %TTY_MAGIC, for
|
||||||
* @flow.stopped: tty stopped/started by tty_stop/tty_start
|
* debugging purposes
|
||||||
* @flow.tco_stopped: tty stopped/started by TCOOFF/TCOON ioctls (it has
|
* @kref: reference counting by tty_kref_get() and tty_kref_put(), reaching zero
|
||||||
* precedense over @flow.stopped)
|
* frees the structure
|
||||||
|
* @dev: class device or %NULL (e.g. ptys, serdev)
|
||||||
|
* @driver: &struct tty_driver operating this tty
|
||||||
|
* @ops: &struct tty_operations of @driver for this tty (open, close, etc.)
|
||||||
|
* @index: index of this tty (e.g. to construct @name like tty12)
|
||||||
|
* @ldisc_sem: protects line discipline changes (@ldisc) -- lock tty not pty
|
||||||
|
* @ldisc: the current line discipline for this tty (n_tty by default)
|
||||||
|
* @atomic_write_lock: protects against concurrent writers, i.e. locks
|
||||||
|
* @write_cnt, @write_buf and similar
|
||||||
|
* @legacy_mutex: leftover from history (BKL -> BTM -> @legacy_mutex),
|
||||||
|
* protecting several operations on this tty
|
||||||
|
* @throttle_mutex: protects against concurrent tty_throttle_safe() and
|
||||||
|
* tty_unthrottle_safe() (but not tty_unthrottle())
|
||||||
|
* @termios_rwsem: protects @termios and @termios_locked
|
||||||
|
* @winsize_mutex: protects @winsize
|
||||||
|
* @termios: termios for the current tty, copied from/to @driver.termios
|
||||||
|
* @termios_locked: locked termios (by %TIOCGLCKTRMIOS and %TIOCSLCKTRMIOS
|
||||||
|
* ioctls)
|
||||||
|
* @name: name of the tty constructed by tty_line_name() (e.g. ttyS3)
|
||||||
|
* @flags: bitwise OR of %TTY_THROTTLED, %TTY_IO_ERROR, ...
|
||||||
|
* @count: count of open processes, reaching zero cancels all the work for
|
||||||
|
* this tty and drops a @kref too (but does not free this tty)
|
||||||
|
* @winsize: size of the terminal "window" (cf. @winsize_mutex)
|
||||||
|
* @flow: flow settings grouped together, see also @flow.unused
|
||||||
|
* @flow.lock: lock for @flow members
|
||||||
|
* @flow.stopped: tty stopped/started by stop_tty()/start_tty()
|
||||||
|
* @flow.tco_stopped: tty stopped/started by %TCOOFF/%TCOON ioctls (it has
|
||||||
|
* precedence over @flow.stopped)
|
||||||
* @flow.unused: alignment for Alpha, so that no members other than @flow.* are
|
* @flow.unused: alignment for Alpha, so that no members other than @flow.* are
|
||||||
* modified by the same 64b word store. The @flow's __aligned is
|
* modified by the same 64b word store. The @flow's __aligned is
|
||||||
* there for the very same reason.
|
* there for the very same reason.
|
||||||
* @ctrl.lock: lock for ctrl members
|
* @ctrl: control settings grouped together, see also @ctrl.unused
|
||||||
|
* @ctrl.lock: lock for @ctrl members
|
||||||
* @ctrl.pgrp: process group of this tty (setpgrp(2))
|
* @ctrl.pgrp: process group of this tty (setpgrp(2))
|
||||||
* @ctrl.session: session of this tty (setsid(2)). Writes are protected by both
|
* @ctrl.session: session of this tty (setsid(2)). Writes are protected by both
|
||||||
* @ctrl.lock and legacy mutex, readers must use at least one of
|
* @ctrl.lock and @legacy_mutex, readers must use at least one of
|
||||||
* them.
|
* them.
|
||||||
* @ctrl.pktstatus: packet mode status (bitwise OR of TIOCPKT_* constants)
|
* @ctrl.pktstatus: packet mode status (bitwise OR of %TIOCPKT_ constants)
|
||||||
* @ctrl.packet: packet mode enabled
|
* @ctrl.packet: packet mode enabled
|
||||||
|
* @ctrl.unused: alignment for Alpha, see @flow.unused for explanation
|
||||||
|
* @hw_stopped: not controlled by the tty layer, under @driver's control for CTS
|
||||||
|
* handling
|
||||||
|
* @receive_room: bytes permitted to feed to @ldisc without any being lost
|
||||||
|
* @flow_change: controls behavior of throttling, see tty_throttle_safe() and
|
||||||
|
* tty_unthrottle_safe()
|
||||||
|
* @link: link to another pty (master -> slave and vice versa)
|
||||||
|
* @fasync: state for %O_ASYNC (for %SIGIO); managed by fasync_helper()
|
||||||
|
* @write_wait: concurrent writers are waiting in this queue until they are
|
||||||
|
* allowed to write
|
||||||
|
* @read_wait: readers wait for data in this queue
|
||||||
|
* @hangup_work: normally a work to perform a hangup (do_tty_hangup()); while
|
||||||
|
* freeing the tty, (re)used to release_one_tty()
|
||||||
|
* @disc_data: pointer to @ldisc's private data (e.g. to &struct n_tty_data)
|
||||||
|
* @driver_data: pointer to @driver's private data (e.g. &struct uart_state)
|
||||||
|
* @files_lock: protects @tty_files list
|
||||||
|
* @tty_files: list of (re)openers of this tty (i.e. linked &struct
|
||||||
|
* tty_file_private)
|
||||||
|
* @closing: when set during close, n_tty processes only START & STOP chars
|
||||||
|
* @write_buf: temporary buffer used during tty_write() to copy user data to
|
||||||
|
* @write_cnt: count of bytes written in tty_write() to @write_buf
|
||||||
|
* @SAK_work: if the tty has a pending do_SAK, it is queued here
|
||||||
|
* @port: persistent storage for this device (i.e. &struct tty_port)
|
||||||
*
|
*
|
||||||
* All of the state associated with a tty while the tty is open. Persistent
|
* All of the state associated with a tty while the tty is open. Persistent
|
||||||
* storage for tty devices is referenced here as @port in struct tty_port.
|
* storage for tty devices is referenced here as @port and is documented in
|
||||||
|
* &struct tty_port.
|
||||||
*/
|
*/
|
||||||
struct tty_struct {
|
struct tty_struct {
|
||||||
int magic;
|
int magic;
|
||||||
struct kref kref;
|
struct kref kref;
|
||||||
struct device *dev; /* class device or NULL (e.g. ptys, serdev) */
|
struct device *dev;
|
||||||
struct tty_driver *driver;
|
struct tty_driver *driver;
|
||||||
const struct tty_operations *ops;
|
const struct tty_operations *ops;
|
||||||
int index;
|
int index;
|
||||||
|
|
||||||
/* Protects ldisc changes: Lock tty not pty */
|
|
||||||
struct ld_semaphore ldisc_sem;
|
struct ld_semaphore ldisc_sem;
|
||||||
struct tty_ldisc *ldisc;
|
struct tty_ldisc *ldisc;
|
||||||
|
|
||||||
|
@ -157,12 +208,11 @@ struct tty_struct {
|
||||||
struct mutex throttle_mutex;
|
struct mutex throttle_mutex;
|
||||||
struct rw_semaphore termios_rwsem;
|
struct rw_semaphore termios_rwsem;
|
||||||
struct mutex winsize_mutex;
|
struct mutex winsize_mutex;
|
||||||
/* Termios values are protected by the termios rwsem */
|
|
||||||
struct ktermios termios, termios_locked;
|
struct ktermios termios, termios_locked;
|
||||||
char name[64];
|
char name[64];
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int count;
|
int count;
|
||||||
struct winsize winsize; /* winsize_mutex */
|
struct winsize winsize;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
@ -181,7 +231,7 @@ struct tty_struct {
|
||||||
} __aligned(sizeof(unsigned long)) ctrl;
|
} __aligned(sizeof(unsigned long)) ctrl;
|
||||||
|
|
||||||
int hw_stopped;
|
int hw_stopped;
|
||||||
unsigned int receive_room; /* Bytes free for queue */
|
unsigned int receive_room;
|
||||||
int flow_change;
|
int flow_change;
|
||||||
|
|
||||||
struct tty_struct *link;
|
struct tty_struct *link;
|
||||||
|
@ -191,7 +241,7 @@ struct tty_struct {
|
||||||
struct work_struct hangup_work;
|
struct work_struct hangup_work;
|
||||||
void *disc_data;
|
void *disc_data;
|
||||||
void *driver_data;
|
void *driver_data;
|
||||||
spinlock_t files_lock; /* protects tty_files list */
|
spinlock_t files_lock;
|
||||||
struct list_head tty_files;
|
struct list_head tty_files;
|
||||||
|
|
||||||
#define N_TTY_BUF_SIZE 4096
|
#define N_TTY_BUF_SIZE 4096
|
||||||
|
@ -199,7 +249,6 @@ struct tty_struct {
|
||||||
int closing;
|
int closing;
|
||||||
unsigned char *write_buf;
|
unsigned char *write_buf;
|
||||||
int write_cnt;
|
int write_cnt;
|
||||||
/* If the tty has a pending do_SAK, queue it here - akpm */
|
|
||||||
struct work_struct SAK_work;
|
struct work_struct SAK_work;
|
||||||
struct tty_port *port;
|
struct tty_port *port;
|
||||||
} __randomize_layout;
|
} __randomize_layout;
|
||||||
|
@ -214,26 +263,72 @@ struct tty_file_private {
|
||||||
/* tty magic number */
|
/* tty magic number */
|
||||||
#define TTY_MAGIC 0x5401
|
#define TTY_MAGIC 0x5401
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* These bits are used in the flags field of the tty structure.
|
* DOC: TTY Struct Flags
|
||||||
|
*
|
||||||
|
* These bits are used in the :c:member:`tty_struct.flags` field.
|
||||||
*
|
*
|
||||||
* So that interrupts won't be able to mess up the queues,
|
* So that interrupts won't be able to mess up the queues,
|
||||||
* copy_to_cooked must be atomic with respect to itself, as must
|
* copy_to_cooked must be atomic with respect to itself, as must
|
||||||
* tty->write. Thus, you must use the inline functions set_bit() and
|
* tty->write. Thus, you must use the inline functions set_bit() and
|
||||||
* clear_bit() to make things atomic.
|
* clear_bit() to make things atomic.
|
||||||
|
*
|
||||||
|
* TTY_THROTTLED
|
||||||
|
* Driver input is throttled. The ldisc should call
|
||||||
|
* :c:member:`tty_driver.unthrottle()` in order to resume reception when
|
||||||
|
* it is ready to process more data (at threshold min).
|
||||||
|
*
|
||||||
|
* TTY_IO_ERROR
|
||||||
|
* If set, causes all subsequent userspace read/write calls on the tty to
|
||||||
|
* fail, returning -%EIO. (May be no ldisc too.)
|
||||||
|
*
|
||||||
|
* TTY_OTHER_CLOSED
|
||||||
|
* Device is a pty and the other side has closed.
|
||||||
|
*
|
||||||
|
* TTY_EXCLUSIVE
|
||||||
|
* Exclusive open mode (a single opener).
|
||||||
|
*
|
||||||
|
* TTY_DO_WRITE_WAKEUP
|
||||||
|
* If set, causes the driver to call the
|
||||||
|
* :c:member:`tty_ldisc_ops.write_wakeup()` method in order to resume
|
||||||
|
* transmission when it can accept more data to transmit.
|
||||||
|
*
|
||||||
|
* TTY_LDISC_OPEN
|
||||||
|
* Indicates that a line discipline is open. For debugging purposes only.
|
||||||
|
*
|
||||||
|
* TTY_PTY_LOCK
|
||||||
|
* A flag private to pty code to implement %TIOCSPTLCK/%TIOCGPTLCK logic.
|
||||||
|
*
|
||||||
|
* TTY_NO_WRITE_SPLIT
|
||||||
|
* Prevent driver from splitting up writes into smaller chunks (preserve
|
||||||
|
* write boundaries to driver).
|
||||||
|
*
|
||||||
|
* TTY_HUPPED
|
||||||
|
* The TTY was hung up. This is set post :c:member:`tty_driver.hangup()`.
|
||||||
|
*
|
||||||
|
* TTY_HUPPING
|
||||||
|
* The TTY is in the process of hanging up to abort potential readers.
|
||||||
|
*
|
||||||
|
* TTY_LDISC_CHANGING
|
||||||
|
* Line discipline for this TTY is being changed. I/O should not block
|
||||||
|
* when this is set. Use tty_io_nonblock() to check.
|
||||||
|
*
|
||||||
|
* TTY_LDISC_HALTED
|
||||||
|
* Line discipline for this TTY was stopped. No work should be queued to
|
||||||
|
* this ldisc.
|
||||||
*/
|
*/
|
||||||
#define TTY_THROTTLED 0 /* Call unthrottle() at threshold min */
|
#define TTY_THROTTLED 0
|
||||||
#define TTY_IO_ERROR 1 /* Cause an I/O error (may be no ldisc too) */
|
#define TTY_IO_ERROR 1
|
||||||
#define TTY_OTHER_CLOSED 2 /* Other side (if any) has closed */
|
#define TTY_OTHER_CLOSED 2
|
||||||
#define TTY_EXCLUSIVE 3 /* Exclusive open mode */
|
#define TTY_EXCLUSIVE 3
|
||||||
#define TTY_DO_WRITE_WAKEUP 5 /* Call write_wakeup after queuing new */
|
#define TTY_DO_WRITE_WAKEUP 5
|
||||||
#define TTY_LDISC_OPEN 11 /* Line discipline is open */
|
#define TTY_LDISC_OPEN 11
|
||||||
#define TTY_PTY_LOCK 16 /* pty private */
|
#define TTY_PTY_LOCK 16
|
||||||
#define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */
|
#define TTY_NO_WRITE_SPLIT 17
|
||||||
#define TTY_HUPPED 18 /* Post driver->hangup() */
|
#define TTY_HUPPED 18
|
||||||
#define TTY_HUPPING 19 /* Hangup in progress */
|
#define TTY_HUPPING 19
|
||||||
#define TTY_LDISC_CHANGING 20 /* Change pending - non-block IO */
|
#define TTY_LDISC_CHANGING 20
|
||||||
#define TTY_LDISC_HALTED 22 /* Line discipline is halted */
|
#define TTY_LDISC_HALTED 22
|
||||||
|
|
||||||
static inline bool tty_io_nonblock(struct tty_struct *tty, struct file *file)
|
static inline bool tty_io_nonblock(struct tty_struct *tty, struct file *file)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,235 +2,6 @@
|
||||||
#ifndef _LINUX_TTY_DRIVER_H
|
#ifndef _LINUX_TTY_DRIVER_H
|
||||||
#define _LINUX_TTY_DRIVER_H
|
#define _LINUX_TTY_DRIVER_H
|
||||||
|
|
||||||
/*
|
|
||||||
* This structure defines the interface between the low-level tty
|
|
||||||
* driver and the tty routines. The following routines can be
|
|
||||||
* defined; unless noted otherwise, they are optional, and can be
|
|
||||||
* filled in with a null pointer.
|
|
||||||
*
|
|
||||||
* struct tty_struct * (*lookup)(struct tty_driver *self, struct file *, int idx)
|
|
||||||
*
|
|
||||||
* Return the tty device corresponding to idx, NULL if there is not
|
|
||||||
* one currently in use and an ERR_PTR value on error. Called under
|
|
||||||
* tty_mutex (for now!)
|
|
||||||
*
|
|
||||||
* Optional method. Default behaviour is to use the ttys array
|
|
||||||
*
|
|
||||||
* int (*install)(struct tty_driver *self, struct tty_struct *tty)
|
|
||||||
*
|
|
||||||
* Install a new tty into the tty driver internal tables. Used in
|
|
||||||
* conjunction with lookup and remove methods.
|
|
||||||
*
|
|
||||||
* Optional method. Default behaviour is to use the ttys array
|
|
||||||
*
|
|
||||||
* void (*remove)(struct tty_driver *self, struct tty_struct *tty)
|
|
||||||
*
|
|
||||||
* Remove a closed tty from the tty driver internal tables. Used in
|
|
||||||
* conjunction with lookup and remove methods.
|
|
||||||
*
|
|
||||||
* Optional method. Default behaviour is to use the ttys array
|
|
||||||
*
|
|
||||||
* int (*open)(struct tty_struct * tty, struct file * filp);
|
|
||||||
*
|
|
||||||
* This routine is called when a particular tty device is opened.
|
|
||||||
* This routine is mandatory; if this routine is not filled in,
|
|
||||||
* the attempted open will fail with ENODEV.
|
|
||||||
*
|
|
||||||
* Required method. Called with tty lock held.
|
|
||||||
*
|
|
||||||
* void (*close)(struct tty_struct * tty, struct file * filp);
|
|
||||||
*
|
|
||||||
* This routine is called when a particular tty device is closed.
|
|
||||||
* Note: called even if the corresponding open() failed.
|
|
||||||
*
|
|
||||||
* Required method. Called with tty lock held.
|
|
||||||
*
|
|
||||||
* void (*shutdown)(struct tty_struct * tty);
|
|
||||||
*
|
|
||||||
* This routine is called under the tty lock when a particular tty device
|
|
||||||
* is closed for the last time. It executes before the tty resources
|
|
||||||
* are freed so may execute while another function holds a tty kref.
|
|
||||||
*
|
|
||||||
* void (*cleanup)(struct tty_struct * tty);
|
|
||||||
*
|
|
||||||
* This routine is called asynchronously when a particular tty device
|
|
||||||
* is closed for the last time freeing up the resources. This is
|
|
||||||
* actually the second part of shutdown for routines that might sleep.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* int (*write)(struct tty_struct * tty,
|
|
||||||
* const unsigned char *buf, int count);
|
|
||||||
*
|
|
||||||
* This routine is called by the kernel to write a series of
|
|
||||||
* characters to the tty device. The characters may come from
|
|
||||||
* user space or kernel space. This routine will return the
|
|
||||||
* number of characters actually accepted for writing.
|
|
||||||
*
|
|
||||||
* Optional: Required for writable devices.
|
|
||||||
*
|
|
||||||
* int (*put_char)(struct tty_struct *tty, unsigned char ch);
|
|
||||||
*
|
|
||||||
* This routine is called by the kernel to write a single
|
|
||||||
* character to the tty device. If the kernel uses this routine,
|
|
||||||
* it must call the flush_chars() routine (if defined) when it is
|
|
||||||
* done stuffing characters into the driver. If there is no room
|
|
||||||
* in the queue, the character is ignored.
|
|
||||||
*
|
|
||||||
* Optional: Kernel will use the write method if not provided.
|
|
||||||
*
|
|
||||||
* Note: Do not call this function directly, call tty_put_char
|
|
||||||
*
|
|
||||||
* void (*flush_chars)(struct tty_struct *tty);
|
|
||||||
*
|
|
||||||
* This routine is called by the kernel after it has written a
|
|
||||||
* series of characters to the tty device using put_char().
|
|
||||||
*
|
|
||||||
* Optional:
|
|
||||||
*
|
|
||||||
* Note: Do not call this function directly, call tty_driver_flush_chars
|
|
||||||
*
|
|
||||||
* unsigned int (*write_room)(struct tty_struct *tty);
|
|
||||||
*
|
|
||||||
* This routine returns the numbers of characters the tty driver
|
|
||||||
* will accept for queuing to be written. This number is subject
|
|
||||||
* to change as output buffers get emptied, or if the output flow
|
|
||||||
* control is acted.
|
|
||||||
*
|
|
||||||
* Required if write method is provided else not needed.
|
|
||||||
*
|
|
||||||
* Note: Do not call this function directly, call tty_write_room
|
|
||||||
*
|
|
||||||
* int (*ioctl)(struct tty_struct *tty, unsigned int cmd, unsigned long arg);
|
|
||||||
*
|
|
||||||
* This routine allows the tty driver to implement
|
|
||||||
* device-specific ioctls. If the ioctl number passed in cmd
|
|
||||||
* is not recognized by the driver, it should return ENOIOCTLCMD.
|
|
||||||
*
|
|
||||||
* Optional
|
|
||||||
*
|
|
||||||
* long (*compat_ioctl)(struct tty_struct *tty,,
|
|
||||||
* unsigned int cmd, unsigned long arg);
|
|
||||||
*
|
|
||||||
* implement ioctl processing for 32 bit process on 64 bit system
|
|
||||||
*
|
|
||||||
* Optional
|
|
||||||
*
|
|
||||||
* void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
|
|
||||||
*
|
|
||||||
* This routine allows the tty driver to be notified when
|
|
||||||
* device's termios settings have changed.
|
|
||||||
*
|
|
||||||
* Optional: Called under the termios lock
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* void (*set_ldisc)(struct tty_struct *tty);
|
|
||||||
*
|
|
||||||
* This routine allows the tty driver to be notified when the
|
|
||||||
* device's termios settings have changed.
|
|
||||||
*
|
|
||||||
* Optional: Called under BKL (currently)
|
|
||||||
*
|
|
||||||
* void (*throttle)(struct tty_struct * tty);
|
|
||||||
*
|
|
||||||
* This routine notifies the tty driver that input buffers for
|
|
||||||
* the line discipline are close to full, and it should somehow
|
|
||||||
* signal that no more characters should be sent to the tty.
|
|
||||||
*
|
|
||||||
* Optional: Always invoke via tty_throttle_safe(), called under the
|
|
||||||
* termios lock.
|
|
||||||
*
|
|
||||||
* void (*unthrottle)(struct tty_struct * tty);
|
|
||||||
*
|
|
||||||
* This routine notifies the tty drivers that it should signals
|
|
||||||
* that characters can now be sent to the tty without fear of
|
|
||||||
* overrunning the input buffers of the line disciplines.
|
|
||||||
*
|
|
||||||
* Optional: Always invoke via tty_unthrottle(), called under the
|
|
||||||
* termios lock.
|
|
||||||
*
|
|
||||||
* void (*stop)(struct tty_struct *tty);
|
|
||||||
*
|
|
||||||
* This routine notifies the tty driver that it should stop
|
|
||||||
* outputting characters to the tty device.
|
|
||||||
*
|
|
||||||
* Called with ->flow.lock held. Serialized with start() method.
|
|
||||||
*
|
|
||||||
* Optional:
|
|
||||||
*
|
|
||||||
* Note: Call stop_tty not this method.
|
|
||||||
*
|
|
||||||
* void (*start)(struct tty_struct *tty);
|
|
||||||
*
|
|
||||||
* This routine notifies the tty driver that it resume sending
|
|
||||||
* characters to the tty device.
|
|
||||||
*
|
|
||||||
* Called with ->flow.lock held. Serialized with stop() method.
|
|
||||||
*
|
|
||||||
* Optional:
|
|
||||||
*
|
|
||||||
* Note: Call start_tty not this method.
|
|
||||||
*
|
|
||||||
* void (*hangup)(struct tty_struct *tty);
|
|
||||||
*
|
|
||||||
* This routine notifies the tty driver that it should hang up the
|
|
||||||
* tty device.
|
|
||||||
*
|
|
||||||
* Optional:
|
|
||||||
*
|
|
||||||
* Called with tty lock held.
|
|
||||||
*
|
|
||||||
* int (*break_ctl)(struct tty_struct *tty, int state);
|
|
||||||
*
|
|
||||||
* This optional routine requests the tty driver to turn on or
|
|
||||||
* off BREAK status on the RS-232 port. If state is -1,
|
|
||||||
* then the BREAK status should be turned on; if state is 0, then
|
|
||||||
* BREAK should be turned off.
|
|
||||||
*
|
|
||||||
* If this routine is implemented, the high-level tty driver will
|
|
||||||
* handle the following ioctls: TCSBRK, TCSBRKP, TIOCSBRK,
|
|
||||||
* TIOCCBRK.
|
|
||||||
*
|
|
||||||
* If the driver sets TTY_DRIVER_HARDWARE_BREAK then the interface
|
|
||||||
* will also be called with actual times and the hardware is expected
|
|
||||||
* to do the delay work itself. 0 and -1 are still used for on/off.
|
|
||||||
*
|
|
||||||
* Optional: Required for TCSBRK/BRKP/etc handling.
|
|
||||||
*
|
|
||||||
* void (*wait_until_sent)(struct tty_struct *tty, int timeout);
|
|
||||||
*
|
|
||||||
* This routine waits until the device has written out all of the
|
|
||||||
* characters in its transmitter FIFO.
|
|
||||||
*
|
|
||||||
* Optional: If not provided the device is assumed to have no FIFO
|
|
||||||
*
|
|
||||||
* Note: Usually correct to call tty_wait_until_sent
|
|
||||||
*
|
|
||||||
* void (*send_xchar)(struct tty_struct *tty, char ch);
|
|
||||||
*
|
|
||||||
* This routine is used to send a high-priority XON/XOFF
|
|
||||||
* character to the device.
|
|
||||||
*
|
|
||||||
* Optional: If not provided then the write method is called under
|
|
||||||
* the atomic write lock to keep it serialized with the ldisc.
|
|
||||||
*
|
|
||||||
* int (*resize)(struct tty_struct *tty, struct winsize *ws)
|
|
||||||
*
|
|
||||||
* Called when a termios request is issued which changes the
|
|
||||||
* requested terminal geometry.
|
|
||||||
*
|
|
||||||
* Optional: the default action is to update the termios structure
|
|
||||||
* without error. This is usually the correct behaviour. Drivers should
|
|
||||||
* not force errors here if they are not resizable objects (eg a serial
|
|
||||||
* line). See tty_do_resize() if you need to wrap the standard method
|
|
||||||
* in your own logic - the usual case.
|
|
||||||
*
|
|
||||||
* int (*get_icount)(struct tty_struct *tty, struct serial_icounter *icount);
|
|
||||||
*
|
|
||||||
* Called when the device receives a TIOCGICOUNT ioctl. Passed a kernel
|
|
||||||
* structure to complete. This method is optional and will only be called
|
|
||||||
* if provided (otherwise ENOTTY will be returned).
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/kref.h>
|
#include <linux/kref.h>
|
||||||
|
@ -244,6 +15,337 @@ struct tty_driver;
|
||||||
struct serial_icounter_struct;
|
struct serial_icounter_struct;
|
||||||
struct serial_struct;
|
struct serial_struct;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct tty_operations -- interface between driver and tty
|
||||||
|
*
|
||||||
|
* @lookup: ``struct tty_struct *()(struct tty_driver *self, struct file *,
|
||||||
|
* int idx)``
|
||||||
|
*
|
||||||
|
* Return the tty device corresponding to @idx, %NULL if there is not
|
||||||
|
* one currently in use and an %ERR_PTR value on error. Called under
|
||||||
|
* %tty_mutex (for now!)
|
||||||
|
*
|
||||||
|
* Optional method. Default behaviour is to use the @self->ttys array.
|
||||||
|
*
|
||||||
|
* @install: ``int ()(struct tty_driver *self, struct tty_struct *tty)``
|
||||||
|
*
|
||||||
|
* Install a new @tty into the @self's internal tables. Used in
|
||||||
|
* conjunction with @lookup and @remove methods.
|
||||||
|
*
|
||||||
|
* Optional method. Default behaviour is to use the @self->ttys array.
|
||||||
|
*
|
||||||
|
* @remove: ``void ()(struct tty_driver *self, struct tty_struct *tty)``
|
||||||
|
*
|
||||||
|
* Remove a closed @tty from the @self's internal tables. Used in
|
||||||
|
* conjunction with @lookup and @remove methods.
|
||||||
|
*
|
||||||
|
* Optional method. Default behaviour is to use the @self->ttys array.
|
||||||
|
*
|
||||||
|
* @open: ``int ()(struct tty_struct *tty, struct file *)``
|
||||||
|
*
|
||||||
|
* This routine is called when a particular @tty device is opened. This
|
||||||
|
* routine is mandatory; if this routine is not filled in, the attempted
|
||||||
|
* open will fail with %ENODEV.
|
||||||
|
*
|
||||||
|
* Required method. Called with tty lock held. May sleep.
|
||||||
|
*
|
||||||
|
* @close: ``void ()(struct tty_struct *tty, struct file *)``
|
||||||
|
*
|
||||||
|
* This routine is called when a particular @tty device is closed. At the
|
||||||
|
* point of return from this call the driver must make no further ldisc
|
||||||
|
* calls of any kind.
|
||||||
|
*
|
||||||
|
* Remark: called even if the corresponding @open() failed.
|
||||||
|
*
|
||||||
|
* Required method. Called with tty lock held. May sleep.
|
||||||
|
*
|
||||||
|
* @shutdown: ``void ()(struct tty_struct *tty)``
|
||||||
|
*
|
||||||
|
* This routine is called under the tty lock when a particular @tty device
|
||||||
|
* is closed for the last time. It executes before the @tty resources
|
||||||
|
* are freed so may execute while another function holds a @tty kref.
|
||||||
|
*
|
||||||
|
* @cleanup: ``void ()(struct tty_struct *tty)``
|
||||||
|
*
|
||||||
|
* This routine is called asynchronously when a particular @tty device
|
||||||
|
* is closed for the last time freeing up the resources. This is
|
||||||
|
* actually the second part of shutdown for routines that might sleep.
|
||||||
|
*
|
||||||
|
* @write: ``int ()(struct tty_struct *tty, const unsigned char *buf,
|
||||||
|
* int count)``
|
||||||
|
*
|
||||||
|
* This routine is called by the kernel to write a series (@count) of
|
||||||
|
* characters (@buf) to the @tty device. The characters may come from
|
||||||
|
* user space or kernel space. This routine will return the
|
||||||
|
* number of characters actually accepted for writing.
|
||||||
|
*
|
||||||
|
* May occur in parallel in special cases. Because this includes panic
|
||||||
|
* paths drivers generally shouldn't try and do clever locking here.
|
||||||
|
*
|
||||||
|
* Optional: Required for writable devices. May not sleep.
|
||||||
|
*
|
||||||
|
* @put_char: ``int ()(struct tty_struct *tty, unsigned char ch)``
|
||||||
|
*
|
||||||
|
* This routine is called by the kernel to write a single character @ch to
|
||||||
|
* the @tty device. If the kernel uses this routine, it must call the
|
||||||
|
* @flush_chars() routine (if defined) when it is done stuffing characters
|
||||||
|
* into the driver. If there is no room in the queue, the character is
|
||||||
|
* ignored.
|
||||||
|
*
|
||||||
|
* Optional: Kernel will use the @write method if not provided. Do not
|
||||||
|
* call this function directly, call tty_put_char().
|
||||||
|
*
|
||||||
|
* @flush_chars: ``void ()(struct tty_struct *tty)``
|
||||||
|
*
|
||||||
|
* This routine is called by the kernel after it has written a
|
||||||
|
* series of characters to the tty device using @put_char().
|
||||||
|
*
|
||||||
|
* Optional. Do not call this function directly, call
|
||||||
|
* tty_driver_flush_chars().
|
||||||
|
*
|
||||||
|
* @write_room: ``unsigned int ()(struct tty_struct *tty)``
|
||||||
|
*
|
||||||
|
* This routine returns the numbers of characters the @tty driver
|
||||||
|
* will accept for queuing to be written. This number is subject
|
||||||
|
* to change as output buffers get emptied, or if the output flow
|
||||||
|
* control is acted.
|
||||||
|
*
|
||||||
|
* The ldisc is responsible for being intelligent about multi-threading of
|
||||||
|
* write_room/write calls
|
||||||
|
*
|
||||||
|
* Required if @write method is provided else not needed. Do not call this
|
||||||
|
* function directly, call tty_write_room()
|
||||||
|
*
|
||||||
|
* @chars_in_buffer: ``unsigned int ()(struct tty_struct *tty)``
|
||||||
|
*
|
||||||
|
* This routine returns the number of characters in the device private
|
||||||
|
* output queue. Used in tty_wait_until_sent() and for poll()
|
||||||
|
* implementation.
|
||||||
|
*
|
||||||
|
* Optional: if not provided, it is assumed there is no queue on the
|
||||||
|
* device. Do not call this function directly, call tty_chars_in_buffer().
|
||||||
|
*
|
||||||
|
* @ioctl: ``int ()(struct tty_struct *tty, unsigned int cmd,
|
||||||
|
* unsigned long arg)``
|
||||||
|
*
|
||||||
|
* This routine allows the @tty driver to implement device-specific
|
||||||
|
* ioctls. If the ioctl number passed in @cmd is not recognized by the
|
||||||
|
* driver, it should return %ENOIOCTLCMD.
|
||||||
|
*
|
||||||
|
* Optional.
|
||||||
|
*
|
||||||
|
* @compat_ioctl: ``long ()(struct tty_struct *tty, unsigned int cmd,
|
||||||
|
* unsigned long arg)``
|
||||||
|
*
|
||||||
|
* Implement ioctl processing for 32 bit process on 64 bit system.
|
||||||
|
*
|
||||||
|
* Optional.
|
||||||
|
*
|
||||||
|
* @set_termios: ``void ()(struct tty_struct *tty, struct ktermios *old)``
|
||||||
|
*
|
||||||
|
* This routine allows the @tty driver to be notified when device's
|
||||||
|
* termios settings have changed. New settings are in @tty->termios.
|
||||||
|
* Previous settings are passed in the @old argument.
|
||||||
|
*
|
||||||
|
* The API is defined such that the driver should return the actual modes
|
||||||
|
* selected. This means that the driver is responsible for modifying any
|
||||||
|
* bits in @tty->termios it cannot fulfill to indicate the actual modes
|
||||||
|
* being used.
|
||||||
|
*
|
||||||
|
* Optional. Called under the @tty->termios_rwsem. May sleep.
|
||||||
|
*
|
||||||
|
* @set_ldisc: ``void ()(struct tty_struct *tty)``
|
||||||
|
*
|
||||||
|
* This routine allows the @tty driver to be notified when the device's
|
||||||
|
* line discipline is being changed. At the point this is done the
|
||||||
|
* discipline is not yet usable.
|
||||||
|
*
|
||||||
|
* Optional. Called under the @tty->ldisc_sem and @tty->termios_rwsem.
|
||||||
|
*
|
||||||
|
* @throttle: ``void ()(struct tty_struct *tty)``
|
||||||
|
*
|
||||||
|
* This routine notifies the @tty driver that input buffers for the line
|
||||||
|
* discipline are close to full, and it should somehow signal that no more
|
||||||
|
* characters should be sent to the @tty.
|
||||||
|
*
|
||||||
|
* Serialization including with @unthrottle() is the job of the ldisc
|
||||||
|
* layer.
|
||||||
|
*
|
||||||
|
* Optional: Always invoke via tty_throttle_safe(). Called under the
|
||||||
|
* @tty->termios_rwsem.
|
||||||
|
*
|
||||||
|
* @unthrottle: ``void ()(struct tty_struct *tty)``
|
||||||
|
*
|
||||||
|
* This routine notifies the @tty driver that it should signal that
|
||||||
|
* characters can now be sent to the @tty without fear of overrunning the
|
||||||
|
* input buffers of the line disciplines.
|
||||||
|
*
|
||||||
|
* Optional. Always invoke via tty_unthrottle(). Called under the
|
||||||
|
* @tty->termios_rwsem.
|
||||||
|
*
|
||||||
|
* @stop: ``void ()(struct tty_struct *tty)``
|
||||||
|
*
|
||||||
|
* This routine notifies the @tty driver that it should stop outputting
|
||||||
|
* characters to the tty device.
|
||||||
|
*
|
||||||
|
* Called with @tty->flow.lock held. Serialized with @start() method.
|
||||||
|
*
|
||||||
|
* Optional. Always invoke via stop_tty().
|
||||||
|
*
|
||||||
|
* @start: ``void ()(struct tty_struct *tty)``
|
||||||
|
*
|
||||||
|
* This routine notifies the @tty driver that it resumed sending
|
||||||
|
* characters to the @tty device.
|
||||||
|
*
|
||||||
|
* Called with @tty->flow.lock held. Serialized with stop() method.
|
||||||
|
*
|
||||||
|
* Optional. Always invoke via start_tty().
|
||||||
|
*
|
||||||
|
* @hangup: ``void ()(struct tty_struct *tty)``
|
||||||
|
*
|
||||||
|
* This routine notifies the @tty driver that it should hang up the @tty
|
||||||
|
* device.
|
||||||
|
*
|
||||||
|
* Optional. Called with tty lock held.
|
||||||
|
*
|
||||||
|
* @break_ctl: ``int ()(struct tty_struct *tty, int state)``
|
||||||
|
*
|
||||||
|
* This optional routine requests the @tty driver to turn on or off BREAK
|
||||||
|
* status on the RS-232 port. If @state is -1, then the BREAK status
|
||||||
|
* should be turned on; if @state is 0, then BREAK should be turned off.
|
||||||
|
*
|
||||||
|
* If this routine is implemented, the high-level tty driver will handle
|
||||||
|
* the following ioctls: %TCSBRK, %TCSBRKP, %TIOCSBRK, %TIOCCBRK.
|
||||||
|
*
|
||||||
|
* If the driver sets %TTY_DRIVER_HARDWARE_BREAK in tty_alloc_driver(),
|
||||||
|
* then the interface will also be called with actual times and the
|
||||||
|
* hardware is expected to do the delay work itself. 0 and -1 are still
|
||||||
|
* used for on/off.
|
||||||
|
*
|
||||||
|
* Optional: Required for %TCSBRK/%BRKP/etc. handling. May sleep.
|
||||||
|
*
|
||||||
|
* @flush_buffer: ``void ()(struct tty_struct *tty)``
|
||||||
|
*
|
||||||
|
* This routine discards device private output buffer. Invoked on close,
|
||||||
|
* hangup, to implement %TCOFLUSH ioctl and similar.
|
||||||
|
*
|
||||||
|
* Optional: if not provided, it is assumed there is no queue on the
|
||||||
|
* device. Do not call this function directly, call
|
||||||
|
* tty_driver_flush_buffer().
|
||||||
|
*
|
||||||
|
* @wait_until_sent: ``void ()(struct tty_struct *tty, int timeout)``
|
||||||
|
*
|
||||||
|
* This routine waits until the device has written out all of the
|
||||||
|
* characters in its transmitter FIFO. Or until @timeout (in jiffies) is
|
||||||
|
* reached.
|
||||||
|
*
|
||||||
|
* Optional: If not provided, the device is assumed to have no FIFO.
|
||||||
|
* Usually correct to invoke via tty_wait_until_sent(). May sleep.
|
||||||
|
*
|
||||||
|
* @send_xchar: ``void ()(struct tty_struct *tty, char ch)``
|
||||||
|
*
|
||||||
|
* This routine is used to send a high-priority XON/XOFF character (@ch)
|
||||||
|
* to the @tty device.
|
||||||
|
*
|
||||||
|
* Optional: If not provided, then the @write method is called under
|
||||||
|
* the @tty->atomic_write_lock to keep it serialized with the ldisc.
|
||||||
|
*
|
||||||
|
* @tiocmget: ``int ()(struct tty_struct *tty)``
|
||||||
|
*
|
||||||
|
* This routine is used to obtain the modem status bits from the @tty
|
||||||
|
* driver.
|
||||||
|
*
|
||||||
|
* Optional: If not provided, then %ENOTTY is returned from the %TIOCMGET
|
||||||
|
* ioctl. Do not call this function directly, call tty_tiocmget().
|
||||||
|
*
|
||||||
|
* @tiocmset: ``int ()(struct tty_struct *tty,
|
||||||
|
* unsigned int set, unsigned int clear)``
|
||||||
|
*
|
||||||
|
* This routine is used to set the modem status bits to the @tty driver.
|
||||||
|
* First, @clear bits should be cleared, then @set bits set.
|
||||||
|
*
|
||||||
|
* Optional: If not provided, then %ENOTTY is returned from the %TIOCMSET
|
||||||
|
* ioctl. Do not call this function directly, call tty_tiocmset().
|
||||||
|
*
|
||||||
|
* @resize: ``int ()(struct tty_struct *tty, struct winsize *ws)``
|
||||||
|
*
|
||||||
|
* Called when a termios request is issued which changes the requested
|
||||||
|
* terminal geometry to @ws.
|
||||||
|
*
|
||||||
|
* Optional: the default action is to update the termios structure
|
||||||
|
* without error. This is usually the correct behaviour. Drivers should
|
||||||
|
* not force errors here if they are not resizable objects (e.g. a serial
|
||||||
|
* line). See tty_do_resize() if you need to wrap the standard method
|
||||||
|
* in your own logic -- the usual case.
|
||||||
|
*
|
||||||
|
* @get_icount: ``int ()(struct tty_struct *tty,
|
||||||
|
* struct serial_icounter *icount)``
|
||||||
|
*
|
||||||
|
* Called when the @tty device receives a %TIOCGICOUNT ioctl. Passed a
|
||||||
|
* kernel structure @icount to complete.
|
||||||
|
*
|
||||||
|
* Optional: called only if provided, otherwise %ENOTTY will be returned.
|
||||||
|
*
|
||||||
|
* @get_serial: ``int ()(struct tty_struct *tty, struct serial_struct *p)``
|
||||||
|
*
|
||||||
|
* Called when the @tty device receives a %TIOCGSERIAL ioctl. Passed a
|
||||||
|
* kernel structure @p (&struct serial_struct) to complete.
|
||||||
|
*
|
||||||
|
* Optional: called only if provided, otherwise %ENOTTY will be returned.
|
||||||
|
* Do not call this function directly, call tty_tiocgserial().
|
||||||
|
*
|
||||||
|
* @set_serial: ``int ()(struct tty_struct *tty, struct serial_struct *p)``
|
||||||
|
*
|
||||||
|
* Called when the @tty device receives a %TIOCSSERIAL ioctl. Passed a
|
||||||
|
* kernel structure @p (&struct serial_struct) to set the values from.
|
||||||
|
*
|
||||||
|
* Optional: called only if provided, otherwise %ENOTTY will be returned.
|
||||||
|
* Do not call this function directly, call tty_tiocsserial().
|
||||||
|
*
|
||||||
|
* @show_fdinfo: ``void ()(struct tty_struct *tty, struct seq_file *m)``
|
||||||
|
*
|
||||||
|
* Called when the @tty device file descriptor receives a fdinfo request
|
||||||
|
* from VFS (to show in /proc/<pid>/fdinfo/). @m should be filled with
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* Optional: called only if provided, otherwise nothing is written to @m.
|
||||||
|
* Do not call this function directly, call tty_show_fdinfo().
|
||||||
|
*
|
||||||
|
* @poll_init: ``int ()(struct tty_driver *driver, int line, char *options)``
|
||||||
|
*
|
||||||
|
* kgdboc support (Documentation/dev-tools/kgdb.rst). This routine is
|
||||||
|
* called to initialize the HW for later use by calling @poll_get_char or
|
||||||
|
* @poll_put_char.
|
||||||
|
*
|
||||||
|
* Optional: called only if provided, otherwise skipped as a non-polling
|
||||||
|
* driver.
|
||||||
|
*
|
||||||
|
* @poll_get_char: ``int ()(struct tty_driver *driver, int line)``
|
||||||
|
*
|
||||||
|
* kgdboc support (see @poll_init). @driver should read a character from a
|
||||||
|
* tty identified by @line and return it.
|
||||||
|
*
|
||||||
|
* Optional: called only if @poll_init provided.
|
||||||
|
*
|
||||||
|
* @poll_put_char: ``void ()(struct tty_driver *driver, int line, char ch)``
|
||||||
|
*
|
||||||
|
* kgdboc support (see @poll_init). @driver should write character @ch to
|
||||||
|
* a tty identified by @line.
|
||||||
|
*
|
||||||
|
* Optional: called only if @poll_init provided.
|
||||||
|
*
|
||||||
|
* @proc_show: ``int ()(struct seq_file *m, void *driver)``
|
||||||
|
*
|
||||||
|
* Driver @driver (cast to &struct tty_driver) can show additional info in
|
||||||
|
* /proc/tty/driver/<driver_name>. It is enough to fill in the information
|
||||||
|
* into @m.
|
||||||
|
*
|
||||||
|
* Optional: called only if provided, otherwise no /proc entry created.
|
||||||
|
*
|
||||||
|
* This structure defines the interface between the low-level tty driver and
|
||||||
|
* the tty routines. These routines can be defined. Unless noted otherwise,
|
||||||
|
* they are optional, and can be filled in with a %NULL pointer.
|
||||||
|
*/
|
||||||
struct tty_operations {
|
struct tty_operations {
|
||||||
struct tty_struct * (*lookup)(struct tty_driver *driver,
|
struct tty_struct * (*lookup)(struct tty_driver *driver,
|
||||||
struct file *filp, int idx);
|
struct file *filp, int idx);
|
||||||
|
@ -288,26 +390,64 @@ struct tty_operations {
|
||||||
int (*poll_get_char)(struct tty_driver *driver, int line);
|
int (*poll_get_char)(struct tty_driver *driver, int line);
|
||||||
void (*poll_put_char)(struct tty_driver *driver, int line, char ch);
|
void (*poll_put_char)(struct tty_driver *driver, int line, char ch);
|
||||||
#endif
|
#endif
|
||||||
int (*proc_show)(struct seq_file *, void *);
|
int (*proc_show)(struct seq_file *m, void *driver);
|
||||||
} __randomize_layout;
|
} __randomize_layout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct tty_driver -- driver for TTY devices
|
||||||
|
*
|
||||||
|
* @magic: set to %TTY_DRIVER_MAGIC in __tty_alloc_driver()
|
||||||
|
* @kref: reference counting. Reaching zero frees all the internals and the
|
||||||
|
* driver.
|
||||||
|
* @cdevs: allocated/registered character /dev devices
|
||||||
|
* @owner: modules owning this driver. Used drivers cannot be rmmod'ed.
|
||||||
|
* Automatically set by tty_alloc_driver().
|
||||||
|
* @driver_name: name of the driver used in /proc/tty
|
||||||
|
* @name: used for constructing /dev node name
|
||||||
|
* @name_base: used as a number base for constructing /dev node name
|
||||||
|
* @major: major /dev device number (zero for autoassignment)
|
||||||
|
* @minor_start: the first minor /dev device number
|
||||||
|
* @num: number of devices allocated
|
||||||
|
* @type: type of tty driver (%TTY_DRIVER_TYPE_)
|
||||||
|
* @subtype: subtype of tty driver (%SYSTEM_TYPE_, %PTY_TYPE_, %SERIAL_TYPE_)
|
||||||
|
* @init_termios: termios to set to each tty initially (e.g. %tty_std_termios)
|
||||||
|
* @flags: tty driver flags (%TTY_DRIVER_)
|
||||||
|
* @proc_entry: proc fs entry, used internally
|
||||||
|
* @other: driver of the linked tty; only used for the PTY driver
|
||||||
|
* @ttys: array of active &struct tty_struct, set by tty_standard_install()
|
||||||
|
* @ports: array of &struct tty_port; can be set during initialization by
|
||||||
|
* tty_port_link_device() and similar
|
||||||
|
* @termios: storage for termios at each TTY close for the next open
|
||||||
|
* @driver_state: pointer to driver's arbitrary data
|
||||||
|
* @ops: driver hooks for TTYs. Set them using tty_set_operations(). Use &struct
|
||||||
|
* tty_port helpers in them as much as possible.
|
||||||
|
* @tty_drivers: used internally to link tty_drivers together
|
||||||
|
*
|
||||||
|
* The usual handling of &struct tty_driver is to allocate it by
|
||||||
|
* tty_alloc_driver(), set up all the necessary members, and register it by
|
||||||
|
* tty_register_driver(). At last, the driver is torn down by calling
|
||||||
|
* tty_unregister_driver() followed by tty_driver_kref_put().
|
||||||
|
*
|
||||||
|
* The fields required to be set before calling tty_register_driver() include
|
||||||
|
* @driver_name, @name, @type, @subtype, @init_termios, and @ops.
|
||||||
|
*/
|
||||||
struct tty_driver {
|
struct tty_driver {
|
||||||
int magic; /* magic number for this structure */
|
int magic;
|
||||||
struct kref kref; /* Reference management */
|
struct kref kref;
|
||||||
struct cdev **cdevs;
|
struct cdev **cdevs;
|
||||||
struct module *owner;
|
struct module *owner;
|
||||||
const char *driver_name;
|
const char *driver_name;
|
||||||
const char *name;
|
const char *name;
|
||||||
int name_base; /* offset of printed name */
|
int name_base;
|
||||||
int major; /* major device number */
|
int major;
|
||||||
int minor_start; /* start of minor device number */
|
int minor_start;
|
||||||
unsigned int num; /* number of devices allocated */
|
unsigned int num;
|
||||||
short type; /* type of tty driver */
|
short type;
|
||||||
short subtype; /* subtype of tty driver */
|
short subtype;
|
||||||
struct ktermios init_termios; /* Initial termios */
|
struct ktermios init_termios;
|
||||||
unsigned long flags; /* tty driver flags */
|
unsigned long flags;
|
||||||
struct proc_dir_entry *proc_entry; /* /proc fs entry */
|
struct proc_dir_entry *proc_entry;
|
||||||
struct tty_driver *other; /* only used for the PTY driver */
|
struct tty_driver *other;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pointer to the tty data structures
|
* Pointer to the tty data structures
|
||||||
|
@ -352,49 +492,53 @@ static inline void tty_set_operations(struct tty_driver *driver,
|
||||||
/* tty driver magic number */
|
/* tty driver magic number */
|
||||||
#define TTY_DRIVER_MAGIC 0x5402
|
#define TTY_DRIVER_MAGIC 0x5402
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* tty driver flags
|
* DOC: TTY Driver Flags
|
||||||
*
|
*
|
||||||
* TTY_DRIVER_RESET_TERMIOS --- requests the tty layer to reset the
|
* TTY_DRIVER_RESET_TERMIOS
|
||||||
* termios setting when the last process has closed the device.
|
* Requests the tty layer to reset the termios setting when the last
|
||||||
* Used for PTY's, in particular.
|
* process has closed the device. Used for PTYs, in particular.
|
||||||
*
|
*
|
||||||
* TTY_DRIVER_REAL_RAW --- if set, indicates that the driver will
|
* TTY_DRIVER_REAL_RAW
|
||||||
* guarantee never to set any special character handling
|
* Indicates that the driver will guarantee not to set any special
|
||||||
* flags if ((IGNBRK || (!BRKINT && !PARMRK)) && (IGNPAR ||
|
* character handling flags if this is set for the tty:
|
||||||
* !INPCK)). That is, if there is no reason for the driver to
|
|
||||||
* send notifications of parity and break characters up to the
|
|
||||||
* line driver, it won't do so. This allows the line driver to
|
|
||||||
* optimize for this case if this flag is set. (Note that there
|
|
||||||
* is also a promise, if the above case is true, not to signal
|
|
||||||
* overruns, either.)
|
|
||||||
*
|
*
|
||||||
* TTY_DRIVER_DYNAMIC_DEV --- if set, the individual tty devices need
|
* ``(IGNBRK || (!BRKINT && !PARMRK)) && (IGNPAR || !INPCK)``
|
||||||
* to be registered with a call to tty_register_device() when the
|
|
||||||
* device is found in the system and unregistered with a call to
|
|
||||||
* tty_unregister_device() so the devices will be show up
|
|
||||||
* properly in sysfs. If not set, driver->num entries will be
|
|
||||||
* created by the tty core in sysfs when tty_register_driver() is
|
|
||||||
* called. This is to be used by drivers that have tty devices
|
|
||||||
* that can appear and disappear while the main tty driver is
|
|
||||||
* registered with the tty core.
|
|
||||||
*
|
*
|
||||||
* TTY_DRIVER_DEVPTS_MEM -- don't use the standard arrays, instead
|
* That is, if there is no reason for the driver to
|
||||||
* use dynamic memory keyed through the devpts filesystem. This
|
* send notifications of parity and break characters up to the line
|
||||||
* is only applicable to the pty driver.
|
* driver, it won't do so. This allows the line driver to optimize for
|
||||||
|
* this case if this flag is set. (Note that there is also a promise, if
|
||||||
|
* the above case is true, not to signal overruns, either.)
|
||||||
*
|
*
|
||||||
* TTY_DRIVER_HARDWARE_BREAK -- hardware handles break signals. Pass
|
* TTY_DRIVER_DYNAMIC_DEV
|
||||||
* the requested timeout to the caller instead of using a simple
|
* The individual tty devices need to be registered with a call to
|
||||||
* on/off interface.
|
* tty_register_device() when the device is found in the system and
|
||||||
|
* unregistered with a call to tty_unregister_device() so the devices will
|
||||||
|
* be show up properly in sysfs. If not set, all &tty_driver.num entries
|
||||||
|
* will be created by the tty core in sysfs when tty_register_driver() is
|
||||||
|
* called. This is to be used by drivers that have tty devices that can
|
||||||
|
* appear and disappear while the main tty driver is registered with the
|
||||||
|
* tty core.
|
||||||
*
|
*
|
||||||
* TTY_DRIVER_DYNAMIC_ALLOC -- do not allocate structures which are
|
* TTY_DRIVER_DEVPTS_MEM
|
||||||
* needed per line for this driver as it would waste memory.
|
* Don't use the standard arrays (&tty_driver.ttys and
|
||||||
* The driver will take care.
|
* &tty_driver.termios), instead use dynamic memory keyed through the
|
||||||
|
* devpts filesystem. This is only applicable to the PTY driver.
|
||||||
*
|
*
|
||||||
* TTY_DRIVER_UNNUMBERED_NODE -- do not create numbered /dev nodes. In
|
* TTY_DRIVER_HARDWARE_BREAK
|
||||||
* other words create /dev/ttyprintk and not /dev/ttyprintk0.
|
* Hardware handles break signals. Pass the requested timeout to the
|
||||||
* Applicable only when a driver for a single tty device is
|
* &tty_operations.break_ctl instead of using a simple on/off interface.
|
||||||
* being allocated.
|
*
|
||||||
|
* TTY_DRIVER_DYNAMIC_ALLOC
|
||||||
|
* Do not allocate structures which are needed per line for this driver
|
||||||
|
* (&tty_driver.ports) as it would waste memory. The driver will take
|
||||||
|
* care. This is only applicable to the PTY driver.
|
||||||
|
*
|
||||||
|
* TTY_DRIVER_UNNUMBERED_NODE
|
||||||
|
* Do not create numbered ``/dev`` nodes. For example, create
|
||||||
|
* ``/dev/ttyprintk`` and not ``/dev/ttyprintk0``. Applicable only when a
|
||||||
|
* driver for a single tty device is being allocated.
|
||||||
*/
|
*/
|
||||||
#define TTY_DRIVER_INSTALLED 0x0001
|
#define TTY_DRIVER_INSTALLED 0x0001
|
||||||
#define TTY_DRIVER_RESET_TERMIOS 0x0002
|
#define TTY_DRIVER_RESET_TERMIOS 0x0002
|
||||||
|
|
|
@ -17,7 +17,6 @@ int tty_insert_flip_string_fixed_flag(struct tty_port *port,
|
||||||
int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars,
|
int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars,
|
||||||
size_t size);
|
size_t size);
|
||||||
void tty_flip_buffer_push(struct tty_port *port);
|
void tty_flip_buffer_push(struct tty_port *port);
|
||||||
void tty_schedule_flip(struct tty_port *port);
|
|
||||||
int __tty_insert_flip_char(struct tty_port *port, unsigned char ch, char flag);
|
int __tty_insert_flip_char(struct tty_port *port, unsigned char ch, char flag);
|
||||||
|
|
||||||
static inline int tty_insert_flip_char(struct tty_port *port,
|
static inline int tty_insert_flip_char(struct tty_port *port,
|
||||||
|
|
|
@ -4,127 +4,6 @@
|
||||||
|
|
||||||
struct tty_struct;
|
struct tty_struct;
|
||||||
|
|
||||||
/*
|
|
||||||
* This structure defines the interface between the tty line discipline
|
|
||||||
* implementation and the tty routines. The following routines can be
|
|
||||||
* defined; unless noted otherwise, they are optional, and can be
|
|
||||||
* filled in with a null pointer.
|
|
||||||
*
|
|
||||||
* int (*open)(struct tty_struct *);
|
|
||||||
*
|
|
||||||
* This function is called when the line discipline is associated
|
|
||||||
* with the tty. The line discipline can use this as an
|
|
||||||
* opportunity to initialize any state needed by the ldisc routines.
|
|
||||||
*
|
|
||||||
* void (*close)(struct tty_struct *);
|
|
||||||
*
|
|
||||||
* This function is called when the line discipline is being
|
|
||||||
* shutdown, either because the tty is being closed or because
|
|
||||||
* the tty is being changed to use a new line discipline
|
|
||||||
*
|
|
||||||
* void (*flush_buffer)(struct tty_struct *tty);
|
|
||||||
*
|
|
||||||
* This function instructs the line discipline to clear its
|
|
||||||
* buffers of any input characters it may have queued to be
|
|
||||||
* delivered to the user mode process.
|
|
||||||
*
|
|
||||||
* ssize_t (*read)(struct tty_struct * tty, struct file * file,
|
|
||||||
* unsigned char * buf, size_t nr);
|
|
||||||
*
|
|
||||||
* This function is called when the user requests to read from
|
|
||||||
* the tty. The line discipline will return whatever characters
|
|
||||||
* it has buffered up for the user. If this function is not
|
|
||||||
* defined, the user will receive an EIO error.
|
|
||||||
*
|
|
||||||
* ssize_t (*write)(struct tty_struct * tty, struct file * file,
|
|
||||||
* const unsigned char * buf, size_t nr);
|
|
||||||
*
|
|
||||||
* This function is called when the user requests to write to the
|
|
||||||
* tty. The line discipline will deliver the characters to the
|
|
||||||
* low-level tty device for transmission, optionally performing
|
|
||||||
* some processing on the characters first. If this function is
|
|
||||||
* not defined, the user will receive an EIO error.
|
|
||||||
*
|
|
||||||
* int (*ioctl)(struct tty_struct * tty, struct file * file,
|
|
||||||
* unsigned int cmd, unsigned long arg);
|
|
||||||
*
|
|
||||||
* This function is called when the user requests an ioctl which
|
|
||||||
* is not handled by the tty layer or the low-level tty driver.
|
|
||||||
* It is intended for ioctls which affect line discpline
|
|
||||||
* operation. Note that the search order for ioctls is (1) tty
|
|
||||||
* layer, (2) tty low-level driver, (3) line discpline. So a
|
|
||||||
* low-level driver can "grab" an ioctl request before the line
|
|
||||||
* discpline has a chance to see it.
|
|
||||||
*
|
|
||||||
* int (*compat_ioctl)(struct tty_struct * tty, struct file * file,
|
|
||||||
* unsigned int cmd, unsigned long arg);
|
|
||||||
*
|
|
||||||
* Process ioctl calls from 32-bit process on 64-bit system
|
|
||||||
*
|
|
||||||
* NOTE: only ioctls that are neither "pointer to compatible
|
|
||||||
* structure" nor tty-generic. Something private that takes
|
|
||||||
* an integer or a pointer to wordsize-sensitive structure
|
|
||||||
* belongs here, but most of ldiscs will happily leave
|
|
||||||
* it NULL.
|
|
||||||
*
|
|
||||||
* void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
|
|
||||||
*
|
|
||||||
* This function notifies the line discpline that a change has
|
|
||||||
* been made to the termios structure.
|
|
||||||
*
|
|
||||||
* int (*poll)(struct tty_struct * tty, struct file * file,
|
|
||||||
* poll_table *wait);
|
|
||||||
*
|
|
||||||
* This function is called when a user attempts to select/poll on a
|
|
||||||
* tty device. It is solely the responsibility of the line
|
|
||||||
* discipline to handle poll requests.
|
|
||||||
*
|
|
||||||
* void (*receive_buf)(struct tty_struct *, const unsigned char *cp,
|
|
||||||
* char *fp, int count);
|
|
||||||
*
|
|
||||||
* This function is called by the low-level tty driver to send
|
|
||||||
* characters received by the hardware to the line discpline for
|
|
||||||
* processing. <cp> is a pointer to the buffer of input
|
|
||||||
* character received by the device. <fp> is a pointer to a
|
|
||||||
* pointer of flag bytes which indicate whether a character was
|
|
||||||
* received with a parity error, etc. <fp> may be NULL to indicate
|
|
||||||
* all data received is TTY_NORMAL.
|
|
||||||
*
|
|
||||||
* void (*write_wakeup)(struct tty_struct *);
|
|
||||||
*
|
|
||||||
* This function is called by the low-level tty driver to signal
|
|
||||||
* that line discpline should try to send more characters to the
|
|
||||||
* low-level driver for transmission. If the line discpline does
|
|
||||||
* not have any more data to send, it can just return. If the line
|
|
||||||
* discipline does have some data to send, please arise a tasklet
|
|
||||||
* or workqueue to do the real data transfer. Do not send data in
|
|
||||||
* this hook, it may leads to a deadlock.
|
|
||||||
*
|
|
||||||
* int (*hangup)(struct tty_struct *)
|
|
||||||
*
|
|
||||||
* Called on a hangup. Tells the discipline that it should
|
|
||||||
* cease I/O to the tty driver. Can sleep. The driver should
|
|
||||||
* seek to perform this action quickly but should wait until
|
|
||||||
* any pending driver I/O is completed.
|
|
||||||
*
|
|
||||||
* void (*dcd_change)(struct tty_struct *tty, unsigned int status)
|
|
||||||
*
|
|
||||||
* Tells the discipline that the DCD pin has changed its status.
|
|
||||||
* Used exclusively by the N_PPS (Pulse-Per-Second) line discipline.
|
|
||||||
*
|
|
||||||
* int (*receive_buf2)(struct tty_struct *, const unsigned char *cp,
|
|
||||||
* char *fp, int count);
|
|
||||||
*
|
|
||||||
* This function is called by the low-level tty driver to send
|
|
||||||
* characters received by the hardware to the line discpline for
|
|
||||||
* processing. <cp> is a pointer to the buffer of input
|
|
||||||
* character received by the device. <fp> is a pointer to a
|
|
||||||
* pointer of flag bytes which indicate whether a character was
|
|
||||||
* received with a parity error, etc. <fp> may be NULL to indicate
|
|
||||||
* all data received is TTY_NORMAL.
|
|
||||||
* If assigned, prefer this function for automatic flow control.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <linux/atomic.h>
|
#include <linux/atomic.h>
|
||||||
|
@ -176,7 +55,147 @@ int ldsem_down_write_nested(struct ld_semaphore *sem, int subclass,
|
||||||
ldsem_down_write(sem, timeout)
|
ldsem_down_write(sem, timeout)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct tty_ldisc_ops - ldisc operations
|
||||||
|
*
|
||||||
|
* @name: name of this ldisc rendered in /proc/tty/ldiscs
|
||||||
|
* @num: ``N_*`` number (%N_TTY, %N_HDLC, ...) reserved to this ldisc
|
||||||
|
*
|
||||||
|
* @open: [TTY] ``int ()(struct tty_struct *tty)``
|
||||||
|
*
|
||||||
|
* This function is called when the line discipline is associated with the
|
||||||
|
* @tty. No other call into the line discipline for this tty will occur
|
||||||
|
* until it completes successfully. It should initialize any state needed
|
||||||
|
* by the ldisc, and set @tty->receive_room to the maximum amount of data
|
||||||
|
* the line discipline is willing to accept from the driver with a single
|
||||||
|
* call to @receive_buf(). Returning an error will prevent the ldisc from
|
||||||
|
* being attached.
|
||||||
|
*
|
||||||
|
* Can sleep.
|
||||||
|
*
|
||||||
|
* @close: [TTY] ``void ()(struct tty_struct *tty)``
|
||||||
|
*
|
||||||
|
* This function is called when the line discipline is being shutdown,
|
||||||
|
* either because the @tty is being closed or because the @tty is being
|
||||||
|
* changed to use a new line discipline. At the point of execution no
|
||||||
|
* further users will enter the ldisc code for this tty.
|
||||||
|
*
|
||||||
|
* Can sleep.
|
||||||
|
*
|
||||||
|
* @flush_buffer: [TTY] ``void ()(struct tty_struct *tty)``
|
||||||
|
*
|
||||||
|
* This function instructs the line discipline to clear its buffers of any
|
||||||
|
* input characters it may have queued to be delivered to the user mode
|
||||||
|
* process. It may be called at any point between open and close.
|
||||||
|
*
|
||||||
|
* @read: [TTY] ``ssize_t ()(struct tty_struct *tty, struct file *file,
|
||||||
|
* unsigned char *buf, size_t nr)``
|
||||||
|
*
|
||||||
|
* This function is called when the user requests to read from the @tty.
|
||||||
|
* The line discipline will return whatever characters it has buffered up
|
||||||
|
* for the user. If this function is not defined, the user will receive
|
||||||
|
* an %EIO error. Multiple read calls may occur in parallel and the ldisc
|
||||||
|
* must deal with serialization issues.
|
||||||
|
*
|
||||||
|
* Can sleep.
|
||||||
|
*
|
||||||
|
* @write: [TTY] ``ssize_t ()(struct tty_struct *tty, struct file *file,
|
||||||
|
* const unsigned char *buf, size_t nr)``
|
||||||
|
*
|
||||||
|
* This function is called when the user requests to write to the @tty.
|
||||||
|
* The line discipline will deliver the characters to the low-level tty
|
||||||
|
* device for transmission, optionally performing some processing on the
|
||||||
|
* characters first. If this function is not defined, the user will
|
||||||
|
* receive an %EIO error.
|
||||||
|
*
|
||||||
|
* Can sleep.
|
||||||
|
*
|
||||||
|
* @ioctl: [TTY] ``int ()(struct tty_struct *tty, unsigned int cmd,
|
||||||
|
* unsigned long arg)``
|
||||||
|
*
|
||||||
|
* This function is called when the user requests an ioctl which is not
|
||||||
|
* handled by the tty layer or the low-level tty driver. It is intended
|
||||||
|
* for ioctls which affect line discpline operation. Note that the search
|
||||||
|
* order for ioctls is (1) tty layer, (2) tty low-level driver, (3) line
|
||||||
|
* discpline. So a low-level driver can "grab" an ioctl request before
|
||||||
|
* the line discpline has a chance to see it.
|
||||||
|
*
|
||||||
|
* @compat_ioctl: [TTY] ``int ()(struct tty_struct *tty, unsigned int cmd,
|
||||||
|
* unsigned long arg)``
|
||||||
|
*
|
||||||
|
* Process ioctl calls from 32-bit process on 64-bit system.
|
||||||
|
*
|
||||||
|
* Note that only ioctls that are neither "pointer to compatible
|
||||||
|
* structure" nor tty-generic. Something private that takes an integer or
|
||||||
|
* a pointer to wordsize-sensitive structure belongs here, but most of
|
||||||
|
* ldiscs will happily leave it %NULL.
|
||||||
|
*
|
||||||
|
* @set_termios: [TTY] ``void ()(struct tty_struct *tty, struct ktermios *old)``
|
||||||
|
*
|
||||||
|
* This function notifies the line discpline that a change has been made
|
||||||
|
* to the termios structure.
|
||||||
|
*
|
||||||
|
* @poll: [TTY] ``int ()(struct tty_struct *tty, struct file *file,
|
||||||
|
* struct poll_table_struct *wait)``
|
||||||
|
*
|
||||||
|
* This function is called when a user attempts to select/poll on a @tty
|
||||||
|
* device. It is solely the responsibility of the line discipline to
|
||||||
|
* handle poll requests.
|
||||||
|
*
|
||||||
|
* @hangup: [TTY] ``void ()(struct tty_struct *tty)``
|
||||||
|
*
|
||||||
|
* Called on a hangup. Tells the discipline that it should cease I/O to
|
||||||
|
* the tty driver. The driver should seek to perform this action quickly
|
||||||
|
* but should wait until any pending driver I/O is completed. No further
|
||||||
|
* calls into the ldisc code will occur.
|
||||||
|
*
|
||||||
|
* Can sleep.
|
||||||
|
*
|
||||||
|
* @receive_buf: [DRV] ``void ()(struct tty_struct *tty,
|
||||||
|
* const unsigned char *cp, const char *fp, int count)``
|
||||||
|
*
|
||||||
|
* This function is called by the low-level tty driver to send characters
|
||||||
|
* received by the hardware to the line discpline for processing. @cp is
|
||||||
|
* a pointer to the buffer of input character received by the device. @fp
|
||||||
|
* is a pointer to an array of flag bytes which indicate whether a
|
||||||
|
* character was received with a parity error, etc. @fp may be %NULL to
|
||||||
|
* indicate all data received is %TTY_NORMAL.
|
||||||
|
*
|
||||||
|
* @write_wakeup: [DRV] ``void ()(struct tty_struct *tty)``
|
||||||
|
*
|
||||||
|
* This function is called by the low-level tty driver to signal that line
|
||||||
|
* discpline should try to send more characters to the low-level driver
|
||||||
|
* for transmission. If the line discpline does not have any more data to
|
||||||
|
* send, it can just return. If the line discipline does have some data to
|
||||||
|
* send, please arise a tasklet or workqueue to do the real data transfer.
|
||||||
|
* Do not send data in this hook, it may lead to a deadlock.
|
||||||
|
*
|
||||||
|
* @dcd_change: [DRV] ``void ()(struct tty_struct *tty, unsigned int status)``
|
||||||
|
*
|
||||||
|
* Tells the discipline that the DCD pin has changed its status. Used
|
||||||
|
* exclusively by the %N_PPS (Pulse-Per-Second) line discipline.
|
||||||
|
*
|
||||||
|
* @receive_buf2: [DRV] ``int ()(struct tty_struct *tty,
|
||||||
|
* const unsigned char *cp, const char *fp, int count)``
|
||||||
|
*
|
||||||
|
* This function is called by the low-level tty driver to send characters
|
||||||
|
* received by the hardware to the line discpline for processing. @cp is a
|
||||||
|
* pointer to the buffer of input character received by the device. @fp
|
||||||
|
* is a pointer to an array of flag bytes which indicate whether a
|
||||||
|
* character was received with a parity error, etc. @fp may be %NULL to
|
||||||
|
* indicate all data received is %TTY_NORMAL. If assigned, prefer this
|
||||||
|
* function for automatic flow control.
|
||||||
|
*
|
||||||
|
* @owner: module containting this ldisc (for reference counting)
|
||||||
|
*
|
||||||
|
* This structure defines the interface between the tty line discipline
|
||||||
|
* implementation and the tty routines. The above routines can be defined.
|
||||||
|
* Unless noted otherwise, they are optional, and can be filled in with a %NULL
|
||||||
|
* pointer.
|
||||||
|
*
|
||||||
|
* Hooks marked [TTY] are invoked from the TTY core, the [DRV] ones from the
|
||||||
|
* tty_driver side.
|
||||||
|
*/
|
||||||
struct tty_ldisc_ops {
|
struct tty_ldisc_ops {
|
||||||
char *name;
|
char *name;
|
||||||
int num;
|
int num;
|
||||||
|
@ -184,31 +203,31 @@ struct tty_ldisc_ops {
|
||||||
/*
|
/*
|
||||||
* The following routines are called from above.
|
* The following routines are called from above.
|
||||||
*/
|
*/
|
||||||
int (*open)(struct tty_struct *);
|
int (*open)(struct tty_struct *tty);
|
||||||
void (*close)(struct tty_struct *);
|
void (*close)(struct tty_struct *tty);
|
||||||
void (*flush_buffer)(struct tty_struct *tty);
|
void (*flush_buffer)(struct tty_struct *tty);
|
||||||
ssize_t (*read)(struct tty_struct *tty, struct file *file,
|
ssize_t (*read)(struct tty_struct *tty, struct file *file,
|
||||||
unsigned char *buf, size_t nr,
|
unsigned char *buf, size_t nr,
|
||||||
void **cookie, unsigned long offset);
|
void **cookie, unsigned long offset);
|
||||||
ssize_t (*write)(struct tty_struct *tty, struct file *file,
|
ssize_t (*write)(struct tty_struct *tty, struct file *file,
|
||||||
const unsigned char *buf, size_t nr);
|
const unsigned char *buf, size_t nr);
|
||||||
int (*ioctl)(struct tty_struct *tty, struct file *file,
|
int (*ioctl)(struct tty_struct *tty, unsigned int cmd,
|
||||||
unsigned int cmd, unsigned long arg);
|
unsigned long arg);
|
||||||
int (*compat_ioctl)(struct tty_struct *tty, struct file *file,
|
int (*compat_ioctl)(struct tty_struct *tty, unsigned int cmd,
|
||||||
unsigned int cmd, unsigned long arg);
|
unsigned long arg);
|
||||||
void (*set_termios)(struct tty_struct *tty, struct ktermios *old);
|
void (*set_termios)(struct tty_struct *tty, struct ktermios *old);
|
||||||
__poll_t (*poll)(struct tty_struct *, struct file *,
|
__poll_t (*poll)(struct tty_struct *tty, struct file *file,
|
||||||
struct poll_table_struct *);
|
struct poll_table_struct *wait);
|
||||||
void (*hangup)(struct tty_struct *tty);
|
void (*hangup)(struct tty_struct *tty);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The following routines are called from below.
|
* The following routines are called from below.
|
||||||
*/
|
*/
|
||||||
void (*receive_buf)(struct tty_struct *, const unsigned char *cp,
|
void (*receive_buf)(struct tty_struct *tty, const unsigned char *cp,
|
||||||
const char *fp, int count);
|
const char *fp, int count);
|
||||||
void (*write_wakeup)(struct tty_struct *);
|
void (*write_wakeup)(struct tty_struct *tty);
|
||||||
void (*dcd_change)(struct tty_struct *, unsigned int);
|
void (*dcd_change)(struct tty_struct *tty, unsigned int status);
|
||||||
int (*receive_buf2)(struct tty_struct *, const unsigned char *cp,
|
int (*receive_buf2)(struct tty_struct *tty, const unsigned char *cp,
|
||||||
const char *fp, int count);
|
const char *fp, int count);
|
||||||
|
|
||||||
struct module *owner;
|
struct module *owner;
|
||||||
|
|
|
@ -7,37 +7,33 @@
|
||||||
#include <linux/tty_buffer.h>
|
#include <linux/tty_buffer.h>
|
||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
|
|
||||||
/*
|
|
||||||
* Port level information. Each device keeps its own port level information
|
|
||||||
* so provide a common structure for those ports wanting to use common support
|
|
||||||
* routines.
|
|
||||||
*
|
|
||||||
* The tty port has a different lifetime to the tty so must be kept apart.
|
|
||||||
* In addition be careful as tty -> port mappings are valid for the life
|
|
||||||
* of the tty object but in many cases port -> tty mappings are valid only
|
|
||||||
* until a hangup so don't use the wrong path.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct attribute_group;
|
struct attribute_group;
|
||||||
struct tty_driver;
|
struct tty_driver;
|
||||||
struct tty_port;
|
struct tty_port;
|
||||||
struct tty_struct;
|
struct tty_struct;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct tty_port_operations -- operations on tty_port
|
||||||
|
* @carrier_raised: return 1 if the carrier is raised on @port
|
||||||
|
* @dtr_rts: raise the DTR line if @raise is nonzero, otherwise lower DTR
|
||||||
|
* @shutdown: called when the last close completes or a hangup finishes IFF the
|
||||||
|
* port was initialized. Do not use to free resources. Turn off the device
|
||||||
|
* only. Called under the port mutex to serialize against @activate and
|
||||||
|
* @shutdown.
|
||||||
|
* @activate: called under the port mutex from tty_port_open(), serialized using
|
||||||
|
* the port mutex. Supposed to turn on the device.
|
||||||
|
*
|
||||||
|
* FIXME: long term getting the tty argument *out* of this would be good
|
||||||
|
* for consoles.
|
||||||
|
*
|
||||||
|
* @destruct: called on the final put of a port. Free resources, possibly incl.
|
||||||
|
* the port itself.
|
||||||
|
*/
|
||||||
struct tty_port_operations {
|
struct tty_port_operations {
|
||||||
/* Return 1 if the carrier is raised */
|
|
||||||
int (*carrier_raised)(struct tty_port *port);
|
int (*carrier_raised)(struct tty_port *port);
|
||||||
/* Control the DTR line */
|
|
||||||
void (*dtr_rts)(struct tty_port *port, int raise);
|
void (*dtr_rts)(struct tty_port *port, int raise);
|
||||||
/* Called when the last close completes or a hangup finishes
|
|
||||||
IFF the port was initialized. Do not use to free resources. Called
|
|
||||||
under the port mutex to serialize against activate/shutdowns */
|
|
||||||
void (*shutdown)(struct tty_port *port);
|
void (*shutdown)(struct tty_port *port);
|
||||||
/* Called under the port mutex from tty_port_open, serialized using
|
|
||||||
the port mutex */
|
|
||||||
/* FIXME: long term getting the tty argument *out* of this would be
|
|
||||||
good for consoles */
|
|
||||||
int (*activate)(struct tty_port *port, struct tty_struct *tty);
|
int (*activate)(struct tty_port *port, struct tty_struct *tty);
|
||||||
/* Called on the final put of a port */
|
|
||||||
void (*destruct)(struct tty_port *port);
|
void (*destruct)(struct tty_port *port);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,29 +44,76 @@ struct tty_port_client_operations {
|
||||||
|
|
||||||
extern const struct tty_port_client_operations tty_port_default_client_ops;
|
extern const struct tty_port_client_operations tty_port_default_client_ops;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct tty_port -- port level information
|
||||||
|
*
|
||||||
|
* @buf: buffer for this port, locked internally
|
||||||
|
* @tty: back pointer to &struct tty_struct, valid only if the tty is open. Use
|
||||||
|
* tty_port_tty_get() to obtain it (and tty_kref_put() to release).
|
||||||
|
* @itty: internal back pointer to &struct tty_struct. Avoid this. It should be
|
||||||
|
* eliminated in the long term.
|
||||||
|
* @ops: tty port operations (like activate, shutdown), see &struct
|
||||||
|
* tty_port_operations
|
||||||
|
* @client_ops: tty port client operations (like receive_buf, write_wakeup).
|
||||||
|
* By default, tty_port_default_client_ops is used.
|
||||||
|
* @lock: lock protecting @tty
|
||||||
|
* @blocked_open: # of procs waiting for open in tty_port_block_til_ready()
|
||||||
|
* @count: usage count
|
||||||
|
* @open_wait: open waiters queue (waiting e.g. for a carrier)
|
||||||
|
* @delta_msr_wait: modem status change queue (waiting for MSR changes)
|
||||||
|
* @flags: user TTY flags (%ASYNC_)
|
||||||
|
* @iflags: internal flags (%TTY_PORT_)
|
||||||
|
* @console: when set, the port is a console
|
||||||
|
* @mutex: locking, for open, shutdown and other port operations
|
||||||
|
* @buf_mutex: @xmit_buf alloc lock
|
||||||
|
* @xmit_buf: optional xmit buffer used by some drivers
|
||||||
|
* @close_delay: delay in jiffies to wait when closing the port
|
||||||
|
* @closing_wait: delay in jiffies for output to be sent before closing
|
||||||
|
* @drain_delay: set to zero if no pure time based drain is needed else set to
|
||||||
|
* size of fifo
|
||||||
|
* @kref: references counter. Reaching zero calls @ops->destruct() if non-%NULL
|
||||||
|
* or frees the port otherwise.
|
||||||
|
* @client_data: pointer to private data, for @client_ops
|
||||||
|
*
|
||||||
|
* Each device keeps its own port level information. &struct tty_port was
|
||||||
|
* introduced as a common structure for such information. As every TTY device
|
||||||
|
* shall have a backing tty_port structure, every driver can use these members.
|
||||||
|
*
|
||||||
|
* The tty port has a different lifetime to the tty so must be kept apart.
|
||||||
|
* In addition be careful as tty -> port mappings are valid for the life
|
||||||
|
* of the tty object but in many cases port -> tty mappings are valid only
|
||||||
|
* until a hangup so don't use the wrong path.
|
||||||
|
*
|
||||||
|
* Tty port shall be initialized by tty_port_init() and shut down either by
|
||||||
|
* tty_port_destroy() (refcounting not used), or tty_port_put() (refcounting).
|
||||||
|
*
|
||||||
|
* There is a lot of helpers around &struct tty_port too. To name the most
|
||||||
|
* significant ones: tty_port_open(), tty_port_close() (or
|
||||||
|
* tty_port_close_start() and tty_port_close_end() separately if need be), and
|
||||||
|
* tty_port_hangup(). These call @ops->activate() and @ops->shutdown() as
|
||||||
|
* needed.
|
||||||
|
*/
|
||||||
struct tty_port {
|
struct tty_port {
|
||||||
struct tty_bufhead buf; /* Locked internally */
|
struct tty_bufhead buf;
|
||||||
struct tty_struct *tty; /* Back pointer */
|
struct tty_struct *tty;
|
||||||
struct tty_struct *itty; /* internal back ptr */
|
struct tty_struct *itty;
|
||||||
const struct tty_port_operations *ops; /* Port operations */
|
const struct tty_port_operations *ops;
|
||||||
const struct tty_port_client_operations *client_ops; /* Port client operations */
|
const struct tty_port_client_operations *client_ops;
|
||||||
spinlock_t lock; /* Lock protecting tty field */
|
spinlock_t lock;
|
||||||
int blocked_open; /* Waiting to open */
|
int blocked_open;
|
||||||
int count; /* Usage count */
|
int count;
|
||||||
wait_queue_head_t open_wait; /* Open waiters */
|
wait_queue_head_t open_wait;
|
||||||
wait_queue_head_t delta_msr_wait; /* Modem status change */
|
wait_queue_head_t delta_msr_wait;
|
||||||
unsigned long flags; /* User TTY flags ASYNC_ */
|
unsigned long flags;
|
||||||
unsigned long iflags; /* Internal flags TTY_PORT_ */
|
unsigned long iflags;
|
||||||
unsigned char console:1; /* port is a console */
|
unsigned char console:1;
|
||||||
struct mutex mutex; /* Locking */
|
struct mutex mutex;
|
||||||
struct mutex buf_mutex; /* Buffer alloc lock */
|
struct mutex buf_mutex;
|
||||||
unsigned char *xmit_buf; /* Optional buffer */
|
unsigned char *xmit_buf;
|
||||||
unsigned int close_delay; /* Close port delay */
|
unsigned int close_delay;
|
||||||
unsigned int closing_wait; /* Delay for output */
|
unsigned int closing_wait;
|
||||||
int drain_delay; /* Set to zero if no pure time
|
int drain_delay;
|
||||||
based drain is needed else
|
struct kref kref;
|
||||||
set to size of fifo */
|
|
||||||
struct kref kref; /* Ref counter */
|
|
||||||
void *client_data;
|
void *client_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -317,14 +317,13 @@ static void nci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
|
||||||
* Arguments:
|
* Arguments:
|
||||||
*
|
*
|
||||||
* tty pointer to tty instance data
|
* tty pointer to tty instance data
|
||||||
* file pointer to open file object for device
|
|
||||||
* cmd IOCTL command code
|
* cmd IOCTL command code
|
||||||
* arg argument for IOCTL call (cmd dependent)
|
* arg argument for IOCTL call (cmd dependent)
|
||||||
*
|
*
|
||||||
* Return Value: Command dependent
|
* Return Value: Command dependent
|
||||||
*/
|
*/
|
||||||
static int nci_uart_tty_ioctl(struct tty_struct *tty, struct file *file,
|
static int nci_uart_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
|
||||||
unsigned int cmd, unsigned long arg)
|
unsigned long arg)
|
||||||
{
|
{
|
||||||
struct nci_uart *nu = (void *)tty->disc_data;
|
struct nci_uart *nu = (void *)tty->disc_data;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
Loading…
Add table
Reference in a new issue