summaryrefslogtreecommitdiffstats
path: root/drivers/tty
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-01-07 14:39:20 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2011-01-07 14:39:20 -0800
commit56b85f32d530d09d6805488ad00775d4e0e3baab (patch)
treee7fbe69e338ef775d3b2dd822aa915d259b4bc94 /drivers/tty
parent3e5b08cbbf78bedd316904ab0cf3b27119433ee5 (diff)
parent568389c257fa7d74ce36c2f78bad31965fded4cf (diff)
downloadlinux-56b85f32d530d09d6805488ad00775d4e0e3baab.tar.gz
linux-56b85f32d530d09d6805488ad00775d4e0e3baab.tar.bz2
linux-56b85f32d530d09d6805488ad00775d4e0e3baab.zip
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).
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/n_gsm.c213
-rw-r--r--drivers/tty/tty_io.c52
-rw-r--r--drivers/tty/vt/vt.c23
3 files changed, 197 insertions, 91 deletions
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index c5f8e5bda2b2..44b8412a04e8 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -19,7 +19,7 @@
*
* TO DO:
* Mostly done: ioctls for setting modes/timing
- * Partly done: hooks so you can pull off frames to non tty devs
+ * Partly done: hooks so you can pull off frames to non tty devs
* Restart DLCI 0 when it closes ?
* Test basic encoding
* Improve the tx engine
@@ -73,8 +73,10 @@ module_param(debug, int, 0600);
#define T2 (2 * HZ)
#endif
-/* Semi-arbitary buffer size limits. 0710 is normally run with 32-64 byte
- limits so this is plenty */
+/*
+ * Semi-arbitary buffer size limits. 0710 is normally run with 32-64 byte
+ * limits so this is plenty
+ */
#define MAX_MRU 512
#define MAX_MTU 512
@@ -184,6 +186,9 @@ struct gsm_mux {
#define GSM_DATA 5
#define GSM_FCS 6
#define GSM_OVERRUN 7
+#define GSM_LEN0 8
+#define GSM_LEN1 9
+#define GSM_SSOF 10
unsigned int len;
unsigned int address;
unsigned int count;
@@ -191,6 +196,7 @@ struct gsm_mux {
int encoding;
u8 control;
u8 fcs;
+ u8 received_fcs;
u8 *txframe; /* TX framing buffer */
/* Methods for the receiver side */
@@ -286,7 +292,7 @@ static spinlock_t gsm_mux_lock;
#define MDM_DV 0x40
#define GSM0_SOF 0xF9
-#define GSM1_SOF 0x7E
+#define GSM1_SOF 0x7E
#define GSM1_ESCAPE 0x7D
#define GSM1_ESCAPE_BITS 0x20
#define XON 0x11
@@ -429,61 +435,63 @@ static void gsm_print_packet(const char *hdr, int addr, int cr,
if (!(debug & 1))
return;
- printk(KERN_INFO "%s %d) %c: ", hdr, addr, "RC"[cr]);
+ pr_info("%s %d) %c: ", hdr, addr, "RC"[cr]);
switch (control & ~PF) {
case SABM:
- printk(KERN_CONT "SABM");
+ pr_cont("SABM");
break;
case UA:
- printk(KERN_CONT "UA");
+ pr_cont("UA");
break;
case DISC:
- printk(KERN_CONT "DISC");
+ pr_cont("DISC");
break;
case DM:
- printk(KERN_CONT "DM");
+ pr_cont("DM");
break;
case UI:
- printk(KERN_CONT "UI");
+ pr_cont("UI");
break;
case UIH:
- printk(KERN_CONT "UIH");
+ pr_cont("UIH");
break;
default:
if (!(control & 0x01)) {
- printk(KERN_CONT "I N(S)%d N(R)%d",
- (control & 0x0E) >> 1, (control & 0xE)>> 5);
+ pr_cont("I N(S)%d N(R)%d",
+ (control & 0x0E) >> 1, (control & 0xE) >> 5);
} else switch (control & 0x0F) {
- case RR:
- printk("RR(%d)", (control & 0xE0) >> 5);
- break;
- case RNR:
- printk("RNR(%d)", (control & 0xE0) >> 5);
- break;
- case REJ:
- printk("REJ(%d)", (control & 0xE0) >> 5);
- break;
- default:
- printk(KERN_CONT "[%02X]", control);
+ case RR:
+ pr_cont("RR(%d)", (control & 0xE0) >> 5);
+ break;
+ case RNR:
+ pr_cont("RNR(%d)", (control & 0xE0) >> 5);
+ break;
+ case REJ:
+ pr_cont("REJ(%d)", (control & 0xE0) >> 5);
+ break;
+ default:
+ pr_cont("[%02X]", control);
}
}
if (control & PF)
- printk(KERN_CONT "(P)");
+ pr_cont("(P)");
else
- printk(KERN_CONT "(F)");
+ pr_cont("(F)");
if (dlen) {
int ct = 0;
while (dlen--) {
- if (ct % 8 == 0)
- printk(KERN_CONT "\n ");
- printk(KERN_CONT "%02X ", *data++);
+ if (ct % 8 == 0) {
+ pr_cont("\n");
+ pr_debug(" ");
+ }
+ pr_cont("%02X ", *data++);
ct++;
}
}
- printk(KERN_CONT "\n");
+ pr_cont("\n");
}
@@ -522,11 +530,13 @@ static void hex_packet(const unsigned char *p, int len)
{
int i;
for (i = 0; i < len; i++) {
- if (i && (i % 16) == 0)
- printk("\n");
- printk("%02X ", *p++);
+ if (i && (i % 16) == 0) {
+ pr_cont("\n");
+ pr_debug("");
+ }
+ pr_cont("%02X ", *p++);
}
- printk("\n");
+ pr_cont("\n");
}
/**
@@ -676,7 +686,7 @@ static void gsm_data_kick(struct gsm_mux *gsm)
}
if (debug & 4) {
- printk("gsm_data_kick: \n");
+ pr_debug("gsm_data_kick:\n");
hex_packet(gsm->txframe, len);
}
@@ -1231,7 +1241,7 @@ static void gsm_control_response(struct gsm_mux *gsm, unsigned int command,
}
/**
- * gsm_control_transmit - send control packet
+ * gsm_control_transmit - send control packet
* @gsm: gsm mux
* @ctrl: frame to send
*
@@ -1361,7 +1371,7 @@ static void gsm_dlci_close(struct gsm_dlci *dlci)
{
del_timer(&dlci->t1);
if (debug & 8)
- printk("DLCI %d goes closed.\n", dlci->addr);
+ pr_debug("DLCI %d goes closed.\n", dlci->addr);
dlci->state = DLCI_CLOSED;
if (dlci->addr != 0) {
struct tty_struct *tty = tty_port_tty_get(&dlci->port);
@@ -1392,7 +1402,7 @@ static void gsm_dlci_open(struct gsm_dlci *dlci)
/* This will let a tty open continue */
dlci->state = DLCI_OPEN;
if (debug & 8)
- printk("DLCI %d goes open.\n", dlci->addr);
+ pr_debug("DLCI %d goes open.\n", dlci->addr);
wake_up(&dlci->gsm->event);
}
@@ -1494,29 +1504,29 @@ static void gsm_dlci_data(struct gsm_dlci *dlci, u8 *data, int len)
unsigned int modem = 0;
if (debug & 16)
- printk("%d bytes for tty %p\n", len, tty);
+ pr_debug("%d bytes for tty %p\n", len, tty);
if (tty) {
switch (dlci->adaption) {
- /* Unsupported types */
- /* Packetised interruptible data */
- case 4:
- break;
- /* Packetised uininterruptible voice/data */
- case 3:
- break;
- /* Asynchronous serial with line state in each frame */
- case 2:
- while (gsm_read_ea(&modem, *data++) == 0) {
- len--;
- if (len == 0)
- return;
- }
- gsm_process_modem(tty, dlci, modem);
- /* Line state will go via DLCI 0 controls only */
- case 1:
- default:
- tty_insert_flip_string(tty, data, len);
- tty_flip_buffer_push(tty);
+ /* Unsupported types */
+ /* Packetised interruptible data */
+ case 4:
+ break;
+ /* Packetised uininterruptible voice/data */
+ case 3:
+ break;
+ /* Asynchronous serial with line state in each frame */
+ case 2:
+ while (gsm_read_ea(&modem, *data++) == 0) {
+ len--;
+ if (len == 0)
+ return;
+ }
+ gsm_process_modem(tty, dlci, modem);
+ /* Line state will go via DLCI 0 controls only */
+ case 1:
+ default:
+ tty_insert_flip_string(tty, data, len);
+ tty_flip_buffer_push(tty);
}
tty_kref_put(tty);
}
@@ -1625,7 +1635,6 @@ static void gsm_dlci_free(struct gsm_dlci *dlci)
kfree(dlci);
}
-
/*
* LAPBish link layer logic
*/
@@ -1650,10 +1659,12 @@ static void gsm_queue(struct gsm_mux *gsm)
if ((gsm->control & ~PF) == UI)
gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->len);
+ /* generate final CRC with received FCS */
+ gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs);
if (gsm->fcs != GOOD_FCS) {
gsm->bad_fcs++;
if (debug & 4)
- printk("BAD FCS %02x\n", gsm->fcs);
+ pr_debug("BAD FCS %02x\n", gsm->fcs);
return;
}
address = gsm->address >> 1;
@@ -1748,6 +1759,8 @@ invalid:
static void gsm0_receive(struct gsm_mux *gsm, unsigned char c)
{
+ unsigned int len;
+
switch (gsm->state) {
case GSM_SEARCH: /* SOF marker */
if (c == GSM0_SOF) {
@@ -1756,8 +1769,8 @@ static void gsm0_receive(struct gsm_mux *gsm, unsigned char c)
gsm->len = 0;
gsm->fcs = INIT_FCS;
}
- break; /* Address EA */
- case GSM_ADDRESS:
+ break;
+ case GSM_ADDRESS: /* Address EA */
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
if (gsm_read_ea(&gsm->address, c))
gsm->state = GSM_CONTROL;
@@ -1765,9 +1778,9 @@ static void gsm0_receive(struct gsm_mux *gsm, unsigned char c)
case GSM_CONTROL: /* Control Byte */
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
gsm->control = c;
- gsm->state = GSM_LEN;
+ gsm->state = GSM_LEN0;
break;
- case GSM_LEN: /* Length EA */
+ case GSM_LEN0: /* Length EA */
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
if (gsm_read_ea(&gsm->len, c)) {
if (gsm->len > gsm->mru) {
@@ -1776,8 +1789,28 @@ static void gsm0_receive(struct gsm_mux *gsm, unsigned char c)
break;
}
gsm->count = 0;
- gsm->state = GSM_DATA;
+ if (!gsm->len)
+ gsm->state = GSM_FCS;
+ else
+ gsm->state = GSM_DATA;
+ break;
+ }
+ gsm->state = GSM_LEN1;
+ break;
+ case GSM_LEN1:
+ gsm->fcs = gsm_fcs_add(gsm->fcs, c);
+ len = c;
+ gsm->len |= len << 7;
+ if (gsm->len > gsm->mru) {
+ gsm->bad_size++;
+ gsm->state = GSM_SEARCH;
+ break;
}
+ gsm->count = 0;
+ if (!gsm->len)
+ gsm->state = GSM_FCS;
+ else
+ gsm->state = GSM_DATA;
break;
case GSM_DATA: /* Data */
gsm->buf[gsm->count++] = c;
@@ -1785,16 +1818,25 @@ static void gsm0_receive(struct gsm_mux *gsm, unsigned char c)
gsm->state = GSM_FCS;
break;
case GSM_FCS: /* FCS follows the packet */
- gsm->fcs = c;
+ gsm->received_fcs = c;
+ if (c == GSM0_SOF) {
+ gsm->state = GSM_SEARCH;
+ break;
+ }
gsm_queue(gsm);
- /* And then back for the next frame */
- gsm->state = GSM_SEARCH;
+ gsm->state = GSM_SSOF;
+ break;
+ case GSM_SSOF:
+ if (c == GSM0_SOF) {
+ gsm->state = GSM_SEARCH;
+ break;
+ }
break;
}
}
/**
- * gsm0_receive - perform processing for non-transparency
+ * gsm1_receive - perform processing for non-transparency
* @gsm: gsm data for this ldisc instance
* @c: character
*
@@ -1856,7 +1898,7 @@ static void gsm1_receive(struct gsm_mux *gsm, unsigned char c)
gsm->state = GSM_DATA;
break;
case GSM_DATA: /* Data */
- if (gsm->count > gsm->mru ) { /* Allow one for the FCS */
+ if (gsm->count > gsm->mru) { /* Allow one for the FCS */
gsm->state = GSM_OVERRUN;
gsm->bad_size++;
} else
@@ -2034,9 +2076,6 @@ struct gsm_mux *gsm_alloc_mux(void)
}
EXPORT_SYMBOL_GPL(gsm_alloc_mux);
-
-
-
/**
* gsmld_output - write to link
* @gsm: our mux
@@ -2054,7 +2093,7 @@ static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len)
return -ENOSPC;
}
if (debug & 4) {
- printk("-->%d bytes out\n", len);
+ pr_debug("-->%d bytes out\n", len);
hex_packet(data, len);
}
gsm->tty->ops->write(gsm->tty, data, len);
@@ -2111,7 +2150,7 @@ static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char flags;
if (debug & 4) {
- printk("Inbytes %dd\n", count);
+ pr_debug("Inbytes %dd\n", count);
hex_packet(cp, count);
}
@@ -2128,7 +2167,7 @@ static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp,
gsm->error(gsm, *dp, flags);
break;
default:
- printk(KERN_ERR "%s: unknown flag %d\n",
+ WARN_ONCE("%s: unknown flag %d\n",
tty_name(tty, buf), flags);
break;
}
@@ -2323,7 +2362,7 @@ static int gsmld_config(struct tty_struct *tty, struct gsm_mux *gsm,
int need_restart = 0;
/* Stuff we don't support yet - UI or I frame transport, windowing */
- if ((c->adaption !=1 && c->adaption != 2) || c->k)
+ if ((c->adaption != 1 && c->adaption != 2) || c->k)
return -EOPNOTSUPP;
/* Check the MRU/MTU range looks sane */
if (c->mru > MAX_MRU || c->mtu > MAX_MTU || c->mru < 8 || c->mtu < 8)
@@ -2418,7 +2457,7 @@ static int gsmld_ioctl(struct tty_struct *tty, struct file *file,
c.i = 1;
else
c.i = 2;
- printk("Ftype %d i %d\n", gsm->ftype, c.i);
+ pr_debug("Ftype %d i %d\n", gsm->ftype, c.i);
c.mru = gsm->mru;
c.mtu = gsm->mtu;
c.k = 0;
@@ -2712,14 +2751,15 @@ static int __init gsm_init(void)
/* Fill in our line protocol discipline, and register it */
int status = tty_register_ldisc(N_GSM0710, &tty_ldisc_packet);
if (status != 0) {
- printk(KERN_ERR "n_gsm: can't register line discipline (err = %d)\n", status);
+ pr_err("n_gsm: can't register line discipline (err = %d)\n",
+ status);
return status;
}
gsm_tty_driver = alloc_tty_driver(256);
if (!gsm_tty_driver) {
tty_unregister_ldisc(N_GSM0710);
- printk(KERN_ERR "gsm_init: tty allocation failed.\n");
+ pr_err("gsm_init: tty allocation failed.\n");
return -EINVAL;
}
gsm_tty_driver->owner = THIS_MODULE;
@@ -2730,7 +2770,7 @@ static int __init gsm_init(void)
gsm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
gsm_tty_driver->subtype = SERIAL_TYPE_NORMAL;
gsm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV
- | TTY_DRIVER_HARDWARE_BREAK;
+ | TTY_DRIVER_HARDWARE_BREAK;
gsm_tty_driver->init_termios = tty_std_termios;
/* Fixme */
gsm_tty_driver->init_termios.c_lflag &= ~ECHO;
@@ -2741,10 +2781,11 @@ static int __init gsm_init(void)
if (tty_register_driver(gsm_tty_driver)) {
put_tty_driver(gsm_tty_driver);
tty_unregister_ldisc(N_GSM0710);
- printk(KERN_ERR "gsm_init: tty registration failed.\n");
+ pr_err("gsm_init: tty registration failed.\n");
return -EBUSY;
}
- printk(KERN_INFO "gsm_init: loaded as %d,%d.\n", gsm_tty_driver->major, gsm_tty_driver->minor_start);
+ pr_debug("gsm_init: loaded as %d,%d.\n",
+ gsm_tty_driver->major, gsm_tty_driver->minor_start);
return 0;
}
@@ -2752,10 +2793,10 @@ static void __exit gsm_exit(void)
{
int status = tty_unregister_ldisc(N_GSM0710);
if (status != 0)
- printk(KERN_ERR "n_gsm: can't unregister line discipline (err = %d)\n", status);
+ pr_err("n_gsm: can't unregister line discipline (err = %d)\n",
+ status);
tty_unregister_driver(gsm_tty_driver);
put_tty_driver(gsm_tty_driver);
- printk(KERN_INFO "gsm_init: unloaded.\n");
}
module_init(gsm_init);
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 35480dd57a30..464d09d97873 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -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);
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index a8ec48ed14d9..76407eca9ab0 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -236,6 +236,14 @@ enum {
};
/*
+ * /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.
*/
static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);
@@ -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();