提交 56b85f32 编写于 作者: L Linus Torvalds

Merge branch 'tty-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty-2.6

* 'tty-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty-2.6: (36 commits)
  serial: apbuart: Fixup apbuart_console_init()
  TTY: Add tty ioctl to figure device node of the system console.
  tty: add 'active' sysfs attribute to tty0 and console device
  drivers: serial: apbuart: Handle OF failures gracefully
  Serial: Avoid unbalanced IRQ wake disable during resume
  tty: fix typos/errors in tty_driver.h comments
  pch_uart : fix warnings for 64bit compile
  8250: fix uninitialized FIFOs
  ip2: fix compiler warning on ip2main_pci_tbl
  specialix: fix compiler warning on specialix_pci_tbl
  rocket: fix compiler warning on rocket_pci_ids
  8250: add a UPIO_DWAPB32 for 32 bit accesses
  8250: use container_of() instead of casting
  serial: omap-serial: Add support for kernel debugger
  serial: fix pch_uart kconfig & build
  drivers: char: hvc: add arm JTAG DCC console support
  RS485 documentation: add 16C950 UART description
  serial: ifx6x60: fix memory leak
  serial: ifx6x60: free IRQ on error
  Serial: EG20T: add PCH_UART driver
  ...

Fixed up conflicts in drivers/serial/apbuart.c with evil merge that
makes the code look fairly sane (unlike either side).
What: /sys/class/tty/console/active
Date: Nov 2010
Contact: Kay Sievers <kay.sievers@vrfy.org>
Description:
Shows the list of currently configured
console devices, like 'tty1 ttyS0'.
The last entry in the file is the active
device connected to /dev/console.
The file supports poll() to detect virtual
console switches.
What: /sys/class/tty/tty0/active
Date: Nov 2010
Contact: Kay Sievers <kay.sievers@vrfy.org>
Description:
Shows the currently active virtual console
device, like 'tty1'.
The file supports poll() to detect virtual
console switches.
......@@ -1181,6 +1181,30 @@ Table 1-12: Files in /proc/fs/ext4/<devname>
mb_groups details of multiblock allocator buddy cache of free blocks
..............................................................................
2.0 /proc/consoles
------------------
Shows registered system console lines.
To see which character device lines are currently used for the system console
/dev/console, you may simply look into the file /proc/consoles:
> cat /proc/consoles
tty0 -WU (ECp) 4:7
ttyS0 -W- (Ep) 4:64
The columns are:
device name of the device
operations R = can do read operations
W = can do write operations
U = can do unblank
flags E = it is enabled
C = it is prefered console
B = it is primary boot console
p = it is used for printk buffer
b = it is not a TTY but a Braille device
a = it is safe to use when cpu is offline
major:minor major and minor number of the device separated by a colon
------------------------------------------------------------------------------
Summary
......
......@@ -14,6 +14,8 @@ riscom8.txt
- notes on using the RISCom/8 multi-port serial driver.
rocket.txt
- info on the Comtrol RocketPort multiport serial driver.
serial-rs485.txt
- info about RS485 structures and support in the kernel.
specialix.txt
- info on hardware/driver for specialix IO8+ multiport serial card.
stallion.txt
......
RS485 SERIAL COMMUNICATIONS
1. INTRODUCTION
EIA-485, also known as TIA/EIA-485 or RS-485, is a standard defining the
electrical characteristics of drivers and receivers for use in balanced
digital multipoint systems.
This standard is widely used for communications in industrial automation
because it can be used effectively over long distances and in electrically
noisy environments.
2. HARDWARE-RELATED CONSIDERATIONS
Some CPUs/UARTs (e.g., Atmel AT91 or 16C950 UART) contain a built-in
half-duplex mode capable of automatically controlling line direction by
toggling RTS or DTR signals. That can be used to control external
half-duplex hardware like an RS485 transceiver or any RS232-connected
half-duplex devices like some modems.
For these microcontrollers, the Linux driver should be made capable of
working in both modes, and proper ioctls (see later) should be made
available at user-level to allow switching from one mode to the other, and
vice versa.
3. DATA STRUCTURES ALREADY AVAILABLE IN THE KERNEL
The Linux kernel provides the serial_rs485 structure (see [1]) to handle
RS485 communications. This data structure is used to set and configure RS485
parameters in the platform data and in ioctls.
Any driver for devices capable of working both as RS232 and RS485 should
provide at least the following ioctls:
- TIOCSRS485 (typically associated with number 0x542F). This ioctl is used
to enable/disable RS485 mode from user-space
- TIOCGRS485 (typically associated with number 0x542E). This ioctl is used
to get RS485 mode from kernel-space (i.e., driver) to user-space.
In other words, the serial driver should contain a code similar to the next
one:
static struct uart_ops atmel_pops = {
/* ... */
.ioctl = handle_ioctl,
};
static int handle_ioctl(struct uart_port *port,
unsigned int cmd,
unsigned long arg)
{
struct serial_rs485 rs485conf;
switch (cmd) {
case TIOCSRS485:
if (copy_from_user(&rs485conf,
(struct serial_rs485 *) arg,
sizeof(rs485conf)))
return -EFAULT;
/* ... */
break;
case TIOCGRS485:
if (copy_to_user((struct serial_rs485 *) arg,
...,
sizeof(rs485conf)))
return -EFAULT;
/* ... */
break;
/* ... */
}
}
4. USAGE FROM USER-LEVEL
From user-level, RS485 configuration can be get/set using the previous
ioctls. For instance, to set RS485 you can use the following code:
#include <linux/serial.h>
/* Driver-specific ioctls: */
#define TIOCGRS485 0x542E
#define TIOCSRS485 0x542F
/* Open your specific device (e.g., /dev/mydevice): */
int fd = open ("/dev/mydevice", O_RDWR);
if (fd < 0) {
/* Error handling. See errno. */
}
struct serial_rs485 rs485conf;
/* Set RS485 mode: */
rs485conf.flags |= SER_RS485_ENABLED;
/* Set rts delay before send, if needed: */
rs485conf.flags |= SER_RS485_RTS_BEFORE_SEND;
rs485conf.delay_rts_before_send = ...;
/* Set rts delay after send, if needed: */
rs485conf.flags |= SER_RS485_RTS_AFTER_SEND;
rs485conf.delay_rts_after_send = ...;
if (ioctl (fd, TIOCSRS485, &rs485conf) < 0) {
/* Error handling. See errno. */
}
/* Use read() and write() syscalls here... */
/* Close the device when finished: */
if (close (fd) < 0) {
/* Error handling. See errno. */
}
5. REFERENCES
[1] include/linux/serial.h
......@@ -92,6 +92,7 @@
#define TIOCGSID 0x5429 /* Return the session ID of FD */
#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
#define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */
#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */
#define TIOCSERCONFIG 0x5453
......
......@@ -83,6 +83,7 @@
#define TCSETSF2 _IOW('T', 0x2D, struct termios2)
#define TIOCGPTN _IOR('T', 0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T', 0x31, int) /* Lock/unlock Pty */
#define TIOCGDEV _IOR('T', 0x32, unsigned int) /* Get primary device node of /dev/console */
#define TIOCSIG _IOW('T', 0x36, int) /* Generate signal on Pty slave */
/* I hope the range from 0x5480 on is free ... */
......
......@@ -52,6 +52,7 @@
#define TCSETSF2 _IOW('T',0x2D, struct termios2)
#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
#define TIOCGDEV _IOR('T',0x32, int) /* Get primary device node of /dev/console */
#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */
#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */
......
......@@ -92,8 +92,6 @@ static int pdc_console_setup(struct console *co, char *options)
static struct timer_list pdc_console_timer;
extern struct console * console_drivers;
static int pdc_console_tty_open(struct tty_struct *tty, struct file *filp)
{
......@@ -169,11 +167,13 @@ static int __init pdc_console_tty_driver_init(void)
* It is unregistered if the pdc console was not selected as the
* primary console. */
struct console *tmp = console_drivers;
struct console *tmp;
for (tmp = console_drivers; tmp; tmp = tmp->next)
acquire_console_sem();
for_each_console(tmp)
if (tmp == &pdc_cons)
break;
release_console_sem();
if (!tmp) {
printk(KERN_INFO "PDC console driver not registered anymore, not creating %s\n", pdc_cons.name);
......
......@@ -94,6 +94,7 @@
#define TIOCSRS485 0x542f
#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
#define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */
#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */
#define TIOCSERCONFIG 0x5453
......
......@@ -85,6 +85,7 @@
#define TCSETSF2 _IOW('T', 45, struct termios2)
#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
#define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */
#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */
#define TIOCSERCONFIG _IO('T', 83) /* 0x5453 */
......
......@@ -19,6 +19,7 @@
#define TCSETS2 _IOW('T', 13, struct termios2)
#define TCSETSW2 _IOW('T', 14, struct termios2)
#define TCSETSF2 _IOW('T', 15, struct termios2)
#define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */
/* Note that all the ioctls that are not available in Linux have a
* double underscore on the front to: a) avoid some programs to
......
......@@ -98,6 +98,7 @@
#define TCSETSF2 _IOW('T', 45, struct termios2)
#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
#define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */
#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */
#define TIOCSERCONFIG _IO('T', 83)
......
......@@ -682,6 +682,15 @@ config HVC_UDBG
select HVC_DRIVER
default n
config HVC_DCC
bool "ARM JTAG DCC console"
depends on ARM
select HVC_DRIVER
help
This console uses the JTAG DCC on ARM to create a console under the HVC
driver. This console is used through a JTAG only on ARM. If you don't have
a JTAG then you probably don't want this option.
config VIRTIO_CONSOLE
tristate "Virtio console"
depends on VIRTIO
......
......@@ -34,6 +34,7 @@ obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi.o
obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o
obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o
obj-$(CONFIG_HVC_TILE) += hvc_tile.o
obj-$(CONFIG_HVC_DCC) += hvc_dcc.o
obj-$(CONFIG_HVC_BEAT) += hvc_beat.o
obj-$(CONFIG_HVC_DRIVER) += hvc_console.o
obj-$(CONFIG_HVC_IRQ) += hvc_irq.o
......
/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include <linux/console.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <asm/processor.h>
#include "hvc_console.h"
/* DCC Status Bits */
#define DCC_STATUS_RX (1 << 30)
#define DCC_STATUS_TX (1 << 29)
static inline u32 __dcc_getstatus(void)
{
u32 __ret;
asm("mrc p14, 0, %0, c0, c1, 0 @ read comms ctrl reg"
: "=r" (__ret) : : "cc");
return __ret;
}
#if defined(CONFIG_CPU_V7)
static inline char __dcc_getchar(void)
{
char __c;
asm("get_wait: mrc p14, 0, pc, c0, c1, 0 \n\
bne get_wait \n\
mrc p14, 0, %0, c0, c5, 0 @ read comms data reg"
: "=r" (__c) : : "cc");
return __c;
}
#else
static inline char __dcc_getchar(void)
{
char __c;
asm("mrc p14, 0, %0, c0, c5, 0 @ read comms data reg"
: "=r" (__c));
return __c;
}
#endif
#if defined(CONFIG_CPU_V7)
static inline void __dcc_putchar(char c)
{
asm("put_wait: mrc p14, 0, pc, c0, c1, 0 \n\
bcs put_wait \n\
mcr p14, 0, %0, c0, c5, 0 "
: : "r" (c) : "cc");
}
#else
static inline void __dcc_putchar(char c)
{
asm("mcr p14, 0, %0, c0, c5, 0 @ write a char"
: /* no output register */
: "r" (c));
}
#endif
static int hvc_dcc_put_chars(uint32_t vt, const char *buf, int count)
{
int i;
for (i = 0; i < count; i++) {
while (__dcc_getstatus() & DCC_STATUS_TX)
cpu_relax();
__dcc_putchar((char)(buf[i] & 0xFF));
}
return count;
}
static int hvc_dcc_get_chars(uint32_t vt, char *buf, int count)
{
int i;
for (i = 0; i < count; ++i) {
int c = -1;
if (__dcc_getstatus() & DCC_STATUS_RX)
c = __dcc_getchar();
if (c < 0)
break;
buf[i] = c;
}
return i;
}
static const struct hv_ops hvc_dcc_get_put_ops = {
.get_chars = hvc_dcc_get_chars,
.put_chars = hvc_dcc_put_chars,
};
static int __init hvc_dcc_console_init(void)
{
hvc_instantiate(0, 0, &hvc_dcc_get_put_ops);
return 0;
}
console_initcall(hvc_dcc_console_init);
static int __init hvc_dcc_init(void)
{
hvc_alloc(0, 0, &hvc_dcc_get_put_ops, 128);
return 0;
}
device_initcall(hvc_dcc_init);
......@@ -3224,7 +3224,7 @@ ip2trace (unsigned short pn, unsigned char cat, unsigned char label, unsigned lo
MODULE_LICENSE("GPL");
static struct pci_device_id ip2main_pci_tbl[] __devinitdata = {
static struct pci_device_id ip2main_pci_tbl[] __devinitdata __used = {
{ PCI_DEVICE(PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_IP2EX) },
{ }
};
......
......@@ -1764,7 +1764,7 @@ static void rp_flush_buffer(struct tty_struct *tty)
#ifdef CONFIG_PCI
static struct pci_device_id __devinitdata rocket_pci_ids[] = {
static struct pci_device_id __devinitdata __used rocket_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_ANY_ID) },
{ }
};
......
......@@ -2355,7 +2355,7 @@ static void __exit specialix_exit_module(void)
func_exit();
}
static struct pci_device_id specialx_pci_tbl[] __devinitdata = {
static struct pci_device_id specialx_pci_tbl[] __devinitdata __used = {
{ PCI_DEVICE(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_IO8) },
{ }
};
......
......@@ -454,21 +454,40 @@ static void tsi_serial_out(struct uart_port *p, int offset, int value)
writeb(value, p->membase + offset);
}
/* Save the LCR value so it can be re-written when a Busy Detect IRQ occurs. */
static inline void dwapb_save_out_value(struct uart_port *p, int offset,
int value)
{
struct uart_8250_port *up =
container_of(p, struct uart_8250_port, port);
if (offset == UART_LCR)
up->lcr = value;
}
/* Read the IER to ensure any interrupt is cleared before returning from ISR. */
static inline void dwapb_check_clear_ier(struct uart_port *p, int offset)
{
if (offset == UART_TX || offset == UART_IER)
p->serial_in(p, UART_IER);
}
static void dwapb_serial_out(struct uart_port *p, int offset, int value)
{
int save_offset = offset;
offset = map_8250_out_reg(p, offset) << p->regshift;
/* Save the LCR value so it can be re-written when a
* Busy Detect interrupt occurs. */
if (save_offset == UART_LCR) {
struct uart_8250_port *up = (struct uart_8250_port *)p;
up->lcr = value;
}
dwapb_save_out_value(p, save_offset, value);
writeb(value, p->membase + offset);
/* Read the IER to ensure any interrupt is cleared before
* returning from ISR. */
if (save_offset == UART_TX || save_offset == UART_IER)
value = p->serial_in(p, UART_IER);
dwapb_check_clear_ier(p, save_offset);
}
static void dwapb32_serial_out(struct uart_port *p, int offset, int value)
{
int save_offset = offset;
offset = map_8250_out_reg(p, offset) << p->regshift;
dwapb_save_out_value(p, save_offset, value);
writel(value, p->membase + offset);
dwapb_check_clear_ier(p, save_offset);
}
static unsigned int io_serial_in(struct uart_port *p, int offset)
......@@ -485,7 +504,8 @@ static void io_serial_out(struct uart_port *p, int offset, int value)
static void set_io_from_upio(struct uart_port *p)
{
struct uart_8250_port *up = (struct uart_8250_port *)p;
struct uart_8250_port *up =
container_of(p, struct uart_8250_port, port);
switch (p->iotype) {
case UPIO_HUB6:
p->serial_in = hub6_serial_in;
......@@ -518,6 +538,11 @@ static void set_io_from_upio(struct uart_port *p)
p->serial_out = dwapb_serial_out;
break;
case UPIO_DWAPB32:
p->serial_in = mem32_serial_in;
p->serial_out = dwapb32_serial_out;
break;
default:
p->serial_in = io_serial_in;
p->serial_out = io_serial_out;
......@@ -536,6 +561,7 @@ serial_out_sync(struct uart_8250_port *up, int offset, int value)
case UPIO_MEM32:
case UPIO_AU:
case UPIO_DWAPB:
case UPIO_DWAPB32:
p->serial_out(p, offset, value);
p->serial_in(p, UART_LCR); /* safe, no side-effects */
break;
......@@ -1319,7 +1345,8 @@ static inline void __stop_tx(struct uart_8250_port *p)
static void serial8250_stop_tx(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
__stop_tx(up);
......@@ -1336,7 +1363,8 @@ static void transmit_chars(struct uart_8250_port *up);
static void serial8250_start_tx(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
if (!(up->ier & UART_IER_THRI)) {
up->ier |= UART_IER_THRI;
......@@ -1364,7 +1392,8 @@ static void serial8250_start_tx(struct uart_port *port)
static void serial8250_stop_rx(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
up->ier &= ~UART_IER_RLSI;
up->port.read_status_mask &= ~UART_LSR_DR;
......@@ -1373,7 +1402,8 @@ static void serial8250_stop_rx(struct uart_port *port)
static void serial8250_enable_ms(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
/* no MSR capabilities */
if (up->bugs & UART_BUG_NOMSR)
......@@ -1581,7 +1611,8 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id)
handled = 1;
end = NULL;
} else if (up->port.iotype == UPIO_DWAPB &&
} else if ((up->port.iotype == UPIO_DWAPB ||
up->port.iotype == UPIO_DWAPB32) &&
(iir & UART_IIR_BUSY) == UART_IIR_BUSY) {
/* The DesignWare APB UART has an Busy Detect (0x07)
* interrupt meaning an LCR write attempt occured while the
......@@ -1781,7 +1812,8 @@ static void serial8250_backup_timeout(unsigned long data)
static unsigned int serial8250_tx_empty(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
unsigned long flags;
unsigned int lsr;
......@@ -1795,7 +1827,8 @@ static unsigned int serial8250_tx_empty(struct uart_port *port)
static unsigned int serial8250_get_mctrl(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
unsigned int status;
unsigned int ret;
......@@ -1815,7 +1848,8 @@ static unsigned int serial8250_get_mctrl(struct uart_port *port)
static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
unsigned char mcr = 0;
if (mctrl & TIOCM_RTS)
......@@ -1836,7 +1870,8 @@ static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
static void serial8250_break_ctl(struct uart_port *port, int break_state)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
unsigned long flags;
spin_lock_irqsave(&up->port.lock, flags);
......@@ -1890,7 +1925,8 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits)
static int serial8250_get_poll_char(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
unsigned char lsr = serial_inp(up, UART_LSR);
if (!(lsr & UART_LSR_DR))
......@@ -1904,7 +1940,8 @@ static void serial8250_put_poll_char(struct uart_port *port,
unsigned char c)
{
unsigned int ier;
struct uart_8250_port *up = (struct uart_8250_port *)port;
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
/*
* First save the IER then disable the interrupts
......@@ -1938,11 +1975,14 @@ static void serial8250_put_poll_char(struct uart_port *port,
static int serial8250_startup(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
unsigned long flags;
unsigned char lsr, iir;
int retval;
up->port.fifosize = uart_config[up->port.type].fifo_size;
up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
up->capabilities = uart_config[up->port.type].flags;
up->mcr = 0;
......@@ -2166,7 +2206,8 @@ static int serial8250_startup(struct uart_port *port)
static void serial8250_shutdown(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
unsigned long flags;
/*
......@@ -2235,7 +2276,8 @@ void
serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
unsigned char cval, fcr = 0;
unsigned long flags;
unsigned int baud, quot;
......@@ -2435,7 +2477,8 @@ serial8250_set_ldisc(struct uart_port *port, int new)
void serial8250_do_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate)
{
struct uart_8250_port *p = (struct uart_8250_port *)port;
struct uart_8250_port *p =
container_of(port, struct uart_8250_port, port);
serial8250_set_sleep(p, state != 0);
}
......@@ -2476,6 +2519,7 @@ static int serial8250_request_std_resource(struct uart_8250_port *up)
case UPIO_MEM32:
case UPIO_MEM:
case UPIO_DWAPB:
case UPIO_DWAPB32:
if (!up->port.mapbase)
break;
......@@ -2513,6 +2557,7 @@ static void serial8250_release_std_resource(struct uart_8250_port *up)
case UPIO_MEM32:
case UPIO_MEM:
case UPIO_DWAPB:
case UPIO_DWAPB32:
if (!up->port.mapbase)
break;
......@@ -2566,7 +2611,8 @@ static void serial8250_release_rsa_resource(struct uart_8250_port *up)
static void serial8250_release_port(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
serial8250_release_std_resource(up);
if (up->port.type == PORT_RSA)
......@@ -2575,7 +2621,8 @@ static void serial8250_release_port(struct uart_port *port)
static int serial8250_request_port(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
int ret = 0;
ret = serial8250_request_std_resource(up);
......@@ -2590,7 +2637,8 @@ static int serial8250_request_port(struct uart_port *port)
static void serial8250_config_port(struct uart_port *port, int flags)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
int probeflags = PROBE_ANY;
int ret;
......@@ -2771,7 +2819,8 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev)
static void serial8250_console_putchar(struct uart_port *port, int ch)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
wait_for_xmitr(up, UART_LSR_THRE);
serial_out(up, UART_TX, ch);
......
......@@ -957,6 +957,22 @@ pci_default_setup(struct serial_private *priv,
return setup_port(priv, port, bar, offset, board->reg_shift);
}
static int
ce4100_serial_setup(struct serial_private *priv,
const struct pciserial_board *board,
struct uart_port *port, int idx)
{
int ret;
ret = setup_port(priv, port, 0, 0, board->reg_shift);
port->iotype = UPIO_MEM32;
port->type = PORT_XSCALE;
port->flags = (port->flags | UPF_FIXED_PORT | UPF_FIXED_TYPE);
port->regshift = 2;
return ret;
}
static int skip_tx_en_setup(struct serial_private *priv,
const struct pciserial_board *board,
struct uart_port *port, int idx)
......@@ -981,6 +997,7 @@ static int skip_tx_en_setup(struct serial_private *priv,
#define PCI_SUBDEVICE_ID_POCTAL232 0x0308
#define PCI_SUBDEVICE_ID_POCTAL422 0x0408
#define PCI_VENDOR_ID_ADVANTECH 0x13fe
#define PCI_DEVICE_ID_INTEL_CE4100_UART 0x2e66
#define PCI_DEVICE_ID_ADVANTECH_PCI3620 0x3620
#define PCI_DEVICE_ID_TITAN_200I 0x8028
#define PCI_DEVICE_ID_TITAN_400I 0x8048
......@@ -1072,6 +1089,13 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.setup = skip_tx_en_setup,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_CE4100_UART,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.setup = ce4100_serial_setup,
},
/*
* ITE
*/
......@@ -1592,6 +1616,7 @@ enum pci_board_num_t {
pbn_ADDIDATA_PCIe_2_3906250,
pbn_ADDIDATA_PCIe_4_3906250,
pbn_ADDIDATA_PCIe_8_3906250,
pbn_ce4100_1_115200,
};
/*
......@@ -2281,6 +2306,12 @@ static struct pciserial_board pci_boards[] __devinitdata = {
.uart_offset = 0x200,
.first_offset = 0x1000,
},
[pbn_ce4100_1_115200] = {
.flags = FL_BASE0,
.num_ports = 1,
.base_baud = 921600,
.reg_shift = 2,
},
};
static const struct pci_device_id softmodem_blacklist[] = {
......@@ -3765,6 +3796,11 @@ static struct pci_device_id serial_pci_tbl[] = {
{ PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865,
0xA000, 0x3004,
0, 0, pbn_b0_bt_4_115200 },
/* Intel CE4100 */
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CE4100_UART,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_ce4100_1_115200 },
/*
* These entries match devices with class COMMUNICATION_SERIAL,
......
......@@ -1381,6 +1381,16 @@ config SERIAL_MSM_CONSOLE
depends on SERIAL_MSM=y
select SERIAL_CORE_CONSOLE
config SERIAL_VT8500
bool "VIA VT8500 on-chip serial port support"
depends on ARM && ARCH_VT8500
select SERIAL_CORE
config SERIAL_VT8500_CONSOLE
bool "VIA VT8500 serial console support"
depends on SERIAL_VT8500=y
select SERIAL_CORE_CONSOLE
config SERIAL_NETX
tristate "NetX serial port support"
depends on ARM && ARCH_NETX
......@@ -1632,4 +1642,19 @@ config SERIAL_ALTERA_UART_CONSOLE
help
Enable a Altera UART port to be the system console.
config SERIAL_IFX6X60
tristate "SPI protocol driver for Infineon 6x60 modem (EXPERIMENTAL)"
depends on GPIOLIB && SPI && EXPERIMENTAL
help
Support for the IFX6x60 modem devices on Intel MID platforms.
config SERIAL_PCH_UART
tristate "Intel EG20T PCH UART"
depends on PCI && DMADEVICES
select SERIAL_CORE
select PCH_DMA
help
This driver is for PCH(Platform controller Hub) UART of Intel EG20T
which is an IOH(Input/Output Hub) for x86 embedded processor.
Enabling PCH_DMA, this PCH UART works as DMA mode.
endmenu
......@@ -80,12 +80,15 @@ obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o
obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
obj-$(CONFIG_SERIAL_QE) += ucc_uart.o
obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o
obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o
obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o
obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o
obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o
obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o
......@@ -521,11 +521,12 @@ static struct console grlib_apbuart_console = {
};
static void grlib_apbuart_configure(void);
static int grlib_apbuart_configure(void);
static int __init apbuart_console_init(void)
{
grlib_apbuart_configure();
if (grlib_apbuart_configure())
return -ENODEV;
register_console(&grlib_apbuart_console);
return 0;
}
......@@ -596,57 +597,49 @@ static struct of_platform_driver grlib_apbuart_of_driver = {
};
static void grlib_apbuart_configure(void)
static int grlib_apbuart_configure(void)
{
static int enum_done;
struct device_node *np, *rp;
struct uart_port *port = NULL;
const u32 *prop;
int freq_khz;
int v = 0, d = 0;
unsigned int addr;
int irq, line;
struct amba_prom_registers *regs;
if (enum_done)
return;
int freq_khz, line = 0;
/* Get bus frequency */
rp = of_find_node_by_path("/");
if (!rp)
return -ENODEV;
rp = of_get_next_child(rp, NULL);
if (!rp)
return -ENODEV;
prop = of_get_property(rp, "clock-frequency", NULL);
if (!prop)
return -ENODEV;
freq_khz = *prop;
line = 0;
for_each_matching_node(np, apbuart_match) {
const int *irqs, *ampopts;
const struct amba_prom_registers *regs;
struct uart_port *port;
unsigned long addr;
int *vendor = (int *) of_get_property(np, "vendor", NULL);
int *device = (int *) of_get_property(np, "device", NULL);
int *irqs = (int *) of_get_property(np, "interrupts", NULL);
int *ampopts = (int *) of_get_property(np, "ampopts", NULL);
regs = (struct amba_prom_registers *)
of_get_property(np, "reg", NULL);
ampopts = of_get_property(np, "ampopts", NULL);
if (ampopts && (*ampopts == 0))
continue; /* Ignore if used by another OS instance */
if (vendor)
v = *vendor;
if (device)
d = *device;
irqs = of_get_property(np, "interrupts", NULL);
regs = of_get_property(np, "reg", NULL);
if (!irqs || !regs)
return;
continue;
grlib_apbuart_nodes[line] = np;
addr = regs->phys_addr;
irq = *irqs;
port = &grlib_apbuart_ports[line];
port->mapbase = addr;
port->membase = ioremap(addr, sizeof(struct grlib_apbuart_regs_map));
port->irq = irq;
port->irq = *irqs;
port->iotype = UPIO_MEM;
port->ops = &grlib_apbuart_ops;
port->flags = UPF_BOOT_AUTOCONF;
......@@ -658,12 +651,10 @@ static void grlib_apbuart_configure(void)
/* We support maximum UART_NR uarts ... */
if (line == UART_NR)
break;
}
enum_done = 1;
grlib_apbuart_driver.nr = grlib_apbuart_port_nr = line;
return line ? 0 : -ENODEV;
}
static int __init grlib_apbuart_init(void)
......@@ -671,7 +662,9 @@ static int __init grlib_apbuart_init(void)
int ret;
/* Find all APBUARTS in device the tree and initialize their ports */
grlib_apbuart_configure();
ret = grlib_apbuart_configure();
if (ret)
return ret;
printk(KERN_INFO "Serial: GRLIB APBUART driver\n");
......
......@@ -76,18 +76,12 @@ struct uart_cpm_port {
unsigned char *tx_buf;
unsigned char *rx_buf;
u32 flags;
void (*set_lineif)(struct uart_cpm_port *);
struct clk *clk;
u8 brg;
uint dp_addr;
void *mem_addr;
dma_addr_t dma_addr;
u32 mem_size;
/* helpers */
int baud;
int bits;
/* Keep track of 'odd' SMC2 wirings */
int is_portb;
/* wait on close if needed */
int wait_closing;
/* value to combine with opcode to form cpm command */
......
......@@ -72,6 +72,8 @@ static void cpm_uart_initbd(struct uart_cpm_port *pinfo);
/**************************************************************/
#define HW_BUF_SPD_THRESHOLD 9600
/*
* Check, if transmit buffers are processed
*/
......@@ -503,6 +505,11 @@ static void cpm_uart_set_termios(struct uart_port *port,
pr_debug("CPM uart[%d]:set_termios\n", port->line);
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
if (baud <= HW_BUF_SPD_THRESHOLD ||
(pinfo->port.state && pinfo->port.state->port.tty->low_latency))
pinfo->rx_fifosize = 1;
else
pinfo->rx_fifosize = RX_BUF_SIZE;
/* Character length programmed into the mode register is the
* sum of: 1 start bit, number of data bits, 0 or 1 parity bit,
......@@ -594,6 +601,17 @@ static void cpm_uart_set_termios(struct uart_port *port,
*/
bits++;
if (IS_SMC(pinfo)) {
/*
* MRBLR can be changed while an SMC/SCC is operating only
* if it is done in a single bus cycle with one 16-bit move
* (not two 8-bit bus cycles back-to-back). This occurs when
* the cp shifts control to the next RxBD, so the change does
* not take effect immediately. To guarantee the exact RxBD
* on which the change occurs, change MRBLR only while the
* SMC/SCC receiver is disabled.
*/
out_be16(&pinfo->smcup->smc_mrblr, pinfo->rx_fifosize);
/* Set the mode register. We want to keep a copy of the
* enables, because we want to put them back if they were
* present.
......@@ -604,6 +622,7 @@ static void cpm_uart_set_termios(struct uart_port *port,
out_be16(&smcp->smc_smcmr, smcr_mk_clen(bits) | cval |
SMCMR_SM_UART | prev_mode);
} else {
out_be16(&pinfo->sccup->scc_genscc.scc_mrblr, pinfo->rx_fifosize);
out_be16(&sccp->scc_psmr, (sbits << 12) | scval);
}
......
此差异已折叠。
/****************************************************************************
*
* Driver for the IFX spi modem.
*
* Copyright (C) 2009, 2010 Intel Corp
* Jim Stanley <jim.stanley@intel.com>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA
*
*
*
*****************************************************************************/
#ifndef _IFX6X60_H
#define _IFX6X60_H
#define DRVNAME "ifx6x60"
#define TTYNAME "ttyIFX"
/* #define IFX_THROTTLE_CODE */
#define IFX_SPI_MAX_MINORS 1
#define IFX_SPI_TRANSFER_SIZE 2048
#define IFX_SPI_FIFO_SIZE 4096
#define IFX_SPI_HEADER_OVERHEAD 4
#define IFX_RESET_TIMEOUT msecs_to_jiffies(50)
/* device flags bitfield definitions */
#define IFX_SPI_STATE_PRESENT 0
#define IFX_SPI_STATE_IO_IN_PROGRESS 1
#define IFX_SPI_STATE_IO_READY 2
#define IFX_SPI_STATE_TIMER_PENDING 3
/* flow control bitfields */
#define IFX_SPI_DCD 0
#define IFX_SPI_CTS 1
#define IFX_SPI_DSR 2
#define IFX_SPI_RI 3
#define IFX_SPI_DTR 4
#define IFX_SPI_RTS 5
#define IFX_SPI_TX_FC 6
#define IFX_SPI_RX_FC 7
#define IFX_SPI_UPDATE 8
#define IFX_SPI_PAYLOAD_SIZE (IFX_SPI_TRANSFER_SIZE - \
IFX_SPI_HEADER_OVERHEAD)
#define IFX_SPI_IRQ_TYPE DETECT_EDGE_RISING
#define IFX_SPI_GPIO_TARGET 0
#define IFX_SPI_GPIO0 0x105
#define IFX_SPI_STATUS_TIMEOUT (2000*HZ)
/* values for bits in power status byte */
#define IFX_SPI_POWER_DATA_PENDING 1
#define IFX_SPI_POWER_SRDY 2
struct ifx_spi_device {
/* Our SPI device */
struct spi_device *spi_dev;
/* Port specific data */
struct kfifo tx_fifo;
spinlock_t fifo_lock;
unsigned long signal_state;
/* TTY Layer logic */
struct tty_port tty_port;
struct device *tty_dev;
int minor;
/* Low level I/O work */
struct tasklet_struct io_work_tasklet;
unsigned long flags;
dma_addr_t rx_dma;
dma_addr_t tx_dma;
int is_6160; /* Modem type */
spinlock_t write_lock;
int write_pending;
spinlock_t power_lock;
unsigned char power_status;
unsigned char *rx_buffer;
unsigned char *tx_buffer;
dma_addr_t rx_bus;
dma_addr_t tx_bus;
unsigned char spi_more;
unsigned char spi_slave_cts;
struct timer_list spi_timer;
struct spi_message spi_msg;
struct spi_transfer spi_xfer;
struct {
/* gpio lines */
unsigned short srdy; /* slave-ready gpio */
unsigned short mrdy; /* master-ready gpio */
unsigned short reset; /* modem-reset gpio */
unsigned short po; /* modem-on gpio */
unsigned short reset_out; /* modem-in-reset gpio */
/* state/stats */
int unack_srdy_int_nb;
} gpio;
/* modem reset */
unsigned long mdm_reset_state;
#define MR_START 0
#define MR_INPROGRESS 1
#define MR_COMPLETE 2
wait_queue_head_t mdm_reset_wait;
};
#endif /* _IFX6X60_H */
......@@ -838,7 +838,11 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new,
static const char *
mpc52xx_uart_type(struct uart_port *port)
{
return port->type == PORT_MPC52xx ? "MPC52xx PSC" : NULL;
/*
* We keep using PORT_MPC52xx for historic reasons although it applies
* for MPC512x, too, but print "MPC5xxx" to not irritate users
*/
return port->type == PORT_MPC52xx ? "MPC5xxx PSC" : NULL;
}
static void
......
......@@ -866,12 +866,6 @@ serial_omap_type(struct uart_port *port)
return up->name;
}
#ifdef CONFIG_SERIAL_OMAP_CONSOLE
static struct uart_omap_port *serial_omap_console_ports[4];
static struct uart_driver serial_omap_reg;
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
static inline void wait_for_xmitr(struct uart_omap_port *up)
......@@ -905,6 +899,34 @@ static inline void wait_for_xmitr(struct uart_omap_port *up)
}
}
#ifdef CONFIG_CONSOLE_POLL
static void serial_omap_poll_put_char(struct uart_port *port, unsigned char ch)
{
struct uart_omap_port *up = (struct uart_omap_port *)port;
wait_for_xmitr(up);
serial_out(up, UART_TX, ch);
}
static int serial_omap_poll_get_char(struct uart_port *port)
{
struct uart_omap_port *up = (struct uart_omap_port *)port;
unsigned int status = serial_in(up, UART_LSR);
if (!(status & UART_LSR_DR))
return NO_POLL_CHAR;
return serial_in(up, UART_RX);
}
#endif /* CONFIG_CONSOLE_POLL */
#ifdef CONFIG_SERIAL_OMAP_CONSOLE
static struct uart_omap_port *serial_omap_console_ports[4];
static struct uart_driver serial_omap_reg;
static void serial_omap_console_putchar(struct uart_port *port, int ch)
{
struct uart_omap_port *up = (struct uart_omap_port *)port;
......@@ -1022,6 +1044,10 @@ static struct uart_ops serial_omap_pops = {
.request_port = serial_omap_request_port,
.config_port = serial_omap_config_port,
.verify_port = serial_omap_verify_port,
#ifdef CONFIG_CONSOLE_POLL
.poll_put_char = serial_omap_poll_put_char,
.poll_get_char = serial_omap_poll_get_char,
#endif
};
static struct uart_driver serial_omap_reg = {
......
此差异已折叠。
......@@ -1985,7 +1985,8 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport)
tty_dev = device_find_child(uport->dev, &match, serial_match_port);
if (device_may_wakeup(tty_dev)) {
enable_irq_wake(uport->irq);
if (!enable_irq_wake(uport->irq))
uport->irq_wake = 1;
put_device(tty_dev);
mutex_unlock(&port->mutex);
return 0;
......@@ -2051,7 +2052,10 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport)
tty_dev = device_find_child(uport->dev, &match, serial_match_port);
if (!uport->suspended && device_may_wakeup(tty_dev)) {
disable_irq_wake(uport->irq);
if (uport->irq_wake) {
disable_irq_wake(uport->irq);
uport->irq_wake = 0;
}
mutex_unlock(&port->mutex);
return 0;
}
......@@ -2134,6 +2138,7 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port)
case UPIO_AU:
case UPIO_TSI:
case UPIO_DWAPB:
case UPIO_DWAPB32:
snprintf(address, sizeof(address),
"MMIO 0x%llx", (unsigned long long)port->mapbase);
break;
......@@ -2554,6 +2559,7 @@ int uart_match_port(struct uart_port *port1, struct uart_port *port2)
case UPIO_AU:
case UPIO_TSI:
case UPIO_DWAPB:
case UPIO_DWAPB32:
return (port1->mapbase == port2->mapbase);
}
return 0;
......
/*
* drivers/serial/vt8500_serial.c
*
* Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
*
* Based on msm_serial.c, which is:
* Copyright (C) 2007 Google, Inc.
* Author: Robert Love <rlove@google.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#if defined(CONFIG_SERIAL_VT8500_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
# define SUPPORT_SYSRQ
#endif
#include <linux/hrtimer.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
/*
* UART Register offsets
*/
#define VT8500_URTDR 0x0000 /* Transmit data */
#define VT8500_URRDR 0x0004 /* Receive data */
#define VT8500_URDIV 0x0008 /* Clock/Baud rate divisor */
#define VT8500_URLCR 0x000C /* Line control */
#define VT8500_URICR 0x0010 /* IrDA control */
#define VT8500_URIER 0x0014 /* Interrupt enable */
#define VT8500_URISR 0x0018 /* Interrupt status */
#define VT8500_URUSR 0x001c /* UART status */
#define VT8500_URFCR 0x0020 /* FIFO control */
#define VT8500_URFIDX 0x0024 /* FIFO index */
#define VT8500_URBKR 0x0028 /* Break signal count */
#define VT8500_URTOD 0x002c /* Time out divisor */
#define VT8500_TXFIFO 0x1000 /* Transmit FIFO (16x8) */
#define VT8500_RXFIFO 0x1020 /* Receive FIFO (16x10) */
/*
* Interrupt enable and status bits
*/
#define TXDE (1 << 0) /* Tx Data empty */
#define RXDF (1 << 1) /* Rx Data full */
#define TXFAE (1 << 2) /* Tx FIFO almost empty */
#define TXFE (1 << 3) /* Tx FIFO empty */
#define RXFAF (1 << 4) /* Rx FIFO almost full */
#define RXFF (1 << 5) /* Rx FIFO full */
#define TXUDR (1 << 6) /* Tx underrun */
#define RXOVER (1 << 7) /* Rx overrun */
#define PER (1 << 8) /* Parity error */
#define FER (1 << 9) /* Frame error */
#define TCTS (1 << 10) /* Toggle of CTS */
#define RXTOUT (1 << 11) /* Rx timeout */
#define BKDONE (1 << 12) /* Break signal done */
#define ERR (1 << 13) /* AHB error response */
#define RX_FIFO_INTS (RXFAF | RXFF | RXOVER | PER | FER | RXTOUT)
#define TX_FIFO_INTS (TXFAE | TXFE | TXUDR)
struct vt8500_port {
struct uart_port uart;
char name[16];
struct clk *clk;
unsigned int ier;
};
static inline void vt8500_write(struct uart_port *port, unsigned int val,
unsigned int off)
{
writel(val, port->membase + off);
}
static inline unsigned int vt8500_read(struct uart_port *port, unsigned int off)
{
return readl(port->membase + off);
}
static void vt8500_stop_tx(struct uart_port *port)
{
struct vt8500_port *vt8500_port = container_of(port,
struct vt8500_port,
uart);
vt8500_port->ier &= ~TX_FIFO_INTS;
vt8500_write(port, vt8500_port->ier, VT8500_URIER);
}
static void vt8500_stop_rx(struct uart_port *port)
{
struct vt8500_port *vt8500_port = container_of(port,
struct vt8500_port,
uart);
vt8500_port->ier &= ~RX_FIFO_INTS;
vt8500_write(port, vt8500_port->ier, VT8500_URIER);
}
static void vt8500_enable_ms(struct uart_port *port)
{
struct vt8500_port *vt8500_port = container_of(port,
struct vt8500_port,
uart);
vt8500_port->ier |= TCTS;
vt8500_write(port, vt8500_port->ier, VT8500_URIER);
}
static void handle_rx(struct uart_port *port)
{
struct tty_struct *tty = tty_port_tty_get(&port->state->port);
if (!tty) {
/* Discard data: no tty available */
int count = (vt8500_read(port, VT8500_URFIDX) & 0x1f00) >> 8;
u16 ch;
while (count--)
ch = readw(port->membase + VT8500_RXFIFO);
return;
}
/*
* Handle overrun
*/
if ((vt8500_read(port, VT8500_URISR) & RXOVER)) {
port->icount.overrun++;
tty_insert_flip_char(tty, 0, TTY_OVERRUN);
}
/* and now the main RX loop */
while (vt8500_read(port, VT8500_URFIDX) & 0x1f00) {
unsigned int c;
char flag = TTY_NORMAL;
c = readw(port->membase + VT8500_RXFIFO) & 0x3ff;
/* Mask conditions we're ignorning. */
c &= ~port->read_status_mask;
if (c & FER) {
port->icount.frame++;
flag = TTY_FRAME;
} else if (c & PER) {
port->icount.parity++;
flag = TTY_PARITY;
}
port->icount.rx++;
if (!uart_handle_sysrq_char(port, c))
tty_insert_flip_char(tty, c, flag);
}
tty_flip_buffer_push(tty);
tty_kref_put(tty);
}
static void handle_tx(struct uart_port *port)
{
struct circ_buf *xmit = &port->state->xmit;
if (port->x_char) {
writeb(port->x_char, port->membase + VT8500_TXFIFO);
port->icount.tx++;
port->x_char = 0;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
vt8500_stop_tx(port);
return;
}
while ((vt8500_read(port, VT8500_URFIDX) & 0x1f) < 16) {
if (uart_circ_empty(xmit))
break;
writeb(xmit->buf[xmit->tail], port->membase + VT8500_TXFIFO);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
if (uart_circ_empty(xmit))
vt8500_stop_tx(port);
}
static void vt8500_start_tx(struct uart_port *port)
{
struct vt8500_port *vt8500_port = container_of(port,
struct vt8500_port,
uart);
vt8500_port->ier &= ~TX_FIFO_INTS;
vt8500_write(port, vt8500_port->ier, VT8500_URIER);
handle_tx(port);
vt8500_port->ier |= TX_FIFO_INTS;
vt8500_write(port, vt8500_port->ier, VT8500_URIER);
}
static void handle_delta_cts(struct uart_port *port)
{
port->icount.cts++;
wake_up_interruptible(&port->state->port.delta_msr_wait);
}
static irqreturn_t vt8500_irq(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
unsigned long isr;
spin_lock(&port->lock);
isr = vt8500_read(port, VT8500_URISR);
/* Acknowledge active status bits */
vt8500_write(port, isr, VT8500_URISR);
if (isr & RX_FIFO_INTS)
handle_rx(port);
if (isr & TX_FIFO_INTS)
handle_tx(port);
if (isr & TCTS)
handle_delta_cts(port);
spin_unlock(&port->lock);
return IRQ_HANDLED;
}
static unsigned int vt8500_tx_empty(struct uart_port *port)
{
return (vt8500_read(port, VT8500_URFIDX) & 0x1f) < 16 ?
TIOCSER_TEMT : 0;
}
static unsigned int vt8500_get_mctrl(struct uart_port *port)
{
unsigned int usr;
usr = vt8500_read(port, VT8500_URUSR);
if (usr & (1 << 4))
return TIOCM_CTS;
else
return 0;
}
static void vt8500_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
}
static void vt8500_break_ctl(struct uart_port *port, int break_ctl)
{
if (break_ctl)
vt8500_write(port, vt8500_read(port, VT8500_URLCR) | (1 << 9),
VT8500_URLCR);
}
static int vt8500_set_baud_rate(struct uart_port *port, unsigned int baud)
{
unsigned long div;
unsigned int loops = 1000;
div = vt8500_read(port, VT8500_URDIV) & ~(0x3ff);
if (unlikely((baud < 900) || (baud > 921600)))
div |= 7;
else
div |= (921600 / baud) - 1;
while ((vt8500_read(port, VT8500_URUSR) & (1 << 5)) && --loops)
cpu_relax();
vt8500_write(port, div, VT8500_URDIV);
return baud;
}
static int vt8500_startup(struct uart_port *port)
{
struct vt8500_port *vt8500_port =
container_of(port, struct vt8500_port, uart);
int ret;
snprintf(vt8500_port->name, sizeof(vt8500_port->name),
"vt8500_serial%d", port->line);
ret = request_irq(port->irq, vt8500_irq, IRQF_TRIGGER_HIGH,
vt8500_port->name, port);
if (unlikely(ret))
return ret;
vt8500_write(port, 0x03, VT8500_URLCR); /* enable TX & RX */
return 0;
}
static void vt8500_shutdown(struct uart_port *port)
{
struct vt8500_port *vt8500_port =
container_of(port, struct vt8500_port, uart);
vt8500_port->ier = 0;
/* disable interrupts and FIFOs */
vt8500_write(&vt8500_port->uart, 0, VT8500_URIER);
vt8500_write(&vt8500_port->uart, 0x880, VT8500_URFCR);
free_irq(port->irq, port);
}
static void vt8500_set_termios(struct uart_port *port,
struct ktermios *termios,
struct ktermios *old)
{
struct vt8500_port *vt8500_port =
container_of(port, struct vt8500_port, uart);
unsigned long flags;
unsigned int baud, lcr;
unsigned int loops = 1000;
spin_lock_irqsave(&port->lock, flags);
/* calculate and set baud rate */
baud = uart_get_baud_rate(port, termios, old, 900, 921600);
baud = vt8500_set_baud_rate(port, baud);
if (tty_termios_baud_rate(termios))
tty_termios_encode_baud_rate(termios, baud, baud);
/* calculate parity */
lcr = vt8500_read(&vt8500_port->uart, VT8500_URLCR);
lcr &= ~((1 << 5) | (1 << 4));
if (termios->c_cflag & PARENB) {
lcr |= (1 << 4);
termios->c_cflag &= ~CMSPAR;
if (termios->c_cflag & PARODD)
lcr |= (1 << 5);
}
/* calculate bits per char */
lcr &= ~(1 << 2);
switch (termios->c_cflag & CSIZE) {
case CS7:
break;
case CS8:
default:
lcr |= (1 << 2);
termios->c_cflag &= ~CSIZE;
termios->c_cflag |= CS8;
break;
}
/* calculate stop bits */
lcr &= ~(1 << 3);
if (termios->c_cflag & CSTOPB)
lcr |= (1 << 3);
/* set parity, bits per char, and stop bit */
vt8500_write(&vt8500_port->uart, lcr, VT8500_URLCR);
/* Configure status bits to ignore based on termio flags. */
port->read_status_mask = 0;
if (termios->c_iflag & IGNPAR)
port->read_status_mask = FER | PER;
uart_update_timeout(port, termios->c_cflag, baud);
/* Reset FIFOs */
vt8500_write(&vt8500_port->uart, 0x88c, VT8500_URFCR);
while ((vt8500_read(&vt8500_port->uart, VT8500_URFCR) & 0xc)
&& --loops)
cpu_relax();
/* Every possible FIFO-related interrupt */
vt8500_port->ier = RX_FIFO_INTS | TX_FIFO_INTS;
/*
* CTS flow control
*/
if (UART_ENABLE_MS(&vt8500_port->uart, termios->c_cflag))
vt8500_port->ier |= TCTS;
vt8500_write(&vt8500_port->uart, 0x881, VT8500_URFCR);
vt8500_write(&vt8500_port->uart, vt8500_port->ier, VT8500_URIER);
spin_unlock_irqrestore(&port->lock, flags);
}
static const char *vt8500_type(struct uart_port *port)
{
struct vt8500_port *vt8500_port =
container_of(port, struct vt8500_port, uart);
return vt8500_port->name;
}
static void vt8500_release_port(struct uart_port *port)
{
}
static int vt8500_request_port(struct uart_port *port)
{
return 0;
}
static void vt8500_config_port(struct uart_port *port, int flags)
{
port->type = PORT_VT8500;
}
static int vt8500_verify_port(struct uart_port *port,
struct serial_struct *ser)
{
if (unlikely(ser->type != PORT_UNKNOWN && ser->type != PORT_VT8500))
return -EINVAL;
if (unlikely(port->irq != ser->irq))
return -EINVAL;
return 0;
}
static struct vt8500_port *vt8500_uart_ports[4];
static struct uart_driver vt8500_uart_driver;
#ifdef CONFIG_SERIAL_VT8500_CONSOLE
static inline void wait_for_xmitr(struct uart_port *port)
{
unsigned int status, tmout = 10000;
/* Wait up to 10ms for the character(s) to be sent. */
do {
status = vt8500_read(port, VT8500_URFIDX);
if (--tmout == 0)
break;
udelay(1);
} while (status & 0x10);
}
static void vt8500_console_putchar(struct uart_port *port, int c)
{
wait_for_xmitr(port);
writeb(c, port->membase + VT8500_TXFIFO);
}
static void vt8500_console_write(struct console *co, const char *s,
unsigned int count)
{
struct vt8500_port *vt8500_port = vt8500_uart_ports[co->index];
unsigned long ier;
BUG_ON(co->index < 0 || co->index >= vt8500_uart_driver.nr);
ier = vt8500_read(&vt8500_port->uart, VT8500_URIER);
vt8500_write(&vt8500_port->uart, VT8500_URIER, 0);
uart_console_write(&vt8500_port->uart, s, count,
vt8500_console_putchar);
/*
* Finally, wait for transmitter to become empty
* and switch back to FIFO
*/
wait_for_xmitr(&vt8500_port->uart);
vt8500_write(&vt8500_port->uart, VT8500_URIER, ier);
}
static int __init vt8500_console_setup(struct console *co, char *options)
{
struct vt8500_port *vt8500_port;
int baud = 9600;
int bits = 8;
int parity = 'n';
int flow = 'n';
if (unlikely(co->index >= vt8500_uart_driver.nr || co->index < 0))
return -ENXIO;
vt8500_port = vt8500_uart_ports[co->index];
if (!vt8500_port)
return -ENODEV;
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
return uart_set_options(&vt8500_port->uart,
co, baud, parity, bits, flow);
}
static struct console vt8500_console = {
.name = "ttyWMT",
.write = vt8500_console_write,
.device = uart_console_device,
.setup = vt8500_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &vt8500_uart_driver,
};
#define VT8500_CONSOLE (&vt8500_console)
#else
#define VT8500_CONSOLE NULL
#endif
static struct uart_ops vt8500_uart_pops = {
.tx_empty = vt8500_tx_empty,
.set_mctrl = vt8500_set_mctrl,
.get_mctrl = vt8500_get_mctrl,
.stop_tx = vt8500_stop_tx,
.start_tx = vt8500_start_tx,
.stop_rx = vt8500_stop_rx,
.enable_ms = vt8500_enable_ms,
.break_ctl = vt8500_break_ctl,
.startup = vt8500_startup,
.shutdown = vt8500_shutdown,
.set_termios = vt8500_set_termios,
.type = vt8500_type,
.release_port = vt8500_release_port,
.request_port = vt8500_request_port,
.config_port = vt8500_config_port,
.verify_port = vt8500_verify_port,
};
static struct uart_driver vt8500_uart_driver = {
.owner = THIS_MODULE,
.driver_name = "vt8500_serial",
.dev_name = "ttyWMT",
.nr = 6,
.cons = VT8500_CONSOLE,
};
static int __init vt8500_serial_probe(struct platform_device *pdev)
{
struct vt8500_port *vt8500_port;
struct resource *mmres, *irqres;
int ret;
mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!mmres || !irqres)
return -ENODEV;
vt8500_port = kzalloc(sizeof(struct vt8500_port), GFP_KERNEL);
if (!vt8500_port)
return -ENOMEM;
vt8500_port->uart.type = PORT_VT8500;
vt8500_port->uart.iotype = UPIO_MEM;
vt8500_port->uart.mapbase = mmres->start;
vt8500_port->uart.irq = irqres->start;
vt8500_port->uart.fifosize = 16;
vt8500_port->uart.ops = &vt8500_uart_pops;
vt8500_port->uart.line = pdev->id;
vt8500_port->uart.dev = &pdev->dev;
vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
vt8500_port->uart.uartclk = 24000000;
snprintf(vt8500_port->name, sizeof(vt8500_port->name),
"VT8500 UART%d", pdev->id);
vt8500_port->uart.membase = ioremap(mmres->start,
mmres->end - mmres->start + 1);
if (!vt8500_port->uart.membase) {
ret = -ENOMEM;
goto err;
}
vt8500_uart_ports[pdev->id] = vt8500_port;
uart_add_one_port(&vt8500_uart_driver, &vt8500_port->uart);
platform_set_drvdata(pdev, vt8500_port);
return 0;
err:
kfree(vt8500_port);
return ret;
}
static int __devexit vt8500_serial_remove(struct platform_device *pdev)
{
struct vt8500_port *vt8500_port = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
uart_remove_one_port(&vt8500_uart_driver, &vt8500_port->uart);
kfree(vt8500_port);
return 0;
}
static struct platform_driver vt8500_platform_driver = {
.probe = vt8500_serial_probe,
.remove = vt8500_serial_remove,
.driver = {
.name = "vt8500_serial",
.owner = THIS_MODULE,
},
};
static int __init vt8500_serial_init(void)
{
int ret;
ret = uart_register_driver(&vt8500_uart_driver);
if (unlikely(ret))
return ret;
ret = platform_driver_register(&vt8500_platform_driver);
if (unlikely(ret))
uart_unregister_driver(&vt8500_uart_driver);
return ret;
}
static void __exit vt8500_serial_exit(void)
{
#ifdef CONFIG_SERIAL_VT8500_CONSOLE
unregister_console(&vt8500_console);
#endif
platform_driver_unregister(&vt8500_platform_driver);
uart_unregister_driver(&vt8500_uart_driver);
}
module_init(vt8500_serial_init);
module_exit(vt8500_serial_exit);
MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
MODULE_DESCRIPTION("Driver for vt8500 serial device");
MODULE_LICENSE("GPL");
此差异已折叠。
......@@ -2627,6 +2627,11 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return put_user(tty->ldisc->ops->num, (int __user *)p);
case TIOCSETD:
return tiocsetd(tty, p);
case TIOCGDEV:
{
unsigned int ret = new_encode_dev(tty_devnum(real_tty));
return put_user(ret, (unsigned int __user *)p);
}
/*
* Break handling
*/
......@@ -3241,9 +3246,45 @@ static int __init tty_class_init(void)
postcore_initcall(tty_class_init);
/* 3/2004 jmc: why do these devices exist? */
static struct cdev tty_cdev, console_cdev;
static ssize_t show_cons_active(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct console *cs[16];
int i = 0;
struct console *c;
ssize_t count = 0;
acquire_console_sem();
for (c = console_drivers; c; c = c->next) {
if (!c->device)
continue;
if (!c->write)
continue;
if ((c->flags & CON_ENABLED) == 0)
continue;
cs[i++] = c;
if (i >= ARRAY_SIZE(cs))
break;
}
while (i--)
count += sprintf(buf + count, "%s%d%c",
cs[i]->name, cs[i]->index, i ? ' ':'\n');
release_console_sem();
return count;
}
static DEVICE_ATTR(active, S_IRUGO, show_cons_active, NULL);
static struct device *consdev;
void console_sysfs_notify(void)
{
if (consdev)
sysfs_notify(&consdev->kobj, NULL, "active");
}
/*
* Ok, now we can initialize the rest of the tty devices and can count
* on memory allocations, interrupts etc..
......@@ -3254,15 +3295,18 @@ int __init tty_init(void)
if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
panic("Couldn't register /dev/tty driver\n");
device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL,
"tty");
device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty");
cdev_init(&console_cdev, &console_fops);
if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
panic("Couldn't register /dev/console driver\n");
device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL,
consdev = device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL,
"console");
if (IS_ERR(consdev))
consdev = NULL;
else
device_create_file(consdev, &dev_attr_active);
#ifdef CONFIG_VT
vty_init(&console_fops);
......
......@@ -235,6 +235,14 @@ enum {
blank_vesa_wait,
};
/*
* /sys/class/tty/tty0/
*
* the attribute 'active' contains the name of the current vc
* console and it supports poll() to detect vc switches
*/
static struct device *tty0dev;
/*
* Notifier list for console events.
*/
......@@ -688,6 +696,8 @@ void redraw_screen(struct vc_data *vc, int is_switch)
save_screen(old_vc);
set_origin(old_vc);
}
if (tty0dev)
sysfs_notify(&tty0dev->kobj, NULL, "active");
} else {
hide_cursor(vc);
redraw = 1;
......@@ -2967,13 +2977,24 @@ static const struct tty_operations con_ops = {
static struct cdev vc0_cdev;
static ssize_t show_tty_active(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "tty%d\n", fg_console + 1);
}
static DEVICE_ATTR(active, S_IRUGO, show_tty_active, NULL);
int __init vty_init(const struct file_operations *console_fops)
{
cdev_init(&vc0_cdev, console_fops);
if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||
register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0)
panic("Couldn't register /dev/tty0 driver\n");
device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0");
tty0dev = device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0");
if (IS_ERR(tty0dev))
tty0dev = NULL;
else
device_create_file(tty0dev, &dev_attr_active);
vcs_init();
......
......@@ -492,7 +492,7 @@ xenfb_make_preferred_console(void)
return;
acquire_console_sem();
for (c = console_drivers; c; c = c->next) {
for_each_console(c) {
if (!strcmp(c->name, "tty") && c->index == 0)
break;
}
......
......@@ -836,6 +836,7 @@ COMPATIBLE_IOCTL(TCSETSW)
COMPATIBLE_IOCTL(TCSETSF)
COMPATIBLE_IOCTL(TIOCLINUX)
COMPATIBLE_IOCTL(TIOCSBRK)
COMPATIBLE_IOCTL(TIOCGDEV)
COMPATIBLE_IOCTL(TIOCCBRK)
COMPATIBLE_IOCTL(TIOCGSID)
COMPATIBLE_IOCTL(TIOCGICOUNT)
......
......@@ -15,6 +15,7 @@ proc-y += devices.o
proc-y += interrupts.o
proc-y += loadavg.o
proc-y += meminfo.o
proc-y += proc_console.o
proc-y += stat.o
proc-y += uptime.o
proc-y += version.o
......
/*
* Copyright (c) 2010 Werner Fink, Jiri Slaby
*
* Licensed under GPLv2
*/
#include <linux/console.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/tty_driver.h>
/*
* This is handler for /proc/consoles
*/
static int show_console_dev(struct seq_file *m, void *v)
{
static const struct {
short flag;
char name;
} con_flags[] = {
{ CON_ENABLED, 'E' },
{ CON_CONSDEV, 'C' },
{ CON_BOOT, 'B' },
{ CON_PRINTBUFFER, 'p' },
{ CON_BRL, 'b' },
{ CON_ANYTIME, 'a' },
};
char flags[ARRAY_SIZE(con_flags) + 1];
struct console *con = v;
unsigned int a;
int len;
dev_t dev = 0;
if (con->device) {
const struct tty_driver *driver;
int index;
driver = con->device(con, &index);
if (driver) {
dev = MKDEV(driver->major, driver->minor_start);
dev += index;
}
}
for (a = 0; a < ARRAY_SIZE(con_flags); a++)
flags[a] = (con->flags & con_flags[a].flag) ?
con_flags[a].name : ' ';
flags[a] = 0;
seq_printf(m, "%s%d%n", con->name, con->index, &len);
len = 21 - len;
if (len < 1)
len = 1;
seq_printf(m, "%*c%c%c%c (%s)", len, ' ', con->read ? 'R' : '-',
con->write ? 'W' : '-', con->unblank ? 'U' : '-',
flags);
if (dev)
seq_printf(m, " %4d:%d", MAJOR(dev), MINOR(dev));
seq_printf(m, "\n");
return 0;
}
static void *c_start(struct seq_file *m, loff_t *pos)
{
struct console *con;
loff_t off = 0;
acquire_console_sem();
for_each_console(con)
if (off++ == *pos)
break;
return con;
}
static void *c_next(struct seq_file *m, void *v, loff_t *pos)
{
struct console *con = v;
++*pos;
return con->next;
}
static void c_stop(struct seq_file *m, void *v)
{
release_console_sem();
}
static const struct seq_operations consoles_op = {
.start = c_start,
.next = c_next,
.stop = c_stop,
.show = show_console_dev
};
static int consoles_open(struct inode *inode, struct file *file)
{
return seq_open(file, &consoles_op);
}
static const struct file_operations proc_consoles_operations = {
.open = consoles_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
static int register_proc_consoles(void)
{
proc_create("consoles", 0, NULL, &proc_consoles_operations);
return 0;
}
module_init(register_proc_consoles);
......@@ -67,6 +67,7 @@
#endif
#define TIOCGPTN _IOR('T', 0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T', 0x31, int) /* Lock/unlock Pty */
#define TIOCGDEV _IOR('T', 0x32, unsigned int) /* Get primary device node of /dev/console */
#define TCGETX 0x5432 /* SYS5 TCGETX compatibility */
#define TCSETX 0x5433
#define TCSETXF 0x5434
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册