From d3cf9c7d3cb6a89c5ee679d866610bc6baaf2c9a Mon Sep 17 00:00:00 2001 From: df157793 Date: Tue, 21 Jun 2005 19:39:15 -0700 Subject: 6245620 su driver install as sun4u specific should really go in /kernel/drv --HG-- rename : usr/src/uts/sun4u/io/su_driver.c => usr/src/uts/sun4/io/su_driver.c rename : usr/src/uts/sun4u/sys/sudev.h => usr/src/uts/sun4/sys/sudev.h --- usr/src/uts/sun4/Makefile.files | 1 + usr/src/uts/sun4/io/su_driver.c | 3457 ++++++++++++++++++++++++++++++++++++++ usr/src/uts/sun4/sys/sudev.h | 400 +++++ usr/src/uts/sun4u/Makefile.files | 1 - usr/src/uts/sun4u/io/su_driver.c | 3457 -------------------------------------- usr/src/uts/sun4u/sys/sudev.h | 400 ----- usr/src/uts/sun4v/Makefile.sun4v | 1 + usr/src/uts/sun4v/su/Makefile | 88 + 8 files changed, 3947 insertions(+), 3858 deletions(-) create mode 100644 usr/src/uts/sun4/io/su_driver.c create mode 100644 usr/src/uts/sun4/sys/sudev.h delete mode 100644 usr/src/uts/sun4u/io/su_driver.c delete mode 100644 usr/src/uts/sun4u/sys/sudev.h create mode 100644 usr/src/uts/sun4v/su/Makefile (limited to 'usr/src/uts') diff --git a/usr/src/uts/sun4/Makefile.files b/usr/src/uts/sun4/Makefile.files index 6a26638401..0ddd3d7d58 100644 --- a/usr/src/uts/sun4/Makefile.files +++ b/usr/src/uts/sun4/Makefile.files @@ -82,6 +82,7 @@ FCODE_OBJS += fcode.o FCODEM_OBJS += fc_ddi.o fc_physio.o fc_ops.o fc_subr.o FCPCI_OBJS += fcpci.o EBUS_OBJS += ebus.o +SU_OBJS += su_driver.o # # Misc modules diff --git a/usr/src/uts/sun4/io/su_driver.c b/usr/src/uts/sun4/io/su_driver.c new file mode 100644 index 0000000000..4400077b45 --- /dev/null +++ b/usr/src/uts/sun4/io/su_driver.c @@ -0,0 +1,3457 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ +/* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Serial I/O driver for 82510/8250/16450/16550AF chips. + * Modified as sparc keyboard/mouse driver. + */ +#define SU_REGISTER_FILE_NO 0 +#define SU_REGOFFSET 0 +#define SU_REGISTER_LEN 8 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef DEBUG +#include +#endif +#include +#include +#include +#include +#include +#include + +#define async_stopc async_ttycommon.t_stopc +#define async_startc async_ttycommon.t_startc + +#define ASY_INIT 1 +#define ASY_NOINIT 0 + +#ifdef DEBUG +#define ASY_DEBUG_INIT 0x001 +#define ASY_DEBUG_INPUT 0x002 +#define ASY_DEBUG_EOT 0x004 +#define ASY_DEBUG_CLOSE 0x008 +#define ASY_DEBUG_HFLOW 0x010 +#define ASY_DEBUG_PROCS 0x020 +#define ASY_DEBUG_STATE 0x040 +#define ASY_DEBUG_INTR 0x080 +static int asydebug = 0; +#endif +static int su_log = 0; + +int su_drain_check = 15000000; /* tunable: exit drain check time */ + +static struct ppsclockev asy_ppsev; + +static int max_asy_instance = -1; +static void *su_asycom; /* soft state asycom pointer */ +static void *su_asyncline; /* soft state asyncline pointer */ +static boolean_t abort_charseq_recognize(uchar_t ch); + +static uint_t asysoftintr(caddr_t intarg); +static uint_t asyintr(caddr_t argasy); + +/* The async interrupt entry points */ +static void async_txint(struct asycom *asy, uchar_t lsr); +static void async_rxint(struct asycom *asy, uchar_t lsr); +static void async_msint(struct asycom *asy); +static int async_softint(struct asycom *asy); + +static void async_ioctl(struct asyncline *async, queue_t *q, mblk_t *mp, + boolean_t iswput); +static void async_reioctl(void *); +static void async_iocdata(queue_t *q, mblk_t *mp); +static void async_restart(void *); +static void async_start(struct asyncline *async); +static void async_nstart(struct asyncline *async, int mode); +static void async_resume(struct asyncline *async); +static int asy_program(struct asycom *asy, int mode); + +static int asymctl(struct asycom *, int, int); +static int asytodm(int, int); +static int dmtoasy(int); +static void asycheckflowcontrol_hw(struct asycom *asy); +static boolean_t asycheckflowcontrol_sw(struct asycom *asy); +static void asy_ppsevent(struct asycom *asy, int msr); + +extern kcondvar_t lbolt_cv; +extern int ddi_create_internal_pathname(dev_info_t *dip, char *name, + int spec_type, minor_t minor_num); + + +/* + * Baud rate table. Indexed by #defines found in sys/termios.h + */ +ushort_t asyspdtab[] = { + 0, /* 0 baud rate */ + 0x900, /* 50 baud rate */ + 0x600, /* 75 baud rate */ + 0x417, /* 110 baud rate (%0.026) */ + 0x359, /* 134 baud rate (%0.058) */ + 0x300, /* 150 baud rate */ + 0x240, /* 200 baud rate */ + 0x180, /* 300 baud rate */ + 0x0c0, /* 600 baud rate */ + 0x060, /* 1200 baud rate */ + 0x040, /* 1800 baud rate */ + 0x030, /* 2400 baud rate */ + 0x018, /* 4800 baud rate */ + 0x00c, /* 9600 baud rate */ + 0x006, /* 19200 baud rate */ + 0x003, /* 38400 baud rate */ + 0x002, /* 57600 baud rate */ + 0, /* 76800 baud rate - not supported */ + 0x001, /* 115200 baud rate */ + 0, /* 153600 baud rate - not supported */ + 0x8002, /* 230400 baud rate - supported on specific platforms */ + 0, /* 307200 baud rate - not supported */ + 0x8001 /* 460800 baud rate - supported on specific platforms */ +}; + +/* + * Number of speeds supported is the number of entries in + * the above table. + */ +#define N_SU_SPEEDS (sizeof (asyspdtab)/sizeof (ushort_t)) + +/* + * Human-readable baud rate table. + * Indexed by #defines found in sys/termios.h + */ +int baudtable[] = { + 0, /* 0 baud rate */ + 50, /* 50 baud rate */ + 75, /* 75 baud rate */ + 110, /* 110 baud rate */ + 134, /* 134 baud rate */ + 150, /* 150 baud rate */ + 200, /* 200 baud rate */ + 300, /* 300 baud rate */ + 600, /* 600 baud rate */ + 1200, /* 1200 baud rate */ + 1800, /* 1800 baud rate */ + 2400, /* 2400 baud rate */ + 4800, /* 4800 baud rate */ + 9600, /* 9600 baud rate */ + 19200, /* 19200 baud rate */ + 38400, /* 38400 baud rate */ + 57600, /* 57600 baud rate */ + 76800, /* 76800 baud rate */ + 115200, /* 115200 baud rate */ + 153600, /* 153600 baud rate */ + 230400, /* 230400 baud rate */ + 307200, /* 307200 baud rate */ + 460800 /* 460800 baud rate */ +}; + +static int asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr); +static int asyclose(queue_t *q, int flag); +static void asywput(queue_t *q, mblk_t *mp); +static void asyrsrv(queue_t *q); + +struct module_info asy_info = { + 0, + "su", + 0, + INFPSZ, + 32*4096, + 4096 +}; + +static struct qinit asy_rint = { + putq, + (int (*)())asyrsrv, + asyopen, + asyclose, + NULL, + &asy_info, + NULL +}; + +static struct qinit asy_wint = { + (int (*)())asywput, + NULL, + NULL, + NULL, + NULL, + &asy_info, + NULL +}; + +struct streamtab asy_str_info = { + &asy_rint, + &asy_wint, + NULL, + NULL +}; + +static int asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, + void **result); +static int asyprobe(dev_info_t *); +static int asyattach(dev_info_t *, ddi_attach_cmd_t); +static int asydetach(dev_info_t *, ddi_detach_cmd_t); + +static struct cb_ops cb_asy_ops = { + nodev, /* cb_open */ + nodev, /* cb_close */ + nodev, /* cb_strategy */ + nodev, /* cb_print */ + nodev, /* cb_dump */ + nodev, /* cb_read */ + nodev, /* cb_write */ + nodev, /* cb_ioctl */ + nodev, /* cb_devmap */ + nodev, /* cb_mmap */ + nodev, /* cb_segmap */ + nochpoll, /* cb_chpoll */ + ddi_prop_op, /* cb_prop_op */ + &asy_str_info, /* cb_stream */ + D_MP /* cb_flag */ +}; + +struct dev_ops asy_ops = { + DEVO_REV, /* devo_rev */ + 0, /* devo_refcnt */ + asyinfo, /* devo_getinfo */ + nulldev, /* devo_identify */ + asyprobe, /* devo_probe */ + asyattach, /* devo_attach */ + asydetach, /* devo_detach */ + nodev, /* devo_reset */ + &cb_asy_ops, /* devo_cb_ops */ +}; + +/* + * Module linkage information for the kernel. + */ + +static struct modldrv modldrv = { + &mod_driverops, /* Type of module. This one is a driver */ + "su driver %I%", + &asy_ops, /* driver ops */ +}; + +static struct modlinkage modlinkage = { + MODREV_1, + &modldrv, + NULL +}; + +int +_init(void) +{ + int status; + + status = ddi_soft_state_init(&su_asycom, sizeof (struct asycom), + SU_INITIAL_SOFT_ITEMS); + if (status != 0) + return (status); + status = ddi_soft_state_init(&su_asyncline, sizeof (struct asyncline), + SU_INITIAL_SOFT_ITEMS); + if (status != 0) { + ddi_soft_state_fini(&su_asycom); + return (status); + } + + if ((status = mod_install(&modlinkage)) != 0) { + ddi_soft_state_fini(&su_asycom); + ddi_soft_state_fini(&su_asyncline); + } + + return (status); +} + +int +_fini(void) +{ + int i; + + i = mod_remove(&modlinkage); + if (i == 0) { + ddi_soft_state_fini(&su_asycom); + ddi_soft_state_fini(&su_asyncline); + } + + return (i); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} + +static int +asyprobe(dev_info_t *devi) +{ + int instance; + ddi_acc_handle_t handle; + uchar_t *addr; + ddi_device_acc_attr_t attr; + + attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; + attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; + attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; + + if (ddi_regs_map_setup(devi, SU_REGISTER_FILE_NO, (caddr_t *)&addr, + SU_REGOFFSET, SU_REGISTER_LEN, &attr, &handle) != DDI_SUCCESS) { + cmn_err(CE_WARN, "asyprobe regs map setup failed"); + return (DDI_PROBE_FAILURE); + } +#ifdef DEBUG + if (asydebug) + printf("Probe address mapped %p\n", (void *)addr); +#endif + + /* + * Probe for the device: + * Ser. int. uses bits 0,1,2; FIFO uses 3,6,7; 4,5 wired low. + * If bit 4 or 5 appears on inb() ISR, board is not there. + */ + if (ddi_get8(handle, addr+ISR) & 0x30) + return (DDI_PROBE_FAILURE); + instance = ddi_get_instance(devi); + if (max_asy_instance < instance) + max_asy_instance = instance; + ddi_regs_map_free(&handle); + + return (DDI_PROBE_SUCCESS); /* hw is present */ +} + +static int +asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd) +{ + register int instance; + struct asycom *asy; + struct asyncline *async; + char name[16]; + + instance = ddi_get_instance(devi); /* find out which unit */ + + asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance); + async = (struct asyncline *)ddi_get_soft_state(su_asyncline, instance); + + switch (cmd) { + case DDI_DETACH: + break; + case DDI_SUSPEND: + /* grab both mutex locks */ + mutex_enter(asy->asy_excl); + mutex_enter(asy->asy_excl_hi); + if (asy->suspended) { + mutex_exit(asy->asy_excl_hi); + mutex_exit(asy->asy_excl); + return (DDI_SUCCESS); + } + asy->suspended = B_TRUE; + /* Disable further interrupts */ + OUTB(ICR, 0); + mutex_exit(asy->asy_excl_hi); + mutex_exit(asy->asy_excl); + return (DDI_SUCCESS); + + default: + return (DDI_FAILURE); + } + +#ifdef DEBUG + if (asydebug & ASY_DEBUG_INIT) + cmn_err(CE_NOTE, "su%d: ASY%s shutdown.", instance, + asy->asy_hwtype == ASY82510 ? "82510" : + asy->asy_hwtype == ASY16550AF ? "16550AF" : + "8250"); +#endif + /* + * Before removing interrupts it is always better to disable + * interrupts if the chip gives a provision to disable the + * serial port interrupts. + */ + mutex_enter(asy->asy_excl); + mutex_enter(asy->asy_excl_hi); + OUTB(ICR, 0); /* disables interrupt */ + mutex_exit(asy->asy_excl_hi); + mutex_exit(asy->asy_excl); + + /* remove minor device node(s) for this device */ + (void) sprintf(name, "%c", (instance+'a')); /* serial-port */ + ddi_remove_minor_node(devi, name); + (void) sprintf(name, "%c,cu", (instance+'a')); /* serial-port:dailout */ + ddi_remove_minor_node(devi, name); + + mutex_destroy(asy->asy_excl); + mutex_destroy(asy->asy_excl_hi); + kmem_free(asy->asy_excl, sizeof (kmutex_t)); + kmem_free(asy->asy_excl_hi, sizeof (kmutex_t)); + cv_destroy(&async->async_flags_cv); + kstat_delete(asy->sukstat); + ddi_remove_intr(devi, 0, asy->asy_iblock); + ddi_regs_map_free(&asy->asy_handle); + ddi_remove_softintr(asy->asy_softintr_id); + mutex_destroy(asy->asy_soft_lock); + kmem_free(asy->asy_soft_lock, sizeof (kmutex_t)); + ddi_soft_state_free(su_asycom, instance); + ddi_soft_state_free(su_asyncline, instance); + return (DDI_SUCCESS); +} + +static int +asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd) +{ + register int instance; + struct asycom *asy; + struct asyncline *async; + char name[40]; + ddi_device_acc_attr_t attr; + enum states { EMPTY, SOFTSTATE, REGSMAP, MUTEXES, ADDINTR, + SOFTINTR, ASYINIT, KSTAT, MINORNODE }; + enum states state = EMPTY; + + instance = ddi_get_instance(devi); /* find out which unit */ + + /* cannot attach a device that has not been probed first */ + if (instance > max_asy_instance) + return (DDI_FAILURE); + + if (cmd != DDI_RESUME) { + /* Allocate soft state space */ + if (ddi_soft_state_zalloc(su_asycom, instance) != DDI_SUCCESS) { + cmn_err(CE_WARN, "su%d: cannot allocate soft state", + instance); + goto error; + } + } + state = SOFTSTATE; + + asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance); + + if (asy == NULL) { + cmn_err(CE_WARN, "su%d: cannot get soft state", instance); + goto error; + } + + switch (cmd) { + case DDI_ATTACH: + break; + case DDI_RESUME: { + struct asyncline *async; + + /* grab both mutex locks */ + mutex_enter(asy->asy_excl); + mutex_enter(asy->asy_excl_hi); + if (!asy->suspended) { + mutex_exit(asy->asy_excl_hi); + mutex_exit(asy->asy_excl); + return (DDI_SUCCESS); + } + /* re-setup all the registers and enable interrupts if needed */ + async = (struct asyncline *)asy->asy_priv; + if ((async) && (async->async_flags & ASYNC_ISOPEN)) + (void) asy_program(asy, ASY_INIT); + asy->suspended = B_FALSE; + mutex_exit(asy->asy_excl_hi); + mutex_exit(asy->asy_excl); + return (DDI_SUCCESS); + } + default: + goto error; + } + + attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; + attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; + attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; + + if (ddi_regs_map_setup(devi, SU_REGISTER_FILE_NO, + (caddr_t *)&asy->asy_ioaddr, SU_REGOFFSET, SU_REGISTER_LEN, + &attr, &asy->asy_handle) != DDI_SUCCESS) { + cmn_err(CE_WARN, "asyprobe regs map setup failed"); + goto error; + } + state = REGSMAP; + +#ifdef DEBUG + if (asydebug) + printf("su attach mapped %p\n", (void *)asy->asy_ioaddr); +#endif + + /* + * Initialize the port with default settings. + */ + asy->asy_fifo_buf = 1; + asy->asy_use_fifo = FIFO_OFF; + + /* + * Check for baudrate generator's "baud-divisor-factor" property setup + * by OBP, since different UART chips might have different baudrate + * generator divisor. e.g., in case of NSPG's Sputnik platform, the + * baud-divisor-factor is 13, it uses dedicated 16552 "DUART" chip + * instead of SuperIO. Since the baud-divisor-factor must be a positive + * integer, the divisors will always be at least as large as the values + * in asyspdtab[]. Make the default factor 1. + */ + asy->asy_baud_divisor_factor = ddi_prop_get_int(DDI_DEV_T_ANY, devi, + DDI_PROP_DONTPASS, "baud-divisor-factor", 1); + + /* set speed cap */ + asy->asy_speed_cap = ddi_prop_get_int(DDI_DEV_T_ANY, devi, + DDI_PROP_DONTPASS, "serial-speed-cap", 115200); + + /* check for ASY82510 chip */ + OUTB(ISR, 0x20); + if (INB(ISR) & 0x20) { /* 82510 chip is present */ + /* + * Since most of the general operation of the 82510 chip + * can be done from BANK 0 (8250A/16450 compatable mode) + * we will default to BANK 0. + */ + asy->asy_hwtype = ASY82510; + OUTB(DAT+7, 0x04); /* clear status */ + OUTB(ISR, 0x40); /* set to bank 2 */ + OUTB(MCR, 0x08); /* IMD */ + OUTB(DAT, 0x21); /* FMD */ + OUTB(ISR, 0x00); /* set to bank 0 */ + asy->asy_trig_level = 0; + } else { /* Set the UART in FIFO mode if it has FIFO buffers */ + asy->asy_hwtype = ASY16550AF; + OUTB(FIFOR, 0x00); /* clear fifo register */ + asy->asy_trig_level = 0x00; /* sets the fifo Threshold to 1 */ + + /* set/Enable FIFO */ + OUTB(FIFOR, FIFO_ON | FIFODMA | FIFOTXFLSH | FIFORXFLSH | + (asy->asy_trig_level & 0xff)); + + if ((INB(ISR) & 0xc0) == 0xc0) + asy->asy_use_fifo = FIFO_ON; + else { + asy->asy_hwtype = ASY8250; + OUTB(FIFOR, 0x00); /* NO FIFOs */ + asy->asy_trig_level = 0; + } + } + + OUTB(ICR, 0); /* disable all interrupts */ + OUTB(LCR, DLAB); /* select baud rate generator */ + /* Set the baud rate to 9600 */ + OUTB(DAT+DLL, (ASY9600*asy->asy_baud_divisor_factor) & 0xff); + OUTB(DAT+DLH, ((ASY9600*asy->asy_baud_divisor_factor) >> 8) & 0xff); + OUTB(LCR, STOP1|BITS8); + OUTB(MCR, (DTR | RTS| OUT2)); + + /* + * Set up the other components of the asycom structure for this port. + */ + asy->asy_excl = (kmutex_t *) + kmem_zalloc(sizeof (kmutex_t), KM_SLEEP); + asy->asy_excl_hi = (kmutex_t *) + kmem_zalloc(sizeof (kmutex_t), KM_SLEEP); + asy->asy_soft_lock = (kmutex_t *) + kmem_zalloc(sizeof (kmutex_t), KM_SLEEP); + asy->asy_unit = instance; + asy->asy_dip = devi; + + if (ddi_get_iblock_cookie(devi, 0, &asy->asy_iblock) != DDI_SUCCESS) { + cmn_err(CE_NOTE, + "Get iblock_cookie failed-Device interrupt%x\n", instance); + goto error; + } + + if (ddi_get_soft_iblock_cookie(devi, DDI_SOFTINT_HIGH, + &asy->asy_soft_iblock) != DDI_SUCCESS) { + cmn_err(CE_NOTE, "Get iblock_cookie failed -soft interrupt%x\n", + instance); + goto error; + } + + mutex_init(asy->asy_soft_lock, NULL, MUTEX_DRIVER, + (void *)asy->asy_soft_iblock); + mutex_init(asy->asy_excl, NULL, MUTEX_DRIVER, NULL); + mutex_init(asy->asy_excl_hi, NULL, MUTEX_DRIVER, + (void *)asy->asy_iblock); + state = MUTEXES; + + /* + * Install interrupt handlers for this device. + */ + if (ddi_add_intr(devi, 0, &(asy->asy_iblock), 0, asyintr, + (caddr_t)asy) != DDI_SUCCESS) { + cmn_err(CE_CONT, + "Cannot set device interrupt for su driver\n"); + goto error; + } + state = ADDINTR; + + if (ddi_add_softintr(devi, DDI_SOFTINT_HIGH, &(asy->asy_softintr_id), + &asy->asy_soft_iblock, 0, asysoftintr, (caddr_t)asy) + != DDI_SUCCESS) { + cmn_err(CE_CONT, "Cannot set soft interrupt for su driver\n"); + goto error; + } + state = SOFTINTR; + + /* initialize the asyncline structure */ + if (ddi_soft_state_zalloc(su_asyncline, instance) != DDI_SUCCESS) { + cmn_err(CE_CONT, "su%d: cannot allocate soft state", instance); + goto error; + } + state = ASYINIT; + + async = (struct asyncline *)ddi_get_soft_state(su_asyncline, instance); + + mutex_enter(asy->asy_excl); + async->async_common = asy; + cv_init(&async->async_flags_cv, NULL, CV_DEFAULT, NULL); + mutex_exit(asy->asy_excl); + + if ((asy->sukstat = kstat_create("su", instance, "serialstat", + "misc", KSTAT_TYPE_NAMED, 2, KSTAT_FLAG_VIRTUAL)) != NULL) { + asy->sukstat->ks_data = &asy->kstats; + kstat_named_init(&asy->kstats.ringover, "ring buffer overflow", + KSTAT_DATA_UINT64); + kstat_named_init(&asy->kstats.siloover, "silo overflow", + KSTAT_DATA_UINT64); + kstat_install(asy->sukstat); + } + state = KSTAT; + + if (strcmp(ddi_node_name(devi), "rsc-console") == 0) { + /* + * If the device is configured as the 'rsc-console' + * create the minor device for this node. + */ + if (ddi_create_minor_node(devi, "ssp", S_IFCHR, + asy->asy_unit | RSC_DEVICE, DDI_PSEUDO, NULL) + == DDI_FAILURE) { + cmn_err(CE_WARN, + "%s%d: Failed to create node rsc-console", + ddi_get_name(devi), ddi_get_instance(devi)); + goto error; + } + + asy->asy_lom_console = 0; + asy->asy_rsc_console = 1; + asy->asy_rsc_control = 0; + asy->asy_device_type = ASY_SERIAL; + asy->asy_flags |= ASY_IGNORE_CD; + + } else if (strcmp(ddi_node_name(devi), "lom-console") == 0) { + /* + * If the device is configured as the 'lom-console' + * create the minor device for this node. + * Do not create a dialout device. + * Use the same minor numbers as would be used for standard + * serial instances. + */ + if (ddi_create_minor_node(devi, "lom-console", S_IFCHR, + instance, DDI_NT_SERIAL_LOMCON, NULL) == DDI_FAILURE) { + cmn_err(CE_WARN, + "%s%d: Failed to create node lom-console", + ddi_get_name(devi), ddi_get_instance(devi)); + goto error; + } + asy->asy_lom_console = 1; + asy->asy_rsc_console = 0; + asy->asy_rsc_control = 0; + asy->asy_device_type = ASY_SERIAL; + asy->asy_flags |= ASY_IGNORE_CD; + + } else if (strcmp(ddi_node_name(devi), "rsc-control") == 0) { + /* + * If the device is configured as the 'rsc-control' + * create the minor device for this node. + */ + if (ddi_create_minor_node(devi, "sspctl", S_IFCHR, + asy->asy_unit | RSC_DEVICE, DDI_PSEUDO, NULL) + == DDI_FAILURE) { + cmn_err(CE_WARN, "%s%d: Failed to create rsc-control", + ddi_get_name(devi), ddi_get_instance(devi)); + goto error; + } + + asy->asy_lom_console = 0; + asy->asy_rsc_console = 0; + asy->asy_rsc_control = 1; + asy->asy_device_type = ASY_SERIAL; + asy->asy_flags |= ASY_IGNORE_CD; + + } else if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, + "keyboard", 0)) { + /* + * If the device is a keyboard, then create an internal + * pathname so that the dacf code will link the node into + * the keyboard console stream. See dacf.conf. + */ + if (ddi_create_internal_pathname(devi, "keyboard", + S_IFCHR, instance) == DDI_FAILURE) { + goto error; + } + asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */ + asy->asy_device_type = ASY_KEYBOARD; /* Device type */ + } else if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, + "mouse", 0)) { + /* + * If the device is a mouse, then create an internal + * pathname so that the dacf code will link the node into + * the mouse stream. See dacf.conf. + */ + if (ddi_create_internal_pathname(devi, "mouse", S_IFCHR, + instance) == DDI_FAILURE) { + goto error; + } + asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */ + asy->asy_device_type = ASY_MOUSE; + } else { + /* + * If not used for keyboard/mouse, create minor devices nodes + * for this device + */ + /* serial-port */ + (void) sprintf(name, "%c", (instance+'a')); + if (ddi_create_minor_node(devi, name, S_IFCHR, instance, + DDI_NT_SERIAL_MB, NULL) == DDI_FAILURE) { + goto error; + } + state = MINORNODE; + /* serial-port:dailout */ + (void) sprintf(name, "%c,cu", (instance+'a')); + if (ddi_create_minor_node(devi, name, S_IFCHR, instance|OUTLINE, + DDI_NT_SERIAL_MB_DO, NULL) == DDI_FAILURE) { + goto error; + } + /* Property for ignoring DCD */ + if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, + "ignore-cd", 0)) { + asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */ + } else { + asy->asy_flags &= ~ASY_IGNORE_CD; + /* + * if ignore-cd is not available it could be + * some old legacy platform, try to see + * whether the old legacy property exists + */ + (void) sprintf(name, + "port-%c-ignore-cd", (instance+ 'a')); + if (ddi_getprop(DDI_DEV_T_ANY, devi, + DDI_PROP_DONTPASS, name, 0)) + asy->asy_flags |= ASY_IGNORE_CD; + } + asy->asy_device_type = ASY_SERIAL; + } + ddi_report_dev(devi); + return (DDI_SUCCESS); + +error: + if (state == MINORNODE) { + (void) sprintf(name, "%c", (instance+'a')); + ddi_remove_minor_node(devi, name); + } + if (state >= KSTAT) + kstat_delete(asy->sukstat); + if (state >= ASYINIT) { + cv_destroy(&async->async_flags_cv); + ddi_soft_state_free(su_asyncline, instance); + } + if (state >= SOFTINTR) + ddi_remove_softintr(asy->asy_softintr_id); + if (state >= ADDINTR) + ddi_remove_intr(devi, 0, asy->asy_iblock); + if (state >= MUTEXES) { + mutex_destroy(asy->asy_excl_hi); + mutex_destroy(asy->asy_excl); + mutex_destroy(asy->asy_soft_lock); + kmem_free(asy->asy_excl_hi, sizeof (kmutex_t)); + kmem_free(asy->asy_excl, sizeof (kmutex_t)); + kmem_free(asy->asy_soft_lock, sizeof (kmutex_t)); + } + if (state >= REGSMAP) + ddi_regs_map_free(&asy->asy_handle); + if (state >= SOFTSTATE) + ddi_soft_state_free(su_asycom, instance); + /* no action for EMPTY state */ + return (DDI_FAILURE); +} + +static int +asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, + void **result) +{ + _NOTE(ARGUNUSED(dip)) + register dev_t dev = (dev_t)arg; + register int instance, error; + struct asycom *asy; + + if ((instance = UNIT(dev)) > max_asy_instance) + return (DDI_FAILURE); + + switch (infocmd) { + case DDI_INFO_DEVT2DEVINFO: + asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance); + if (asy->asy_dip == NULL) + error = DDI_FAILURE; + else { + *result = (void *) asy->asy_dip; + error = DDI_SUCCESS; + } + break; + case DDI_INFO_DEVT2INSTANCE: + *result = (void *)instance; + error = DDI_SUCCESS; + break; + default: + error = DDI_FAILURE; + } + return (error); +} + +static int +asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr) +{ + _NOTE(ARGUNUSED(sflag)) + struct asycom *asy; + struct asyncline *async; + int mcr; + int unit; + int len; + struct termios *termiosp; + +#ifdef DEBUG + if (asydebug & ASY_DEBUG_CLOSE) + printf("open\n"); +#endif + unit = UNIT(*dev); + if (unit > max_asy_instance) + return (ENXIO); /* unit not configured */ + + async = (struct asyncline *)ddi_get_soft_state(su_asyncline, unit); + if (async == NULL) + return (ENXIO); + + asy = async->async_common; + if (asy == NULL) + return (ENXIO); /* device not found by autoconfig */ + + mutex_enter(asy->asy_excl); + asy->asy_priv = (caddr_t)async; + +again: + mutex_enter(asy->asy_excl_hi); + /* + * Block waiting for carrier to come up, unless this is a no-delay open. + */ + if (!(async->async_flags & ASYNC_ISOPEN)) { + /* + * If this port is for a RSC console or control + * use the following termio info + */ + if (asy->asy_rsc_console || asy->asy_rsc_control) { + async->async_ttycommon.t_cflag = CIBAUDEXT | CBAUDEXT | + (B115200 & CBAUD); + async->async_ttycommon.t_cflag |= ((B115200 << IBSHIFT) + & CIBAUD); + async->async_ttycommon.t_cflag |= CS8 | CREAD | CLOCAL; + } else if (asy->asy_lom_console) { + async->async_ttycommon.t_cflag = B9600 & CBAUD; + async->async_ttycommon.t_cflag |= ((B9600 << IBSHIFT) + & CIBAUD); + async->async_ttycommon.t_cflag |= CS8 | CREAD | CLOCAL; + } else { + + /* + * Set the default termios settings (cflag). + * Others are set in ldterm. Release the spin + * mutex as we can block here, reaquire before + * calling asy_program. + */ + mutex_exit(asy->asy_excl_hi); + if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(), + 0, "ttymodes", (caddr_t)&termiosp, &len) + == DDI_PROP_SUCCESS && + len == sizeof (struct termios)) { + async->async_ttycommon.t_cflag = + termiosp->c_cflag; + kmem_free(termiosp, len); + } else { + cmn_err(CE_WARN, + "su: couldn't get ttymodes property!"); + } + mutex_enter(asy->asy_excl_hi); + } + async->async_ttycommon.t_iflag = 0; + async->async_ttycommon.t_iocpending = NULL; + async->async_ttycommon.t_size.ws_row = 0; + async->async_ttycommon.t_size.ws_col = 0; + async->async_ttycommon.t_size.ws_xpixel = 0; + async->async_ttycommon.t_size.ws_ypixel = 0; + async->async_dev = *dev; + async->async_wbufcid = 0; + + async->async_startc = CSTART; + async->async_stopc = CSTOP; + (void) asy_program(asy, ASY_INIT); + } else if ((async->async_ttycommon.t_flags & TS_XCLUDE) && + secpolicy_excl_open(cr) != 0) { + mutex_exit(asy->asy_excl_hi); + mutex_exit(asy->asy_excl); + return (EBUSY); + } else if ((*dev & OUTLINE) && !(async->async_flags & ASYNC_OUT)) { + mutex_exit(asy->asy_excl_hi); + mutex_exit(asy->asy_excl); + return (EBUSY); + } + + if (*dev & OUTLINE) + async->async_flags |= ASYNC_OUT; + + /* Raise DTR on every open */ + mcr = INB(MCR); + OUTB(MCR, mcr|DTR); + + /* + * Check carrier. + */ + if (asy->asy_flags & ASY_IGNORE_CD) + async->async_ttycommon.t_flags |= TS_SOFTCAR; + if ((async->async_ttycommon.t_flags & TS_SOFTCAR) || + (INB(MSR) & DCD)) + async->async_flags |= ASYNC_CARR_ON; + else + async->async_flags &= ~ASYNC_CARR_ON; + mutex_exit(asy->asy_excl_hi); + + /* + * If FNDELAY and FNONBLOCK are clear, block until carrier up. + * Quit on interrupt. + */ + if (!(flag & (FNDELAY|FNONBLOCK)) && + !(async->async_ttycommon.t_cflag & CLOCAL)) { + if (!(async->async_flags & (ASYNC_CARR_ON|ASYNC_OUT)) || + ((async->async_flags & ASYNC_OUT) && + !(*dev & OUTLINE))) { + async->async_flags |= ASYNC_WOPEN; + if (cv_wait_sig(&async->async_flags_cv, + asy->asy_excl) == 0) { + async->async_flags &= ~ASYNC_WOPEN; + mutex_exit(asy->asy_excl); + return (EINTR); + } + async->async_flags &= ~ASYNC_WOPEN; + goto again; + } + } else if ((async->async_flags & ASYNC_OUT) && !(*dev & OUTLINE)) { + mutex_exit(asy->asy_excl); + return (EBUSY); + } + + if (asy->suspended) { + mutex_exit(asy->asy_excl); + (void) ddi_dev_is_needed(asy->asy_dip, 0, 1); + mutex_enter(asy->asy_excl); + } + + async->async_ttycommon.t_readq = rq; + async->async_ttycommon.t_writeq = WR(rq); + rq->q_ptr = WR(rq)->q_ptr = (caddr_t)async; + mutex_exit(asy->asy_excl); + qprocson(rq); + async->async_flags |= ASYNC_ISOPEN; + async->async_polltid = 0; + return (0); +} + +static void +async_progress_check(void *arg) +{ + struct asyncline *async = arg; + struct asycom *asy = async->async_common; + mblk_t *bp; + + /* + * We define "progress" as either waiting on a timed break or delay, or + * having had at least one transmitter interrupt. If none of these are + * true, then just terminate the output and wake up that close thread. + */ + mutex_enter(asy->asy_excl); + mutex_enter(asy->asy_excl_hi); + if (!(async->async_flags & (ASYNC_BREAK|ASYNC_DELAY|ASYNC_PROGRESS))) { + async->async_ocnt = 0; + async->async_flags &= ~ASYNC_BUSY; + async->async_timer = 0; + bp = async->async_xmitblk; + async->async_xmitblk = NULL; + mutex_exit(asy->asy_excl_hi); + if (bp != NULL) + freeb(bp); + /* + * Since this timer is running, we know that we're in exit(2). + * That means that the user can't possibly be waiting on any + * valid ioctl(2) completion anymore, and we should just flush + * everything. + */ + flushq(async->async_ttycommon.t_writeq, FLUSHALL); + cv_broadcast(&async->async_flags_cv); + } else { + async->async_flags &= ~ASYNC_PROGRESS; + async->async_timer = timeout(async_progress_check, async, + drv_usectohz(su_drain_check)); + mutex_exit(asy->asy_excl_hi); + } + mutex_exit(asy->asy_excl); +} + +/* + * Close routine. + */ +static int +asyclose(queue_t *q, int flag) +{ + struct asyncline *async; + struct asycom *asy; + int icr, lcr; + int nohupcl; + + +#ifdef DEBUG + if (asydebug & ASY_DEBUG_CLOSE) + printf("close\n"); +#endif + async = q->q_ptr; + ASSERT(async != NULL); + asy = async->async_common; + + /* get the nohupcl OBP property of this device */ + nohupcl = ddi_getprop(DDI_DEV_T_ANY, asy->asy_dip, DDI_PROP_DONTPASS, + "nohupcl", 0); + + mutex_enter(asy->asy_excl); + async->async_flags |= ASYNC_CLOSING; + + /* + * Turn off PPS handling early to avoid events occuring during + * close. Also reset the DCD edge monitoring bit. + */ + mutex_enter(asy->asy_excl_hi); + asy->asy_flags &= ~(ASY_PPS | ASY_PPS_EDGE); + mutex_exit(asy->asy_excl_hi); + + /* + * There are two flavors of break -- timed (M_BREAK or TCSBRK) and + * untimed (TIOCSBRK). For the timed case, these are enqueued on our + * write queue and there's a timer running, so we don't have to worry + * about them. For the untimed case, though, the user obviously made a + * mistake, because these are handled immediately. We'll terminate the + * break now and honor his implicit request by discarding the rest of + * the data. + */ + if (!(async->async_flags & ASYNC_BREAK)) { + mutex_enter(asy->asy_excl_hi); + lcr = INB(LCR); + if (lcr & SETBREAK) { + OUTB(LCR, (lcr & ~SETBREAK)); + } + mutex_exit(asy->asy_excl_hi); + if (lcr & SETBREAK) + goto nodrain; + } + + /* + * If the user told us not to delay the close ("non-blocking"), then + * don't bother trying to drain. + * + * If the user did M_STOP (ASYNC_STOPPED), there's no hope of ever + * getting an M_START (since these messages aren't enqueued), and the + * only other way to clear the stop condition is by loss of DCD, which + * would discard the queue data. Thus, we drop the output data if + * ASYNC_STOPPED is set. + */ + if ((flag & (FNDELAY|FNONBLOCK)) || + (async->async_flags & ASYNC_STOPPED)) { + goto nodrain; + } + + /* + * If there's any pending output, then we have to try to drain it. + * There are two main cases to be handled: + * - called by close(2): need to drain until done or until + * a signal is received. No timeout. + * - called by exit(2): need to drain while making progress + * or until a timeout occurs. No signals. + * + * If we can't rely on receiving a signal to get us out of a hung + * session, then we have to use a timer. In this case, we set a timer + * to check for progress in sending the output data -- all that we ask + * (at each interval) is that there's been some progress made. Since + * the interrupt routine grabs buffers from the write queue, we can't + * trust async_ocnt. Instead, we use a flag. + * + * Note that loss of carrier will cause the output queue to be flushed, + * and we'll wake up again and finish normally. + */ + if (!ddi_can_receive_sig() && su_drain_check != 0) { + async->async_flags &= ~ASYNC_PROGRESS; + async->async_timer = timeout(async_progress_check, async, + drv_usectohz(su_drain_check)); + } + + while (async->async_ocnt > 0 || + async->async_ttycommon.t_writeq->q_first != NULL || + (async->async_flags & (ASYNC_BUSY|ASYNC_BREAK|ASYNC_DELAY))) { + if (cv_wait_sig(&async->async_flags_cv, asy->asy_excl) == 0) + break; + } + if (async->async_timer != 0) { + (void) untimeout(async->async_timer); + async->async_timer = 0; + } + +nodrain: + mutex_enter(asy->asy_excl_hi); + + /* turn off the loopback mode */ + if ((async->async_dev != rconsdev) && + (async->async_dev != kbddev) && + (async->async_dev != stdindev)) { + OUTB(MCR, INB(MCR) & ~ ASY_LOOP); + } + + async->async_ocnt = 0; + if (async->async_xmitblk != NULL) + freeb(async->async_xmitblk); + async->async_xmitblk = NULL; + + /* + * If the "nohupcl" OBP property is set for this device, do + * not turn off DTR and RTS no matter what. Otherwise, if the + * line has HUPCL set or is incompletely opened, turn off DTR + * and RTS to fix the modem line. + */ + if (!nohupcl && ((async->async_ttycommon.t_cflag & HUPCL) || + (async->async_flags & ASYNC_WOPEN))) { + /* turn off DTR, RTS but NOT interrupt to 386 */ + OUTB(MCR, OUT2); + mutex_exit(asy->asy_excl_hi); + /* + * Don't let an interrupt in the middle of close + * bounce us back to the top; just continue closing + * as if nothing had happened. + */ + if (cv_wait_sig(&lbolt_cv, asy->asy_excl) == 0) + goto out; + mutex_enter(asy->asy_excl_hi); + } + + /* + * If nobody's using it now, turn off receiver interrupts. + */ + if ((async->async_flags & (ASYNC_WOPEN|ASYNC_ISOPEN)) == 0) { + icr = INB(ICR); + OUTB(ICR, (icr & ~RIEN)); + } + mutex_exit(asy->asy_excl_hi); +out: + /* + * Clear out device state. + */ + async->async_flags = 0; + ttycommon_close(&async->async_ttycommon); + cv_broadcast(&async->async_flags_cv); + + /* + * Clear ASY_DOINGSOFT and ASY_NEEDSOFT in case we were in + * async_softint or an interrupt was pending when the process + * using the port exited. + */ + asy->asy_flags &= ~ASY_DOINGSOFT & ~ASY_NEEDSOFT; + + /* + * Cancel outstanding "bufcall" request. + */ + if (async->async_wbufcid) { + unbufcall(async->async_wbufcid); + async->async_wbufcid = 0; + } + + /* + * If inperim is true, it means the port is closing while there's + * a pending software interrupt. async_flags has been zeroed out, + * so this instance of leaveq() needs to be called before we call + * qprocsoff() to disable services on the q. If inperim is false, + * leaveq() has already been called or we're not in a perimeter. + */ + if (asy->inperim == B_TRUE) { + asy->inperim = B_FALSE; + mutex_exit(asy->asy_excl); + leaveq(q); + } else { + mutex_exit(asy->asy_excl); + } + + /* Note that qprocsoff can't be done until after interrupts are off */ + qprocsoff(q); + q->q_ptr = WR(q)->q_ptr = NULL; + async->async_ttycommon.t_readq = NULL; + async->async_ttycommon.t_writeq = NULL; + + return (0); +} + +/* + * Checks to see if the serial port is still transmitting + * characters. It returns true when there are characters + * queued to transmit, when the holding register contains + * a byte, or when the shifting register still contains + * data to send. + * + */ +static boolean_t +asy_isbusy(struct asycom *asy) +{ + struct asyncline *async; + +#ifdef DEBUG + if (asydebug & ASY_DEBUG_EOT) + printf("isbusy\n"); +#endif + async = (struct asyncline *)asy->asy_priv; + ASSERT(mutex_owned(asy->asy_excl)); + ASSERT(mutex_owned(asy->asy_excl_hi)); + return ((async->async_ocnt > 0) || + ((INB(LSR) & XSRE) == 0)); +} + +/* + * Program the ASY port. Most of the async operation is based on the values + * of 'c_iflag' and 'c_cflag'. + */ +static int +asy_program(struct asycom *asy, int mode) +{ + struct asyncline *async; + int baudrate, c_flag; + int icr, lcr; + int ocflags; + int error = 0; + + ASSERT(mutex_owned(asy->asy_excl)); + ASSERT(mutex_owned(asy->asy_excl_hi)); + +#ifdef DEBUG + if (asydebug & ASY_DEBUG_PROCS) + printf("program\n"); +#endif + async = (struct asyncline *)asy->asy_priv; + + baudrate = async->async_ttycommon.t_cflag & CBAUD; + if (async->async_ttycommon.t_cflag & CBAUDEXT) + baudrate += 16; + + /* Limit baudrate so it can't index out of baudtable */ + if (baudrate >= N_SU_SPEEDS) baudrate = B9600; + + /* + * If baud rate requested is greater than the speed cap + * or is an unsupported baud rate then reset t_cflag baud + * to the last valid baud rate. If this is the initial + * pass through asy_program then set it to 9600. + */ + if (((baudrate > 0) && (asyspdtab[baudrate] == 0)) || + (baudtable[baudrate] > asy->asy_speed_cap)) { + async->async_ttycommon.t_cflag &= ~CBAUD & ~CBAUDEXT & + ~CIBAUD & ~CIBAUDEXT; + if (mode == ASY_INIT) { + async->async_ttycommon.t_cflag |= B9600; + baudrate = B9600; + } else { + async->async_ttycommon.t_cflag |= + (asy->asy_ocflags & (CBAUD | CBAUDEXT | + CIBAUD | CIBAUDEXT)); + } + error = EINVAL; + goto end; + } + + /* set the baud rate */ + if (async->async_ttycommon.t_cflag & (CIBAUD|CIBAUDEXT)) { + async->async_ttycommon.t_cflag &= ~(CIBAUD); + if (baudrate > CBAUD) { + async->async_ttycommon.t_cflag |= CIBAUDEXT; + async->async_ttycommon.t_cflag |= + (((baudrate - CBAUD -1)<< IBSHIFT) & CIBAUD); + } else { + async->async_ttycommon.t_cflag &= ~CIBAUDEXT; + async->async_ttycommon.t_cflag |= + ((baudrate << IBSHIFT) & CIBAUD); + } + } + + c_flag = async->async_ttycommon.t_cflag & + (CLOCAL | CREAD | CSTOPB | CSIZE | PARENB | PARODD | CBAUD | + CBAUDEXT | CIBAUD | CIBAUDEXT); + OUTB(ICR, 0); /* disable interrupts */ + + ocflags = asy->asy_ocflags; + + /* flush/reset the status registers */ + if (mode == ASY_INIT) { + (void) INB(DAT); + (void) INB(ISR); + (void) INB(LSR); + (void) INB(MSR); + } + + if (ocflags != (c_flag & ~CLOCAL) || mode == ASY_INIT) { + /* Set line control */ + lcr = INB(LCR); + lcr &= ~(WLS0|WLS1|STB|PEN|EPS); + + if (c_flag & CSTOPB) + lcr |= STB; /* 2 stop bits */ + + if (c_flag & PARENB) + lcr |= PEN; + + if ((c_flag & PARODD) == 0) + lcr |= EPS; + + switch (c_flag & CSIZE) { + case CS5: + lcr |= BITS5; + break; + case CS6: + lcr |= BITS6; + break; + case CS7: + lcr |= BITS7; + break; + case CS8: + lcr |= BITS8; + break; + } + + /* set the baud rate when the rate is NOT B0 */ + if (baudrate != 0) { + OUTB(LCR, DLAB); + OUTB(DAT, (asyspdtab[baudrate] * + asy->asy_baud_divisor_factor) & 0xff); + OUTB(ICR, ((asyspdtab[baudrate] * + asy->asy_baud_divisor_factor) >> 8) & 0xff); + } + /* set the line control modes */ + OUTB(LCR, lcr); + + /* + * if transitioning from CREAD off to CREAD on, + * flush the FIFO buffer if we have one. + */ + if ((ocflags & CREAD) == 0 && (c_flag & CREAD)) { + if (asy->asy_use_fifo == FIFO_ON) { + OUTB(FIFOR, FIFO_ON | FIFODMA | FIFORXFLSH | + (asy->asy_trig_level & 0xff)); + } + } + + /* remember the new cflags */ + asy->asy_ocflags = c_flag & ~CLOCAL; + } + + /* whether or not CLOCAL is set, modify the modem control lines */ + if (baudrate == 0) + /* B0 has been issued, lower DTR */ + OUTB(MCR, RTS|OUT2); + else + /* raise DTR */ + OUTB(MCR, DTR|RTS|OUT2); + + /* + * Call the modem status interrupt handler to check for the carrier + * in case CLOCAL was turned off after the carrier came on. + * (Note: Modem status interrupt is not enabled if CLOCAL is ON.) + */ + async_msint(asy); + + /* Set interrupt control */ + if ((c_flag & CLOCAL) && !(async->async_ttycommon.t_cflag & CRTSCTS)) + /* + * direct-wired line ignores DCD, so we don't enable modem + * status interrupts. + */ + icr = (TIEN | SIEN); + else + icr = (TIEN | SIEN | MIEN); + + if (c_flag & CREAD) + icr |= RIEN; + + OUTB(ICR, icr); +end: + return (error); +} + +/* + * asyintr() is the High Level Interrupt Handler. + * + * There are four different interrupt types indexed by ISR register values: + * 0: modem + * 1: Tx holding register is empty, ready for next char + * 2: Rx register now holds a char to be picked up + * 3: error or break on line + * This routine checks the Bit 0 (interrupt-not-pending) to determine if + * the interrupt is from this port. + */ +uint_t +asyintr(caddr_t argasy) +{ + struct asycom *asy = (struct asycom *)argasy; + struct asyncline *async; + int ret_status = DDI_INTR_UNCLAIMED; + uchar_t interrupt_id, lsr; + + interrupt_id = INB(ISR) & 0x0F; + async = (struct asyncline *)asy->asy_priv; + if ((async == NULL) || + !(async->async_flags & (ASYNC_ISOPEN|ASYNC_WOPEN))) { + if (interrupt_id & NOINTERRUPT) { + return (DDI_INTR_UNCLAIMED); + } else { + lsr = INB(LSR); + if ((lsr & BRKDET) && + ((abort_enable == KIOCABORTENABLE) && + (async->async_dev == rconsdev))) + abort_sequence_enter((char *)NULL); + else { + /* reset line status */ + (void) INB(LSR); + /* discard any data */ + (void) INB(DAT); + /* reset modem status */ + (void) INB(MSR); + return (DDI_INTR_CLAIMED); + } + } + } + /* + * Spurious interrupts happen in this driver + * because of the transmission on serial port not handled + * properly. + * + * The reasons for Spurious interrupts are: + * 1. There is a path in async_nstart which transmits + * characters without going through interrupt services routine + * which causes spurious interrupts to happen. + * 2. In the async_txint more than one character is sent + * in one interrupt service. + * 3. In async_rxint more than one characters are received in + * in one interrupt service. + * + * Hence we have flags to indicate that such scenerio has happened. + * and claim only such interrupts and others we donot claim it + * as it could be a indicator of some hardware problem. + * + */ + if (interrupt_id & NOINTERRUPT) { + mutex_enter(asy->asy_excl_hi); + if ((asy->asy_xmit_count > 1) || + (asy->asy_out_of_band_xmit > 0) || + (asy->asy_rx_count > 1)) { + asy->asy_xmit_count = 0; + asy->asy_out_of_band_xmit = 0; + asy->asy_rx_count = 0; + mutex_exit(asy->asy_excl_hi); + return (DDI_INTR_CLAIMED); + } else { + mutex_exit(asy->asy_excl_hi); + return (DDI_INTR_UNCLAIMED); + } + } + ret_status = DDI_INTR_CLAIMED; + mutex_enter(asy->asy_excl_hi); + if (asy->asy_hwtype == ASY82510) + OUTB(ISR, 0x00); /* set bank 0 */ + +#ifdef DEBUG + if (asydebug & ASY_DEBUG_INTR) + prom_printf("l"); +#endif + lsr = INB(LSR); + switch (interrupt_id) { + case RxRDY: + case RSTATUS: + case FFTMOUT: + /* receiver interrupt or receiver errors */ + async_rxint(asy, lsr); + break; + case TxRDY: + /* transmit interrupt */ + async_txint(asy, lsr); + break; + case MSTATUS: + /* modem status interrupt */ + async_msint(asy); + break; + } + mutex_exit(asy->asy_excl_hi); + return (ret_status); +} + +/* + * Transmitter interrupt service routine. + * If there is more data to transmit in the current pseudo-DMA block, + * send the next character if output is not stopped or draining. + * Otherwise, queue up a soft interrupt. + * + * XXX - Needs review for HW FIFOs. + */ +static void +async_txint(struct asycom *asy, uchar_t lsr) +{ + struct asyncline *async = (struct asyncline *)asy->asy_priv; + int fifo_len; + int xmit_progress; + + asycheckflowcontrol_hw(asy); + + /* + * If ASYNC_BREAK has been set, return to asyintr()'s context to + * claim the interrupt without performing any action. + */ + if (async->async_flags & ASYNC_BREAK) + return; + + fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */ + + /* + * Check for flow control and do the needed action. + */ + if (asycheckflowcontrol_sw(asy)) { + return; + } + + if (async->async_ocnt > 0 && + !(async->async_flags & (ASYNC_HW_OUT_FLW|ASYNC_STOPPED))) { + xmit_progress = 0; + while (fifo_len > 0 && async->async_ocnt > 0) { + if (lsr & XHRE) { + OUTB(DAT, *async->async_optr++); + fifo_len--; + async->async_ocnt--; + xmit_progress++; + } + /* + * Reading the lsr, (moved reading at the end of + * while loop) as already we have read once at + * the beginning of interrupt service + */ + lsr = INB(LSR); + } + asy->asy_xmit_count = xmit_progress; + if (xmit_progress > 0) + async->async_flags |= ASYNC_PROGRESS; + } + + if (fifo_len == 0) { + return; + } + + + ASYSETSOFT(asy); +} + +/* + * Receiver interrupt: RxRDY interrupt, FIFO timeout interrupt or receive + * error interrupt. + * Try to put the character into the circular buffer for this line; if it + * overflows, indicate a circular buffer overrun. If this port is always + * to be serviced immediately, or the character is a STOP character, or + * more than 15 characters have arrived, queue up a soft interrupt to + * drain the circular buffer. + * XXX - needs review for hw FIFOs support. + */ + +static void +async_rxint(struct asycom *asy, uchar_t lsr) +{ + struct asyncline *async = (struct asyncline *)asy->asy_priv; + uchar_t c = 0; + uint_t s = 0, needsoft = 0; + register tty_common_t *tp; + + tp = &async->async_ttycommon; + if (!(tp->t_cflag & CREAD)) { + if (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) { + (void) (INB(DAT) & 0xff); + } + return; /* line is not open for read? */ + } + asy->asy_rx_count = 0; + while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) { + c = 0; + s = 0; + asy->asy_rx_count++; + if (lsr & RCA) { + c = INB(DAT) & 0xff; + /* + * Even a single character is received + * we need Soft interrupt to pass it to + * higher layers. + */ + needsoft = 1; + } + + /* Check for character break sequence */ + if ((abort_enable == KIOCABORTALTERNATE) && + (async->async_dev == rconsdev)) { + if (abort_charseq_recognize(c)) + abort_sequence_enter((char *)NULL); + } + + /* Handle framing errors */ + if (lsr & (PARERR|FRMERR|BRKDET|OVRRUN)) { + if (lsr & PARERR) { + if (tp->t_iflag & INPCK) /* parity enabled */ + s |= PERROR; + } + if (lsr & (FRMERR|BRKDET)) + s |= FRERROR; + if (lsr & OVRRUN) { + async->async_hw_overrun = 1; + s |= OVERRUN; + } + } + + if (s == 0) + if ((tp->t_iflag & PARMRK) && + !(tp->t_iflag & (IGNPAR|ISTRIP)) && + (c == 0377)) + if (RING_POK(async, 2)) { + RING_PUT(async, 0377); + RING_PUT(async, c); + } else + async->async_sw_overrun = 1; + else + if (RING_POK(async, 1)) + RING_PUT(async, c); + else + async->async_sw_overrun = 1; + else + if (s & FRERROR) { /* Handle framing errors */ + if (c == 0) { + /* Look for break on kbd, stdin, or rconsdev */ + if ((async->async_dev == kbddev) || + ((async->async_dev == rconsdev) || + (async->async_dev == stdindev)) && + (abort_enable != + KIOCABORTALTERNATE)) + abort_sequence_enter((char *)0); + else + async->async_break++; + } else { + if (RING_POK(async, 1)) + RING_MARK(async, c, s); + else + async->async_sw_overrun = 1; + } + } else { /* Parity errors handled by ldterm */ + if (RING_POK(async, 1)) + RING_MARK(async, c, s); + else + async->async_sw_overrun = 1; + } + lsr = INB(LSR); + if (asy->asy_rx_count > 16) break; + } + /* Check whether there is a request for hw/sw inbound/input flow ctrl */ + if ((async->async_ttycommon.t_cflag & CRTSXOFF) || + (async->async_ttycommon.t_iflag & IXOFF)) + if ((int)(RING_CNT(async)) > (RINGSIZE * 3)/4) { +#ifdef DEBUG + if (asydebug & ASY_DEBUG_HFLOW) + printf("asy%d: hardware flow stop input.\n", + UNIT(async->async_dev)); +#endif + async->async_flags |= ASYNC_HW_IN_FLOW; + async->async_flowc = async->async_stopc; + async->async_ringbuf_overflow = 1; + } + + if ((async->async_flags & ASYNC_SERVICEIMM) || needsoft || + (RING_FRAC(async)) || (async->async_polltid == 0)) + ASYSETSOFT(asy); /* need a soft interrupt */ +} + +/* + * Interrupt on port: handle PPS event. This function is only called + * for a port on which PPS event handling has been enabled. + */ +static void +asy_ppsevent(struct asycom *asy, int msr) +{ + if (asy->asy_flags & ASY_PPS_EDGE) { + /* Have seen leading edge, now look for and record drop */ + if ((msr & DCD) == 0) + asy->asy_flags &= ~ASY_PPS_EDGE; + /* + * Waiting for leading edge, look for rise; stamp event and + * calibrate kernel clock. + */ + } else if (msr & DCD) { + /* + * This code captures a timestamp at the designated + * transition of the PPS signal (DCD asserted). The + * code provides a pointer to the timestamp, as well + * as the hardware counter value at the capture. + * + * Note: the kernel has nano based time values while + * NTP requires micro based, an in-line fast algorithm + * to convert nsec to usec is used here -- see hrt2ts() + * in common/os/timers.c for a full description. + */ + struct timeval *tvp = &asy_ppsev.tv; + timestruc_t ts; + long nsec, usec; + + asy->asy_flags |= ASY_PPS_EDGE; + gethrestime(&ts); + nsec = ts.tv_nsec; + usec = nsec + (nsec >> 2); + usec = nsec + (usec >> 1); + usec = nsec + (usec >> 2); + usec = nsec + (usec >> 4); + usec = nsec - (usec >> 3); + usec = nsec + (usec >> 2); + usec = nsec + (usec >> 3); + usec = nsec + (usec >> 4); + usec = nsec + (usec >> 1); + usec = nsec + (usec >> 6); + tvp->tv_usec = usec >> 10; + tvp->tv_sec = ts.tv_sec; + + ++asy_ppsev.serial; + + /* + * Because the kernel keeps a high-resolution time, + * pass the current highres timestamp in tvp and zero + * in usec. + */ + ddi_hardpps(tvp, 0); + } +} + +/* + * Modem status interrupt. + * + * (Note: It is assumed that the MSR hasn't been read by asyintr().) + */ + +static void +async_msint(struct asycom *asy) +{ + struct asyncline *async = (struct asyncline *)asy->asy_priv; + int msr; + + msr = INB(MSR); /* this resets the interrupt */ + asy->asy_cached_msr = msr; +#ifdef DEBUG + if (asydebug & ASY_DEBUG_STATE) { + printf(" transition: %3s %3s %3s %3s\n" + "current state: %3s %3s %3s %3s\n", + (msr & DCTS) ? "CTS" : " ", + (msr & DDSR) ? "DSR" : " ", + (msr & DRI) ? "RI " : " ", + (msr & DDCD) ? "DCD" : " ", + (msr & CTS) ? "CTS" : " ", + (msr & DSR) ? "DSR" : " ", + (msr & RI) ? "RI " : " ", + (msr & DCD) ? "DCD" : " "); + } +#endif + if (async->async_ttycommon.t_cflag & CRTSCTS && !(msr & CTS)) { +#ifdef DEBUG + if (asydebug & ASY_DEBUG_HFLOW) + printf("asy%d: hflow start\n", + UNIT(async->async_dev)); +#endif + async->async_flags |= ASYNC_HW_OUT_FLW; + } + if (asy->asy_hwtype == ASY82510) + OUTB(MSR, (msr & 0xF0)); + + /* Handle PPS event */ + if (asy->asy_flags & ASY_PPS) + asy_ppsevent(asy, msr); + + async->async_ext++; + ASYSETSOFT(asy); +} + +/* + * Handle a second-stage interrupt. + */ +uint_t +asysoftintr(caddr_t intarg) +{ + struct asycom *asy = (struct asycom *)intarg; + struct asyncline *async; + int rv; + int cc; + /* + * Test and clear soft interrupt. + */ + mutex_enter(asy->asy_soft_lock); +#ifdef DEBUG + if (asydebug & ASY_DEBUG_PROCS) + printf("softintr\n"); +#endif + rv = asy->asysoftpend; + if (rv != 0) + asy->asysoftpend = 0; + mutex_exit(asy->asy_soft_lock); + + if (rv) { + if (asy->asy_priv == NULL) + return (rv); + async = (struct asyncline *)asy->asy_priv; + mutex_enter(asy->asy_excl_hi); + if (asy->asy_flags & ASY_NEEDSOFT) { + asy->asy_flags &= ~ASY_NEEDSOFT; + mutex_exit(asy->asy_excl_hi); + (void) async_softint(asy); + mutex_enter(asy->asy_excl_hi); + } + /* + * There are some instances where the softintr is not + * scheduled and hence not called. It so happened that makes + * the last few characters to be stuck in ringbuffer. + * Hence, call once again the handler so that the last few + * characters are cleared. + */ + cc = RING_CNT(async); + mutex_exit(asy->asy_excl_hi); + if (cc > 0) { + (void) async_softint(asy); + } + } + return (rv); +} + +/* + * Handle a software interrupt. + */ +static int +async_softint(struct asycom *asy) +{ + struct asyncline *async = (struct asyncline *)asy->asy_priv; + uint_t cc; + mblk_t *bp; + queue_t *q; + uchar_t val; + uchar_t c; + tty_common_t *tp; + +#ifdef DEBUG + if (asydebug & ASY_DEBUG_PROCS) + printf("process\n"); +#endif + mutex_enter(asy->asy_excl); + if (asy->asy_flags & ASY_DOINGSOFT) { + mutex_exit(asy->asy_excl); + return (0); + } + tp = &async->async_ttycommon; + q = tp->t_readq; + if (q != NULL) { + mutex_exit(asy->asy_excl); + enterq(q); + mutex_enter(asy->asy_excl); + } + mutex_enter(asy->asy_excl_hi); + asy->asy_flags |= ASY_DOINGSOFT; + + if (INB(ICR) & MIEN) + val = asy->asy_cached_msr & 0xFF; + else + val = INB(MSR) & 0xFF; + + if (async->async_ttycommon.t_cflag & CRTSCTS) { + if ((val & CTS) && (async->async_flags & ASYNC_HW_OUT_FLW)) { +#ifdef DEBUG + if (asydebug & ASY_DEBUG_HFLOW) + printf("asy%d: hflow start\n", + UNIT(async->async_dev)); +#endif + async->async_flags &= ~ASYNC_HW_OUT_FLW; + mutex_exit(asy->asy_excl_hi); + if (async->async_ocnt > 0) { + mutex_enter(asy->asy_excl_hi); + async_resume(async); + mutex_exit(asy->asy_excl_hi); + } else { + async_start(async); + } + mutex_enter(asy->asy_excl_hi); + } + } + if (async->async_ext) { + async->async_ext = 0; + /* check for carrier up */ + if ((val & DCD) || (tp->t_flags & TS_SOFTCAR)) { + /* carrier present */ + if ((async->async_flags & ASYNC_CARR_ON) == 0) { + async->async_flags |= ASYNC_CARR_ON; + mutex_exit(asy->asy_excl_hi); + mutex_exit(asy->asy_excl); + if (async->async_flags & ASYNC_ISOPEN) + (void) putctl(q, M_UNHANGUP); + cv_broadcast(&async->async_flags_cv); + mutex_enter(asy->asy_excl); + mutex_enter(asy->asy_excl_hi); + } + } else { + if ((async->async_flags & ASYNC_CARR_ON) && + !(tp->t_cflag & CLOCAL)) { + int flushflag; + + /* + * Carrier went away. + * Drop DTR, abort any output in + * progress, indicate that output is + * not stopped, and send a hangup + * notification upstream. + * + * If we're in the midst of close, then flush + * everything. Don't leave stale ioctls lying + * about. + */ + val = INB(MCR); + OUTB(MCR, (val & ~DTR)); + flushflag = (async->async_flags & + ASYNC_CLOSING) ? FLUSHALL : FLUSHDATA; + flushq(tp->t_writeq, flushflag); + if (async->async_xmitblk != NULL) { + freeb(async->async_xmitblk); + async->async_xmitblk = NULL; + } + if (async->async_flags & ASYNC_BUSY) { + async->async_ocnt = 0; + async->async_flags &= ~ASYNC_BUSY; + } + async->async_flags &= ~ASYNC_STOPPED; + if (async->async_flags & ASYNC_ISOPEN) { + mutex_exit(asy->asy_excl_hi); + mutex_exit(asy->asy_excl); + (void) putctl(q, M_HANGUP); + mutex_enter(asy->asy_excl); + mutex_enter(asy->asy_excl_hi); + } + } + async->async_flags &= ~ASYNC_CARR_ON; + mutex_exit(asy->asy_excl_hi); + cv_broadcast(&async->async_flags_cv); + mutex_enter(asy->asy_excl_hi); + } + } + + /* + * If data has been added to the circular buffer, remove + * it from the buffer, and send it up the stream if there's + * somebody listening. Try to do it 16 bytes at a time. If we + * have more than 16 bytes to move, move 16 byte chunks and + * leave the rest for next time around (maybe it will grow). + */ + if (!(async->async_flags & ASYNC_ISOPEN)) { + RING_INIT(async); + goto rv; + } + if ((cc = RING_CNT(async)) == 0) { + goto rv; + } + mutex_exit(asy->asy_excl_hi); + + if (!canput(q)) { + if ((async->async_flags & ASYNC_HW_IN_FLOW) == 0) { +#ifdef DEBUG + if (!(asydebug & ASY_DEBUG_HFLOW)) { + printf("asy%d: hflow stop input.\n", + UNIT(async->async_dev)); + if (canputnext(q)) + printf("asy%d: next queue is " + "ready\n", + UNIT(async->async_dev)); + } +#endif + mutex_enter(asy->asy_excl_hi); + async->async_flags |= ASYNC_HW_IN_FLOW; + async->async_flowc = async->async_stopc; + } else mutex_enter(asy->asy_excl_hi); + goto rv; + } + + if (async->async_ringbuf_overflow) { + if ((async->async_flags & ASYNC_HW_IN_FLOW) && + ((int)(RING_CNT(async)) < (RINGSIZE/4))) { +#ifdef DEBUG + if (asydebug & ASY_DEBUG_HFLOW) + printf("asy%d: hflow start input.\n", + UNIT(async->async_dev)); +#endif + mutex_enter(asy->asy_excl_hi); + async->async_flags &= ~ASYNC_HW_IN_FLOW; + async->async_flowc = async->async_startc; + async->async_ringbuf_overflow = 0; + goto rv; + } + } +#ifdef DEBUG + if (asydebug & ASY_DEBUG_INPUT) + printf("asy%d: %d char(s) in queue.\n", + UNIT(async->async_dev), cc); +#endif + /* + * Before you pull the characters from the RING BUF + * Check whether you can put into the queue again + */ + if ((!canputnext(q)) || (!canput(q))) { + mutex_enter(asy->asy_excl_hi); + if ((async->async_flags & ASYNC_HW_IN_FLOW) == 0) { + async->async_flags |= ASYNC_HW_IN_FLOW; + async->async_flowc = async->async_stopc; + async->async_queue_full = 1; + } + goto rv; + } + mutex_enter(asy->asy_excl_hi); + if (async->async_queue_full) { + /* + * Last time the Stream queue didnot allow + * now it allows so, relax, the flow control + */ + if (async->async_flags & ASYNC_HW_IN_FLOW) { + async->async_flags &= ~ASYNC_HW_IN_FLOW; + async->async_queue_full = 0; + async->async_flowc = async->async_startc; + goto rv; + } else + async->async_queue_full = 0; + } + mutex_exit(asy->asy_excl_hi); + if (!(bp = allocb(cc, BPRI_MED))) { + ttycommon_qfull(&async->async_ttycommon, q); + mutex_enter(asy->asy_excl_hi); + goto rv; + } + mutex_enter(asy->asy_excl_hi); + do { + if (RING_ERR(async, S_ERRORS)) { + RING_UNMARK(async); + c = RING_GET(async); + break; + } else { + *bp->b_wptr++ = RING_GET(async); + } + } while (--cc); + + mutex_exit(asy->asy_excl_hi); + mutex_exit(asy->asy_excl); + if (bp->b_wptr > bp->b_rptr) { + if (!canputnext(q)) { + if (!canput(q)) { + /* + * Even after taking all precautions that + * Still we are unable to queue, then we + * cannot do anything, just drop the block + */ + cmn_err(CE_NOTE, + "su%d: local queue full\n", + UNIT(async->async_dev)); + freemsg(bp); + mutex_enter(asy->asy_excl_hi); + if ((async->async_flags & + ASYNC_HW_IN_FLOW) == 0) { + async->async_flags |= + ASYNC_HW_IN_FLOW; + async->async_flowc = + async->async_stopc; + async->async_queue_full = 1; + } + mutex_exit(asy->asy_excl_hi); + } else { + (void) putq(q, bp); + } + } else { + putnext(q, bp); + } + } else { + freemsg(bp); + } + /* + * If we have a parity error, then send + * up an M_BREAK with the "bad" + * character as an argument. Let ldterm + * figure out what to do with the error. + */ + if (cc) + (void) putctl1(q, M_BREAK, c); + mutex_enter(asy->asy_excl); + mutex_enter(asy->asy_excl_hi); +rv: + /* + * If a transmission has finished, indicate that it's finished, + * and start that line up again. + */ + if (async->async_break) { + async->async_break = 0; + if (async->async_flags & ASYNC_ISOPEN) { + mutex_exit(asy->asy_excl_hi); + mutex_exit(asy->asy_excl); + (void) putctl(q, M_BREAK); + mutex_enter(asy->asy_excl); + mutex_enter(asy->asy_excl_hi); + } + } + if ((async->async_ocnt <= 0 && (async->async_flags & ASYNC_BUSY)) || + async->async_queue_full) { + async->async_flags &= ~ASYNC_BUSY; + mutex_exit(asy->asy_excl_hi); + if (async->async_xmitblk) + freeb(async->async_xmitblk); + async->async_xmitblk = NULL; + if (async->async_flags & ASYNC_ISOPEN) { + asy->inperim = B_TRUE; + mutex_exit(asy->asy_excl); + enterq(async->async_ttycommon.t_writeq); + mutex_enter(asy->asy_excl); + } + async_start(async); + /* + * We need to check for inperim and ISOPEN due to + * multi-threading implications; it's possible to close the + * port and nullify async_flags while completing the software + * interrupt. If the port is closed, leaveq() will have already + * been called. We don't want to call it twice. + */ + if ((asy->inperim) && (async->async_flags & ASYNC_ISOPEN)) { + mutex_exit(asy->asy_excl); + leaveq(async->async_ttycommon.t_writeq); + mutex_enter(asy->asy_excl); + asy->inperim = B_FALSE; + } + if (!(async->async_flags & ASYNC_BUSY)) + cv_broadcast(&async->async_flags_cv); + mutex_enter(asy->asy_excl_hi); + } + /* + * A note about these overrun bits: all they do is *tell* someone + * about an error- They do not track multiple errors. In fact, + * you could consider them latched register bits if you like. + * We are only interested in printing the error message once for + * any cluster of overrun errrors. + */ + if (async->async_hw_overrun) { + if (async->async_flags & ASYNC_ISOPEN) { + if (su_log > 0) { + mutex_exit(asy->asy_excl_hi); + mutex_exit(asy->asy_excl); + cmn_err(CE_NOTE, "su%d: silo overflow\n", + UNIT(async->async_dev)); + mutex_enter(asy->asy_excl); + mutex_enter(asy->asy_excl_hi); + } + INC64_KSTAT(asy, siloover); + } + async->async_hw_overrun = 0; + } + if (async->async_sw_overrun) { + if (async->async_flags & ASYNC_ISOPEN) { + if (su_log > 0) { + mutex_exit(asy->asy_excl_hi); + mutex_exit(asy->asy_excl); + cmn_err(CE_NOTE, "su%d: ring buffer overflow\n", + UNIT(async->async_dev)); + mutex_enter(asy->asy_excl); + mutex_enter(asy->asy_excl_hi); + } + INC64_KSTAT(asy, ringover); + } + async->async_sw_overrun = 0; + } + asy->asy_flags &= ~ASY_DOINGSOFT; + mutex_exit(asy->asy_excl_hi); + mutex_exit(asy->asy_excl); + if (q != NULL) + leaveq(q); + return (0); +} + +/* + * Restart output on a line after a delay or break timer expired. + */ +static void +async_restart(void *arg) +{ + struct asyncline *async = arg; + struct asycom *asy = async->async_common; + queue_t *q; + uchar_t lcr; + + /* + * If break timer expired, turn off the break bit. + */ +#ifdef DEBUG + if (asydebug & ASY_DEBUG_PROCS) + printf("restart\n"); +#endif + mutex_enter(asy->asy_excl); + if (async->async_flags & ASYNC_BREAK) { + mutex_enter(asy->asy_excl_hi); + lcr = INB(LCR); + OUTB(LCR, (lcr & ~SETBREAK)); + mutex_exit(asy->asy_excl_hi); + } + async->async_flags &= ~(ASYNC_DELAY|ASYNC_BREAK|ASYNC_DRAINING); + if ((q = async->async_ttycommon.t_writeq) != NULL) { + mutex_exit(asy->asy_excl); + enterq(q); + mutex_enter(asy->asy_excl); + } + async_start(async); + mutex_exit(asy->asy_excl); + if (q != NULL) + leaveq(q); + + /* cleared break or delay flag; may have made some output progress */ + cv_broadcast(&async->async_flags_cv); +} + +static void +async_start(struct asyncline *async) +{ + async_nstart(async, 0); +} + +/* + * Start output on a line, unless it's busy, frozen, or otherwise. + */ +static void +async_nstart(struct asyncline *async, int mode) +{ + register struct asycom *asy = async->async_common; + register int cc; + register queue_t *q; + mblk_t *bp, *nbp; + uchar_t *xmit_addr; + uchar_t val; + int fifo_len = 1; + int xmit_progress; + +#ifdef DEBUG + if (asydebug & ASY_DEBUG_PROCS) + printf("start\n"); +#endif + if (asy->asy_use_fifo == FIFO_ON) + fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */ + + ASSERT(mutex_owned(asy->asy_excl)); + mutex_enter(asy->asy_excl_hi); + asycheckflowcontrol_hw(asy); + + /* + * If the chip is busy (i.e., we're waiting for a break timeout + * to expire, or for the current transmission to finish, or for + * output to finish draining from chip), don't grab anything new. + */ + if (async->async_flags & (ASYNC_BREAK|ASYNC_BUSY|ASYNC_DRAINING)) { + mutex_exit(asy->asy_excl_hi); +#ifdef DEBUG + if (mode && asydebug & ASY_DEBUG_CLOSE) + printf("asy%d: start %s.\n", + UNIT(async->async_dev), + async->async_flags & ASYNC_BREAK + ? "break" : "busy"); +#endif + return; + } + + /* + * If we have a flow-control character to transmit, do it now. + */ + if (asycheckflowcontrol_sw(asy)) { + mutex_exit(asy->asy_excl_hi); + return; + } + mutex_exit(asy->asy_excl_hi); + /* + * If we're waiting for a delay timeout to expire, don't grab + * anything new. + */ + if (async->async_flags & ASYNC_DELAY) { +#ifdef DEBUG + if (mode && asydebug & ASY_DEBUG_CLOSE) + printf("asy%d: start ASYNC_DELAY.\n", + UNIT(async->async_dev)); +#endif + return; + } + + if ((q = async->async_ttycommon.t_writeq) == NULL) { +#ifdef DEBUG + if (mode && asydebug & ASY_DEBUG_CLOSE) + printf("asy%d: start writeq is null.\n", + UNIT(async->async_dev)); +#endif + return; /* not attached to a stream */ + } + + for (;;) { + if ((bp = getq(q)) == NULL) + return; /* no data to transmit */ + + /* + * We have a message block to work on. + * Check whether it's a break, a delay, or an ioctl (the latter + * occurs if the ioctl in question was waiting for the output + * to drain). If it's one of those, process it immediately. + */ + switch (bp->b_datap->db_type) { + + case M_BREAK: + /* + * Set the break bit, and arrange for "async_restart" + * to be called in 1/4 second; it will turn the + * break bit off, and call "async_start" to grab + * the next message. + */ + mutex_enter(asy->asy_excl_hi); + val = INB(LCR); + OUTB(LCR, (val | SETBREAK)); + mutex_exit(asy->asy_excl_hi); + async->async_flags |= ASYNC_BREAK; + (void) timeout(async_restart, async, hz / 4); + freemsg(bp); + return; /* wait for this to finish */ + + case M_DELAY: + /* + * Arrange for "async_restart" to be called when the + * delay expires; it will turn ASYNC_DELAY off, + * and call "async_start" to grab the next message. + */ + (void) timeout(async_restart, async, + (clock_t)(*(unsigned char *)bp->b_rptr + 6)); + async->async_flags |= ASYNC_DELAY; + freemsg(bp); + return; /* wait for this to finish */ + + case M_IOCTL: + /* + * This ioctl needs to wait for the output ahead of + * it to drain. Try to do it, and then either + * redo the ioctl at a later time or grab the next + * message after it. + */ + + mutex_enter(asy->asy_excl_hi); + if (asy_isbusy(asy)) { + /* + * Get the divisor by calculating the rate + */ + unsigned int rate; + + mutex_exit(asy->asy_excl_hi); + rate = async->async_ttycommon.t_cflag & CBAUD; + if (async->async_ttycommon.t_cflag & CBAUDEXT) + rate += 16; + if (rate >= N_SU_SPEEDS || rate == B0) { + rate = B9600; + } + + /* + * We need to do a callback as the port will + * be set to drain + */ + async->async_flags |= ASYNC_DRAINING; + + /* + * Put the message we just processed back onto + * the end of the queue + */ + if (putq(q, bp) == 0) + freemsg(bp); + + /* + * We need to delay until the TSR and THR + * have been exhausted. We base the delay on + * the amount of time it takes to transmit + * 2 chars at the current baud rate in + * microseconds. + * + * Therefore, the wait period is: + * + * (#TSR bits + #THR bits) * + * 1 MICROSEC / baud rate + */ + (void) timeout(async_restart, async, + drv_usectohz(16 * MICROSEC / + baudtable[rate])); + return; + } + mutex_exit(asy->asy_excl_hi); + mutex_exit(asy->asy_excl); + async_ioctl(async, q, bp, B_FALSE); + mutex_enter(asy->asy_excl); + continue; + } + + while (bp != NULL && (cc = bp->b_wptr - bp->b_rptr) == 0) { + nbp = bp->b_cont; + freeb(bp); + bp = nbp; + } + if (bp != NULL) + break; + } + + /* + * We have data to transmit. If output is stopped, put + * it back and try again later. + */ + if (async->async_flags & (ASYNC_HW_OUT_FLW|ASYNC_STOPPED)) { +#ifdef DEBUG + if (asydebug & ASY_DEBUG_HFLOW && + async->async_flags & ASYNC_HW_OUT_FLW) + printf("asy%d: output hflow in effect.\n", + UNIT(async->async_dev)); +#endif + mutex_exit(asy->asy_excl); + (void) putbq(q, bp); + /* + * We entered the routine owning the lock, we need to + * exit the routine owning the lock. + */ + mutex_enter(asy->asy_excl); + return; + } + + async->async_xmitblk = bp; + xmit_addr = bp->b_rptr; + bp = bp->b_cont; + if (bp != NULL) { + mutex_exit(asy->asy_excl); + (void) putbq(q, bp); /* not done with this message yet */ + mutex_enter(asy->asy_excl); + } + + /* + * In 5-bit mode, the high order bits are used + * to indicate character sizes less than five, + * so we need to explicitly mask before transmitting + */ + if ((async->async_ttycommon.t_cflag & CSIZE) == CS5) { + register unsigned char *p = xmit_addr; + register int cnt = cc; + + while (cnt--) + *p++ &= (unsigned char) 0x1f; + } + + /* + * Set up this block for pseudo-DMA. + */ + mutex_enter(asy->asy_excl_hi); + async->async_optr = xmit_addr; + async->async_ocnt = cc; + /* + * If the transmitter is ready, shove some + * characters out. + */ + xmit_progress = 0; + while (fifo_len-- && async->async_ocnt) { + if (INB(LSR) & XHRE) { + OUTB(DAT, *async->async_optr++); + async->async_ocnt--; + xmit_progress++; + } + } + asy->asy_out_of_band_xmit = xmit_progress; + if (xmit_progress > 0) + async->async_flags |= ASYNC_PROGRESS; + async->async_flags |= ASYNC_BUSY; + mutex_exit(asy->asy_excl_hi); +} + +/* + * Resume output by poking the transmitter. + */ +static void +async_resume(struct asyncline *async) +{ + register struct asycom *asy = async->async_common; + + ASSERT(mutex_owned(asy->asy_excl_hi)); +#ifdef DEBUG + if (asydebug & ASY_DEBUG_PROCS) + printf("resume\n"); +#endif + + asycheckflowcontrol_hw(asy); + + if (INB(LSR) & XHRE) { + if (asycheckflowcontrol_sw(asy)) { + return; + } else if (async->async_ocnt > 0) { + OUTB(DAT, *async->async_optr++); + async->async_ocnt--; + async->async_flags |= ASYNC_PROGRESS; + } + } +} + +/* + * Process an "ioctl" message sent down to us. + * Note that we don't need to get any locks until we are ready to access + * the hardware. Nothing we access until then is going to be altered + * outside of the STREAMS framework, so we should be safe. + */ +static void +async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp, boolean_t iswput) +{ + register struct asycom *asy = async->async_common; + register tty_common_t *tp = &async->async_ttycommon; + register struct iocblk *iocp; + register unsigned datasize; + mblk_t *datamp; + int error = 0; + uchar_t val, icr; +#ifdef DEBUG + if (asydebug & ASY_DEBUG_PROCS) + printf("ioctl\n"); +#endif + + if (tp->t_iocpending != NULL) { + /* + * We were holding an "ioctl" response pending the + * availability of an "mblk" to hold data to be passed up; + * another "ioctl" came through, which means that "ioctl" + * must have timed out or been aborted. + */ + freemsg(async->async_ttycommon.t_iocpending); + async->async_ttycommon.t_iocpending = NULL; + } + + iocp = (struct iocblk *)mp->b_rptr; + + /* + * For TIOCMGET, TIOCMBIC, TIOCMBIS, TIOCMSET, and PPS, do NOT call + * ttycommon_ioctl() because this function frees up the message block + * (mp->b_cont) that contains the address of the user variable where + * we need to pass back the bit array. + */ + if (iocp->ioc_cmd == TIOCMGET || + iocp->ioc_cmd == TIOCMBIC || + iocp->ioc_cmd == TIOCMBIS || + iocp->ioc_cmd == TIOCMSET || + iocp->ioc_cmd == TIOCGPPS || + iocp->ioc_cmd == TIOCSPPS || + iocp->ioc_cmd == TIOCGPPSEV) + error = -1; /* Do Nothing */ + else + + /* + * The only way in which "ttycommon_ioctl" can fail is if the "ioctl" + * requires a response containing data to be returned to the user, + * and no mblk could be allocated for the data. + * No such "ioctl" alters our state. Thus, we always go ahead and + * do any state-changes the "ioctl" calls for. If we couldn't allocate + * the data, "ttycommon_ioctl" has stashed the "ioctl" away safely, so + * we just call "bufcall" to request that we be called back when we + * stand a better chance of allocating the data. + */ + if ((datasize = ttycommon_ioctl(tp, wq, mp, &error)) != 0) { + if (async->async_wbufcid) + unbufcall(async->async_wbufcid); + async->async_wbufcid = bufcall(datasize, BPRI_HI, async_reioctl, + async); + return; + } + + mutex_enter(asy->asy_excl); + + if (error == 0) { + /* + * "ttycommon_ioctl" did most of the work; we just use the + * data it set up. + */ + switch (iocp->ioc_cmd) { + + case TCSETS: + if (!(asy->asy_rsc_console || asy->asy_rsc_control || + asy->asy_lom_console)) { + mutex_enter(asy->asy_excl_hi); + error = asy_program(asy, ASY_NOINIT); + mutex_exit(asy->asy_excl_hi); + } + break; + case TCSETSF: + case TCSETSW: + case TCSETA: + case TCSETAW: + case TCSETAF: + if (!(asy->asy_rsc_console || asy->asy_rsc_control || + asy->asy_lom_console)) { + mutex_enter(asy->asy_excl_hi); + if (iswput && asy_isbusy(asy)) { + if (putq(wq, mp) == 0) + freemsg(mp); + mutex_exit(asy->asy_excl_hi); + mutex_exit(asy->asy_excl); + return; + } + error = asy_program(asy, ASY_NOINIT); + mutex_exit(asy->asy_excl_hi); + } + break; + case TIOCSSOFTCAR: + /* Set the driver state appropriately */ + mutex_enter(asy->asy_excl_hi); + if (tp->t_flags & TS_SOFTCAR) + asy->asy_flags |= ASY_IGNORE_CD; + else + asy->asy_flags &= ~ASY_IGNORE_CD; + mutex_exit(asy->asy_excl_hi); + break; + } + } else if (error < 0) { + /* + * "ttycommon_ioctl" didn't do anything; we process it here. + */ + error = 0; + switch (iocp->ioc_cmd) { + + case TIOCGPPS: + /* + * Get PPS on/off. + */ + if (mp->b_cont != NULL) + freemsg(mp->b_cont); + + mp->b_cont = allocb(sizeof (int), BPRI_HI); + if (mp->b_cont == NULL) { + error = ENOMEM; + break; + } + if (asy->asy_flags & ASY_PPS) + *(int *)mp->b_cont->b_wptr = 1; + else + *(int *)mp->b_cont->b_wptr = 0; + mp->b_cont->b_wptr += sizeof (int); + mp->b_datap->db_type = M_IOCACK; + iocp->ioc_count = sizeof (int); + break; + + case TIOCSPPS: + /* + * Set PPS on/off. + */ + error = miocpullup(mp, sizeof (int)); + if (error != 0) + break; + + mutex_enter(asy->asy_excl_hi); + if (*(int *)mp->b_cont->b_rptr) + asy->asy_flags |= ASY_PPS; + else + asy->asy_flags &= ~ASY_PPS; + /* Reset edge sense */ + asy->asy_flags &= ~ASY_PPS_EDGE; + mutex_exit(asy->asy_excl_hi); + mp->b_datap->db_type = M_IOCACK; + break; + + case TIOCGPPSEV: { + /* + * Get PPS event data. + */ + mblk_t *bp; + void *buf; +#ifdef _SYSCALL32_IMPL + struct ppsclockev32 p32; +#endif + struct ppsclockev ppsclockev; + + if (mp->b_cont != NULL) { + freemsg(mp->b_cont); + mp->b_cont = NULL; + } + + if ((asy->asy_flags & ASY_PPS) == 0) { + error = ENXIO; + break; + } + + /* Protect from incomplete asy_ppsev */ + mutex_enter(asy->asy_excl_hi); + ppsclockev = asy_ppsev; + mutex_exit(asy->asy_excl_hi); + +#ifdef _SYSCALL32_IMPL + if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) { + TIMEVAL_TO_TIMEVAL32(&p32.tv, &ppsclockev.tv); + p32.serial = ppsclockev.serial; + buf = &p32; + iocp->ioc_count = sizeof (struct ppsclockev32); + } else +#endif + { + buf = &ppsclockev; + iocp->ioc_count = sizeof (struct ppsclockev); + } + + if ((bp = allocb(iocp->ioc_count, BPRI_HI)) == NULL) { + error = ENOMEM; + break; + } + mp->b_cont = bp; + + bcopy(buf, bp->b_wptr, iocp->ioc_count); + bp->b_wptr += iocp->ioc_count; + mp->b_datap->db_type = M_IOCACK; + break; + } + + case TCSBRK: + error = miocpullup(mp, sizeof (int)); + if (error != 0) + break; + + mutex_enter(asy->asy_excl_hi); + if (*(int *)mp->b_cont->b_rptr == 0) { + /* + * Get the divisor by calculating the rate + */ + unsigned int rate, divisor; + rate = async->async_ttycommon.t_cflag & CBAUD; + if (async->async_ttycommon.t_cflag & CBAUDEXT) + rate += 16; + if (rate >= N_SU_SPEEDS) rate = B9600; + divisor = asyspdtab[rate] & 0xfff; + + /* + * To ensure that erroneous characters are + * not sent out when the break is set, SB + * recommends three steps: + * + * 1) pad the TSR with 0 bits + * 2) When the TSR is full, set break + * 3) When the TSR has been flushed, unset + * the break when transmission must be + * restored. + * + * We loop until the TSR is empty and then + * set the break. ASYNC_BREAK has been set + * to ensure that no characters are + * transmitted while the TSR is being + * flushed and SOUT is being used for the + * break signal. + * + * The wait period is equal to + * clock / (baud * 16) * 16 * 2. + */ + async->async_flags |= ASYNC_BREAK; + while ((INB(LSR) & XSRE) == 0) { + mutex_exit(asy->asy_excl_hi); + mutex_exit(asy->asy_excl); + drv_usecwait(32*divisor); + mutex_enter(asy->asy_excl); + mutex_enter(asy->asy_excl_hi); + } + + /* + * Set the break bit, and arrange for + * "async_restart" to be called in 1/4 second; + * it will turn the break bit off, and call + * "async_start" to grab the next message. + */ + val = INB(LCR); + OUTB(LCR, (val | SETBREAK)); + mutex_exit(asy->asy_excl_hi); + (void) timeout(async_restart, async, hz / 4); + } else { +#ifdef DEBUG + if (asydebug & ASY_DEBUG_CLOSE) + printf("asy%d: wait for flush.\n", + UNIT(async->async_dev)); +#endif + if (iswput && asy_isbusy(asy)) { + if (putq(wq, mp) == 0) + freemsg(mp); + mutex_exit(asy->asy_excl_hi); + mutex_exit(asy->asy_excl); + return; + } + mutex_exit(asy->asy_excl_hi); +#ifdef DEBUG + if (asydebug & ASY_DEBUG_CLOSE) + printf("asy%d: ldterm satisfied.\n", + UNIT(async->async_dev)); +#endif + } + break; + + case TIOCSBRK: + mutex_enter(asy->asy_excl_hi); + val = INB(LCR); + OUTB(LCR, (val | SETBREAK)); + mutex_exit(asy->asy_excl_hi); + mutex_exit(asy->asy_excl); + miocack(wq, mp, 0, 0); + return; + + case TIOCCBRK: + mutex_enter(asy->asy_excl_hi); + val = INB(LCR); + OUTB(LCR, (val & ~SETBREAK)); + mutex_exit(asy->asy_excl_hi); + mutex_exit(asy->asy_excl); + miocack(wq, mp, 0, 0); + return; + + case TIOCMSET: + case TIOCMBIS: + case TIOCMBIC: + if (iocp->ioc_count == TRANSPARENT) + mcopyin(mp, NULL, sizeof (int), NULL); + else { + error = miocpullup(mp, sizeof (int)); + if (error != 0) + break; + + mutex_enter(asy->asy_excl_hi); + + (void) asymctl(asy, + dmtoasy(*(int *)mp->b_cont->b_rptr), + iocp->ioc_cmd); + + mutex_exit(asy->asy_excl_hi); + iocp->ioc_error = 0; + mp->b_datap->db_type = M_IOCACK; + } + break; + + case TIOCSILOOP: + mutex_enter(asy->asy_excl_hi); + /* + * If somebody misues this Ioctl when used for + * driving keyboard and mouse indicate not supported + */ + if ((asy->asy_device_type == ASY_KEYBOARD) || + (asy->asy_device_type == ASY_MOUSE)) { + mutex_exit(asy->asy_excl_hi); + error = ENOTTY; + break; + } + + /* should not use when we're the console */ + if ((async->async_dev == kbddev) || + (async->async_dev == rconsdev) || + (async->async_dev == stdindev)) { + mutex_exit(asy->asy_excl_hi); + error = EINVAL; + break; + } + + val = INB(MCR); + icr = INB(ICR); + /* + * Disable the Modem Status Interrupt + * The reason for disabling is the status of + * modem signal are in the higher 4 bits instead of + * lower four bits when in loopback mode, + * so, donot worry about Modem interrupt when + * you are planning to set + * this in loopback mode until it is cleared by + * another ioctl to get out of the loopback mode + */ + OUTB(ICR, icr & ~ MIEN); + OUTB(MCR, val | ASY_LOOP); + mutex_exit(asy->asy_excl_hi); + iocp->ioc_error = 0; + mp->b_datap->db_type = M_IOCACK; + break; + + case TIOCMGET: + datamp = allocb(sizeof (int), BPRI_MED); + if (datamp == NULL) { + error = EAGAIN; + break; + } + + mutex_enter(asy->asy_excl_hi); + *(int *)datamp->b_rptr = asymctl(asy, 0, TIOCMGET); + mutex_exit(asy->asy_excl_hi); + + if (iocp->ioc_count == TRANSPARENT) { + mcopyout(mp, NULL, sizeof (int), NULL, datamp); + } else { + if (mp->b_cont != NULL) + freemsg(mp->b_cont); + mp->b_cont = datamp; + mp->b_cont->b_wptr += sizeof (int); + mp->b_datap->db_type = M_IOCACK; + iocp->ioc_count = sizeof (int); + } + break; + + default: /* unexpected ioctl type */ + /* + * If we don't understand it, it's an error. NAK it. + */ + error = EINVAL; + break; + } + } + if (error != 0) { + iocp->ioc_error = error; + mp->b_datap->db_type = M_IOCNAK; + } + mutex_exit(asy->asy_excl); + qreply(wq, mp); +} + +static void +asyrsrv(queue_t *q) +{ + mblk_t *bp; + struct asyncline *async; + + async = (struct asyncline *)q->q_ptr; + + while (canputnext(q) && (bp = getq(q))) + putnext(q, bp); + ASYSETSOFT(async->async_common); + async->async_polltid = 0; +} + +/* + * Put procedure for write queue. + * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here; + * set the flow control character for M_STOPI and M_STARTI messages; + * queue up M_BREAK, M_DELAY, and M_DATA messages for processing + * by the start routine, and then call the start routine; discard + * everything else. Note that this driver does not incorporate any + * mechanism to negotiate to handle the canonicalization process. + * It expects that these functions are handled in upper module(s), + * as we do in ldterm. + */ +static void +asywput(queue_t *q, mblk_t *mp) +{ + register struct asyncline *async; + register struct asycom *asy; + int error; + + async = (struct asyncline *)q->q_ptr; + asy = async->async_common; + + switch (mp->b_datap->db_type) { + + case M_STOP: + /* + * Since we don't do real DMA, we can just let the + * chip coast to a stop after applying the brakes. + */ + mutex_enter(asy->asy_excl); + async->async_flags |= ASYNC_STOPPED; + mutex_exit(asy->asy_excl); + freemsg(mp); + break; + + case M_START: + mutex_enter(asy->asy_excl); + if (async->async_flags & ASYNC_STOPPED) { + async->async_flags &= ~ASYNC_STOPPED; + /* + * If an output operation is in progress, + * resume it. Otherwise, prod the start + * routine. + */ + if (async->async_ocnt > 0) { + mutex_enter(asy->asy_excl_hi); + async_resume(async); + mutex_exit(asy->asy_excl_hi); + } else { + async_start(async); + } + } + mutex_exit(asy->asy_excl); + freemsg(mp); + break; + + case M_IOCTL: + switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) { + + case TCSBRK: + error = miocpullup(mp, sizeof (int)); + if (error != 0) { + miocnak(q, mp, 0, error); + return; + } + + if (*(int *)mp->b_cont->b_rptr != 0) { +#ifdef DEBUG + if (asydebug & ASY_DEBUG_CLOSE) + printf("asy%d: flush request.\n", + UNIT(async->async_dev)); +#endif + (void) putq(q, mp); + mutex_enter(asy->asy_excl); + async_nstart(async, 1); + mutex_exit(asy->asy_excl); + break; + } + /*FALLTHROUGH*/ + case TCSETSW: + case TCSETSF: + case TCSETAW: + case TCSETAF: + /* + * The changes do not take effect until all + * output queued before them is drained. + * Put this message on the queue, so that + * "async_start" will see it when it's done + * with the output before it. Poke the + * start routine, just in case. + */ + (void) putq(q, mp); + mutex_enter(asy->asy_excl); + async_start(async); + mutex_exit(asy->asy_excl); + break; + + default: + /* + * Do it now. + */ + async_ioctl(async, q, mp, B_TRUE); + break; + } + break; + + case M_FLUSH: + if (*mp->b_rptr & FLUSHW) { + mutex_enter(asy->asy_excl); + + /* + * Abort any output in progress. + */ + mutex_enter(asy->asy_excl_hi); + if (async->async_flags & ASYNC_BUSY) { + async->async_ocnt = 0; + async->async_flags &= ~ASYNC_BUSY; + } + mutex_exit(asy->asy_excl_hi); + + /* Flush FIFO buffers */ + if (asy->asy_use_fifo == FIFO_ON) { + OUTB(FIFOR, FIFO_ON | FIFODMA | FIFOTXFLSH | + (asy->asy_trig_level & 0xff)); + } + + /* + * Flush our write queue. + */ + flushq(q, FLUSHDATA); /* XXX doesn't flush M_DELAY */ + if (async->async_xmitblk != NULL) { + freeb(async->async_xmitblk); + async->async_xmitblk = NULL; + } + + mutex_exit(asy->asy_excl); + *mp->b_rptr &= ~FLUSHW; /* it has been flushed */ + } + if (*mp->b_rptr & FLUSHR) { + /* Flush FIFO buffers */ + if (asy->asy_use_fifo == FIFO_ON) { + OUTB(FIFOR, FIFO_ON | FIFODMA | FIFORXFLSH | + (asy->asy_trig_level & 0xff)); + } + flushq(RD(q), FLUSHDATA); + qreply(q, mp); /* give the read queues a crack at it */ + } else { + freemsg(mp); + } + + /* + * We must make sure we process messages that survive the + * write-side flush. Without this call, the close protocol + * with ldterm can hang forever. (ldterm will have sent us a + * TCSBRK ioctl that it expects a response to.) + */ + mutex_enter(asy->asy_excl); + async_start(async); + mutex_exit(asy->asy_excl); + break; + case M_BREAK: + case M_DELAY: + case M_DATA: + /* + * Queue the message up to be transmitted, + * and poke the start routine. + */ + (void) putq(q, mp); + mutex_enter(asy->asy_excl); + async_start(async); + mutex_exit(asy->asy_excl); + break; + + case M_STOPI: + mutex_enter(asy->asy_excl); + async->async_flowc = async->async_stopc; + async_start(async); /* poke the start routine */ + mutex_exit(asy->asy_excl); + freemsg(mp); + break; + + case M_STARTI: + mutex_enter(asy->asy_excl); + async->async_flowc = async->async_startc; + async_start(async); /* poke the start routine */ + mutex_exit(asy->asy_excl); + freemsg(mp); + break; + + case M_CTL: + if (MBLKL(mp) >= sizeof (struct iocblk) && + ((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) { + ((struct iocblk *)mp->b_rptr)->ioc_cmd = MC_HAS_POSIX; + qreply(q, mp); + } else { + /* + * These MC_SERVICE type messages are used by upper + * modules to tell this driver to send input up + * immediately, or that it can wait for normal + * processing that may or may not be done. Sun + * requires these for the mouse module. + * (XXX - for x86?) + */ + mutex_enter(asy->asy_excl); + switch (*mp->b_rptr) { + + case MC_SERVICEIMM: + async->async_flags |= ASYNC_SERVICEIMM; + break; + + case MC_SERVICEDEF: + async->async_flags &= ~ASYNC_SERVICEIMM; + break; + } + mutex_exit(asy->asy_excl); + freemsg(mp); + } + break; + + case M_IOCDATA: + async_iocdata(q, mp); + break; + + default: + freemsg(mp); + break; + } +} + +/* + * Retry an "ioctl", now that "bufcall" claims we may be able to allocate + * the buffer we need. + */ +static void +async_reioctl(void *arg) +{ + struct asyncline *async = arg; + struct asycom *asy = async->async_common; + queue_t *q; + mblk_t *mp; + + /* + * The bufcall is no longer pending. + */ + mutex_enter(asy->asy_excl); + async->async_wbufcid = 0; + if ((q = async->async_ttycommon.t_writeq) == NULL) { + mutex_exit(asy->asy_excl); + return; + } + if ((mp = async->async_ttycommon.t_iocpending) != NULL) { + /* not pending any more */ + async->async_ttycommon.t_iocpending = NULL; + mutex_exit(asy->asy_excl); + /* not in STREAMS queue; we no longer know if we're in wput */ + async_ioctl(async, q, mp, B_TRUE); + } else + mutex_exit(asy->asy_excl); +} + +static void +async_iocdata(queue_t *q, mblk_t *mp) +{ + struct asyncline *async = (struct asyncline *)q->q_ptr; + struct asycom *asy; + struct copyresp *csp; + + asy = async->async_common; + csp = (struct copyresp *)mp->b_rptr; + + if (csp->cp_rval != 0) { + freemsg(mp); + return; + } + + mutex_enter(asy->asy_excl); + + switch (csp->cp_cmd) { + case TIOCMSET: + case TIOCMBIS: + case TIOCMBIC: + if (mp->b_cont == NULL) { + mutex_exit(asy->asy_excl); + miocnak(q, mp, 0, EINVAL); + break; + } + + mutex_enter(asy->asy_excl_hi); + (void) asymctl(asy, dmtoasy(*(int *)mp->b_cont->b_rptr), + csp->cp_cmd); + mutex_exit(asy->asy_excl_hi); + + freemsg(mp->b_cont); + mp->b_cont = NULL; + mutex_exit(asy->asy_excl); + miocack(q, mp, 0, 0); + break; + + case TIOCMGET: + if (mp->b_cont != NULL) { + freemsg(mp->b_cont); + mp->b_cont = NULL; + } + mutex_exit(asy->asy_excl); + miocack(q, mp, 0, 0); + break; + + default: + mutex_exit(asy->asy_excl); + miocnak(q, mp, 0, EINVAL); + break; + } +} + + +/* + * Set or get the modem control status. + */ +static int +asymctl(struct asycom *asy, int bits, int how) +{ + register int mcr_r, msr_r; + + ASSERT(mutex_owned(asy->asy_excl_hi)); + ASSERT(mutex_owned(asy->asy_excl)); + + /* Read Modem Control Registers */ + mcr_r = INB(MCR); + + switch (how) { + + case TIOCMSET: + mcr_r = bits; + break; + + case TIOCMBIS: + mcr_r |= bits; /* Set bits from input */ + break; + + case TIOCMBIC: + mcr_r &= ~bits; /* Set ~bits from input */ + break; + + case TIOCMGET: + /* Read Modem Status Registers */ + if (INB(ICR) & MIEN) + msr_r = asy->asy_cached_msr; + else + msr_r = INB(MSR); + return (asytodm(mcr_r, msr_r)); + } + + OUTB(MCR, mcr_r); + + return (mcr_r); +} + +static int +asytodm(int mcr_r, int msr_r) +{ + register int b = 0; + + + /* MCR registers */ + if (mcr_r & RTS) + b |= TIOCM_RTS; + + if (mcr_r & DTR) + b |= TIOCM_DTR; + + /* MSR registers */ + if (msr_r & DCD) + b |= TIOCM_CAR; + + if (msr_r & CTS) + b |= TIOCM_CTS; + + if (msr_r & DSR) + b |= TIOCM_DSR; + + if (msr_r & RI) + b |= TIOCM_RNG; + + return (b); +} + +static int +dmtoasy(int bits) +{ + register int b = 0; + +#ifdef CAN_NOT_SET /* only DTR and RTS can be set */ + if (bits & TIOCM_CAR) + b |= DCD; + if (bits & TIOCM_CTS) + b |= CTS; + if (bits & TIOCM_DSR) + b |= DSR; + if (bits & TIOCM_RNG) + b |= RI; +#endif + + if (bits & TIOCM_RTS) + b |= RTS; + if (bits & TIOCM_DTR) + b |= DTR; + + return (b); +} + +static void +asycheckflowcontrol_hw(struct asycom *asy) +{ + struct asyncline *async; + uchar_t mcr, flag; + + ASSERT(mutex_owned(asy->asy_excl_hi)); + + async = (struct asyncline *)asy->asy_priv; + ASSERT(async != NULL); + + if (async->async_ttycommon.t_cflag & CRTSXOFF) { + mcr = INB(MCR); + flag = (async->async_flags & ASYNC_HW_IN_FLOW) ? 0 : RTS; + if (((mcr ^ flag) & RTS) != 0) { + OUTB(MCR, (mcr ^ RTS)); + } + } +} + +static boolean_t +asycheckflowcontrol_sw(struct asycom *asy) +{ + uchar_t ss; + struct asyncline *async; + int rval = B_FALSE; + + ASSERT(mutex_owned(asy->asy_excl_hi)); + + async = (struct asyncline *)asy->asy_priv; + ASSERT(async != NULL); + + if ((ss = async->async_flowc) != '\0' && (INB(LSR) & XHRE)) { + /* + * If we get this far, then we know that flowc is non-zero and + * that there's transmit room available. We've "handled" the + * request now, so clear it. If the user didn't ask for IXOFF, + * then don't actually send anything, but wait for the next + * opportunity. + */ + async->async_flowc = '\0'; + if (async->async_ttycommon.t_iflag & IXOFF) { + async->async_flags |= ASYNC_BUSY; + OUTB(DAT, ss); + rval = B_TRUE; + } + } + + return (rval); +} + +/* + * Check for abort character sequence + */ +static boolean_t +abort_charseq_recognize(uchar_t ch) +{ + static int state = 0; +#define CNTRL(c) ((c)&037) + static char sequence[] = { '\r', '~', CNTRL('b') }; + + if (ch == sequence[state]) { + if (++state >= sizeof (sequence)) { + state = 0; + return (B_TRUE); + } + } else { + state = (ch == sequence[0]) ? 1 : 0; + } + return (B_FALSE); +} diff --git a/usr/src/uts/sun4/sys/sudev.h b/usr/src/uts/sun4/sys/sudev.h new file mode 100644 index 0000000000..2effc7528e --- /dev/null +++ b/usr/src/uts/sun4/sys/sudev.h @@ -0,0 +1,400 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ +/* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_SUDEV_H +#define _SYS_SUDEV_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include + +/* + * Definitions for INS8250 / 16550 chips + */ + +/* defined as offsets from the data register */ +#define DAT 0 /* receive/transmit data */ +#define ICR 1 /* interrupt control register */ +#define ISR 2 /* interrupt status register */ +#define LCR 3 /* line control register */ +#define MCR 4 /* modem control register */ +#define LSR 5 /* line status register */ +#define MSR 6 /* modem status register */ +#define DLL 0 /* divisor latch (lsb) */ +#define DLH 1 /* divisor latch (msb) */ +#define FIFOR ISR /* FIFO register for 16550 */ +#define OUTB(offset, value) ddi_put8(asy->asy_handle, \ + asy->asy_ioaddr+offset, value) +#define INB(offset) ddi_get8(asy->asy_handle, asy->asy_ioaddr+offset) + +/* + * INTEL 8210-A/B & 16450/16550 Registers Structure. + */ + +/* Line Control Register */ +#define WLS0 0x01 /* word length select bit 0 */ +#define WLS1 0x02 /* word length select bit 2 */ +#define STB 0x04 /* number of stop bits */ +#define PEN 0x08 /* parity enable */ +#define EPS 0x10 /* even parity select */ +#define SETBREAK 0x40 /* break key */ +#define DLAB 0x80 /* divisor latch access bit */ +#define RXLEN 0x03 /* # of data bits per received/xmitted char */ +#define STOP1 0x00 +#define STOP2 0x04 +#define PAREN 0x08 +#define PAREVN 0x10 +#define PARMARK 0x20 +#define SNDBRK 0x40 + + +#define BITS5 0x00 /* 5 bits per char */ +#define BITS6 0x01 /* 6 bits per char */ +#define BITS7 0x02 /* 7 bits per char */ +#define BITS8 0x03 /* 8 bits per char */ + +/* baud rate definitions */ +#define ASY110 1047 /* 110 baud rate for serial console */ +#define ASY150 768 /* 150 baud rate for serial console */ +#define ASY300 384 /* 300 baud rate for serial console */ +#define ASY600 192 /* 600 baud rate for serial console */ +#define ASY1200 96 /* 1200 baud rate for serial console */ +#define ASY2400 48 /* 2400 baud rate for serial console */ +#define ASY4800 24 /* 4800 baud rate for serial console */ +#define ASY9600 12 /* 9600 baud rate for serial console */ + +/* Line Status Register */ +#define RCA 0x01 /* data ready */ +#define OVRRUN 0x02 /* overrun error */ +#define PARERR 0x04 /* parity error */ +#define FRMERR 0x08 /* framing error */ +#define BRKDET 0x10 /* a break has arrived */ +#define XHRE 0x20 /* tx hold reg is now empty */ +#define XSRE 0x40 /* tx shift reg is now empty */ +#define RFBE 0x80 /* rx FIFO Buffer error */ + +/* Interrupt Id Regisger */ +#define MSTATUS 0x00 /* modem status changed */ +#define NOINTERRUPT 0x01 /* no interrupt pending */ +#define TxRDY 0x02 /* Transmitter Holding Register Empty */ +#define RxRDY 0x04 /* Receiver Data Available */ +#define FFTMOUT 0x0c /* FIFO timeout - 16550AF */ +#define RSTATUS 0x06 /* Receiver Line Status */ + +/* Interrupt Enable Register */ +#define RIEN 0x01 /* Received Data Ready */ +#define TIEN 0x02 /* Tx Hold Register Empty */ +#define SIEN 0x04 /* Receiver Line Status */ +#define MIEN 0x08 /* Modem Status */ + +/* Modem Control Register */ +#define DTR 0x01 /* Data Terminal Ready */ +#define RTS 0x02 /* Request To Send */ +#define OUT1 0x04 /* Aux output - not used */ +#define OUT2 0x08 /* dis/enable int per INO on ALI1535D+ */ +#define ASY_LOOP 0x10 /* loopback for diagnostics */ + +/* Modem Status Register */ +#define DCTS 0x01 /* Delta Clear To Send */ +#define DDSR 0x02 /* Delta Data Set Ready */ +#define DRI 0x04 /* Trail Edge Ring Indicator */ +#define DDCD 0x08 /* Delta Data Carrier Detect */ +#define CTS 0x10 /* Clear To Send */ +#define DSR 0x20 /* Data Set Ready */ +#define RI 0x40 /* Ring Indicator */ +#define DCD 0x80 /* Data Carrier Detect */ + +#define DELTAS(x) ((x)&(DCTS|DDSR|DRI|DDCD)) +#define STATES(x) ((x)(CTS|DSR|RI|DCD)) + +/* flags for FCR (FIFO Control register) */ +#define FIFO_OFF 0x00 /* fifo disabled */ +#define FIFO_ON 0x01 /* fifo enabled */ +#define FIFOEN 0x8f /* fifo enabled, w/ 8 byte trigger */ +#define FIFORCLR 0x8b /* Clear receiver FIFO only */ + +#define FIFORXFLSH 0x02 /* flush receiver FIFO */ +#define FIFOTXFLSH 0x04 /* flush transmitter FIFO */ +#define FIFODMA 0x08 /* DMA mode 1 */ +#define FIFO_TRIG_1 0x00 /* 1 byte trigger level */ +#define FIFO_TRIG_4 0x40 /* 4 byte trigger level */ +#define FIFO_TRIG_8 0x80 /* 8 byte trigger level */ +#define FIFO_TRIG_14 0xC0 /* 14 byte trigger level */ + +/* + * Defines for ioctl calls (VP/ix) + */ + +#define AIOC ('A'<<8) +#define AIOCINTTYPE (AIOC|60) /* set interrupt type */ +#define AIOCDOSMODE (AIOC|61) /* set DOS mode */ +#define AIOCNONDOSMODE (AIOC|62) /* reset DOS mode */ +#define AIOCSERIALOUT (AIOC|63) /* serial device data write */ +#define AIOCSERIALIN (AIOC|64) /* serial device data read */ +#define AIOCSETSS (AIOC|65) /* set start/stop chars */ +#define AIOCINFO (AIOC|66) /* tell usr what device we are */ + +/* Ioctl alternate names used by VP/ix */ +#define VPC_SERIAL_DOS AIOCDOSMODE +#define VPC_SERIAL_NONDOS AIOCNONDOSMODE +#define VPC_SERIAL_INFO AIOCINFO +#define VPC_SERIAL_OUT AIOCSERIALOUT +#define VPC_SERIAL_IN AIOCSERIALIN + +/* Serial in/out requests */ +#define SO_DIVLLSB 1 +#define SO_DIVLMSB 2 +#define SO_LCR 3 +#define SO_MCR 4 +#define SI_MSR 1 +#define SIO_MASK(elem) (1<<((elem)-1)) + +#define OVERRUN 040000 +#define FRERROR 020000 +#define PERROR 010000 +#define S_ERRORS (PERROR|OVERRUN|FRERROR) + +/* + * Ring buffer and async line management definitions. + */ +#define RINGBITS 16 /* # of bits in ring ptrs */ +#define RINGSIZE (1<async_rput = (ap)->async_rget = 0) +#define RING_CNT(ap) (((ap)->async_rput - (ap)->async_rget) & RINGMASK) +#define RING_FRAC(ap) ((int)RING_CNT(ap) >= (int)(RINGSIZE/RINGFRAC)) +#define RING_POK(ap, n) ((int)RING_CNT(ap) < (int)(RINGSIZE-(n))) +#define RING_PUT(ap, c) \ + ((ap)->async_ring[(ap)->async_rput++ & RINGMASK] = (uchar_t)(c)) +#define RING_UNPUT(ap) ((ap)->async_rput--) +#define RING_GOK(ap, n) ((int)RING_CNT(ap) >= (int)(n)) +#define RING_GET(ap) ((ap)->async_ring[(ap)->async_rget++ & RINGMASK]) +#define RING_EAT(ap, n) ((ap)->async_rget += (n)) +#define RING_MARK(ap, c, s) \ + ((ap)->async_ring[(ap)->async_rput++ & RINGMASK] = ((uchar_t)(c)|(s))) +#define RING_UNMARK(ap) \ + ((ap)->async_ring[((ap)->async_rget) & RINGMASK] &= ~S_ERRORS) +#define RING_ERR(ap, c) \ + ((ap)->async_ring[((ap)->async_rget) & RINGMASK] & (c)) + +/* + * Serial kstats structure and macro to increment an individual kstat + */ +struct serial_kstats { + kstat_named_t ringover; /* ring buffer overflow */ + kstat_named_t siloover; /* silo overflow */ +}; + +#define INC64_KSTAT(asy, stat) (asy)->kstats.stat.value.ui64++; + +/* + * Hardware channel common data. One structure per port. + * Each of the fields in this structure is required to be protected by a + * mutex lock at the highest priority at which it can be altered. + * The asy_flags, and asy_next fields can be altered by interrupt + * handling code that must be protected by the mutex whose handle is + * stored in asy_excl_hi. All others can be protected by the asy_excl + * mutex, which is lower priority and adaptive. + */ +struct asycom { + int asy_flags; /* random flags */ + /* protected by asy_excl_hi lock */ + uint_t asy_hwtype; /* HW type: ASY82510, etc. */ + uint_t asy_use_fifo; /* HW FIFO use it or not ?? */ + uint_t asy_fifo_buf; /* With FIFO = 16, otherwise = 1 */ + uchar_t *asy_ioaddr; /* i/o address of ASY port */ + uint_t asy_vect; /* IRQ number */ + boolean_t suspended; /* TRUE if driver suspended */ + caddr_t asy_priv; /* protocol private data */ + dev_info_t *asy_dip; /* dev_info */ + long asy_unit; /* which port */ + ddi_iblock_cookie_t asy_iblock; + kmutex_t *asy_excl; /* asy adaptive mutex */ + kmutex_t *asy_excl_hi; /* asy spinlock mutex */ + ddi_acc_handle_t asy_handle; /* ddi_get/put handle */ + ushort_t asy_rsc_console; /* RSC console port */ + ushort_t asy_rsc_control; /* RSC control port */ + ushort_t asy_lom_console; /* LOM console port */ + uint_t asy_xmit_count; /* Count the no of xmits in one intr */ + uint_t asy_out_of_band_xmit; /* Out of band xmission */ + uint_t asy_rx_count; /* No. of bytes rx'eved in one intr */ + uchar_t asy_device_type; /* Currently used for this device */ + uchar_t asy_trig_level; /* Receive FIFO trig level */ + kmutex_t *asy_soft_lock; /* soft lock for gaurding softpend. */ + int asysoftpend; /* Flag indicating soft int pending. */ + ddi_softintr_t asy_softintr_id; + ddi_iblock_cookie_t asy_soft_iblock; + int asy_baud_divisor_factor; /* for different chips */ + int asy_ocflags; /* old cflags used in asy_program() */ + uint_t asy_cached_msr; /* a cache for the MSR register */ + int asy_speed_cap; /* maximum baud rate */ + kstat_t *sukstat; /* ptr to serial kstats */ + struct serial_kstats kstats; /* serial kstats structure */ + boolean_t inperim; /* in streams q perimeter */ +}; + +/* + * Asychronous protocol private data structure for ASY. + * Each of the fields in the structure is required to be protected by + * the lower priority lock except the fields that are set only at + * base level but cleared (with out lock) at interrupt level. + */ +struct asyncline { + int async_flags; /* random flags */ + kcondvar_t async_flags_cv; /* condition variable for flags */ + dev_t async_dev; /* device major/minor numbers */ + mblk_t *async_xmitblk; /* transmit: active msg block */ + struct asycom *async_common; /* device common data */ + tty_common_t async_ttycommon; /* tty driver common data */ + bufcall_id_t async_wbufcid; /* id for pending write-side bufcall */ + timeout_id_t async_polltid; /* softint poll timeout id */ + + /* + * The following fields are protected by the asy_excl_hi lock. + * Some, such as async_flowc, are set only at the base level and + * cleared (without the lock) only by the interrupt level. + */ + uchar_t *async_optr; /* output pointer */ + int async_ocnt; /* output count */ + uint_t async_rput; /* producing pointer for input */ + uint_t async_rget; /* consuming pointer for input */ + uchar_t async_flowc; /* flow control char to send */ + + /* + * Each character stuffed into the ring has two bytes associated + * with it. The first byte is used to indicate special conditions + * and the second byte is the actual data. The ring buffer + * needs to be defined as ushort_t to accomodate this. + */ + ushort_t async_ring[RINGSIZE]; + + short async_break; /* break count */ + + union { + struct { + uchar_t _hw; /* overrun (hw) */ + uchar_t _sw; /* overrun (sw) */ + } _a; + ushort_t uover_overrun; + } async_uover; +#define async_overrun async_uover._a.uover_overrun +#define async_hw_overrun async_uover._a._hw +#define async_sw_overrun async_uover._a._sw + short async_ext; /* modem status change count */ + short async_work; /* work to do flag */ + uchar_t async_queue_full; /* Streams Queue Full */ + uchar_t async_ringbuf_overflow; /* when ring buffer overflows */ + timeout_id_t async_timer; /* close drain progress timer */ +}; + +/* definitions for async_flags field */ +#define ASYNC_EXCL_OPEN 0x10000000 /* exclusive open */ +#define ASYNC_WOPEN 0x00000001 /* waiting for open to complete */ +#define ASYNC_ISOPEN 0x00000002 /* open is complete */ +#define ASYNC_OUT 0x00000004 /* line being used for dialout */ +#define ASYNC_CARR_ON 0x00000008 /* carrier on last time we looked */ +#define ASYNC_STOPPED 0x00000010 /* output is stopped */ +#define ASYNC_DELAY 0x00000020 /* waiting for delay to finish */ +#define ASYNC_BREAK 0x00000040 /* waiting for break to finish */ +#define ASYNC_BUSY 0x00000080 /* waiting for transmission to finish */ +#define ASYNC_DRAINING 0x00000100 /* waiting for output to drain */ +#define ASYNC_SERVICEIMM 0x00000200 /* queue soft interrupt as soon as */ +#define ASYNC_HW_IN_FLOW 0x00000400 /* input flow control in effect */ +#define ASYNC_HW_OUT_FLW 0x00000800 /* output flow control in effect */ +#define ASYNC_PROGRESS 0x00001000 /* made progress on output effort */ +#define ASYNC_CLOSING 0x00002000 /* closing the stream */ + +/* asy_hwtype definitions */ +#define ASY82510 0x1 +#define ASY16550AF 0x2 +#define ASY8250 0x3 /* 8250 or 16450 or 16550 */ + +/* definitions for asy_flags field */ +#define ASY_NEEDSOFT 0x00000001 +#define ASY_DOINGSOFT 0x00000002 +#define ASY_PPS 0x00000004 +#define ASY_PPS_EDGE 0x00000008 +#define ASY_IGNORE_CD 0x00000040 + +/* + * Different devices this driver supports and what it is used to drive + * currently + */ +#define ASY_KEYBOARD 0x01 +#define ASY_MOUSE 0x02 +#define ASY_SERIAL 0x03 + +/* + * RSC_DEVICE defines the bit in the minor device number that specifies + * the tty line is to be used for console/controlling a RSC device. + */ +#define RSC_DEVICE (1 << (NBITSMINOR32 - 4)) + +/* + * OUTLINE defines the high-order flag bit in the minor device number that + * controls use of a tty line for dialin and dialout simultaneously. + */ +#define OUTLINE (1 << (NBITSMINOR32 - 1)) +#define UNIT(x) (getminor(x) & ~(OUTLINE | RSC_DEVICE)) + +/* suggested number of soft state instances */ +#define SU_INITIAL_SOFT_ITEMS 0x02 + +/* + * ASYSETSOFT macro to pend a soft interrupt if one isn't already pending. + */ + +#define ASYSETSOFT(asy) { \ + if (mutex_tryenter(asy->asy_soft_lock)) { \ + asy->asy_flags |= ASY_NEEDSOFT; \ + if (!asy->asysoftpend) { \ + asy->asysoftpend = 1; \ + mutex_exit(asy->asy_soft_lock);\ + ddi_trigger_softintr(asy->asy_softintr_id);\ + } else \ + mutex_exit(asy->asy_soft_lock);\ + } \ +} + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_SUDEV_H */ diff --git a/usr/src/uts/sun4u/Makefile.files b/usr/src/uts/sun4u/Makefile.files index 1701aa855f..9d9ca808e2 100644 --- a/usr/src/uts/sun4u/Makefile.files +++ b/usr/src/uts/sun4u/Makefile.files @@ -95,7 +95,6 @@ PSYCHO_PCI_OBJS += $(PCI_COMMON_OBJS) pcipsy.o SCHIZO_PCI_OBJS += $(PCI_COMMON_OBJS) pcisch_asm.o pcisch.o pcix.o SIMBA_PCI_OBJS += simba.o DB21554_OBJS += db21554.o -SU_OBJS += su_driver.o US_OBJS += us_drv.o POWER_OBJS += power.o GRBEEP_OBJS += grbeep.o diff --git a/usr/src/uts/sun4u/io/su_driver.c b/usr/src/uts/sun4u/io/su_driver.c deleted file mode 100644 index 4400077b45..0000000000 --- a/usr/src/uts/sun4u/io/su_driver.c +++ /dev/null @@ -1,3457 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ -/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ -/* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */ -/* All Rights Reserved */ - -/* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#pragma ident "%Z%%M% %I% %E% SMI" - -/* - * Serial I/O driver for 82510/8250/16450/16550AF chips. - * Modified as sparc keyboard/mouse driver. - */ -#define SU_REGISTER_FILE_NO 0 -#define SU_REGOFFSET 0 -#define SU_REGISTER_LEN 8 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef DEBUG -#include -#endif -#include -#include -#include -#include -#include -#include - -#define async_stopc async_ttycommon.t_stopc -#define async_startc async_ttycommon.t_startc - -#define ASY_INIT 1 -#define ASY_NOINIT 0 - -#ifdef DEBUG -#define ASY_DEBUG_INIT 0x001 -#define ASY_DEBUG_INPUT 0x002 -#define ASY_DEBUG_EOT 0x004 -#define ASY_DEBUG_CLOSE 0x008 -#define ASY_DEBUG_HFLOW 0x010 -#define ASY_DEBUG_PROCS 0x020 -#define ASY_DEBUG_STATE 0x040 -#define ASY_DEBUG_INTR 0x080 -static int asydebug = 0; -#endif -static int su_log = 0; - -int su_drain_check = 15000000; /* tunable: exit drain check time */ - -static struct ppsclockev asy_ppsev; - -static int max_asy_instance = -1; -static void *su_asycom; /* soft state asycom pointer */ -static void *su_asyncline; /* soft state asyncline pointer */ -static boolean_t abort_charseq_recognize(uchar_t ch); - -static uint_t asysoftintr(caddr_t intarg); -static uint_t asyintr(caddr_t argasy); - -/* The async interrupt entry points */ -static void async_txint(struct asycom *asy, uchar_t lsr); -static void async_rxint(struct asycom *asy, uchar_t lsr); -static void async_msint(struct asycom *asy); -static int async_softint(struct asycom *asy); - -static void async_ioctl(struct asyncline *async, queue_t *q, mblk_t *mp, - boolean_t iswput); -static void async_reioctl(void *); -static void async_iocdata(queue_t *q, mblk_t *mp); -static void async_restart(void *); -static void async_start(struct asyncline *async); -static void async_nstart(struct asyncline *async, int mode); -static void async_resume(struct asyncline *async); -static int asy_program(struct asycom *asy, int mode); - -static int asymctl(struct asycom *, int, int); -static int asytodm(int, int); -static int dmtoasy(int); -static void asycheckflowcontrol_hw(struct asycom *asy); -static boolean_t asycheckflowcontrol_sw(struct asycom *asy); -static void asy_ppsevent(struct asycom *asy, int msr); - -extern kcondvar_t lbolt_cv; -extern int ddi_create_internal_pathname(dev_info_t *dip, char *name, - int spec_type, minor_t minor_num); - - -/* - * Baud rate table. Indexed by #defines found in sys/termios.h - */ -ushort_t asyspdtab[] = { - 0, /* 0 baud rate */ - 0x900, /* 50 baud rate */ - 0x600, /* 75 baud rate */ - 0x417, /* 110 baud rate (%0.026) */ - 0x359, /* 134 baud rate (%0.058) */ - 0x300, /* 150 baud rate */ - 0x240, /* 200 baud rate */ - 0x180, /* 300 baud rate */ - 0x0c0, /* 600 baud rate */ - 0x060, /* 1200 baud rate */ - 0x040, /* 1800 baud rate */ - 0x030, /* 2400 baud rate */ - 0x018, /* 4800 baud rate */ - 0x00c, /* 9600 baud rate */ - 0x006, /* 19200 baud rate */ - 0x003, /* 38400 baud rate */ - 0x002, /* 57600 baud rate */ - 0, /* 76800 baud rate - not supported */ - 0x001, /* 115200 baud rate */ - 0, /* 153600 baud rate - not supported */ - 0x8002, /* 230400 baud rate - supported on specific platforms */ - 0, /* 307200 baud rate - not supported */ - 0x8001 /* 460800 baud rate - supported on specific platforms */ -}; - -/* - * Number of speeds supported is the number of entries in - * the above table. - */ -#define N_SU_SPEEDS (sizeof (asyspdtab)/sizeof (ushort_t)) - -/* - * Human-readable baud rate table. - * Indexed by #defines found in sys/termios.h - */ -int baudtable[] = { - 0, /* 0 baud rate */ - 50, /* 50 baud rate */ - 75, /* 75 baud rate */ - 110, /* 110 baud rate */ - 134, /* 134 baud rate */ - 150, /* 150 baud rate */ - 200, /* 200 baud rate */ - 300, /* 300 baud rate */ - 600, /* 600 baud rate */ - 1200, /* 1200 baud rate */ - 1800, /* 1800 baud rate */ - 2400, /* 2400 baud rate */ - 4800, /* 4800 baud rate */ - 9600, /* 9600 baud rate */ - 19200, /* 19200 baud rate */ - 38400, /* 38400 baud rate */ - 57600, /* 57600 baud rate */ - 76800, /* 76800 baud rate */ - 115200, /* 115200 baud rate */ - 153600, /* 153600 baud rate */ - 230400, /* 230400 baud rate */ - 307200, /* 307200 baud rate */ - 460800 /* 460800 baud rate */ -}; - -static int asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr); -static int asyclose(queue_t *q, int flag); -static void asywput(queue_t *q, mblk_t *mp); -static void asyrsrv(queue_t *q); - -struct module_info asy_info = { - 0, - "su", - 0, - INFPSZ, - 32*4096, - 4096 -}; - -static struct qinit asy_rint = { - putq, - (int (*)())asyrsrv, - asyopen, - asyclose, - NULL, - &asy_info, - NULL -}; - -static struct qinit asy_wint = { - (int (*)())asywput, - NULL, - NULL, - NULL, - NULL, - &asy_info, - NULL -}; - -struct streamtab asy_str_info = { - &asy_rint, - &asy_wint, - NULL, - NULL -}; - -static int asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, - void **result); -static int asyprobe(dev_info_t *); -static int asyattach(dev_info_t *, ddi_attach_cmd_t); -static int asydetach(dev_info_t *, ddi_detach_cmd_t); - -static struct cb_ops cb_asy_ops = { - nodev, /* cb_open */ - nodev, /* cb_close */ - nodev, /* cb_strategy */ - nodev, /* cb_print */ - nodev, /* cb_dump */ - nodev, /* cb_read */ - nodev, /* cb_write */ - nodev, /* cb_ioctl */ - nodev, /* cb_devmap */ - nodev, /* cb_mmap */ - nodev, /* cb_segmap */ - nochpoll, /* cb_chpoll */ - ddi_prop_op, /* cb_prop_op */ - &asy_str_info, /* cb_stream */ - D_MP /* cb_flag */ -}; - -struct dev_ops asy_ops = { - DEVO_REV, /* devo_rev */ - 0, /* devo_refcnt */ - asyinfo, /* devo_getinfo */ - nulldev, /* devo_identify */ - asyprobe, /* devo_probe */ - asyattach, /* devo_attach */ - asydetach, /* devo_detach */ - nodev, /* devo_reset */ - &cb_asy_ops, /* devo_cb_ops */ -}; - -/* - * Module linkage information for the kernel. - */ - -static struct modldrv modldrv = { - &mod_driverops, /* Type of module. This one is a driver */ - "su driver %I%", - &asy_ops, /* driver ops */ -}; - -static struct modlinkage modlinkage = { - MODREV_1, - &modldrv, - NULL -}; - -int -_init(void) -{ - int status; - - status = ddi_soft_state_init(&su_asycom, sizeof (struct asycom), - SU_INITIAL_SOFT_ITEMS); - if (status != 0) - return (status); - status = ddi_soft_state_init(&su_asyncline, sizeof (struct asyncline), - SU_INITIAL_SOFT_ITEMS); - if (status != 0) { - ddi_soft_state_fini(&su_asycom); - return (status); - } - - if ((status = mod_install(&modlinkage)) != 0) { - ddi_soft_state_fini(&su_asycom); - ddi_soft_state_fini(&su_asyncline); - } - - return (status); -} - -int -_fini(void) -{ - int i; - - i = mod_remove(&modlinkage); - if (i == 0) { - ddi_soft_state_fini(&su_asycom); - ddi_soft_state_fini(&su_asyncline); - } - - return (i); -} - -int -_info(struct modinfo *modinfop) -{ - return (mod_info(&modlinkage, modinfop)); -} - -static int -asyprobe(dev_info_t *devi) -{ - int instance; - ddi_acc_handle_t handle; - uchar_t *addr; - ddi_device_acc_attr_t attr; - - attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; - attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; - attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; - - if (ddi_regs_map_setup(devi, SU_REGISTER_FILE_NO, (caddr_t *)&addr, - SU_REGOFFSET, SU_REGISTER_LEN, &attr, &handle) != DDI_SUCCESS) { - cmn_err(CE_WARN, "asyprobe regs map setup failed"); - return (DDI_PROBE_FAILURE); - } -#ifdef DEBUG - if (asydebug) - printf("Probe address mapped %p\n", (void *)addr); -#endif - - /* - * Probe for the device: - * Ser. int. uses bits 0,1,2; FIFO uses 3,6,7; 4,5 wired low. - * If bit 4 or 5 appears on inb() ISR, board is not there. - */ - if (ddi_get8(handle, addr+ISR) & 0x30) - return (DDI_PROBE_FAILURE); - instance = ddi_get_instance(devi); - if (max_asy_instance < instance) - max_asy_instance = instance; - ddi_regs_map_free(&handle); - - return (DDI_PROBE_SUCCESS); /* hw is present */ -} - -static int -asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd) -{ - register int instance; - struct asycom *asy; - struct asyncline *async; - char name[16]; - - instance = ddi_get_instance(devi); /* find out which unit */ - - asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance); - async = (struct asyncline *)ddi_get_soft_state(su_asyncline, instance); - - switch (cmd) { - case DDI_DETACH: - break; - case DDI_SUSPEND: - /* grab both mutex locks */ - mutex_enter(asy->asy_excl); - mutex_enter(asy->asy_excl_hi); - if (asy->suspended) { - mutex_exit(asy->asy_excl_hi); - mutex_exit(asy->asy_excl); - return (DDI_SUCCESS); - } - asy->suspended = B_TRUE; - /* Disable further interrupts */ - OUTB(ICR, 0); - mutex_exit(asy->asy_excl_hi); - mutex_exit(asy->asy_excl); - return (DDI_SUCCESS); - - default: - return (DDI_FAILURE); - } - -#ifdef DEBUG - if (asydebug & ASY_DEBUG_INIT) - cmn_err(CE_NOTE, "su%d: ASY%s shutdown.", instance, - asy->asy_hwtype == ASY82510 ? "82510" : - asy->asy_hwtype == ASY16550AF ? "16550AF" : - "8250"); -#endif - /* - * Before removing interrupts it is always better to disable - * interrupts if the chip gives a provision to disable the - * serial port interrupts. - */ - mutex_enter(asy->asy_excl); - mutex_enter(asy->asy_excl_hi); - OUTB(ICR, 0); /* disables interrupt */ - mutex_exit(asy->asy_excl_hi); - mutex_exit(asy->asy_excl); - - /* remove minor device node(s) for this device */ - (void) sprintf(name, "%c", (instance+'a')); /* serial-port */ - ddi_remove_minor_node(devi, name); - (void) sprintf(name, "%c,cu", (instance+'a')); /* serial-port:dailout */ - ddi_remove_minor_node(devi, name); - - mutex_destroy(asy->asy_excl); - mutex_destroy(asy->asy_excl_hi); - kmem_free(asy->asy_excl, sizeof (kmutex_t)); - kmem_free(asy->asy_excl_hi, sizeof (kmutex_t)); - cv_destroy(&async->async_flags_cv); - kstat_delete(asy->sukstat); - ddi_remove_intr(devi, 0, asy->asy_iblock); - ddi_regs_map_free(&asy->asy_handle); - ddi_remove_softintr(asy->asy_softintr_id); - mutex_destroy(asy->asy_soft_lock); - kmem_free(asy->asy_soft_lock, sizeof (kmutex_t)); - ddi_soft_state_free(su_asycom, instance); - ddi_soft_state_free(su_asyncline, instance); - return (DDI_SUCCESS); -} - -static int -asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd) -{ - register int instance; - struct asycom *asy; - struct asyncline *async; - char name[40]; - ddi_device_acc_attr_t attr; - enum states { EMPTY, SOFTSTATE, REGSMAP, MUTEXES, ADDINTR, - SOFTINTR, ASYINIT, KSTAT, MINORNODE }; - enum states state = EMPTY; - - instance = ddi_get_instance(devi); /* find out which unit */ - - /* cannot attach a device that has not been probed first */ - if (instance > max_asy_instance) - return (DDI_FAILURE); - - if (cmd != DDI_RESUME) { - /* Allocate soft state space */ - if (ddi_soft_state_zalloc(su_asycom, instance) != DDI_SUCCESS) { - cmn_err(CE_WARN, "su%d: cannot allocate soft state", - instance); - goto error; - } - } - state = SOFTSTATE; - - asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance); - - if (asy == NULL) { - cmn_err(CE_WARN, "su%d: cannot get soft state", instance); - goto error; - } - - switch (cmd) { - case DDI_ATTACH: - break; - case DDI_RESUME: { - struct asyncline *async; - - /* grab both mutex locks */ - mutex_enter(asy->asy_excl); - mutex_enter(asy->asy_excl_hi); - if (!asy->suspended) { - mutex_exit(asy->asy_excl_hi); - mutex_exit(asy->asy_excl); - return (DDI_SUCCESS); - } - /* re-setup all the registers and enable interrupts if needed */ - async = (struct asyncline *)asy->asy_priv; - if ((async) && (async->async_flags & ASYNC_ISOPEN)) - (void) asy_program(asy, ASY_INIT); - asy->suspended = B_FALSE; - mutex_exit(asy->asy_excl_hi); - mutex_exit(asy->asy_excl); - return (DDI_SUCCESS); - } - default: - goto error; - } - - attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; - attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; - attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; - - if (ddi_regs_map_setup(devi, SU_REGISTER_FILE_NO, - (caddr_t *)&asy->asy_ioaddr, SU_REGOFFSET, SU_REGISTER_LEN, - &attr, &asy->asy_handle) != DDI_SUCCESS) { - cmn_err(CE_WARN, "asyprobe regs map setup failed"); - goto error; - } - state = REGSMAP; - -#ifdef DEBUG - if (asydebug) - printf("su attach mapped %p\n", (void *)asy->asy_ioaddr); -#endif - - /* - * Initialize the port with default settings. - */ - asy->asy_fifo_buf = 1; - asy->asy_use_fifo = FIFO_OFF; - - /* - * Check for baudrate generator's "baud-divisor-factor" property setup - * by OBP, since different UART chips might have different baudrate - * generator divisor. e.g., in case of NSPG's Sputnik platform, the - * baud-divisor-factor is 13, it uses dedicated 16552 "DUART" chip - * instead of SuperIO. Since the baud-divisor-factor must be a positive - * integer, the divisors will always be at least as large as the values - * in asyspdtab[]. Make the default factor 1. - */ - asy->asy_baud_divisor_factor = ddi_prop_get_int(DDI_DEV_T_ANY, devi, - DDI_PROP_DONTPASS, "baud-divisor-factor", 1); - - /* set speed cap */ - asy->asy_speed_cap = ddi_prop_get_int(DDI_DEV_T_ANY, devi, - DDI_PROP_DONTPASS, "serial-speed-cap", 115200); - - /* check for ASY82510 chip */ - OUTB(ISR, 0x20); - if (INB(ISR) & 0x20) { /* 82510 chip is present */ - /* - * Since most of the general operation of the 82510 chip - * can be done from BANK 0 (8250A/16450 compatable mode) - * we will default to BANK 0. - */ - asy->asy_hwtype = ASY82510; - OUTB(DAT+7, 0x04); /* clear status */ - OUTB(ISR, 0x40); /* set to bank 2 */ - OUTB(MCR, 0x08); /* IMD */ - OUTB(DAT, 0x21); /* FMD */ - OUTB(ISR, 0x00); /* set to bank 0 */ - asy->asy_trig_level = 0; - } else { /* Set the UART in FIFO mode if it has FIFO buffers */ - asy->asy_hwtype = ASY16550AF; - OUTB(FIFOR, 0x00); /* clear fifo register */ - asy->asy_trig_level = 0x00; /* sets the fifo Threshold to 1 */ - - /* set/Enable FIFO */ - OUTB(FIFOR, FIFO_ON | FIFODMA | FIFOTXFLSH | FIFORXFLSH | - (asy->asy_trig_level & 0xff)); - - if ((INB(ISR) & 0xc0) == 0xc0) - asy->asy_use_fifo = FIFO_ON; - else { - asy->asy_hwtype = ASY8250; - OUTB(FIFOR, 0x00); /* NO FIFOs */ - asy->asy_trig_level = 0; - } - } - - OUTB(ICR, 0); /* disable all interrupts */ - OUTB(LCR, DLAB); /* select baud rate generator */ - /* Set the baud rate to 9600 */ - OUTB(DAT+DLL, (ASY9600*asy->asy_baud_divisor_factor) & 0xff); - OUTB(DAT+DLH, ((ASY9600*asy->asy_baud_divisor_factor) >> 8) & 0xff); - OUTB(LCR, STOP1|BITS8); - OUTB(MCR, (DTR | RTS| OUT2)); - - /* - * Set up the other components of the asycom structure for this port. - */ - asy->asy_excl = (kmutex_t *) - kmem_zalloc(sizeof (kmutex_t), KM_SLEEP); - asy->asy_excl_hi = (kmutex_t *) - kmem_zalloc(sizeof (kmutex_t), KM_SLEEP); - asy->asy_soft_lock = (kmutex_t *) - kmem_zalloc(sizeof (kmutex_t), KM_SLEEP); - asy->asy_unit = instance; - asy->asy_dip = devi; - - if (ddi_get_iblock_cookie(devi, 0, &asy->asy_iblock) != DDI_SUCCESS) { - cmn_err(CE_NOTE, - "Get iblock_cookie failed-Device interrupt%x\n", instance); - goto error; - } - - if (ddi_get_soft_iblock_cookie(devi, DDI_SOFTINT_HIGH, - &asy->asy_soft_iblock) != DDI_SUCCESS) { - cmn_err(CE_NOTE, "Get iblock_cookie failed -soft interrupt%x\n", - instance); - goto error; - } - - mutex_init(asy->asy_soft_lock, NULL, MUTEX_DRIVER, - (void *)asy->asy_soft_iblock); - mutex_init(asy->asy_excl, NULL, MUTEX_DRIVER, NULL); - mutex_init(asy->asy_excl_hi, NULL, MUTEX_DRIVER, - (void *)asy->asy_iblock); - state = MUTEXES; - - /* - * Install interrupt handlers for this device. - */ - if (ddi_add_intr(devi, 0, &(asy->asy_iblock), 0, asyintr, - (caddr_t)asy) != DDI_SUCCESS) { - cmn_err(CE_CONT, - "Cannot set device interrupt for su driver\n"); - goto error; - } - state = ADDINTR; - - if (ddi_add_softintr(devi, DDI_SOFTINT_HIGH, &(asy->asy_softintr_id), - &asy->asy_soft_iblock, 0, asysoftintr, (caddr_t)asy) - != DDI_SUCCESS) { - cmn_err(CE_CONT, "Cannot set soft interrupt for su driver\n"); - goto error; - } - state = SOFTINTR; - - /* initialize the asyncline structure */ - if (ddi_soft_state_zalloc(su_asyncline, instance) != DDI_SUCCESS) { - cmn_err(CE_CONT, "su%d: cannot allocate soft state", instance); - goto error; - } - state = ASYINIT; - - async = (struct asyncline *)ddi_get_soft_state(su_asyncline, instance); - - mutex_enter(asy->asy_excl); - async->async_common = asy; - cv_init(&async->async_flags_cv, NULL, CV_DEFAULT, NULL); - mutex_exit(asy->asy_excl); - - if ((asy->sukstat = kstat_create("su", instance, "serialstat", - "misc", KSTAT_TYPE_NAMED, 2, KSTAT_FLAG_VIRTUAL)) != NULL) { - asy->sukstat->ks_data = &asy->kstats; - kstat_named_init(&asy->kstats.ringover, "ring buffer overflow", - KSTAT_DATA_UINT64); - kstat_named_init(&asy->kstats.siloover, "silo overflow", - KSTAT_DATA_UINT64); - kstat_install(asy->sukstat); - } - state = KSTAT; - - if (strcmp(ddi_node_name(devi), "rsc-console") == 0) { - /* - * If the device is configured as the 'rsc-console' - * create the minor device for this node. - */ - if (ddi_create_minor_node(devi, "ssp", S_IFCHR, - asy->asy_unit | RSC_DEVICE, DDI_PSEUDO, NULL) - == DDI_FAILURE) { - cmn_err(CE_WARN, - "%s%d: Failed to create node rsc-console", - ddi_get_name(devi), ddi_get_instance(devi)); - goto error; - } - - asy->asy_lom_console = 0; - asy->asy_rsc_console = 1; - asy->asy_rsc_control = 0; - asy->asy_device_type = ASY_SERIAL; - asy->asy_flags |= ASY_IGNORE_CD; - - } else if (strcmp(ddi_node_name(devi), "lom-console") == 0) { - /* - * If the device is configured as the 'lom-console' - * create the minor device for this node. - * Do not create a dialout device. - * Use the same minor numbers as would be used for standard - * serial instances. - */ - if (ddi_create_minor_node(devi, "lom-console", S_IFCHR, - instance, DDI_NT_SERIAL_LOMCON, NULL) == DDI_FAILURE) { - cmn_err(CE_WARN, - "%s%d: Failed to create node lom-console", - ddi_get_name(devi), ddi_get_instance(devi)); - goto error; - } - asy->asy_lom_console = 1; - asy->asy_rsc_console = 0; - asy->asy_rsc_control = 0; - asy->asy_device_type = ASY_SERIAL; - asy->asy_flags |= ASY_IGNORE_CD; - - } else if (strcmp(ddi_node_name(devi), "rsc-control") == 0) { - /* - * If the device is configured as the 'rsc-control' - * create the minor device for this node. - */ - if (ddi_create_minor_node(devi, "sspctl", S_IFCHR, - asy->asy_unit | RSC_DEVICE, DDI_PSEUDO, NULL) - == DDI_FAILURE) { - cmn_err(CE_WARN, "%s%d: Failed to create rsc-control", - ddi_get_name(devi), ddi_get_instance(devi)); - goto error; - } - - asy->asy_lom_console = 0; - asy->asy_rsc_console = 0; - asy->asy_rsc_control = 1; - asy->asy_device_type = ASY_SERIAL; - asy->asy_flags |= ASY_IGNORE_CD; - - } else if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, - "keyboard", 0)) { - /* - * If the device is a keyboard, then create an internal - * pathname so that the dacf code will link the node into - * the keyboard console stream. See dacf.conf. - */ - if (ddi_create_internal_pathname(devi, "keyboard", - S_IFCHR, instance) == DDI_FAILURE) { - goto error; - } - asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */ - asy->asy_device_type = ASY_KEYBOARD; /* Device type */ - } else if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, - "mouse", 0)) { - /* - * If the device is a mouse, then create an internal - * pathname so that the dacf code will link the node into - * the mouse stream. See dacf.conf. - */ - if (ddi_create_internal_pathname(devi, "mouse", S_IFCHR, - instance) == DDI_FAILURE) { - goto error; - } - asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */ - asy->asy_device_type = ASY_MOUSE; - } else { - /* - * If not used for keyboard/mouse, create minor devices nodes - * for this device - */ - /* serial-port */ - (void) sprintf(name, "%c", (instance+'a')); - if (ddi_create_minor_node(devi, name, S_IFCHR, instance, - DDI_NT_SERIAL_MB, NULL) == DDI_FAILURE) { - goto error; - } - state = MINORNODE; - /* serial-port:dailout */ - (void) sprintf(name, "%c,cu", (instance+'a')); - if (ddi_create_minor_node(devi, name, S_IFCHR, instance|OUTLINE, - DDI_NT_SERIAL_MB_DO, NULL) == DDI_FAILURE) { - goto error; - } - /* Property for ignoring DCD */ - if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, - "ignore-cd", 0)) { - asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */ - } else { - asy->asy_flags &= ~ASY_IGNORE_CD; - /* - * if ignore-cd is not available it could be - * some old legacy platform, try to see - * whether the old legacy property exists - */ - (void) sprintf(name, - "port-%c-ignore-cd", (instance+ 'a')); - if (ddi_getprop(DDI_DEV_T_ANY, devi, - DDI_PROP_DONTPASS, name, 0)) - asy->asy_flags |= ASY_IGNORE_CD; - } - asy->asy_device_type = ASY_SERIAL; - } - ddi_report_dev(devi); - return (DDI_SUCCESS); - -error: - if (state == MINORNODE) { - (void) sprintf(name, "%c", (instance+'a')); - ddi_remove_minor_node(devi, name); - } - if (state >= KSTAT) - kstat_delete(asy->sukstat); - if (state >= ASYINIT) { - cv_destroy(&async->async_flags_cv); - ddi_soft_state_free(su_asyncline, instance); - } - if (state >= SOFTINTR) - ddi_remove_softintr(asy->asy_softintr_id); - if (state >= ADDINTR) - ddi_remove_intr(devi, 0, asy->asy_iblock); - if (state >= MUTEXES) { - mutex_destroy(asy->asy_excl_hi); - mutex_destroy(asy->asy_excl); - mutex_destroy(asy->asy_soft_lock); - kmem_free(asy->asy_excl_hi, sizeof (kmutex_t)); - kmem_free(asy->asy_excl, sizeof (kmutex_t)); - kmem_free(asy->asy_soft_lock, sizeof (kmutex_t)); - } - if (state >= REGSMAP) - ddi_regs_map_free(&asy->asy_handle); - if (state >= SOFTSTATE) - ddi_soft_state_free(su_asycom, instance); - /* no action for EMPTY state */ - return (DDI_FAILURE); -} - -static int -asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, - void **result) -{ - _NOTE(ARGUNUSED(dip)) - register dev_t dev = (dev_t)arg; - register int instance, error; - struct asycom *asy; - - if ((instance = UNIT(dev)) > max_asy_instance) - return (DDI_FAILURE); - - switch (infocmd) { - case DDI_INFO_DEVT2DEVINFO: - asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance); - if (asy->asy_dip == NULL) - error = DDI_FAILURE; - else { - *result = (void *) asy->asy_dip; - error = DDI_SUCCESS; - } - break; - case DDI_INFO_DEVT2INSTANCE: - *result = (void *)instance; - error = DDI_SUCCESS; - break; - default: - error = DDI_FAILURE; - } - return (error); -} - -static int -asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr) -{ - _NOTE(ARGUNUSED(sflag)) - struct asycom *asy; - struct asyncline *async; - int mcr; - int unit; - int len; - struct termios *termiosp; - -#ifdef DEBUG - if (asydebug & ASY_DEBUG_CLOSE) - printf("open\n"); -#endif - unit = UNIT(*dev); - if (unit > max_asy_instance) - return (ENXIO); /* unit not configured */ - - async = (struct asyncline *)ddi_get_soft_state(su_asyncline, unit); - if (async == NULL) - return (ENXIO); - - asy = async->async_common; - if (asy == NULL) - return (ENXIO); /* device not found by autoconfig */ - - mutex_enter(asy->asy_excl); - asy->asy_priv = (caddr_t)async; - -again: - mutex_enter(asy->asy_excl_hi); - /* - * Block waiting for carrier to come up, unless this is a no-delay open. - */ - if (!(async->async_flags & ASYNC_ISOPEN)) { - /* - * If this port is for a RSC console or control - * use the following termio info - */ - if (asy->asy_rsc_console || asy->asy_rsc_control) { - async->async_ttycommon.t_cflag = CIBAUDEXT | CBAUDEXT | - (B115200 & CBAUD); - async->async_ttycommon.t_cflag |= ((B115200 << IBSHIFT) - & CIBAUD); - async->async_ttycommon.t_cflag |= CS8 | CREAD | CLOCAL; - } else if (asy->asy_lom_console) { - async->async_ttycommon.t_cflag = B9600 & CBAUD; - async->async_ttycommon.t_cflag |= ((B9600 << IBSHIFT) - & CIBAUD); - async->async_ttycommon.t_cflag |= CS8 | CREAD | CLOCAL; - } else { - - /* - * Set the default termios settings (cflag). - * Others are set in ldterm. Release the spin - * mutex as we can block here, reaquire before - * calling asy_program. - */ - mutex_exit(asy->asy_excl_hi); - if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(), - 0, "ttymodes", (caddr_t)&termiosp, &len) - == DDI_PROP_SUCCESS && - len == sizeof (struct termios)) { - async->async_ttycommon.t_cflag = - termiosp->c_cflag; - kmem_free(termiosp, len); - } else { - cmn_err(CE_WARN, - "su: couldn't get ttymodes property!"); - } - mutex_enter(asy->asy_excl_hi); - } - async->async_ttycommon.t_iflag = 0; - async->async_ttycommon.t_iocpending = NULL; - async->async_ttycommon.t_size.ws_row = 0; - async->async_ttycommon.t_size.ws_col = 0; - async->async_ttycommon.t_size.ws_xpixel = 0; - async->async_ttycommon.t_size.ws_ypixel = 0; - async->async_dev = *dev; - async->async_wbufcid = 0; - - async->async_startc = CSTART; - async->async_stopc = CSTOP; - (void) asy_program(asy, ASY_INIT); - } else if ((async->async_ttycommon.t_flags & TS_XCLUDE) && - secpolicy_excl_open(cr) != 0) { - mutex_exit(asy->asy_excl_hi); - mutex_exit(asy->asy_excl); - return (EBUSY); - } else if ((*dev & OUTLINE) && !(async->async_flags & ASYNC_OUT)) { - mutex_exit(asy->asy_excl_hi); - mutex_exit(asy->asy_excl); - return (EBUSY); - } - - if (*dev & OUTLINE) - async->async_flags |= ASYNC_OUT; - - /* Raise DTR on every open */ - mcr = INB(MCR); - OUTB(MCR, mcr|DTR); - - /* - * Check carrier. - */ - if (asy->asy_flags & ASY_IGNORE_CD) - async->async_ttycommon.t_flags |= TS_SOFTCAR; - if ((async->async_ttycommon.t_flags & TS_SOFTCAR) || - (INB(MSR) & DCD)) - async->async_flags |= ASYNC_CARR_ON; - else - async->async_flags &= ~ASYNC_CARR_ON; - mutex_exit(asy->asy_excl_hi); - - /* - * If FNDELAY and FNONBLOCK are clear, block until carrier up. - * Quit on interrupt. - */ - if (!(flag & (FNDELAY|FNONBLOCK)) && - !(async->async_ttycommon.t_cflag & CLOCAL)) { - if (!(async->async_flags & (ASYNC_CARR_ON|ASYNC_OUT)) || - ((async->async_flags & ASYNC_OUT) && - !(*dev & OUTLINE))) { - async->async_flags |= ASYNC_WOPEN; - if (cv_wait_sig(&async->async_flags_cv, - asy->asy_excl) == 0) { - async->async_flags &= ~ASYNC_WOPEN; - mutex_exit(asy->asy_excl); - return (EINTR); - } - async->async_flags &= ~ASYNC_WOPEN; - goto again; - } - } else if ((async->async_flags & ASYNC_OUT) && !(*dev & OUTLINE)) { - mutex_exit(asy->asy_excl); - return (EBUSY); - } - - if (asy->suspended) { - mutex_exit(asy->asy_excl); - (void) ddi_dev_is_needed(asy->asy_dip, 0, 1); - mutex_enter(asy->asy_excl); - } - - async->async_ttycommon.t_readq = rq; - async->async_ttycommon.t_writeq = WR(rq); - rq->q_ptr = WR(rq)->q_ptr = (caddr_t)async; - mutex_exit(asy->asy_excl); - qprocson(rq); - async->async_flags |= ASYNC_ISOPEN; - async->async_polltid = 0; - return (0); -} - -static void -async_progress_check(void *arg) -{ - struct asyncline *async = arg; - struct asycom *asy = async->async_common; - mblk_t *bp; - - /* - * We define "progress" as either waiting on a timed break or delay, or - * having had at least one transmitter interrupt. If none of these are - * true, then just terminate the output and wake up that close thread. - */ - mutex_enter(asy->asy_excl); - mutex_enter(asy->asy_excl_hi); - if (!(async->async_flags & (ASYNC_BREAK|ASYNC_DELAY|ASYNC_PROGRESS))) { - async->async_ocnt = 0; - async->async_flags &= ~ASYNC_BUSY; - async->async_timer = 0; - bp = async->async_xmitblk; - async->async_xmitblk = NULL; - mutex_exit(asy->asy_excl_hi); - if (bp != NULL) - freeb(bp); - /* - * Since this timer is running, we know that we're in exit(2). - * That means that the user can't possibly be waiting on any - * valid ioctl(2) completion anymore, and we should just flush - * everything. - */ - flushq(async->async_ttycommon.t_writeq, FLUSHALL); - cv_broadcast(&async->async_flags_cv); - } else { - async->async_flags &= ~ASYNC_PROGRESS; - async->async_timer = timeout(async_progress_check, async, - drv_usectohz(su_drain_check)); - mutex_exit(asy->asy_excl_hi); - } - mutex_exit(asy->asy_excl); -} - -/* - * Close routine. - */ -static int -asyclose(queue_t *q, int flag) -{ - struct asyncline *async; - struct asycom *asy; - int icr, lcr; - int nohupcl; - - -#ifdef DEBUG - if (asydebug & ASY_DEBUG_CLOSE) - printf("close\n"); -#endif - async = q->q_ptr; - ASSERT(async != NULL); - asy = async->async_common; - - /* get the nohupcl OBP property of this device */ - nohupcl = ddi_getprop(DDI_DEV_T_ANY, asy->asy_dip, DDI_PROP_DONTPASS, - "nohupcl", 0); - - mutex_enter(asy->asy_excl); - async->async_flags |= ASYNC_CLOSING; - - /* - * Turn off PPS handling early to avoid events occuring during - * close. Also reset the DCD edge monitoring bit. - */ - mutex_enter(asy->asy_excl_hi); - asy->asy_flags &= ~(ASY_PPS | ASY_PPS_EDGE); - mutex_exit(asy->asy_excl_hi); - - /* - * There are two flavors of break -- timed (M_BREAK or TCSBRK) and - * untimed (TIOCSBRK). For the timed case, these are enqueued on our - * write queue and there's a timer running, so we don't have to worry - * about them. For the untimed case, though, the user obviously made a - * mistake, because these are handled immediately. We'll terminate the - * break now and honor his implicit request by discarding the rest of - * the data. - */ - if (!(async->async_flags & ASYNC_BREAK)) { - mutex_enter(asy->asy_excl_hi); - lcr = INB(LCR); - if (lcr & SETBREAK) { - OUTB(LCR, (lcr & ~SETBREAK)); - } - mutex_exit(asy->asy_excl_hi); - if (lcr & SETBREAK) - goto nodrain; - } - - /* - * If the user told us not to delay the close ("non-blocking"), then - * don't bother trying to drain. - * - * If the user did M_STOP (ASYNC_STOPPED), there's no hope of ever - * getting an M_START (since these messages aren't enqueued), and the - * only other way to clear the stop condition is by loss of DCD, which - * would discard the queue data. Thus, we drop the output data if - * ASYNC_STOPPED is set. - */ - if ((flag & (FNDELAY|FNONBLOCK)) || - (async->async_flags & ASYNC_STOPPED)) { - goto nodrain; - } - - /* - * If there's any pending output, then we have to try to drain it. - * There are two main cases to be handled: - * - called by close(2): need to drain until done or until - * a signal is received. No timeout. - * - called by exit(2): need to drain while making progress - * or until a timeout occurs. No signals. - * - * If we can't rely on receiving a signal to get us out of a hung - * session, then we have to use a timer. In this case, we set a timer - * to check for progress in sending the output data -- all that we ask - * (at each interval) is that there's been some progress made. Since - * the interrupt routine grabs buffers from the write queue, we can't - * trust async_ocnt. Instead, we use a flag. - * - * Note that loss of carrier will cause the output queue to be flushed, - * and we'll wake up again and finish normally. - */ - if (!ddi_can_receive_sig() && su_drain_check != 0) { - async->async_flags &= ~ASYNC_PROGRESS; - async->async_timer = timeout(async_progress_check, async, - drv_usectohz(su_drain_check)); - } - - while (async->async_ocnt > 0 || - async->async_ttycommon.t_writeq->q_first != NULL || - (async->async_flags & (ASYNC_BUSY|ASYNC_BREAK|ASYNC_DELAY))) { - if (cv_wait_sig(&async->async_flags_cv, asy->asy_excl) == 0) - break; - } - if (async->async_timer != 0) { - (void) untimeout(async->async_timer); - async->async_timer = 0; - } - -nodrain: - mutex_enter(asy->asy_excl_hi); - - /* turn off the loopback mode */ - if ((async->async_dev != rconsdev) && - (async->async_dev != kbddev) && - (async->async_dev != stdindev)) { - OUTB(MCR, INB(MCR) & ~ ASY_LOOP); - } - - async->async_ocnt = 0; - if (async->async_xmitblk != NULL) - freeb(async->async_xmitblk); - async->async_xmitblk = NULL; - - /* - * If the "nohupcl" OBP property is set for this device, do - * not turn off DTR and RTS no matter what. Otherwise, if the - * line has HUPCL set or is incompletely opened, turn off DTR - * and RTS to fix the modem line. - */ - if (!nohupcl && ((async->async_ttycommon.t_cflag & HUPCL) || - (async->async_flags & ASYNC_WOPEN))) { - /* turn off DTR, RTS but NOT interrupt to 386 */ - OUTB(MCR, OUT2); - mutex_exit(asy->asy_excl_hi); - /* - * Don't let an interrupt in the middle of close - * bounce us back to the top; just continue closing - * as if nothing had happened. - */ - if (cv_wait_sig(&lbolt_cv, asy->asy_excl) == 0) - goto out; - mutex_enter(asy->asy_excl_hi); - } - - /* - * If nobody's using it now, turn off receiver interrupts. - */ - if ((async->async_flags & (ASYNC_WOPEN|ASYNC_ISOPEN)) == 0) { - icr = INB(ICR); - OUTB(ICR, (icr & ~RIEN)); - } - mutex_exit(asy->asy_excl_hi); -out: - /* - * Clear out device state. - */ - async->async_flags = 0; - ttycommon_close(&async->async_ttycommon); - cv_broadcast(&async->async_flags_cv); - - /* - * Clear ASY_DOINGSOFT and ASY_NEEDSOFT in case we were in - * async_softint or an interrupt was pending when the process - * using the port exited. - */ - asy->asy_flags &= ~ASY_DOINGSOFT & ~ASY_NEEDSOFT; - - /* - * Cancel outstanding "bufcall" request. - */ - if (async->async_wbufcid) { - unbufcall(async->async_wbufcid); - async->async_wbufcid = 0; - } - - /* - * If inperim is true, it means the port is closing while there's - * a pending software interrupt. async_flags has been zeroed out, - * so this instance of leaveq() needs to be called before we call - * qprocsoff() to disable services on the q. If inperim is false, - * leaveq() has already been called or we're not in a perimeter. - */ - if (asy->inperim == B_TRUE) { - asy->inperim = B_FALSE; - mutex_exit(asy->asy_excl); - leaveq(q); - } else { - mutex_exit(asy->asy_excl); - } - - /* Note that qprocsoff can't be done until after interrupts are off */ - qprocsoff(q); - q->q_ptr = WR(q)->q_ptr = NULL; - async->async_ttycommon.t_readq = NULL; - async->async_ttycommon.t_writeq = NULL; - - return (0); -} - -/* - * Checks to see if the serial port is still transmitting - * characters. It returns true when there are characters - * queued to transmit, when the holding register contains - * a byte, or when the shifting register still contains - * data to send. - * - */ -static boolean_t -asy_isbusy(struct asycom *asy) -{ - struct asyncline *async; - -#ifdef DEBUG - if (asydebug & ASY_DEBUG_EOT) - printf("isbusy\n"); -#endif - async = (struct asyncline *)asy->asy_priv; - ASSERT(mutex_owned(asy->asy_excl)); - ASSERT(mutex_owned(asy->asy_excl_hi)); - return ((async->async_ocnt > 0) || - ((INB(LSR) & XSRE) == 0)); -} - -/* - * Program the ASY port. Most of the async operation is based on the values - * of 'c_iflag' and 'c_cflag'. - */ -static int -asy_program(struct asycom *asy, int mode) -{ - struct asyncline *async; - int baudrate, c_flag; - int icr, lcr; - int ocflags; - int error = 0; - - ASSERT(mutex_owned(asy->asy_excl)); - ASSERT(mutex_owned(asy->asy_excl_hi)); - -#ifdef DEBUG - if (asydebug & ASY_DEBUG_PROCS) - printf("program\n"); -#endif - async = (struct asyncline *)asy->asy_priv; - - baudrate = async->async_ttycommon.t_cflag & CBAUD; - if (async->async_ttycommon.t_cflag & CBAUDEXT) - baudrate += 16; - - /* Limit baudrate so it can't index out of baudtable */ - if (baudrate >= N_SU_SPEEDS) baudrate = B9600; - - /* - * If baud rate requested is greater than the speed cap - * or is an unsupported baud rate then reset t_cflag baud - * to the last valid baud rate. If this is the initial - * pass through asy_program then set it to 9600. - */ - if (((baudrate > 0) && (asyspdtab[baudrate] == 0)) || - (baudtable[baudrate] > asy->asy_speed_cap)) { - async->async_ttycommon.t_cflag &= ~CBAUD & ~CBAUDEXT & - ~CIBAUD & ~CIBAUDEXT; - if (mode == ASY_INIT) { - async->async_ttycommon.t_cflag |= B9600; - baudrate = B9600; - } else { - async->async_ttycommon.t_cflag |= - (asy->asy_ocflags & (CBAUD | CBAUDEXT | - CIBAUD | CIBAUDEXT)); - } - error = EINVAL; - goto end; - } - - /* set the baud rate */ - if (async->async_ttycommon.t_cflag & (CIBAUD|CIBAUDEXT)) { - async->async_ttycommon.t_cflag &= ~(CIBAUD); - if (baudrate > CBAUD) { - async->async_ttycommon.t_cflag |= CIBAUDEXT; - async->async_ttycommon.t_cflag |= - (((baudrate - CBAUD -1)<< IBSHIFT) & CIBAUD); - } else { - async->async_ttycommon.t_cflag &= ~CIBAUDEXT; - async->async_ttycommon.t_cflag |= - ((baudrate << IBSHIFT) & CIBAUD); - } - } - - c_flag = async->async_ttycommon.t_cflag & - (CLOCAL | CREAD | CSTOPB | CSIZE | PARENB | PARODD | CBAUD | - CBAUDEXT | CIBAUD | CIBAUDEXT); - OUTB(ICR, 0); /* disable interrupts */ - - ocflags = asy->asy_ocflags; - - /* flush/reset the status registers */ - if (mode == ASY_INIT) { - (void) INB(DAT); - (void) INB(ISR); - (void) INB(LSR); - (void) INB(MSR); - } - - if (ocflags != (c_flag & ~CLOCAL) || mode == ASY_INIT) { - /* Set line control */ - lcr = INB(LCR); - lcr &= ~(WLS0|WLS1|STB|PEN|EPS); - - if (c_flag & CSTOPB) - lcr |= STB; /* 2 stop bits */ - - if (c_flag & PARENB) - lcr |= PEN; - - if ((c_flag & PARODD) == 0) - lcr |= EPS; - - switch (c_flag & CSIZE) { - case CS5: - lcr |= BITS5; - break; - case CS6: - lcr |= BITS6; - break; - case CS7: - lcr |= BITS7; - break; - case CS8: - lcr |= BITS8; - break; - } - - /* set the baud rate when the rate is NOT B0 */ - if (baudrate != 0) { - OUTB(LCR, DLAB); - OUTB(DAT, (asyspdtab[baudrate] * - asy->asy_baud_divisor_factor) & 0xff); - OUTB(ICR, ((asyspdtab[baudrate] * - asy->asy_baud_divisor_factor) >> 8) & 0xff); - } - /* set the line control modes */ - OUTB(LCR, lcr); - - /* - * if transitioning from CREAD off to CREAD on, - * flush the FIFO buffer if we have one. - */ - if ((ocflags & CREAD) == 0 && (c_flag & CREAD)) { - if (asy->asy_use_fifo == FIFO_ON) { - OUTB(FIFOR, FIFO_ON | FIFODMA | FIFORXFLSH | - (asy->asy_trig_level & 0xff)); - } - } - - /* remember the new cflags */ - asy->asy_ocflags = c_flag & ~CLOCAL; - } - - /* whether or not CLOCAL is set, modify the modem control lines */ - if (baudrate == 0) - /* B0 has been issued, lower DTR */ - OUTB(MCR, RTS|OUT2); - else - /* raise DTR */ - OUTB(MCR, DTR|RTS|OUT2); - - /* - * Call the modem status interrupt handler to check for the carrier - * in case CLOCAL was turned off after the carrier came on. - * (Note: Modem status interrupt is not enabled if CLOCAL is ON.) - */ - async_msint(asy); - - /* Set interrupt control */ - if ((c_flag & CLOCAL) && !(async->async_ttycommon.t_cflag & CRTSCTS)) - /* - * direct-wired line ignores DCD, so we don't enable modem - * status interrupts. - */ - icr = (TIEN | SIEN); - else - icr = (TIEN | SIEN | MIEN); - - if (c_flag & CREAD) - icr |= RIEN; - - OUTB(ICR, icr); -end: - return (error); -} - -/* - * asyintr() is the High Level Interrupt Handler. - * - * There are four different interrupt types indexed by ISR register values: - * 0: modem - * 1: Tx holding register is empty, ready for next char - * 2: Rx register now holds a char to be picked up - * 3: error or break on line - * This routine checks the Bit 0 (interrupt-not-pending) to determine if - * the interrupt is from this port. - */ -uint_t -asyintr(caddr_t argasy) -{ - struct asycom *asy = (struct asycom *)argasy; - struct asyncline *async; - int ret_status = DDI_INTR_UNCLAIMED; - uchar_t interrupt_id, lsr; - - interrupt_id = INB(ISR) & 0x0F; - async = (struct asyncline *)asy->asy_priv; - if ((async == NULL) || - !(async->async_flags & (ASYNC_ISOPEN|ASYNC_WOPEN))) { - if (interrupt_id & NOINTERRUPT) { - return (DDI_INTR_UNCLAIMED); - } else { - lsr = INB(LSR); - if ((lsr & BRKDET) && - ((abort_enable == KIOCABORTENABLE) && - (async->async_dev == rconsdev))) - abort_sequence_enter((char *)NULL); - else { - /* reset line status */ - (void) INB(LSR); - /* discard any data */ - (void) INB(DAT); - /* reset modem status */ - (void) INB(MSR); - return (DDI_INTR_CLAIMED); - } - } - } - /* - * Spurious interrupts happen in this driver - * because of the transmission on serial port not handled - * properly. - * - * The reasons for Spurious interrupts are: - * 1. There is a path in async_nstart which transmits - * characters without going through interrupt services routine - * which causes spurious interrupts to happen. - * 2. In the async_txint more than one character is sent - * in one interrupt service. - * 3. In async_rxint more than one characters are received in - * in one interrupt service. - * - * Hence we have flags to indicate that such scenerio has happened. - * and claim only such interrupts and others we donot claim it - * as it could be a indicator of some hardware problem. - * - */ - if (interrupt_id & NOINTERRUPT) { - mutex_enter(asy->asy_excl_hi); - if ((asy->asy_xmit_count > 1) || - (asy->asy_out_of_band_xmit > 0) || - (asy->asy_rx_count > 1)) { - asy->asy_xmit_count = 0; - asy->asy_out_of_band_xmit = 0; - asy->asy_rx_count = 0; - mutex_exit(asy->asy_excl_hi); - return (DDI_INTR_CLAIMED); - } else { - mutex_exit(asy->asy_excl_hi); - return (DDI_INTR_UNCLAIMED); - } - } - ret_status = DDI_INTR_CLAIMED; - mutex_enter(asy->asy_excl_hi); - if (asy->asy_hwtype == ASY82510) - OUTB(ISR, 0x00); /* set bank 0 */ - -#ifdef DEBUG - if (asydebug & ASY_DEBUG_INTR) - prom_printf("l"); -#endif - lsr = INB(LSR); - switch (interrupt_id) { - case RxRDY: - case RSTATUS: - case FFTMOUT: - /* receiver interrupt or receiver errors */ - async_rxint(asy, lsr); - break; - case TxRDY: - /* transmit interrupt */ - async_txint(asy, lsr); - break; - case MSTATUS: - /* modem status interrupt */ - async_msint(asy); - break; - } - mutex_exit(asy->asy_excl_hi); - return (ret_status); -} - -/* - * Transmitter interrupt service routine. - * If there is more data to transmit in the current pseudo-DMA block, - * send the next character if output is not stopped or draining. - * Otherwise, queue up a soft interrupt. - * - * XXX - Needs review for HW FIFOs. - */ -static void -async_txint(struct asycom *asy, uchar_t lsr) -{ - struct asyncline *async = (struct asyncline *)asy->asy_priv; - int fifo_len; - int xmit_progress; - - asycheckflowcontrol_hw(asy); - - /* - * If ASYNC_BREAK has been set, return to asyintr()'s context to - * claim the interrupt without performing any action. - */ - if (async->async_flags & ASYNC_BREAK) - return; - - fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */ - - /* - * Check for flow control and do the needed action. - */ - if (asycheckflowcontrol_sw(asy)) { - return; - } - - if (async->async_ocnt > 0 && - !(async->async_flags & (ASYNC_HW_OUT_FLW|ASYNC_STOPPED))) { - xmit_progress = 0; - while (fifo_len > 0 && async->async_ocnt > 0) { - if (lsr & XHRE) { - OUTB(DAT, *async->async_optr++); - fifo_len--; - async->async_ocnt--; - xmit_progress++; - } - /* - * Reading the lsr, (moved reading at the end of - * while loop) as already we have read once at - * the beginning of interrupt service - */ - lsr = INB(LSR); - } - asy->asy_xmit_count = xmit_progress; - if (xmit_progress > 0) - async->async_flags |= ASYNC_PROGRESS; - } - - if (fifo_len == 0) { - return; - } - - - ASYSETSOFT(asy); -} - -/* - * Receiver interrupt: RxRDY interrupt, FIFO timeout interrupt or receive - * error interrupt. - * Try to put the character into the circular buffer for this line; if it - * overflows, indicate a circular buffer overrun. If this port is always - * to be serviced immediately, or the character is a STOP character, or - * more than 15 characters have arrived, queue up a soft interrupt to - * drain the circular buffer. - * XXX - needs review for hw FIFOs support. - */ - -static void -async_rxint(struct asycom *asy, uchar_t lsr) -{ - struct asyncline *async = (struct asyncline *)asy->asy_priv; - uchar_t c = 0; - uint_t s = 0, needsoft = 0; - register tty_common_t *tp; - - tp = &async->async_ttycommon; - if (!(tp->t_cflag & CREAD)) { - if (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) { - (void) (INB(DAT) & 0xff); - } - return; /* line is not open for read? */ - } - asy->asy_rx_count = 0; - while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) { - c = 0; - s = 0; - asy->asy_rx_count++; - if (lsr & RCA) { - c = INB(DAT) & 0xff; - /* - * Even a single character is received - * we need Soft interrupt to pass it to - * higher layers. - */ - needsoft = 1; - } - - /* Check for character break sequence */ - if ((abort_enable == KIOCABORTALTERNATE) && - (async->async_dev == rconsdev)) { - if (abort_charseq_recognize(c)) - abort_sequence_enter((char *)NULL); - } - - /* Handle framing errors */ - if (lsr & (PARERR|FRMERR|BRKDET|OVRRUN)) { - if (lsr & PARERR) { - if (tp->t_iflag & INPCK) /* parity enabled */ - s |= PERROR; - } - if (lsr & (FRMERR|BRKDET)) - s |= FRERROR; - if (lsr & OVRRUN) { - async->async_hw_overrun = 1; - s |= OVERRUN; - } - } - - if (s == 0) - if ((tp->t_iflag & PARMRK) && - !(tp->t_iflag & (IGNPAR|ISTRIP)) && - (c == 0377)) - if (RING_POK(async, 2)) { - RING_PUT(async, 0377); - RING_PUT(async, c); - } else - async->async_sw_overrun = 1; - else - if (RING_POK(async, 1)) - RING_PUT(async, c); - else - async->async_sw_overrun = 1; - else - if (s & FRERROR) { /* Handle framing errors */ - if (c == 0) { - /* Look for break on kbd, stdin, or rconsdev */ - if ((async->async_dev == kbddev) || - ((async->async_dev == rconsdev) || - (async->async_dev == stdindev)) && - (abort_enable != - KIOCABORTALTERNATE)) - abort_sequence_enter((char *)0); - else - async->async_break++; - } else { - if (RING_POK(async, 1)) - RING_MARK(async, c, s); - else - async->async_sw_overrun = 1; - } - } else { /* Parity errors handled by ldterm */ - if (RING_POK(async, 1)) - RING_MARK(async, c, s); - else - async->async_sw_overrun = 1; - } - lsr = INB(LSR); - if (asy->asy_rx_count > 16) break; - } - /* Check whether there is a request for hw/sw inbound/input flow ctrl */ - if ((async->async_ttycommon.t_cflag & CRTSXOFF) || - (async->async_ttycommon.t_iflag & IXOFF)) - if ((int)(RING_CNT(async)) > (RINGSIZE * 3)/4) { -#ifdef DEBUG - if (asydebug & ASY_DEBUG_HFLOW) - printf("asy%d: hardware flow stop input.\n", - UNIT(async->async_dev)); -#endif - async->async_flags |= ASYNC_HW_IN_FLOW; - async->async_flowc = async->async_stopc; - async->async_ringbuf_overflow = 1; - } - - if ((async->async_flags & ASYNC_SERVICEIMM) || needsoft || - (RING_FRAC(async)) || (async->async_polltid == 0)) - ASYSETSOFT(asy); /* need a soft interrupt */ -} - -/* - * Interrupt on port: handle PPS event. This function is only called - * for a port on which PPS event handling has been enabled. - */ -static void -asy_ppsevent(struct asycom *asy, int msr) -{ - if (asy->asy_flags & ASY_PPS_EDGE) { - /* Have seen leading edge, now look for and record drop */ - if ((msr & DCD) == 0) - asy->asy_flags &= ~ASY_PPS_EDGE; - /* - * Waiting for leading edge, look for rise; stamp event and - * calibrate kernel clock. - */ - } else if (msr & DCD) { - /* - * This code captures a timestamp at the designated - * transition of the PPS signal (DCD asserted). The - * code provides a pointer to the timestamp, as well - * as the hardware counter value at the capture. - * - * Note: the kernel has nano based time values while - * NTP requires micro based, an in-line fast algorithm - * to convert nsec to usec is used here -- see hrt2ts() - * in common/os/timers.c for a full description. - */ - struct timeval *tvp = &asy_ppsev.tv; - timestruc_t ts; - long nsec, usec; - - asy->asy_flags |= ASY_PPS_EDGE; - gethrestime(&ts); - nsec = ts.tv_nsec; - usec = nsec + (nsec >> 2); - usec = nsec + (usec >> 1); - usec = nsec + (usec >> 2); - usec = nsec + (usec >> 4); - usec = nsec - (usec >> 3); - usec = nsec + (usec >> 2); - usec = nsec + (usec >> 3); - usec = nsec + (usec >> 4); - usec = nsec + (usec >> 1); - usec = nsec + (usec >> 6); - tvp->tv_usec = usec >> 10; - tvp->tv_sec = ts.tv_sec; - - ++asy_ppsev.serial; - - /* - * Because the kernel keeps a high-resolution time, - * pass the current highres timestamp in tvp and zero - * in usec. - */ - ddi_hardpps(tvp, 0); - } -} - -/* - * Modem status interrupt. - * - * (Note: It is assumed that the MSR hasn't been read by asyintr().) - */ - -static void -async_msint(struct asycom *asy) -{ - struct asyncline *async = (struct asyncline *)asy->asy_priv; - int msr; - - msr = INB(MSR); /* this resets the interrupt */ - asy->asy_cached_msr = msr; -#ifdef DEBUG - if (asydebug & ASY_DEBUG_STATE) { - printf(" transition: %3s %3s %3s %3s\n" - "current state: %3s %3s %3s %3s\n", - (msr & DCTS) ? "CTS" : " ", - (msr & DDSR) ? "DSR" : " ", - (msr & DRI) ? "RI " : " ", - (msr & DDCD) ? "DCD" : " ", - (msr & CTS) ? "CTS" : " ", - (msr & DSR) ? "DSR" : " ", - (msr & RI) ? "RI " : " ", - (msr & DCD) ? "DCD" : " "); - } -#endif - if (async->async_ttycommon.t_cflag & CRTSCTS && !(msr & CTS)) { -#ifdef DEBUG - if (asydebug & ASY_DEBUG_HFLOW) - printf("asy%d: hflow start\n", - UNIT(async->async_dev)); -#endif - async->async_flags |= ASYNC_HW_OUT_FLW; - } - if (asy->asy_hwtype == ASY82510) - OUTB(MSR, (msr & 0xF0)); - - /* Handle PPS event */ - if (asy->asy_flags & ASY_PPS) - asy_ppsevent(asy, msr); - - async->async_ext++; - ASYSETSOFT(asy); -} - -/* - * Handle a second-stage interrupt. - */ -uint_t -asysoftintr(caddr_t intarg) -{ - struct asycom *asy = (struct asycom *)intarg; - struct asyncline *async; - int rv; - int cc; - /* - * Test and clear soft interrupt. - */ - mutex_enter(asy->asy_soft_lock); -#ifdef DEBUG - if (asydebug & ASY_DEBUG_PROCS) - printf("softintr\n"); -#endif - rv = asy->asysoftpend; - if (rv != 0) - asy->asysoftpend = 0; - mutex_exit(asy->asy_soft_lock); - - if (rv) { - if (asy->asy_priv == NULL) - return (rv); - async = (struct asyncline *)asy->asy_priv; - mutex_enter(asy->asy_excl_hi); - if (asy->asy_flags & ASY_NEEDSOFT) { - asy->asy_flags &= ~ASY_NEEDSOFT; - mutex_exit(asy->asy_excl_hi); - (void) async_softint(asy); - mutex_enter(asy->asy_excl_hi); - } - /* - * There are some instances where the softintr is not - * scheduled and hence not called. It so happened that makes - * the last few characters to be stuck in ringbuffer. - * Hence, call once again the handler so that the last few - * characters are cleared. - */ - cc = RING_CNT(async); - mutex_exit(asy->asy_excl_hi); - if (cc > 0) { - (void) async_softint(asy); - } - } - return (rv); -} - -/* - * Handle a software interrupt. - */ -static int -async_softint(struct asycom *asy) -{ - struct asyncline *async = (struct asyncline *)asy->asy_priv; - uint_t cc; - mblk_t *bp; - queue_t *q; - uchar_t val; - uchar_t c; - tty_common_t *tp; - -#ifdef DEBUG - if (asydebug & ASY_DEBUG_PROCS) - printf("process\n"); -#endif - mutex_enter(asy->asy_excl); - if (asy->asy_flags & ASY_DOINGSOFT) { - mutex_exit(asy->asy_excl); - return (0); - } - tp = &async->async_ttycommon; - q = tp->t_readq; - if (q != NULL) { - mutex_exit(asy->asy_excl); - enterq(q); - mutex_enter(asy->asy_excl); - } - mutex_enter(asy->asy_excl_hi); - asy->asy_flags |= ASY_DOINGSOFT; - - if (INB(ICR) & MIEN) - val = asy->asy_cached_msr & 0xFF; - else - val = INB(MSR) & 0xFF; - - if (async->async_ttycommon.t_cflag & CRTSCTS) { - if ((val & CTS) && (async->async_flags & ASYNC_HW_OUT_FLW)) { -#ifdef DEBUG - if (asydebug & ASY_DEBUG_HFLOW) - printf("asy%d: hflow start\n", - UNIT(async->async_dev)); -#endif - async->async_flags &= ~ASYNC_HW_OUT_FLW; - mutex_exit(asy->asy_excl_hi); - if (async->async_ocnt > 0) { - mutex_enter(asy->asy_excl_hi); - async_resume(async); - mutex_exit(asy->asy_excl_hi); - } else { - async_start(async); - } - mutex_enter(asy->asy_excl_hi); - } - } - if (async->async_ext) { - async->async_ext = 0; - /* check for carrier up */ - if ((val & DCD) || (tp->t_flags & TS_SOFTCAR)) { - /* carrier present */ - if ((async->async_flags & ASYNC_CARR_ON) == 0) { - async->async_flags |= ASYNC_CARR_ON; - mutex_exit(asy->asy_excl_hi); - mutex_exit(asy->asy_excl); - if (async->async_flags & ASYNC_ISOPEN) - (void) putctl(q, M_UNHANGUP); - cv_broadcast(&async->async_flags_cv); - mutex_enter(asy->asy_excl); - mutex_enter(asy->asy_excl_hi); - } - } else { - if ((async->async_flags & ASYNC_CARR_ON) && - !(tp->t_cflag & CLOCAL)) { - int flushflag; - - /* - * Carrier went away. - * Drop DTR, abort any output in - * progress, indicate that output is - * not stopped, and send a hangup - * notification upstream. - * - * If we're in the midst of close, then flush - * everything. Don't leave stale ioctls lying - * about. - */ - val = INB(MCR); - OUTB(MCR, (val & ~DTR)); - flushflag = (async->async_flags & - ASYNC_CLOSING) ? FLUSHALL : FLUSHDATA; - flushq(tp->t_writeq, flushflag); - if (async->async_xmitblk != NULL) { - freeb(async->async_xmitblk); - async->async_xmitblk = NULL; - } - if (async->async_flags & ASYNC_BUSY) { - async->async_ocnt = 0; - async->async_flags &= ~ASYNC_BUSY; - } - async->async_flags &= ~ASYNC_STOPPED; - if (async->async_flags & ASYNC_ISOPEN) { - mutex_exit(asy->asy_excl_hi); - mutex_exit(asy->asy_excl); - (void) putctl(q, M_HANGUP); - mutex_enter(asy->asy_excl); - mutex_enter(asy->asy_excl_hi); - } - } - async->async_flags &= ~ASYNC_CARR_ON; - mutex_exit(asy->asy_excl_hi); - cv_broadcast(&async->async_flags_cv); - mutex_enter(asy->asy_excl_hi); - } - } - - /* - * If data has been added to the circular buffer, remove - * it from the buffer, and send it up the stream if there's - * somebody listening. Try to do it 16 bytes at a time. If we - * have more than 16 bytes to move, move 16 byte chunks and - * leave the rest for next time around (maybe it will grow). - */ - if (!(async->async_flags & ASYNC_ISOPEN)) { - RING_INIT(async); - goto rv; - } - if ((cc = RING_CNT(async)) == 0) { - goto rv; - } - mutex_exit(asy->asy_excl_hi); - - if (!canput(q)) { - if ((async->async_flags & ASYNC_HW_IN_FLOW) == 0) { -#ifdef DEBUG - if (!(asydebug & ASY_DEBUG_HFLOW)) { - printf("asy%d: hflow stop input.\n", - UNIT(async->async_dev)); - if (canputnext(q)) - printf("asy%d: next queue is " - "ready\n", - UNIT(async->async_dev)); - } -#endif - mutex_enter(asy->asy_excl_hi); - async->async_flags |= ASYNC_HW_IN_FLOW; - async->async_flowc = async->async_stopc; - } else mutex_enter(asy->asy_excl_hi); - goto rv; - } - - if (async->async_ringbuf_overflow) { - if ((async->async_flags & ASYNC_HW_IN_FLOW) && - ((int)(RING_CNT(async)) < (RINGSIZE/4))) { -#ifdef DEBUG - if (asydebug & ASY_DEBUG_HFLOW) - printf("asy%d: hflow start input.\n", - UNIT(async->async_dev)); -#endif - mutex_enter(asy->asy_excl_hi); - async->async_flags &= ~ASYNC_HW_IN_FLOW; - async->async_flowc = async->async_startc; - async->async_ringbuf_overflow = 0; - goto rv; - } - } -#ifdef DEBUG - if (asydebug & ASY_DEBUG_INPUT) - printf("asy%d: %d char(s) in queue.\n", - UNIT(async->async_dev), cc); -#endif - /* - * Before you pull the characters from the RING BUF - * Check whether you can put into the queue again - */ - if ((!canputnext(q)) || (!canput(q))) { - mutex_enter(asy->asy_excl_hi); - if ((async->async_flags & ASYNC_HW_IN_FLOW) == 0) { - async->async_flags |= ASYNC_HW_IN_FLOW; - async->async_flowc = async->async_stopc; - async->async_queue_full = 1; - } - goto rv; - } - mutex_enter(asy->asy_excl_hi); - if (async->async_queue_full) { - /* - * Last time the Stream queue didnot allow - * now it allows so, relax, the flow control - */ - if (async->async_flags & ASYNC_HW_IN_FLOW) { - async->async_flags &= ~ASYNC_HW_IN_FLOW; - async->async_queue_full = 0; - async->async_flowc = async->async_startc; - goto rv; - } else - async->async_queue_full = 0; - } - mutex_exit(asy->asy_excl_hi); - if (!(bp = allocb(cc, BPRI_MED))) { - ttycommon_qfull(&async->async_ttycommon, q); - mutex_enter(asy->asy_excl_hi); - goto rv; - } - mutex_enter(asy->asy_excl_hi); - do { - if (RING_ERR(async, S_ERRORS)) { - RING_UNMARK(async); - c = RING_GET(async); - break; - } else { - *bp->b_wptr++ = RING_GET(async); - } - } while (--cc); - - mutex_exit(asy->asy_excl_hi); - mutex_exit(asy->asy_excl); - if (bp->b_wptr > bp->b_rptr) { - if (!canputnext(q)) { - if (!canput(q)) { - /* - * Even after taking all precautions that - * Still we are unable to queue, then we - * cannot do anything, just drop the block - */ - cmn_err(CE_NOTE, - "su%d: local queue full\n", - UNIT(async->async_dev)); - freemsg(bp); - mutex_enter(asy->asy_excl_hi); - if ((async->async_flags & - ASYNC_HW_IN_FLOW) == 0) { - async->async_flags |= - ASYNC_HW_IN_FLOW; - async->async_flowc = - async->async_stopc; - async->async_queue_full = 1; - } - mutex_exit(asy->asy_excl_hi); - } else { - (void) putq(q, bp); - } - } else { - putnext(q, bp); - } - } else { - freemsg(bp); - } - /* - * If we have a parity error, then send - * up an M_BREAK with the "bad" - * character as an argument. Let ldterm - * figure out what to do with the error. - */ - if (cc) - (void) putctl1(q, M_BREAK, c); - mutex_enter(asy->asy_excl); - mutex_enter(asy->asy_excl_hi); -rv: - /* - * If a transmission has finished, indicate that it's finished, - * and start that line up again. - */ - if (async->async_break) { - async->async_break = 0; - if (async->async_flags & ASYNC_ISOPEN) { - mutex_exit(asy->asy_excl_hi); - mutex_exit(asy->asy_excl); - (void) putctl(q, M_BREAK); - mutex_enter(asy->asy_excl); - mutex_enter(asy->asy_excl_hi); - } - } - if ((async->async_ocnt <= 0 && (async->async_flags & ASYNC_BUSY)) || - async->async_queue_full) { - async->async_flags &= ~ASYNC_BUSY; - mutex_exit(asy->asy_excl_hi); - if (async->async_xmitblk) - freeb(async->async_xmitblk); - async->async_xmitblk = NULL; - if (async->async_flags & ASYNC_ISOPEN) { - asy->inperim = B_TRUE; - mutex_exit(asy->asy_excl); - enterq(async->async_ttycommon.t_writeq); - mutex_enter(asy->asy_excl); - } - async_start(async); - /* - * We need to check for inperim and ISOPEN due to - * multi-threading implications; it's possible to close the - * port and nullify async_flags while completing the software - * interrupt. If the port is closed, leaveq() will have already - * been called. We don't want to call it twice. - */ - if ((asy->inperim) && (async->async_flags & ASYNC_ISOPEN)) { - mutex_exit(asy->asy_excl); - leaveq(async->async_ttycommon.t_writeq); - mutex_enter(asy->asy_excl); - asy->inperim = B_FALSE; - } - if (!(async->async_flags & ASYNC_BUSY)) - cv_broadcast(&async->async_flags_cv); - mutex_enter(asy->asy_excl_hi); - } - /* - * A note about these overrun bits: all they do is *tell* someone - * about an error- They do not track multiple errors. In fact, - * you could consider them latched register bits if you like. - * We are only interested in printing the error message once for - * any cluster of overrun errrors. - */ - if (async->async_hw_overrun) { - if (async->async_flags & ASYNC_ISOPEN) { - if (su_log > 0) { - mutex_exit(asy->asy_excl_hi); - mutex_exit(asy->asy_excl); - cmn_err(CE_NOTE, "su%d: silo overflow\n", - UNIT(async->async_dev)); - mutex_enter(asy->asy_excl); - mutex_enter(asy->asy_excl_hi); - } - INC64_KSTAT(asy, siloover); - } - async->async_hw_overrun = 0; - } - if (async->async_sw_overrun) { - if (async->async_flags & ASYNC_ISOPEN) { - if (su_log > 0) { - mutex_exit(asy->asy_excl_hi); - mutex_exit(asy->asy_excl); - cmn_err(CE_NOTE, "su%d: ring buffer overflow\n", - UNIT(async->async_dev)); - mutex_enter(asy->asy_excl); - mutex_enter(asy->asy_excl_hi); - } - INC64_KSTAT(asy, ringover); - } - async->async_sw_overrun = 0; - } - asy->asy_flags &= ~ASY_DOINGSOFT; - mutex_exit(asy->asy_excl_hi); - mutex_exit(asy->asy_excl); - if (q != NULL) - leaveq(q); - return (0); -} - -/* - * Restart output on a line after a delay or break timer expired. - */ -static void -async_restart(void *arg) -{ - struct asyncline *async = arg; - struct asycom *asy = async->async_common; - queue_t *q; - uchar_t lcr; - - /* - * If break timer expired, turn off the break bit. - */ -#ifdef DEBUG - if (asydebug & ASY_DEBUG_PROCS) - printf("restart\n"); -#endif - mutex_enter(asy->asy_excl); - if (async->async_flags & ASYNC_BREAK) { - mutex_enter(asy->asy_excl_hi); - lcr = INB(LCR); - OUTB(LCR, (lcr & ~SETBREAK)); - mutex_exit(asy->asy_excl_hi); - } - async->async_flags &= ~(ASYNC_DELAY|ASYNC_BREAK|ASYNC_DRAINING); - if ((q = async->async_ttycommon.t_writeq) != NULL) { - mutex_exit(asy->asy_excl); - enterq(q); - mutex_enter(asy->asy_excl); - } - async_start(async); - mutex_exit(asy->asy_excl); - if (q != NULL) - leaveq(q); - - /* cleared break or delay flag; may have made some output progress */ - cv_broadcast(&async->async_flags_cv); -} - -static void -async_start(struct asyncline *async) -{ - async_nstart(async, 0); -} - -/* - * Start output on a line, unless it's busy, frozen, or otherwise. - */ -static void -async_nstart(struct asyncline *async, int mode) -{ - register struct asycom *asy = async->async_common; - register int cc; - register queue_t *q; - mblk_t *bp, *nbp; - uchar_t *xmit_addr; - uchar_t val; - int fifo_len = 1; - int xmit_progress; - -#ifdef DEBUG - if (asydebug & ASY_DEBUG_PROCS) - printf("start\n"); -#endif - if (asy->asy_use_fifo == FIFO_ON) - fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */ - - ASSERT(mutex_owned(asy->asy_excl)); - mutex_enter(asy->asy_excl_hi); - asycheckflowcontrol_hw(asy); - - /* - * If the chip is busy (i.e., we're waiting for a break timeout - * to expire, or for the current transmission to finish, or for - * output to finish draining from chip), don't grab anything new. - */ - if (async->async_flags & (ASYNC_BREAK|ASYNC_BUSY|ASYNC_DRAINING)) { - mutex_exit(asy->asy_excl_hi); -#ifdef DEBUG - if (mode && asydebug & ASY_DEBUG_CLOSE) - printf("asy%d: start %s.\n", - UNIT(async->async_dev), - async->async_flags & ASYNC_BREAK - ? "break" : "busy"); -#endif - return; - } - - /* - * If we have a flow-control character to transmit, do it now. - */ - if (asycheckflowcontrol_sw(asy)) { - mutex_exit(asy->asy_excl_hi); - return; - } - mutex_exit(asy->asy_excl_hi); - /* - * If we're waiting for a delay timeout to expire, don't grab - * anything new. - */ - if (async->async_flags & ASYNC_DELAY) { -#ifdef DEBUG - if (mode && asydebug & ASY_DEBUG_CLOSE) - printf("asy%d: start ASYNC_DELAY.\n", - UNIT(async->async_dev)); -#endif - return; - } - - if ((q = async->async_ttycommon.t_writeq) == NULL) { -#ifdef DEBUG - if (mode && asydebug & ASY_DEBUG_CLOSE) - printf("asy%d: start writeq is null.\n", - UNIT(async->async_dev)); -#endif - return; /* not attached to a stream */ - } - - for (;;) { - if ((bp = getq(q)) == NULL) - return; /* no data to transmit */ - - /* - * We have a message block to work on. - * Check whether it's a break, a delay, or an ioctl (the latter - * occurs if the ioctl in question was waiting for the output - * to drain). If it's one of those, process it immediately. - */ - switch (bp->b_datap->db_type) { - - case M_BREAK: - /* - * Set the break bit, and arrange for "async_restart" - * to be called in 1/4 second; it will turn the - * break bit off, and call "async_start" to grab - * the next message. - */ - mutex_enter(asy->asy_excl_hi); - val = INB(LCR); - OUTB(LCR, (val | SETBREAK)); - mutex_exit(asy->asy_excl_hi); - async->async_flags |= ASYNC_BREAK; - (void) timeout(async_restart, async, hz / 4); - freemsg(bp); - return; /* wait for this to finish */ - - case M_DELAY: - /* - * Arrange for "async_restart" to be called when the - * delay expires; it will turn ASYNC_DELAY off, - * and call "async_start" to grab the next message. - */ - (void) timeout(async_restart, async, - (clock_t)(*(unsigned char *)bp->b_rptr + 6)); - async->async_flags |= ASYNC_DELAY; - freemsg(bp); - return; /* wait for this to finish */ - - case M_IOCTL: - /* - * This ioctl needs to wait for the output ahead of - * it to drain. Try to do it, and then either - * redo the ioctl at a later time or grab the next - * message after it. - */ - - mutex_enter(asy->asy_excl_hi); - if (asy_isbusy(asy)) { - /* - * Get the divisor by calculating the rate - */ - unsigned int rate; - - mutex_exit(asy->asy_excl_hi); - rate = async->async_ttycommon.t_cflag & CBAUD; - if (async->async_ttycommon.t_cflag & CBAUDEXT) - rate += 16; - if (rate >= N_SU_SPEEDS || rate == B0) { - rate = B9600; - } - - /* - * We need to do a callback as the port will - * be set to drain - */ - async->async_flags |= ASYNC_DRAINING; - - /* - * Put the message we just processed back onto - * the end of the queue - */ - if (putq(q, bp) == 0) - freemsg(bp); - - /* - * We need to delay until the TSR and THR - * have been exhausted. We base the delay on - * the amount of time it takes to transmit - * 2 chars at the current baud rate in - * microseconds. - * - * Therefore, the wait period is: - * - * (#TSR bits + #THR bits) * - * 1 MICROSEC / baud rate - */ - (void) timeout(async_restart, async, - drv_usectohz(16 * MICROSEC / - baudtable[rate])); - return; - } - mutex_exit(asy->asy_excl_hi); - mutex_exit(asy->asy_excl); - async_ioctl(async, q, bp, B_FALSE); - mutex_enter(asy->asy_excl); - continue; - } - - while (bp != NULL && (cc = bp->b_wptr - bp->b_rptr) == 0) { - nbp = bp->b_cont; - freeb(bp); - bp = nbp; - } - if (bp != NULL) - break; - } - - /* - * We have data to transmit. If output is stopped, put - * it back and try again later. - */ - if (async->async_flags & (ASYNC_HW_OUT_FLW|ASYNC_STOPPED)) { -#ifdef DEBUG - if (asydebug & ASY_DEBUG_HFLOW && - async->async_flags & ASYNC_HW_OUT_FLW) - printf("asy%d: output hflow in effect.\n", - UNIT(async->async_dev)); -#endif - mutex_exit(asy->asy_excl); - (void) putbq(q, bp); - /* - * We entered the routine owning the lock, we need to - * exit the routine owning the lock. - */ - mutex_enter(asy->asy_excl); - return; - } - - async->async_xmitblk = bp; - xmit_addr = bp->b_rptr; - bp = bp->b_cont; - if (bp != NULL) { - mutex_exit(asy->asy_excl); - (void) putbq(q, bp); /* not done with this message yet */ - mutex_enter(asy->asy_excl); - } - - /* - * In 5-bit mode, the high order bits are used - * to indicate character sizes less than five, - * so we need to explicitly mask before transmitting - */ - if ((async->async_ttycommon.t_cflag & CSIZE) == CS5) { - register unsigned char *p = xmit_addr; - register int cnt = cc; - - while (cnt--) - *p++ &= (unsigned char) 0x1f; - } - - /* - * Set up this block for pseudo-DMA. - */ - mutex_enter(asy->asy_excl_hi); - async->async_optr = xmit_addr; - async->async_ocnt = cc; - /* - * If the transmitter is ready, shove some - * characters out. - */ - xmit_progress = 0; - while (fifo_len-- && async->async_ocnt) { - if (INB(LSR) & XHRE) { - OUTB(DAT, *async->async_optr++); - async->async_ocnt--; - xmit_progress++; - } - } - asy->asy_out_of_band_xmit = xmit_progress; - if (xmit_progress > 0) - async->async_flags |= ASYNC_PROGRESS; - async->async_flags |= ASYNC_BUSY; - mutex_exit(asy->asy_excl_hi); -} - -/* - * Resume output by poking the transmitter. - */ -static void -async_resume(struct asyncline *async) -{ - register struct asycom *asy = async->async_common; - - ASSERT(mutex_owned(asy->asy_excl_hi)); -#ifdef DEBUG - if (asydebug & ASY_DEBUG_PROCS) - printf("resume\n"); -#endif - - asycheckflowcontrol_hw(asy); - - if (INB(LSR) & XHRE) { - if (asycheckflowcontrol_sw(asy)) { - return; - } else if (async->async_ocnt > 0) { - OUTB(DAT, *async->async_optr++); - async->async_ocnt--; - async->async_flags |= ASYNC_PROGRESS; - } - } -} - -/* - * Process an "ioctl" message sent down to us. - * Note that we don't need to get any locks until we are ready to access - * the hardware. Nothing we access until then is going to be altered - * outside of the STREAMS framework, so we should be safe. - */ -static void -async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp, boolean_t iswput) -{ - register struct asycom *asy = async->async_common; - register tty_common_t *tp = &async->async_ttycommon; - register struct iocblk *iocp; - register unsigned datasize; - mblk_t *datamp; - int error = 0; - uchar_t val, icr; -#ifdef DEBUG - if (asydebug & ASY_DEBUG_PROCS) - printf("ioctl\n"); -#endif - - if (tp->t_iocpending != NULL) { - /* - * We were holding an "ioctl" response pending the - * availability of an "mblk" to hold data to be passed up; - * another "ioctl" came through, which means that "ioctl" - * must have timed out or been aborted. - */ - freemsg(async->async_ttycommon.t_iocpending); - async->async_ttycommon.t_iocpending = NULL; - } - - iocp = (struct iocblk *)mp->b_rptr; - - /* - * For TIOCMGET, TIOCMBIC, TIOCMBIS, TIOCMSET, and PPS, do NOT call - * ttycommon_ioctl() because this function frees up the message block - * (mp->b_cont) that contains the address of the user variable where - * we need to pass back the bit array. - */ - if (iocp->ioc_cmd == TIOCMGET || - iocp->ioc_cmd == TIOCMBIC || - iocp->ioc_cmd == TIOCMBIS || - iocp->ioc_cmd == TIOCMSET || - iocp->ioc_cmd == TIOCGPPS || - iocp->ioc_cmd == TIOCSPPS || - iocp->ioc_cmd == TIOCGPPSEV) - error = -1; /* Do Nothing */ - else - - /* - * The only way in which "ttycommon_ioctl" can fail is if the "ioctl" - * requires a response containing data to be returned to the user, - * and no mblk could be allocated for the data. - * No such "ioctl" alters our state. Thus, we always go ahead and - * do any state-changes the "ioctl" calls for. If we couldn't allocate - * the data, "ttycommon_ioctl" has stashed the "ioctl" away safely, so - * we just call "bufcall" to request that we be called back when we - * stand a better chance of allocating the data. - */ - if ((datasize = ttycommon_ioctl(tp, wq, mp, &error)) != 0) { - if (async->async_wbufcid) - unbufcall(async->async_wbufcid); - async->async_wbufcid = bufcall(datasize, BPRI_HI, async_reioctl, - async); - return; - } - - mutex_enter(asy->asy_excl); - - if (error == 0) { - /* - * "ttycommon_ioctl" did most of the work; we just use the - * data it set up. - */ - switch (iocp->ioc_cmd) { - - case TCSETS: - if (!(asy->asy_rsc_console || asy->asy_rsc_control || - asy->asy_lom_console)) { - mutex_enter(asy->asy_excl_hi); - error = asy_program(asy, ASY_NOINIT); - mutex_exit(asy->asy_excl_hi); - } - break; - case TCSETSF: - case TCSETSW: - case TCSETA: - case TCSETAW: - case TCSETAF: - if (!(asy->asy_rsc_console || asy->asy_rsc_control || - asy->asy_lom_console)) { - mutex_enter(asy->asy_excl_hi); - if (iswput && asy_isbusy(asy)) { - if (putq(wq, mp) == 0) - freemsg(mp); - mutex_exit(asy->asy_excl_hi); - mutex_exit(asy->asy_excl); - return; - } - error = asy_program(asy, ASY_NOINIT); - mutex_exit(asy->asy_excl_hi); - } - break; - case TIOCSSOFTCAR: - /* Set the driver state appropriately */ - mutex_enter(asy->asy_excl_hi); - if (tp->t_flags & TS_SOFTCAR) - asy->asy_flags |= ASY_IGNORE_CD; - else - asy->asy_flags &= ~ASY_IGNORE_CD; - mutex_exit(asy->asy_excl_hi); - break; - } - } else if (error < 0) { - /* - * "ttycommon_ioctl" didn't do anything; we process it here. - */ - error = 0; - switch (iocp->ioc_cmd) { - - case TIOCGPPS: - /* - * Get PPS on/off. - */ - if (mp->b_cont != NULL) - freemsg(mp->b_cont); - - mp->b_cont = allocb(sizeof (int), BPRI_HI); - if (mp->b_cont == NULL) { - error = ENOMEM; - break; - } - if (asy->asy_flags & ASY_PPS) - *(int *)mp->b_cont->b_wptr = 1; - else - *(int *)mp->b_cont->b_wptr = 0; - mp->b_cont->b_wptr += sizeof (int); - mp->b_datap->db_type = M_IOCACK; - iocp->ioc_count = sizeof (int); - break; - - case TIOCSPPS: - /* - * Set PPS on/off. - */ - error = miocpullup(mp, sizeof (int)); - if (error != 0) - break; - - mutex_enter(asy->asy_excl_hi); - if (*(int *)mp->b_cont->b_rptr) - asy->asy_flags |= ASY_PPS; - else - asy->asy_flags &= ~ASY_PPS; - /* Reset edge sense */ - asy->asy_flags &= ~ASY_PPS_EDGE; - mutex_exit(asy->asy_excl_hi); - mp->b_datap->db_type = M_IOCACK; - break; - - case TIOCGPPSEV: { - /* - * Get PPS event data. - */ - mblk_t *bp; - void *buf; -#ifdef _SYSCALL32_IMPL - struct ppsclockev32 p32; -#endif - struct ppsclockev ppsclockev; - - if (mp->b_cont != NULL) { - freemsg(mp->b_cont); - mp->b_cont = NULL; - } - - if ((asy->asy_flags & ASY_PPS) == 0) { - error = ENXIO; - break; - } - - /* Protect from incomplete asy_ppsev */ - mutex_enter(asy->asy_excl_hi); - ppsclockev = asy_ppsev; - mutex_exit(asy->asy_excl_hi); - -#ifdef _SYSCALL32_IMPL - if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) { - TIMEVAL_TO_TIMEVAL32(&p32.tv, &ppsclockev.tv); - p32.serial = ppsclockev.serial; - buf = &p32; - iocp->ioc_count = sizeof (struct ppsclockev32); - } else -#endif - { - buf = &ppsclockev; - iocp->ioc_count = sizeof (struct ppsclockev); - } - - if ((bp = allocb(iocp->ioc_count, BPRI_HI)) == NULL) { - error = ENOMEM; - break; - } - mp->b_cont = bp; - - bcopy(buf, bp->b_wptr, iocp->ioc_count); - bp->b_wptr += iocp->ioc_count; - mp->b_datap->db_type = M_IOCACK; - break; - } - - case TCSBRK: - error = miocpullup(mp, sizeof (int)); - if (error != 0) - break; - - mutex_enter(asy->asy_excl_hi); - if (*(int *)mp->b_cont->b_rptr == 0) { - /* - * Get the divisor by calculating the rate - */ - unsigned int rate, divisor; - rate = async->async_ttycommon.t_cflag & CBAUD; - if (async->async_ttycommon.t_cflag & CBAUDEXT) - rate += 16; - if (rate >= N_SU_SPEEDS) rate = B9600; - divisor = asyspdtab[rate] & 0xfff; - - /* - * To ensure that erroneous characters are - * not sent out when the break is set, SB - * recommends three steps: - * - * 1) pad the TSR with 0 bits - * 2) When the TSR is full, set break - * 3) When the TSR has been flushed, unset - * the break when transmission must be - * restored. - * - * We loop until the TSR is empty and then - * set the break. ASYNC_BREAK has been set - * to ensure that no characters are - * transmitted while the TSR is being - * flushed and SOUT is being used for the - * break signal. - * - * The wait period is equal to - * clock / (baud * 16) * 16 * 2. - */ - async->async_flags |= ASYNC_BREAK; - while ((INB(LSR) & XSRE) == 0) { - mutex_exit(asy->asy_excl_hi); - mutex_exit(asy->asy_excl); - drv_usecwait(32*divisor); - mutex_enter(asy->asy_excl); - mutex_enter(asy->asy_excl_hi); - } - - /* - * Set the break bit, and arrange for - * "async_restart" to be called in 1/4 second; - * it will turn the break bit off, and call - * "async_start" to grab the next message. - */ - val = INB(LCR); - OUTB(LCR, (val | SETBREAK)); - mutex_exit(asy->asy_excl_hi); - (void) timeout(async_restart, async, hz / 4); - } else { -#ifdef DEBUG - if (asydebug & ASY_DEBUG_CLOSE) - printf("asy%d: wait for flush.\n", - UNIT(async->async_dev)); -#endif - if (iswput && asy_isbusy(asy)) { - if (putq(wq, mp) == 0) - freemsg(mp); - mutex_exit(asy->asy_excl_hi); - mutex_exit(asy->asy_excl); - return; - } - mutex_exit(asy->asy_excl_hi); -#ifdef DEBUG - if (asydebug & ASY_DEBUG_CLOSE) - printf("asy%d: ldterm satisfied.\n", - UNIT(async->async_dev)); -#endif - } - break; - - case TIOCSBRK: - mutex_enter(asy->asy_excl_hi); - val = INB(LCR); - OUTB(LCR, (val | SETBREAK)); - mutex_exit(asy->asy_excl_hi); - mutex_exit(asy->asy_excl); - miocack(wq, mp, 0, 0); - return; - - case TIOCCBRK: - mutex_enter(asy->asy_excl_hi); - val = INB(LCR); - OUTB(LCR, (val & ~SETBREAK)); - mutex_exit(asy->asy_excl_hi); - mutex_exit(asy->asy_excl); - miocack(wq, mp, 0, 0); - return; - - case TIOCMSET: - case TIOCMBIS: - case TIOCMBIC: - if (iocp->ioc_count == TRANSPARENT) - mcopyin(mp, NULL, sizeof (int), NULL); - else { - error = miocpullup(mp, sizeof (int)); - if (error != 0) - break; - - mutex_enter(asy->asy_excl_hi); - - (void) asymctl(asy, - dmtoasy(*(int *)mp->b_cont->b_rptr), - iocp->ioc_cmd); - - mutex_exit(asy->asy_excl_hi); - iocp->ioc_error = 0; - mp->b_datap->db_type = M_IOCACK; - } - break; - - case TIOCSILOOP: - mutex_enter(asy->asy_excl_hi); - /* - * If somebody misues this Ioctl when used for - * driving keyboard and mouse indicate not supported - */ - if ((asy->asy_device_type == ASY_KEYBOARD) || - (asy->asy_device_type == ASY_MOUSE)) { - mutex_exit(asy->asy_excl_hi); - error = ENOTTY; - break; - } - - /* should not use when we're the console */ - if ((async->async_dev == kbddev) || - (async->async_dev == rconsdev) || - (async->async_dev == stdindev)) { - mutex_exit(asy->asy_excl_hi); - error = EINVAL; - break; - } - - val = INB(MCR); - icr = INB(ICR); - /* - * Disable the Modem Status Interrupt - * The reason for disabling is the status of - * modem signal are in the higher 4 bits instead of - * lower four bits when in loopback mode, - * so, donot worry about Modem interrupt when - * you are planning to set - * this in loopback mode until it is cleared by - * another ioctl to get out of the loopback mode - */ - OUTB(ICR, icr & ~ MIEN); - OUTB(MCR, val | ASY_LOOP); - mutex_exit(asy->asy_excl_hi); - iocp->ioc_error = 0; - mp->b_datap->db_type = M_IOCACK; - break; - - case TIOCMGET: - datamp = allocb(sizeof (int), BPRI_MED); - if (datamp == NULL) { - error = EAGAIN; - break; - } - - mutex_enter(asy->asy_excl_hi); - *(int *)datamp->b_rptr = asymctl(asy, 0, TIOCMGET); - mutex_exit(asy->asy_excl_hi); - - if (iocp->ioc_count == TRANSPARENT) { - mcopyout(mp, NULL, sizeof (int), NULL, datamp); - } else { - if (mp->b_cont != NULL) - freemsg(mp->b_cont); - mp->b_cont = datamp; - mp->b_cont->b_wptr += sizeof (int); - mp->b_datap->db_type = M_IOCACK; - iocp->ioc_count = sizeof (int); - } - break; - - default: /* unexpected ioctl type */ - /* - * If we don't understand it, it's an error. NAK it. - */ - error = EINVAL; - break; - } - } - if (error != 0) { - iocp->ioc_error = error; - mp->b_datap->db_type = M_IOCNAK; - } - mutex_exit(asy->asy_excl); - qreply(wq, mp); -} - -static void -asyrsrv(queue_t *q) -{ - mblk_t *bp; - struct asyncline *async; - - async = (struct asyncline *)q->q_ptr; - - while (canputnext(q) && (bp = getq(q))) - putnext(q, bp); - ASYSETSOFT(async->async_common); - async->async_polltid = 0; -} - -/* - * Put procedure for write queue. - * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here; - * set the flow control character for M_STOPI and M_STARTI messages; - * queue up M_BREAK, M_DELAY, and M_DATA messages for processing - * by the start routine, and then call the start routine; discard - * everything else. Note that this driver does not incorporate any - * mechanism to negotiate to handle the canonicalization process. - * It expects that these functions are handled in upper module(s), - * as we do in ldterm. - */ -static void -asywput(queue_t *q, mblk_t *mp) -{ - register struct asyncline *async; - register struct asycom *asy; - int error; - - async = (struct asyncline *)q->q_ptr; - asy = async->async_common; - - switch (mp->b_datap->db_type) { - - case M_STOP: - /* - * Since we don't do real DMA, we can just let the - * chip coast to a stop after applying the brakes. - */ - mutex_enter(asy->asy_excl); - async->async_flags |= ASYNC_STOPPED; - mutex_exit(asy->asy_excl); - freemsg(mp); - break; - - case M_START: - mutex_enter(asy->asy_excl); - if (async->async_flags & ASYNC_STOPPED) { - async->async_flags &= ~ASYNC_STOPPED; - /* - * If an output operation is in progress, - * resume it. Otherwise, prod the start - * routine. - */ - if (async->async_ocnt > 0) { - mutex_enter(asy->asy_excl_hi); - async_resume(async); - mutex_exit(asy->asy_excl_hi); - } else { - async_start(async); - } - } - mutex_exit(asy->asy_excl); - freemsg(mp); - break; - - case M_IOCTL: - switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) { - - case TCSBRK: - error = miocpullup(mp, sizeof (int)); - if (error != 0) { - miocnak(q, mp, 0, error); - return; - } - - if (*(int *)mp->b_cont->b_rptr != 0) { -#ifdef DEBUG - if (asydebug & ASY_DEBUG_CLOSE) - printf("asy%d: flush request.\n", - UNIT(async->async_dev)); -#endif - (void) putq(q, mp); - mutex_enter(asy->asy_excl); - async_nstart(async, 1); - mutex_exit(asy->asy_excl); - break; - } - /*FALLTHROUGH*/ - case TCSETSW: - case TCSETSF: - case TCSETAW: - case TCSETAF: - /* - * The changes do not take effect until all - * output queued before them is drained. - * Put this message on the queue, so that - * "async_start" will see it when it's done - * with the output before it. Poke the - * start routine, just in case. - */ - (void) putq(q, mp); - mutex_enter(asy->asy_excl); - async_start(async); - mutex_exit(asy->asy_excl); - break; - - default: - /* - * Do it now. - */ - async_ioctl(async, q, mp, B_TRUE); - break; - } - break; - - case M_FLUSH: - if (*mp->b_rptr & FLUSHW) { - mutex_enter(asy->asy_excl); - - /* - * Abort any output in progress. - */ - mutex_enter(asy->asy_excl_hi); - if (async->async_flags & ASYNC_BUSY) { - async->async_ocnt = 0; - async->async_flags &= ~ASYNC_BUSY; - } - mutex_exit(asy->asy_excl_hi); - - /* Flush FIFO buffers */ - if (asy->asy_use_fifo == FIFO_ON) { - OUTB(FIFOR, FIFO_ON | FIFODMA | FIFOTXFLSH | - (asy->asy_trig_level & 0xff)); - } - - /* - * Flush our write queue. - */ - flushq(q, FLUSHDATA); /* XXX doesn't flush M_DELAY */ - if (async->async_xmitblk != NULL) { - freeb(async->async_xmitblk); - async->async_xmitblk = NULL; - } - - mutex_exit(asy->asy_excl); - *mp->b_rptr &= ~FLUSHW; /* it has been flushed */ - } - if (*mp->b_rptr & FLUSHR) { - /* Flush FIFO buffers */ - if (asy->asy_use_fifo == FIFO_ON) { - OUTB(FIFOR, FIFO_ON | FIFODMA | FIFORXFLSH | - (asy->asy_trig_level & 0xff)); - } - flushq(RD(q), FLUSHDATA); - qreply(q, mp); /* give the read queues a crack at it */ - } else { - freemsg(mp); - } - - /* - * We must make sure we process messages that survive the - * write-side flush. Without this call, the close protocol - * with ldterm can hang forever. (ldterm will have sent us a - * TCSBRK ioctl that it expects a response to.) - */ - mutex_enter(asy->asy_excl); - async_start(async); - mutex_exit(asy->asy_excl); - break; - case M_BREAK: - case M_DELAY: - case M_DATA: - /* - * Queue the message up to be transmitted, - * and poke the start routine. - */ - (void) putq(q, mp); - mutex_enter(asy->asy_excl); - async_start(async); - mutex_exit(asy->asy_excl); - break; - - case M_STOPI: - mutex_enter(asy->asy_excl); - async->async_flowc = async->async_stopc; - async_start(async); /* poke the start routine */ - mutex_exit(asy->asy_excl); - freemsg(mp); - break; - - case M_STARTI: - mutex_enter(asy->asy_excl); - async->async_flowc = async->async_startc; - async_start(async); /* poke the start routine */ - mutex_exit(asy->asy_excl); - freemsg(mp); - break; - - case M_CTL: - if (MBLKL(mp) >= sizeof (struct iocblk) && - ((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) { - ((struct iocblk *)mp->b_rptr)->ioc_cmd = MC_HAS_POSIX; - qreply(q, mp); - } else { - /* - * These MC_SERVICE type messages are used by upper - * modules to tell this driver to send input up - * immediately, or that it can wait for normal - * processing that may or may not be done. Sun - * requires these for the mouse module. - * (XXX - for x86?) - */ - mutex_enter(asy->asy_excl); - switch (*mp->b_rptr) { - - case MC_SERVICEIMM: - async->async_flags |= ASYNC_SERVICEIMM; - break; - - case MC_SERVICEDEF: - async->async_flags &= ~ASYNC_SERVICEIMM; - break; - } - mutex_exit(asy->asy_excl); - freemsg(mp); - } - break; - - case M_IOCDATA: - async_iocdata(q, mp); - break; - - default: - freemsg(mp); - break; - } -} - -/* - * Retry an "ioctl", now that "bufcall" claims we may be able to allocate - * the buffer we need. - */ -static void -async_reioctl(void *arg) -{ - struct asyncline *async = arg; - struct asycom *asy = async->async_common; - queue_t *q; - mblk_t *mp; - - /* - * The bufcall is no longer pending. - */ - mutex_enter(asy->asy_excl); - async->async_wbufcid = 0; - if ((q = async->async_ttycommon.t_writeq) == NULL) { - mutex_exit(asy->asy_excl); - return; - } - if ((mp = async->async_ttycommon.t_iocpending) != NULL) { - /* not pending any more */ - async->async_ttycommon.t_iocpending = NULL; - mutex_exit(asy->asy_excl); - /* not in STREAMS queue; we no longer know if we're in wput */ - async_ioctl(async, q, mp, B_TRUE); - } else - mutex_exit(asy->asy_excl); -} - -static void -async_iocdata(queue_t *q, mblk_t *mp) -{ - struct asyncline *async = (struct asyncline *)q->q_ptr; - struct asycom *asy; - struct copyresp *csp; - - asy = async->async_common; - csp = (struct copyresp *)mp->b_rptr; - - if (csp->cp_rval != 0) { - freemsg(mp); - return; - } - - mutex_enter(asy->asy_excl); - - switch (csp->cp_cmd) { - case TIOCMSET: - case TIOCMBIS: - case TIOCMBIC: - if (mp->b_cont == NULL) { - mutex_exit(asy->asy_excl); - miocnak(q, mp, 0, EINVAL); - break; - } - - mutex_enter(asy->asy_excl_hi); - (void) asymctl(asy, dmtoasy(*(int *)mp->b_cont->b_rptr), - csp->cp_cmd); - mutex_exit(asy->asy_excl_hi); - - freemsg(mp->b_cont); - mp->b_cont = NULL; - mutex_exit(asy->asy_excl); - miocack(q, mp, 0, 0); - break; - - case TIOCMGET: - if (mp->b_cont != NULL) { - freemsg(mp->b_cont); - mp->b_cont = NULL; - } - mutex_exit(asy->asy_excl); - miocack(q, mp, 0, 0); - break; - - default: - mutex_exit(asy->asy_excl); - miocnak(q, mp, 0, EINVAL); - break; - } -} - - -/* - * Set or get the modem control status. - */ -static int -asymctl(struct asycom *asy, int bits, int how) -{ - register int mcr_r, msr_r; - - ASSERT(mutex_owned(asy->asy_excl_hi)); - ASSERT(mutex_owned(asy->asy_excl)); - - /* Read Modem Control Registers */ - mcr_r = INB(MCR); - - switch (how) { - - case TIOCMSET: - mcr_r = bits; - break; - - case TIOCMBIS: - mcr_r |= bits; /* Set bits from input */ - break; - - case TIOCMBIC: - mcr_r &= ~bits; /* Set ~bits from input */ - break; - - case TIOCMGET: - /* Read Modem Status Registers */ - if (INB(ICR) & MIEN) - msr_r = asy->asy_cached_msr; - else - msr_r = INB(MSR); - return (asytodm(mcr_r, msr_r)); - } - - OUTB(MCR, mcr_r); - - return (mcr_r); -} - -static int -asytodm(int mcr_r, int msr_r) -{ - register int b = 0; - - - /* MCR registers */ - if (mcr_r & RTS) - b |= TIOCM_RTS; - - if (mcr_r & DTR) - b |= TIOCM_DTR; - - /* MSR registers */ - if (msr_r & DCD) - b |= TIOCM_CAR; - - if (msr_r & CTS) - b |= TIOCM_CTS; - - if (msr_r & DSR) - b |= TIOCM_DSR; - - if (msr_r & RI) - b |= TIOCM_RNG; - - return (b); -} - -static int -dmtoasy(int bits) -{ - register int b = 0; - -#ifdef CAN_NOT_SET /* only DTR and RTS can be set */ - if (bits & TIOCM_CAR) - b |= DCD; - if (bits & TIOCM_CTS) - b |= CTS; - if (bits & TIOCM_DSR) - b |= DSR; - if (bits & TIOCM_RNG) - b |= RI; -#endif - - if (bits & TIOCM_RTS) - b |= RTS; - if (bits & TIOCM_DTR) - b |= DTR; - - return (b); -} - -static void -asycheckflowcontrol_hw(struct asycom *asy) -{ - struct asyncline *async; - uchar_t mcr, flag; - - ASSERT(mutex_owned(asy->asy_excl_hi)); - - async = (struct asyncline *)asy->asy_priv; - ASSERT(async != NULL); - - if (async->async_ttycommon.t_cflag & CRTSXOFF) { - mcr = INB(MCR); - flag = (async->async_flags & ASYNC_HW_IN_FLOW) ? 0 : RTS; - if (((mcr ^ flag) & RTS) != 0) { - OUTB(MCR, (mcr ^ RTS)); - } - } -} - -static boolean_t -asycheckflowcontrol_sw(struct asycom *asy) -{ - uchar_t ss; - struct asyncline *async; - int rval = B_FALSE; - - ASSERT(mutex_owned(asy->asy_excl_hi)); - - async = (struct asyncline *)asy->asy_priv; - ASSERT(async != NULL); - - if ((ss = async->async_flowc) != '\0' && (INB(LSR) & XHRE)) { - /* - * If we get this far, then we know that flowc is non-zero and - * that there's transmit room available. We've "handled" the - * request now, so clear it. If the user didn't ask for IXOFF, - * then don't actually send anything, but wait for the next - * opportunity. - */ - async->async_flowc = '\0'; - if (async->async_ttycommon.t_iflag & IXOFF) { - async->async_flags |= ASYNC_BUSY; - OUTB(DAT, ss); - rval = B_TRUE; - } - } - - return (rval); -} - -/* - * Check for abort character sequence - */ -static boolean_t -abort_charseq_recognize(uchar_t ch) -{ - static int state = 0; -#define CNTRL(c) ((c)&037) - static char sequence[] = { '\r', '~', CNTRL('b') }; - - if (ch == sequence[state]) { - if (++state >= sizeof (sequence)) { - state = 0; - return (B_TRUE); - } - } else { - state = (ch == sequence[0]) ? 1 : 0; - } - return (B_FALSE); -} diff --git a/usr/src/uts/sun4u/sys/sudev.h b/usr/src/uts/sun4u/sys/sudev.h deleted file mode 100644 index 2effc7528e..0000000000 --- a/usr/src/uts/sun4u/sys/sudev.h +++ /dev/null @@ -1,400 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ -/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ -/* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */ -/* All Rights Reserved */ - -/* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#ifndef _SYS_SUDEV_H -#define _SYS_SUDEV_H - -#pragma ident "%Z%%M% %I% %E% SMI" - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -#include -#include - -/* - * Definitions for INS8250 / 16550 chips - */ - -/* defined as offsets from the data register */ -#define DAT 0 /* receive/transmit data */ -#define ICR 1 /* interrupt control register */ -#define ISR 2 /* interrupt status register */ -#define LCR 3 /* line control register */ -#define MCR 4 /* modem control register */ -#define LSR 5 /* line status register */ -#define MSR 6 /* modem status register */ -#define DLL 0 /* divisor latch (lsb) */ -#define DLH 1 /* divisor latch (msb) */ -#define FIFOR ISR /* FIFO register for 16550 */ -#define OUTB(offset, value) ddi_put8(asy->asy_handle, \ - asy->asy_ioaddr+offset, value) -#define INB(offset) ddi_get8(asy->asy_handle, asy->asy_ioaddr+offset) - -/* - * INTEL 8210-A/B & 16450/16550 Registers Structure. - */ - -/* Line Control Register */ -#define WLS0 0x01 /* word length select bit 0 */ -#define WLS1 0x02 /* word length select bit 2 */ -#define STB 0x04 /* number of stop bits */ -#define PEN 0x08 /* parity enable */ -#define EPS 0x10 /* even parity select */ -#define SETBREAK 0x40 /* break key */ -#define DLAB 0x80 /* divisor latch access bit */ -#define RXLEN 0x03 /* # of data bits per received/xmitted char */ -#define STOP1 0x00 -#define STOP2 0x04 -#define PAREN 0x08 -#define PAREVN 0x10 -#define PARMARK 0x20 -#define SNDBRK 0x40 - - -#define BITS5 0x00 /* 5 bits per char */ -#define BITS6 0x01 /* 6 bits per char */ -#define BITS7 0x02 /* 7 bits per char */ -#define BITS8 0x03 /* 8 bits per char */ - -/* baud rate definitions */ -#define ASY110 1047 /* 110 baud rate for serial console */ -#define ASY150 768 /* 150 baud rate for serial console */ -#define ASY300 384 /* 300 baud rate for serial console */ -#define ASY600 192 /* 600 baud rate for serial console */ -#define ASY1200 96 /* 1200 baud rate for serial console */ -#define ASY2400 48 /* 2400 baud rate for serial console */ -#define ASY4800 24 /* 4800 baud rate for serial console */ -#define ASY9600 12 /* 9600 baud rate for serial console */ - -/* Line Status Register */ -#define RCA 0x01 /* data ready */ -#define OVRRUN 0x02 /* overrun error */ -#define PARERR 0x04 /* parity error */ -#define FRMERR 0x08 /* framing error */ -#define BRKDET 0x10 /* a break has arrived */ -#define XHRE 0x20 /* tx hold reg is now empty */ -#define XSRE 0x40 /* tx shift reg is now empty */ -#define RFBE 0x80 /* rx FIFO Buffer error */ - -/* Interrupt Id Regisger */ -#define MSTATUS 0x00 /* modem status changed */ -#define NOINTERRUPT 0x01 /* no interrupt pending */ -#define TxRDY 0x02 /* Transmitter Holding Register Empty */ -#define RxRDY 0x04 /* Receiver Data Available */ -#define FFTMOUT 0x0c /* FIFO timeout - 16550AF */ -#define RSTATUS 0x06 /* Receiver Line Status */ - -/* Interrupt Enable Register */ -#define RIEN 0x01 /* Received Data Ready */ -#define TIEN 0x02 /* Tx Hold Register Empty */ -#define SIEN 0x04 /* Receiver Line Status */ -#define MIEN 0x08 /* Modem Status */ - -/* Modem Control Register */ -#define DTR 0x01 /* Data Terminal Ready */ -#define RTS 0x02 /* Request To Send */ -#define OUT1 0x04 /* Aux output - not used */ -#define OUT2 0x08 /* dis/enable int per INO on ALI1535D+ */ -#define ASY_LOOP 0x10 /* loopback for diagnostics */ - -/* Modem Status Register */ -#define DCTS 0x01 /* Delta Clear To Send */ -#define DDSR 0x02 /* Delta Data Set Ready */ -#define DRI 0x04 /* Trail Edge Ring Indicator */ -#define DDCD 0x08 /* Delta Data Carrier Detect */ -#define CTS 0x10 /* Clear To Send */ -#define DSR 0x20 /* Data Set Ready */ -#define RI 0x40 /* Ring Indicator */ -#define DCD 0x80 /* Data Carrier Detect */ - -#define DELTAS(x) ((x)&(DCTS|DDSR|DRI|DDCD)) -#define STATES(x) ((x)(CTS|DSR|RI|DCD)) - -/* flags for FCR (FIFO Control register) */ -#define FIFO_OFF 0x00 /* fifo disabled */ -#define FIFO_ON 0x01 /* fifo enabled */ -#define FIFOEN 0x8f /* fifo enabled, w/ 8 byte trigger */ -#define FIFORCLR 0x8b /* Clear receiver FIFO only */ - -#define FIFORXFLSH 0x02 /* flush receiver FIFO */ -#define FIFOTXFLSH 0x04 /* flush transmitter FIFO */ -#define FIFODMA 0x08 /* DMA mode 1 */ -#define FIFO_TRIG_1 0x00 /* 1 byte trigger level */ -#define FIFO_TRIG_4 0x40 /* 4 byte trigger level */ -#define FIFO_TRIG_8 0x80 /* 8 byte trigger level */ -#define FIFO_TRIG_14 0xC0 /* 14 byte trigger level */ - -/* - * Defines for ioctl calls (VP/ix) - */ - -#define AIOC ('A'<<8) -#define AIOCINTTYPE (AIOC|60) /* set interrupt type */ -#define AIOCDOSMODE (AIOC|61) /* set DOS mode */ -#define AIOCNONDOSMODE (AIOC|62) /* reset DOS mode */ -#define AIOCSERIALOUT (AIOC|63) /* serial device data write */ -#define AIOCSERIALIN (AIOC|64) /* serial device data read */ -#define AIOCSETSS (AIOC|65) /* set start/stop chars */ -#define AIOCINFO (AIOC|66) /* tell usr what device we are */ - -/* Ioctl alternate names used by VP/ix */ -#define VPC_SERIAL_DOS AIOCDOSMODE -#define VPC_SERIAL_NONDOS AIOCNONDOSMODE -#define VPC_SERIAL_INFO AIOCINFO -#define VPC_SERIAL_OUT AIOCSERIALOUT -#define VPC_SERIAL_IN AIOCSERIALIN - -/* Serial in/out requests */ -#define SO_DIVLLSB 1 -#define SO_DIVLMSB 2 -#define SO_LCR 3 -#define SO_MCR 4 -#define SI_MSR 1 -#define SIO_MASK(elem) (1<<((elem)-1)) - -#define OVERRUN 040000 -#define FRERROR 020000 -#define PERROR 010000 -#define S_ERRORS (PERROR|OVERRUN|FRERROR) - -/* - * Ring buffer and async line management definitions. - */ -#define RINGBITS 16 /* # of bits in ring ptrs */ -#define RINGSIZE (1<async_rput = (ap)->async_rget = 0) -#define RING_CNT(ap) (((ap)->async_rput - (ap)->async_rget) & RINGMASK) -#define RING_FRAC(ap) ((int)RING_CNT(ap) >= (int)(RINGSIZE/RINGFRAC)) -#define RING_POK(ap, n) ((int)RING_CNT(ap) < (int)(RINGSIZE-(n))) -#define RING_PUT(ap, c) \ - ((ap)->async_ring[(ap)->async_rput++ & RINGMASK] = (uchar_t)(c)) -#define RING_UNPUT(ap) ((ap)->async_rput--) -#define RING_GOK(ap, n) ((int)RING_CNT(ap) >= (int)(n)) -#define RING_GET(ap) ((ap)->async_ring[(ap)->async_rget++ & RINGMASK]) -#define RING_EAT(ap, n) ((ap)->async_rget += (n)) -#define RING_MARK(ap, c, s) \ - ((ap)->async_ring[(ap)->async_rput++ & RINGMASK] = ((uchar_t)(c)|(s))) -#define RING_UNMARK(ap) \ - ((ap)->async_ring[((ap)->async_rget) & RINGMASK] &= ~S_ERRORS) -#define RING_ERR(ap, c) \ - ((ap)->async_ring[((ap)->async_rget) & RINGMASK] & (c)) - -/* - * Serial kstats structure and macro to increment an individual kstat - */ -struct serial_kstats { - kstat_named_t ringover; /* ring buffer overflow */ - kstat_named_t siloover; /* silo overflow */ -}; - -#define INC64_KSTAT(asy, stat) (asy)->kstats.stat.value.ui64++; - -/* - * Hardware channel common data. One structure per port. - * Each of the fields in this structure is required to be protected by a - * mutex lock at the highest priority at which it can be altered. - * The asy_flags, and asy_next fields can be altered by interrupt - * handling code that must be protected by the mutex whose handle is - * stored in asy_excl_hi. All others can be protected by the asy_excl - * mutex, which is lower priority and adaptive. - */ -struct asycom { - int asy_flags; /* random flags */ - /* protected by asy_excl_hi lock */ - uint_t asy_hwtype; /* HW type: ASY82510, etc. */ - uint_t asy_use_fifo; /* HW FIFO use it or not ?? */ - uint_t asy_fifo_buf; /* With FIFO = 16, otherwise = 1 */ - uchar_t *asy_ioaddr; /* i/o address of ASY port */ - uint_t asy_vect; /* IRQ number */ - boolean_t suspended; /* TRUE if driver suspended */ - caddr_t asy_priv; /* protocol private data */ - dev_info_t *asy_dip; /* dev_info */ - long asy_unit; /* which port */ - ddi_iblock_cookie_t asy_iblock; - kmutex_t *asy_excl; /* asy adaptive mutex */ - kmutex_t *asy_excl_hi; /* asy spinlock mutex */ - ddi_acc_handle_t asy_handle; /* ddi_get/put handle */ - ushort_t asy_rsc_console; /* RSC console port */ - ushort_t asy_rsc_control; /* RSC control port */ - ushort_t asy_lom_console; /* LOM console port */ - uint_t asy_xmit_count; /* Count the no of xmits in one intr */ - uint_t asy_out_of_band_xmit; /* Out of band xmission */ - uint_t asy_rx_count; /* No. of bytes rx'eved in one intr */ - uchar_t asy_device_type; /* Currently used for this device */ - uchar_t asy_trig_level; /* Receive FIFO trig level */ - kmutex_t *asy_soft_lock; /* soft lock for gaurding softpend. */ - int asysoftpend; /* Flag indicating soft int pending. */ - ddi_softintr_t asy_softintr_id; - ddi_iblock_cookie_t asy_soft_iblock; - int asy_baud_divisor_factor; /* for different chips */ - int asy_ocflags; /* old cflags used in asy_program() */ - uint_t asy_cached_msr; /* a cache for the MSR register */ - int asy_speed_cap; /* maximum baud rate */ - kstat_t *sukstat; /* ptr to serial kstats */ - struct serial_kstats kstats; /* serial kstats structure */ - boolean_t inperim; /* in streams q perimeter */ -}; - -/* - * Asychronous protocol private data structure for ASY. - * Each of the fields in the structure is required to be protected by - * the lower priority lock except the fields that are set only at - * base level but cleared (with out lock) at interrupt level. - */ -struct asyncline { - int async_flags; /* random flags */ - kcondvar_t async_flags_cv; /* condition variable for flags */ - dev_t async_dev; /* device major/minor numbers */ - mblk_t *async_xmitblk; /* transmit: active msg block */ - struct asycom *async_common; /* device common data */ - tty_common_t async_ttycommon; /* tty driver common data */ - bufcall_id_t async_wbufcid; /* id for pending write-side bufcall */ - timeout_id_t async_polltid; /* softint poll timeout id */ - - /* - * The following fields are protected by the asy_excl_hi lock. - * Some, such as async_flowc, are set only at the base level and - * cleared (without the lock) only by the interrupt level. - */ - uchar_t *async_optr; /* output pointer */ - int async_ocnt; /* output count */ - uint_t async_rput; /* producing pointer for input */ - uint_t async_rget; /* consuming pointer for input */ - uchar_t async_flowc; /* flow control char to send */ - - /* - * Each character stuffed into the ring has two bytes associated - * with it. The first byte is used to indicate special conditions - * and the second byte is the actual data. The ring buffer - * needs to be defined as ushort_t to accomodate this. - */ - ushort_t async_ring[RINGSIZE]; - - short async_break; /* break count */ - - union { - struct { - uchar_t _hw; /* overrun (hw) */ - uchar_t _sw; /* overrun (sw) */ - } _a; - ushort_t uover_overrun; - } async_uover; -#define async_overrun async_uover._a.uover_overrun -#define async_hw_overrun async_uover._a._hw -#define async_sw_overrun async_uover._a._sw - short async_ext; /* modem status change count */ - short async_work; /* work to do flag */ - uchar_t async_queue_full; /* Streams Queue Full */ - uchar_t async_ringbuf_overflow; /* when ring buffer overflows */ - timeout_id_t async_timer; /* close drain progress timer */ -}; - -/* definitions for async_flags field */ -#define ASYNC_EXCL_OPEN 0x10000000 /* exclusive open */ -#define ASYNC_WOPEN 0x00000001 /* waiting for open to complete */ -#define ASYNC_ISOPEN 0x00000002 /* open is complete */ -#define ASYNC_OUT 0x00000004 /* line being used for dialout */ -#define ASYNC_CARR_ON 0x00000008 /* carrier on last time we looked */ -#define ASYNC_STOPPED 0x00000010 /* output is stopped */ -#define ASYNC_DELAY 0x00000020 /* waiting for delay to finish */ -#define ASYNC_BREAK 0x00000040 /* waiting for break to finish */ -#define ASYNC_BUSY 0x00000080 /* waiting for transmission to finish */ -#define ASYNC_DRAINING 0x00000100 /* waiting for output to drain */ -#define ASYNC_SERVICEIMM 0x00000200 /* queue soft interrupt as soon as */ -#define ASYNC_HW_IN_FLOW 0x00000400 /* input flow control in effect */ -#define ASYNC_HW_OUT_FLW 0x00000800 /* output flow control in effect */ -#define ASYNC_PROGRESS 0x00001000 /* made progress on output effort */ -#define ASYNC_CLOSING 0x00002000 /* closing the stream */ - -/* asy_hwtype definitions */ -#define ASY82510 0x1 -#define ASY16550AF 0x2 -#define ASY8250 0x3 /* 8250 or 16450 or 16550 */ - -/* definitions for asy_flags field */ -#define ASY_NEEDSOFT 0x00000001 -#define ASY_DOINGSOFT 0x00000002 -#define ASY_PPS 0x00000004 -#define ASY_PPS_EDGE 0x00000008 -#define ASY_IGNORE_CD 0x00000040 - -/* - * Different devices this driver supports and what it is used to drive - * currently - */ -#define ASY_KEYBOARD 0x01 -#define ASY_MOUSE 0x02 -#define ASY_SERIAL 0x03 - -/* - * RSC_DEVICE defines the bit in the minor device number that specifies - * the tty line is to be used for console/controlling a RSC device. - */ -#define RSC_DEVICE (1 << (NBITSMINOR32 - 4)) - -/* - * OUTLINE defines the high-order flag bit in the minor device number that - * controls use of a tty line for dialin and dialout simultaneously. - */ -#define OUTLINE (1 << (NBITSMINOR32 - 1)) -#define UNIT(x) (getminor(x) & ~(OUTLINE | RSC_DEVICE)) - -/* suggested number of soft state instances */ -#define SU_INITIAL_SOFT_ITEMS 0x02 - -/* - * ASYSETSOFT macro to pend a soft interrupt if one isn't already pending. - */ - -#define ASYSETSOFT(asy) { \ - if (mutex_tryenter(asy->asy_soft_lock)) { \ - asy->asy_flags |= ASY_NEEDSOFT; \ - if (!asy->asysoftpend) { \ - asy->asysoftpend = 1; \ - mutex_exit(asy->asy_soft_lock);\ - ddi_trigger_softintr(asy->asy_softintr_id);\ - } else \ - mutex_exit(asy->asy_soft_lock);\ - } \ -} - -#ifdef __cplusplus -} -#endif - -#endif /* _SYS_SUDEV_H */ diff --git a/usr/src/uts/sun4v/Makefile.sun4v b/usr/src/uts/sun4v/Makefile.sun4v index 1449304a05..af2a340293 100644 --- a/usr/src/uts/sun4v/Makefile.sun4v +++ b/usr/src/uts/sun4v/Makefile.sun4v @@ -330,6 +330,7 @@ DRV_KMODS += bge DRV_KMODS += mdesc DRV_KMODS += ncp DRV_KMODS += ebus +DRV_KMODS += su # # Exec Class Modules (/kernel/exec): # diff --git a/usr/src/uts/sun4v/su/Makefile b/usr/src/uts/sun4v/su/Makefile new file mode 100644 index 0000000000..9dc4c9ce2c --- /dev/null +++ b/usr/src/uts/sun4v/su/Makefile @@ -0,0 +1,88 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# uts/sun4v/su/Makefile +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of the sun4v "su" driver module. +# +# sun4v implementation architecture dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = su +OBJECTS = $(SU_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(SU_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_PSM_DRV_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/sun4v/Makefile.sun4v + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# lint pass one enforcement +# +CFLAGS += $(CCVERBOSE) + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/sun4v/Makefile.targ -- cgit v1.2.3