diff options
author | randyf <none@none> | 2007-10-20 16:00:42 -0700 |
---|---|---|
committer | randyf <none@none> | 2007-10-20 16:00:42 -0700 |
commit | 2df1fe9ca32bb227b9158c67f5c00b54c20b10fd (patch) | |
tree | 358c576f885c00d42a760d9e35e5b66e77209fe2 /usr/src/uts/common/io | |
parent | 10b3fbf593a6678eec9b50a01903ef4eb73111e4 (diff) | |
download | illumos-gate-2df1fe9ca32bb227b9158c67f5c00b54c20b10fd.tar.gz |
PSARC/2005/469 X86 Energy Star compliance
PSARC/2006/632 PSMI extension for state save and restore
6330209 nge needs to support DDI_SUSPEND/DDI_RESUME
6381827 Suspend to RAM on x86
6393154 audio810 needs to support DDI_SUSPEND/DDI_RESUME
6397047 fd, fdc needs to support Suspend/Resume
6401974 cannot enter S3 with ohci PME enable set on Tyan 2865 with Sun or Tyan 2.01 BIOS
6422613 memscrubber doesn't re-acquire lock before CALLB_CPR_EXIT
6455736 ata/dadk/cmdk should support DDI_SUSPEND/DDI_RESUME
6511370 CPR on SPARC regression
6586018 TODOP Macros in i86pc/sys/machclock.h not in sun4u/sun4v equivilent (Sparc only)
6610124 It takes more than 3 minutes after printing "pci_pre_resume nv_sata:0"
6617143 powerd/pmconfig emits a different default message for an existing on or off action.
--HG--
rename : usr/src/cmd/power/power.conf => usr/src/cmd/power/power.conf.sparc
Diffstat (limited to 'usr/src/uts/common/io')
-rw-r--r-- | usr/src/uts/common/io/asy.c | 871 | ||||
-rw-r--r-- | usr/src/uts/common/io/audio/sada/drv/audio810/audio810.c | 258 | ||||
-rw-r--r-- | usr/src/uts/common/io/fdc.c | 111 | ||||
-rw-r--r-- | usr/src/uts/common/io/i8042.c | 72 | ||||
-rw-r--r-- | usr/src/uts/common/io/kb8042/kb8042.c | 90 | ||||
-rw-r--r-- | usr/src/uts/common/io/kb8042/kb8042.h | 12 | ||||
-rw-r--r-- | usr/src/uts/common/io/pci-ide/pci-ide.c | 74 | ||||
-rw-r--r-- | usr/src/uts/common/io/pm.c | 359 | ||||
-rw-r--r-- | usr/src/uts/common/io/ppm/ppm.c | 175 | ||||
-rw-r--r-- | usr/src/uts/common/io/ppm/ppm_subr.c | 74 | ||||
-rw-r--r-- | usr/src/uts/common/io/sata/adapters/nv_sata/nv_sata.c | 59 | ||||
-rwxr-xr-x | usr/src/uts/common/io/srn.c | 563 | ||||
-rwxr-xr-x | usr/src/uts/common/io/srn.conf | 27 | ||||
-rw-r--r-- | usr/src/uts/common/io/usb/hcd/ehci/ehci.c | 7 | ||||
-rw-r--r-- | usr/src/uts/common/io/usb/hcd/ehci/ehci_util.c | 3 | ||||
-rw-r--r-- | usr/src/uts/common/io/usb/hcd/openhci/ohci.c | 4 |
16 files changed, 2264 insertions, 495 deletions
diff --git a/usr/src/uts/common/io/asy.c b/usr/src/uts/common/io/asy.c index 1062cd28f8..12ff96c905 100644 --- a/usr/src/uts/common/io/asy.c +++ b/usr/src/uts/common/io/asy.c @@ -230,6 +230,15 @@ static const int standard_com_ports[] = { static int *com_ports; static uint_t num_com_ports; +#ifdef DEBUG +/* + * Set this to true to make the driver pretend to do a suspend. Useful + * for debugging suspend/resume code with a serial debugger. + */ +boolean_t asy_nosuspend = B_FALSE; +#endif + + /* * Baud rate table. Indexed by #defines found in sys/termios.h */ @@ -272,6 +281,7 @@ ushort_t asyspdtab[] = { static int asyrsrv(queue_t *q); static int asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr); static int asyclose(queue_t *q, int flag, cred_t *credp); +static int asywputdo(queue_t *q, mblk_t *mp, boolean_t); static int asywput(queue_t *q, mblk_t *mp); struct module_info asy_info = { @@ -392,7 +402,7 @@ _fini(void) asy_addedsoft = 0; /* free "motherboard-serial-ports" property if allocated */ if (com_ports != NULL && com_ports != (int *)standard_com_ports) - ddi_prop_free(com_ports); + ddi_prop_free(com_ports); com_ports = NULL; mutex_destroy(&asy_soft_lock); ddi_soft_state_fini(&asy_soft_state); @@ -406,6 +416,59 @@ _info(struct modinfo *modinfop) return (mod_info(&modlinkage, modinfop)); } +void +async_put_suspq(struct asycom *asy, mblk_t *mp) +{ + struct asyncline *async = asy->asy_priv; + + ASSERT(mutex_owned(&asy->asy_excl)); + + if (async->async_suspqf == NULL) + async->async_suspqf = mp; + else + async->async_suspqb->b_next = mp; + + async->async_suspqb = mp; +} + +static mblk_t * +async_get_suspq(struct asycom *asy) +{ + struct asyncline *async = asy->asy_priv; + mblk_t *mp; + + ASSERT(mutex_owned(&asy->asy_excl)); + + if ((mp = async->async_suspqf) != NULL) { + async->async_suspqf = mp->b_next; + mp->b_next = NULL; + } else { + async->async_suspqb = NULL; + } + return (mp); +} + +static void +async_process_suspq(struct asycom *asy) +{ + struct asyncline *async = asy->asy_priv; + mblk_t *mp; + + ASSERT(mutex_owned(&asy->asy_excl)); + + while ((mp = async_get_suspq(asy)) != NULL) { + queue_t *q; + + q = async->async_ttycommon.t_writeq; + ASSERT(q != NULL); + mutex_exit(&asy->asy_excl); + (void) asywputdo(q, mp, B_FALSE); + mutex_enter(&asy->asy_excl); + } + async->async_flags &= ~ASYNC_DDI_SUSPENDED; + cv_broadcast(&async->async_flags_cv); +} + static int asy_get_bus_type(dev_info_t *devinfo) { @@ -494,7 +557,7 @@ asy_get_io_regnum_isa(dev_info_t *devi, struct asycom *asy) if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, "reg", (caddr_t)®list, ®len) != DDI_PROP_SUCCESS) { cmn_err(CE_WARN, "asy_get_io_regnum: reg property not found " - "in devices property list"); + "in devices property list"); return (-1); } @@ -545,9 +608,6 @@ asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd) struct asycom *asy; struct asyncline *async; - if (cmd != DDI_DETACH) - return (DDI_FAILURE); - instance = ddi_get_instance(devi); /* find out which unit */ asy = ddi_get_soft_state(asy_soft_state, instance); @@ -555,25 +615,104 @@ asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd) return (DDI_FAILURE); async = asy->asy_priv; - DEBUGNOTE2(ASY_DEBUG_INIT, "asy%d: %s shutdown.", - instance, asy_hw_name(asy)); + switch (cmd) { + case DDI_DETACH: + DEBUGNOTE2(ASY_DEBUG_INIT, "asy%d: %s shutdown.", + instance, asy_hw_name(asy)); - /* cancel DTR hold timeout */ - if (async->async_dtrtid != 0) { - (void) untimeout(async->async_dtrtid); - async->async_dtrtid = 0; - } + /* cancel DTR hold timeout */ + if (async->async_dtrtid != 0) { + (void) untimeout(async->async_dtrtid); + async->async_dtrtid = 0; + } + + /* remove all minor device node(s) for this device */ + ddi_remove_minor_node(devi, NULL); + + mutex_destroy(&asy->asy_excl); + mutex_destroy(&asy->asy_excl_hi); + cv_destroy(&async->async_flags_cv); + ddi_remove_intr(devi, 0, asy->asy_iblock); + ddi_regs_map_free(&asy->asy_iohandle); + asy_soft_state_free(asy); + DEBUGNOTE1(ASY_DEBUG_INIT, "asy%d: shutdown complete", + instance); + break; + case DDI_SUSPEND: + { + unsigned i; + uchar_t lsr; + +#ifdef DEBUG + if (asy_nosuspend) + return (DDI_SUCCESS); +#endif + mutex_enter(&asy->asy_excl); + + ASSERT(async->async_ops >= 0); + while (async->async_ops > 0) + cv_wait(&async->async_ops_cv, &asy->asy_excl); + + async->async_flags |= ASYNC_DDI_SUSPENDED; + + /* Wait for timed break and delay to complete */ + while ((async->async_flags & (ASYNC_BREAK|ASYNC_DELAY))) { + if (cv_wait_sig(&async->async_flags_cv, &asy->asy_excl) + == 0) { + async_process_suspq(asy); + mutex_exit(&asy->asy_excl); + return (DDI_FAILURE); + } + } + + /* Clear untimed break */ + if (async->async_flags & ASYNC_OUT_SUSPEND) + async_resume_utbrk(async); + + mutex_exit(&asy->asy_excl); + + mutex_enter(&asy->asy_soft_sr); + mutex_enter(&asy->asy_excl); + if (async->async_wbufcid != 0) { + bufcall_id_t bcid = async->async_wbufcid; + async->async_wbufcid = 0; + async->async_flags |= ASYNC_RESUME_BUFCALL; + mutex_exit(&asy->asy_excl); + unbufcall(bcid); + mutex_enter(&asy->asy_excl); + } + mutex_enter(&asy->asy_excl_hi); - /* remove all minor device node(s) for this device */ - ddi_remove_minor_node(devi, NULL); + /* Disable interrupts from chip */ + ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0); + asy->asy_flags |= ASY_DDI_SUSPENDED; + + /* Process remaining RX characters and RX errors, if any */ + lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR); + async_rxint(asy, lsr); + + /* Wait for TX to drain */ + for (i = 1000; i > 0; i--) { + lsr = ddi_get8(asy->asy_iohandle, + asy->asy_ioaddr + LSR); + if ((lsr & (XSRE | XHRE)) == (XSRE | XHRE)) + break; + delay(drv_usectohz(10000)); + } + if (i == 0) + cmn_err(CE_WARN, + "asy: transmitter wasn't drained before " + "driver was suspended"); + + mutex_exit(&asy->asy_excl_hi); + mutex_exit(&asy->asy_excl); + mutex_exit(&asy->asy_soft_sr); + break; + } + default: + return (DDI_FAILURE); + } - mutex_destroy(&asy->asy_excl); - mutex_destroy(&asy->asy_excl_hi); - cv_destroy(&async->async_flags_cv); - ddi_remove_intr(devi, 0, asy->asy_iblock); - ddi_regs_map_free(&asy->asy_iohandle); - asy_soft_state_free(asy); - DEBUGNOTE1(ASY_DEBUG_INIT, "asy%d: shutdown complete", instance); return (DDI_SUCCESS); } @@ -610,10 +749,73 @@ asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd) DDI_STRICTORDER_ACC, }; - if (cmd != DDI_ATTACH) + instance = ddi_get_instance(devi); /* find out which unit */ + + switch (cmd) { + case DDI_ATTACH: + break; + case DDI_RESUME: + { + struct asyncline *async; + +#ifdef DEBUG + if (asy_nosuspend) + return (DDI_SUCCESS); +#endif + asy = ddi_get_soft_state(asy_soft_state, instance); + if (asy == NULL) + return (DDI_FAILURE); + + mutex_enter(&asy->asy_soft_sr); + mutex_enter(&asy->asy_excl); + mutex_enter(&asy->asy_excl_hi); + + async = asy->asy_priv; + /* Disable interrupts */ + ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0); + if (asy_identify_chip(devi, asy) != DDI_SUCCESS) { + mutex_exit(&asy->asy_excl_hi); + mutex_exit(&asy->asy_excl); + mutex_exit(&asy->asy_soft_sr); + cmn_err(CE_WARN, "Cannot identify UART chip at %p\n", + (void *)asy->asy_ioaddr); + return (DDI_FAILURE); + } + asy->asy_flags &= ~ASY_DDI_SUSPENDED; + if (async->async_flags & ASYNC_ISOPEN) { + asy_program(asy, ASY_INIT); + /* Kick off output */ + if (async->async_ocnt > 0) { + async_resume(async); + } else { + mutex_exit(&asy->asy_excl_hi); + if (async->async_xmitblk) + freeb(async->async_xmitblk); + async->async_xmitblk = NULL; + async_start(async); + mutex_enter(&asy->asy_excl_hi); + } + ASYSETSOFT(asy); + } + mutex_exit(&asy->asy_excl_hi); + mutex_exit(&asy->asy_excl); + mutex_exit(&asy->asy_soft_sr); + + mutex_enter(&asy->asy_excl); + if (async->async_flags & ASYNC_RESUME_BUFCALL) { + async->async_wbufcid = bufcall(async->async_wbufcds, + BPRI_HI, (void (*)(void *)) async_reioctl, + (void *)(intptr_t)async->async_common->asy_unit); + async->async_flags &= ~ASYNC_RESUME_BUFCALL; + } + async_process_suspq(asy); + mutex_exit(&asy->asy_excl); + return (DDI_SUCCESS); + } + default: return (DDI_FAILURE); + } - instance = ddi_get_instance(devi); /* find out which unit */ ret = ddi_soft_state_zalloc(asy_soft_state, instance); if (ret != DDI_SUCCESS) return (DDI_FAILURE); @@ -773,7 +975,8 @@ asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd) */ mutex_init(&asy->asy_excl, NULL, MUTEX_DRIVER, asy_soft_iblock); mutex_init(&asy->asy_excl_hi, NULL, MUTEX_DRIVER, - (void *)asy->asy_iblock); + (void *)asy->asy_iblock); + mutex_init(&asy->asy_soft_sr, NULL, MUTEX_DRIVER, asy_soft_iblock); mutex_enter(&asy->asy_excl); mutex_enter(&asy->asy_excl_hi); @@ -783,6 +986,7 @@ asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd) mutex_exit(&asy->asy_excl); mutex_destroy(&asy->asy_excl); mutex_destroy(&asy->asy_excl_hi); + mutex_destroy(&asy->asy_soft_sr); ddi_regs_map_free(&asy->asy_iohandle); cmn_err(CE_CONT, "Cannot identify UART chip at %p\n", (void *)asy->asy_ioaddr); @@ -796,11 +1000,10 @@ asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd) ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, DLAB); /* Set the baud rate to 9600 */ ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + (DAT+DLL), - asyspdtab[asy->asy_bidx] & 0xff); + asyspdtab[asy->asy_bidx] & 0xff); ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + (DAT+DLH), - (asyspdtab[asy->asy_bidx] >> 8) & 0xff); - ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, - asy->asy_lcr); + (asyspdtab[asy->asy_bidx] >> 8) & 0xff); + ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, asy->asy_lcr); ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, mcr); mutex_exit(&asy->asy_excl_hi); @@ -821,12 +1024,12 @@ asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd) ddi_regs_map_free(&asy->asy_iohandle); mutex_exit(&asy_glob_lock); cmn_err(CE_CONT, - "Can not set soft interrupt for ASY driver\n"); + "Can not set soft interrupt for ASY driver\n"); asy_soft_state_free(asy); return (DDI_FAILURE); } mutex_init(&asy_soft_lock, NULL, MUTEX_DRIVER, - (void *)asy->asy_iblock); + (void *)asy->asy_iblock); asy_addedsoft++; } mutex_exit(&asy_glob_lock); @@ -845,7 +1048,7 @@ asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd) mutex_destroy(&asy->asy_excl_hi); ddi_regs_map_free(&asy->asy_iohandle); cmn_err(CE_CONT, - "Can not set device interrupt for ASY driver\n"); + "Can not set device interrupt for ASY driver\n"); asy_soft_state_free(asy); return (DDI_FAILURE); } @@ -959,20 +1162,17 @@ asy_getproperty(dev_info_t *devi, struct asycom *asy, const char *property) if (ret != DDI_PROP_SUCCESS) { (void) sprintf(name, "com%c-%s", number, property); len = sizeof (val); - ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, - &len); + ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); } if (ret != DDI_PROP_SUCCESS) { (void) sprintf(name, "tty0%c-%s", number, property); len = sizeof (val); - ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, - &len); + ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); } if (ret != DDI_PROP_SUCCESS) { (void) sprintf(name, "port-%c-%s", letter, property); len = sizeof (val); - ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, - &len); + ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); } if (ret != DDI_PROP_SUCCESS) return (-1); /* property non-existant */ @@ -1375,7 +1575,7 @@ again: kmem_free(termiosp, len); } else cmn_err(CE_WARN, - "asy: couldn't get ttymodes property!"); + "asy: couldn't get ttymodes property!"); mutex_enter(&asy->asy_excl_hi); /* eeprom mode support - respect properties */ @@ -1394,8 +1594,9 @@ again: async->async_startc = CSTART; async->async_stopc = CSTOP; asy_program(asy, ASY_INIT); - } else if ((async->async_ttycommon.t_flags & TS_XCLUDE) && - secpolicy_excl_open(cr) != 0) { + } 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); @@ -1427,17 +1628,18 @@ again: mcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR); ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, - mcr|(asy->asy_mcr&DTR)); + mcr|(asy->asy_mcr&DTR)); DEBUGCONT3(ASY_DEBUG_INIT, - "asy%dopen: \"Raise DTR on every open\": make mcr = %x, " - "make TS_SOFTCAR = %s\n", - unit, mcr|(asy->asy_mcr&DTR), - (asy->asy_flags & ASY_IGNORE_CD) ? "ON" : "OFF"); + "asy%dopen: \"Raise DTR on every open\": make mcr = %x, " + "make TS_SOFTCAR = %s\n", + unit, mcr|(asy->asy_mcr&DTR), + (asy->asy_flags & ASY_IGNORE_CD) ? "ON" : "OFF"); + if (asy->asy_flags & ASY_IGNORE_CD) { DEBUGCONT1(ASY_DEBUG_MODEM, - "asy%dopen: ASY_IGNORE_CD set, set TS_SOFTCAR\n", - unit); + "asy%dopen: ASY_IGNORE_CD set, set TS_SOFTCAR\n", + unit); async->async_ttycommon.t_flags |= TS_SOFTCAR; } else @@ -1448,10 +1650,11 @@ again: */ asy->asy_msr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR); DEBUGCONT3(ASY_DEBUG_INIT, "asy%dopen: TS_SOFTCAR is %s, " - "MSR & DCD is %s\n", - unit, - (async->async_ttycommon.t_flags & TS_SOFTCAR) ? "set" : "clear", - (asy->asy_msr & DCD) ? "set" : "clear"); + "MSR & DCD is %s\n", + unit, + (async->async_ttycommon.t_flags & TS_SOFTCAR) ? "set" : "clear", + (asy->asy_msr & DCD) ? "set" : "clear"); + if (asy->asy_msr & DCD) async->async_flags |= ASYNC_CARR_ON; else @@ -1671,33 +1874,34 @@ nodrain: * If line has HUPCL set or is incompletely opened fix up the modem * lines. */ - DEBUGCONT1(ASY_DEBUG_MODEM, - "asy%dclose: next check HUPCL flag\n", instance); + DEBUGCONT1(ASY_DEBUG_MODEM, "asy%dclose: next check HUPCL flag\n", + instance); mutex_enter(&asy->asy_excl_hi); if ((async->async_ttycommon.t_cflag & HUPCL) || (async->async_flags & ASYNC_WOPEN)) { DEBUGCONT3(ASY_DEBUG_MODEM, - "asy%dclose: HUPCL flag = %x, ASYNC_WOPEN flag = %x\n", - instance, - async->async_ttycommon.t_cflag & HUPCL, - async->async_ttycommon.t_cflag & ASYNC_WOPEN); + "asy%dclose: HUPCL flag = %x, ASYNC_WOPEN flag = %x\n", + instance, + async->async_ttycommon.t_cflag & HUPCL, + async->async_ttycommon.t_cflag & ASYNC_WOPEN); async->async_flags |= ASYNC_DTR_DELAY; /* turn off DTR, RTS but NOT interrupt to 386 */ if (asy->asy_flags & (ASY_IGNORE_CD|ASY_RTS_DTR_OFF)) { DEBUGCONT3(ASY_DEBUG_MODEM, - "asy%dclose: ASY_IGNORE_CD flag = %x, " - "ASY_RTS_DTR_OFF flag = %x\n", - instance, - asy->asy_flags & ASY_IGNORE_CD, - asy->asy_flags & ASY_RTS_DTR_OFF); - ddi_put8(asy->asy_iohandle, - asy->asy_ioaddr + MCR, asy->asy_mcr|OUT2); + "asy%dclose: ASY_IGNORE_CD flag = %x, " + "ASY_RTS_DTR_OFF flag = %x\n", + instance, + asy->asy_flags & ASY_IGNORE_CD, + asy->asy_flags & ASY_RTS_DTR_OFF); + + ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, + asy->asy_mcr|OUT2); } else { DEBUGCONT1(ASY_DEBUG_MODEM, "asy%dclose: Dropping DTR and RTS\n", instance); - ddi_put8(asy->asy_iohandle, - asy->asy_ioaddr + MCR, OUT2); + ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, + OUT2); } async->async_dtrtid = timeout((void (*)())async_dtr_free, @@ -1707,10 +1911,9 @@ nodrain: * If nobody's using it now, turn off receiver interrupts. */ if ((async->async_flags & (ASYNC_WOPEN|ASYNC_ISOPEN)) == 0) { - icr = ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + ICR); + icr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ICR); ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, - (icr & ~RIEN)); + (icr & ~RIEN)); } mutex_exit(&asy->asy_excl_hi); out: @@ -1750,9 +1953,12 @@ asy_isbusy(struct asycom *asy) async = asy->asy_priv; ASSERT(mutex_owned(&asy->asy_excl)); ASSERT(mutex_owned(&asy->asy_excl_hi)); +/* + * XXXX this should be recoded + */ return ((async->async_ocnt > 0) || - ((ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + LSR) & (XSRE|XHRE)) == 0)); + ((ddi_get8(asy->asy_iohandle, + asy->asy_ioaddr + LSR) & (XSRE|XHRE)) == 0)); } static void @@ -1826,7 +2032,7 @@ asy_program(struct asycom *asy, int mode) #ifdef DEBUG instance = UNIT(async->async_dev); DEBUGCONT2(ASY_DEBUG_PROCS, - "asy%d_program: mode = 0x%08X, enter\n", instance, mode); + "asy%d_program: mode = 0x%08X, enter\n", instance, mode); #endif baudrate = BAUDINDEX(async->async_ttycommon.t_cflag); @@ -1836,15 +2042,15 @@ asy_program(struct asycom *asy, int mode) if (baudrate > CBAUD) { async->async_ttycommon.t_cflag |= CIBAUDEXT; async->async_ttycommon.t_cflag |= - (((baudrate - CBAUD - 1) << IBSHIFT) & CIBAUD); + (((baudrate - CBAUD - 1) << IBSHIFT) & CIBAUD); } else { async->async_ttycommon.t_cflag &= ~CIBAUDEXT; async->async_ttycommon.t_cflag |= - ((baudrate << IBSHIFT) & CIBAUD); + ((baudrate << IBSHIFT) & CIBAUD); } c_flag = async->async_ttycommon.t_cflag & - (CLOCAL|CREAD|CSTOPB|CSIZE|PARENB|PARODD|CBAUD|CBAUDEXT); + (CLOCAL|CREAD|CSTOPB|CSIZE|PARENB|PARODD|CBAUD|CBAUDEXT); /* disable interrupts */ ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0); @@ -1855,7 +2061,7 @@ asy_program(struct asycom *asy, int mode) (void) ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ISR); (void) ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR); asy->asy_msr = flush_reg = ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + MSR); + asy->asy_ioaddr + MSR); /* * The device is programmed in the open sequence, if we * have to hardware handshake, then this is a good time @@ -1892,17 +2098,16 @@ asy_program(struct asycom *asy, int mode) if (asy->asy_use_fifo == FIFO_ON) { for (flush_reg = asy->asy_fifo_buf; flush_reg-- > 0; ) { (void) ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + DAT); + asy->asy_ioaddr + DAT); } } else { flush_reg = ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + DAT); + asy->asy_ioaddr + DAT); } if (ocflags != (c_flag & ~CLOCAL) || mode == ASY_INIT) { /* Set line control */ - lcr = ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + LCR); + lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR); lcr &= ~(WLS0|WLS1|STB|PEN|EPS); if (c_flag & CSTOPB) @@ -1930,13 +2135,13 @@ asy_program(struct asycom *asy, int mode) } /* set the baud rate, unless it is "0" */ - ddi_put8(asy->asy_iohandle, - asy->asy_ioaddr + LCR, DLAB); + ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, DLAB); + if (baudrate != 0) { ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT, - asyspdtab[baudrate] & 0xff); + asyspdtab[baudrate] & 0xff); ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, - (asyspdtab[baudrate] >> 8) & 0xff); + (asyspdtab[baudrate] >> 8) & 0xff); } /* set the line control modes */ ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, lcr); @@ -1957,10 +2162,10 @@ asy_program(struct asycom *asy, int mode) if (baudrate == 0) ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, - (asy->asy_mcr & RTS) | OUT2); + (asy->asy_mcr & RTS) | OUT2); else ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, - asy->asy_mcr | OUT2); + asy->asy_mcr | OUT2); /* * Call the modem status interrupt handler to check for the carrier @@ -1971,10 +2176,10 @@ asy_program(struct asycom *asy, int mode) /* Set interrupt control */ DEBUGCONT3(ASY_DEBUG_MODM2, - "asy%d_program: c_flag & CLOCAL = %x t_cflag & CRTSCTS = %x\n", - instance, - c_flag & CLOCAL, - async->async_ttycommon.t_cflag & CRTSCTS); + "asy%d_program: c_flag & CLOCAL = %x t_cflag & CRTSCTS = %x\n", + instance, c_flag & CLOCAL, + async->async_ttycommon.t_cflag & CRTSCTS); + if ((c_flag & CLOCAL) && !(async->async_ttycommon.t_cflag & CRTSCTS)) /* * direct-wired line ignores DCD, so we don't enable modem @@ -2026,10 +2231,11 @@ asyintr(caddr_t argasy) uchar_t interrupt_id, lsr; interrupt_id = ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + ISR) & 0x0F; + asy->asy_ioaddr + ISR) & 0x0F; async = asy->asy_priv; + if ((async == NULL) || asy_addedsoft == 0 || - !(async->async_flags & (ASYNC_ISOPEN|ASYNC_WOPEN))) { + !(async->async_flags & (ASYNC_ISOPEN|ASYNC_WOPEN))) { if (interrupt_id & NOINTERRUPT) return (DDI_INTR_UNCLAIMED); else { @@ -2040,30 +2246,31 @@ asyintr(caddr_t argasy) * reading modem status */ (void) ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + LSR); + asy->asy_ioaddr + LSR); (void) ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + DAT); + asy->asy_ioaddr + DAT); asy->asy_msr = ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + MSR); + asy->asy_ioaddr + MSR); return (DDI_INTR_CLAIMED); } } + mutex_enter(&asy->asy_excl_hi); /* * We will loop until the interrupt line is pulled low. asy * interrupt is edge triggered. */ /* CSTYLED */ - for (;; interrupt_id = (ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + ISR) & 0x0F)) { + for (;; interrupt_id = + (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ISR) & 0x0F)) { + if (interrupt_id & NOINTERRUPT) break; ret_status = DDI_INTR_CLAIMED; - DEBUGCONT1(ASY_DEBUG_INTR, - "asyintr: interrupt_id = 0x%d\n", interrupt_id); - lsr = ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + LSR); + DEBUGCONT1(ASY_DEBUG_INTR, "asyintr: interrupt_id = 0x%d\n", + interrupt_id); + lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR); switch (interrupt_id) { case RxRDY: case RSTATUS: @@ -2218,9 +2425,9 @@ async_rxint(struct asycom *asy, uchar_t lsr) if (!(tp->t_cflag & CREAD)) { while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) { (void) (ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + DAT) & 0xff); + asy->asy_ioaddr + DAT) & 0xff); lsr = ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + LSR); + asy->asy_ioaddr + LSR); if (looplim-- < 0) /* limit loop */ break; } @@ -2232,7 +2439,7 @@ async_rxint(struct asycom *asy, uchar_t lsr) s = 0; /* reset error status */ if (lsr & RCA) { c = ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + DAT) & 0xff; + asy->asy_ioaddr + DAT) & 0xff; /* * We handle XON/XOFF char if IXON is set, @@ -2319,8 +2526,7 @@ async_rxint(struct asycom *asy, uchar_t lsr) else async->async_sw_overrun = 1; check_looplim: - lsr = ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + LSR); + lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR); if (looplim-- < 0) /* limit loop */ break; } @@ -2355,19 +2561,19 @@ async_msint_retry: /* this resets the interrupt */ msr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR); DEBUGCONT10(ASY_DEBUG_STATE, - "async%d_msint call #%d:\n" - " transition: %3s %3s %3s %3s\n" - "current state: %3s %3s %3s %3s\n", - instance, - ++(asy->asy_msint_cnt), - (msr & DCTS) ? "DCTS" : " ", - (msr & DDSR) ? "DDSR" : " ", - (msr & DRI) ? "DRI " : " ", - (msr & DDCD) ? "DDCD" : " ", - (msr & CTS) ? "CTS " : " ", - (msr & DSR) ? "DSR " : " ", - (msr & RI) ? "RI " : " ", - (msr & DCD) ? "DCD " : " "); + "async%d_msint call #%d:\n" + " transition: %3s %3s %3s %3s\n" + "current state: %3s %3s %3s %3s\n", + instance, + ++(asy->asy_msint_cnt), + (msr & DCTS) ? "DCTS" : " ", + (msr & DDSR) ? "DDSR" : " ", + (msr & DRI) ? "DRI " : " ", + (msr & DDCD) ? "DDCD" : " ", + (msr & CTS) ? "CTS " : " ", + (msr & DSR) ? "DSR " : " ", + (msr & RI) ? "RI " : " ", + (msr & DCD) ? "DCD " : " "); /* If CTS status is changed, do H/W output flow control */ if ((t_cflag & CRTSCTS) && (((asy->asy_msr ^ msr) & CTS) != 0)) @@ -2489,17 +2695,16 @@ begin: async->async_ext = 0; /* check for carrier up */ DEBUGCONT3(ASY_DEBUG_MODM2, - "async%d_softint: asy_msr & DCD = %x, " - "tp->t_flags & TS_SOFTCAR = %x\n", - instance, - asy->asy_msr & DCD, - tp->t_flags & TS_SOFTCAR); + "async%d_softint: asy_msr & DCD = %x, " + "tp->t_flags & TS_SOFTCAR = %x\n", + instance, asy->asy_msr & DCD, tp->t_flags & TS_SOFTCAR); + if (asy->asy_msr & DCD) { /* carrier present */ if ((async->async_flags & ASYNC_CARR_ON) == 0) { DEBUGCONT1(ASY_DEBUG_MODM2, - "async%d_softint: set ASYNC_CARR_ON\n", - instance); + "async%d_softint: set ASYNC_CARR_ON\n", + instance); async->async_flags |= ASYNC_CARR_ON; if (async->async_flags & ASYNC_ISOPEN) { mutex_exit(&asy->asy_excl_hi); @@ -2517,9 +2722,9 @@ begin: int flushflag; DEBUGCONT1(ASY_DEBUG_MODEM, - "async%d_softint: carrier dropped, " - "so drop DTR\n", - instance); + "async%d_softint: carrier dropped, " + "so drop DTR\n", + instance); /* * Carrier went away. * Drop DTR, abort any output in @@ -2528,60 +2733,62 @@ begin: * notification upstream. */ val = ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + MCR); + asy->asy_ioaddr + MCR); ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, (val & ~DTR)); + if (async->async_flags & ASYNC_BUSY) { - DEBUGCONT0(ASY_DEBUG_BUSY, + DEBUGCONT0(ASY_DEBUG_BUSY, "async_softint: " "Carrier dropped. " "Clearing async_ocnt\n"); - async->async_ocnt = 0; + async->async_ocnt = 0; } /* if */ 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); - DEBUGCONT1(ASY_DEBUG_MODEM, - "async%d_softint: " - "putctl(q, M_HANGUP)\n", - instance); - /* - * Flush FIFO buffers - * Any data left in there is invalid now - */ - if (asy->asy_use_fifo == FIFO_ON) - asy_reset_fifo(asy, FIFOTXFLSH); - /* - * Flush our write queue if we have one. - * - * If we're in the midst of close, then flush - * everything. Don't leave stale ioctls lying - * about. - */ - flushflag = (async->async_flags & - ASYNC_CLOSING) ? FLUSHALL : FLUSHDATA; - flushq(tp->t_writeq, flushflag); - - bp = async->async_xmitblk; /* active msg */ - if (bp != NULL) { - freeb(bp); - async->async_xmitblk = NULL; - } + mutex_exit(&asy->asy_excl_hi); + mutex_exit(&asy->asy_excl); + (void) putctl(q, M_HANGUP); + mutex_enter(&asy->asy_excl); + DEBUGCONT1(ASY_DEBUG_MODEM, + "async%d_softint: " + "putctl(q, M_HANGUP)\n", + instance); + /* + * Flush FIFO buffers + * Any data left in there is invalid now + */ + if (asy->asy_use_fifo == FIFO_ON) + asy_reset_fifo(asy, FIFOTXFLSH); + /* + * Flush our write queue if we have one. + * If we're in the midst of close, then + * flush everything. Don't leave stale + * ioctls lying about. + */ + flushflag = (async->async_flags & + ASYNC_CLOSING) ? FLUSHALL : + FLUSHDATA; + flushq(tp->t_writeq, flushflag); + + /* active msg */ + bp = async->async_xmitblk; + if (bp != NULL) { + freeb(bp); + async->async_xmitblk = NULL; + } - mutex_enter(&asy->asy_excl_hi); - async->async_flags &= ~ASYNC_BUSY; - /* - * This message warns of Carrier loss - * with data left to transmit can hang the - * system. - */ - DEBUGCONT0(ASY_DEBUG_MODEM, - "async_softint: Flushing to " - "prevent HUPCL hanging\n"); + mutex_enter(&asy->asy_excl_hi); + async->async_flags &= ~ASYNC_BUSY; + /* + * This message warns of Carrier loss + * with data left to transmit can hang + * the system. + */ + DEBUGCONT0(ASY_DEBUG_MODEM, + "async_softint: Flushing to " + "prevent HUPCL hanging\n"); } /* if (ASYNC_ISOPEN) */ } /* if (ASYNC_CARR_ON && CLOCAL) */ async->async_flags &= ~ASYNC_CARR_ON; @@ -2625,8 +2832,10 @@ begin: IN_FLOW_STREAMS); mutex_exit(&asy->asy_excl_hi); } - DEBUGCONT2(ASY_DEBUG_INPUT, - "async%d_softint: %d char(s) in queue.\n", instance, cc); + + DEBUGCONT2(ASY_DEBUG_INPUT, "async%d_softint: %d char(s) in queue.\n", + instance, cc); + if (!(bp = allocb(cc, BPRI_MED))) { mutex_exit(&asy->asy_excl); ttycommon_qfull(&async->async_ttycommon, q); @@ -2648,7 +2857,7 @@ begin: if (bp->b_wptr > bp->b_rptr) { if (!canput(q)) { asyerror(CE_NOTE, "asy%d: local queue full", - instance); + instance); freemsg(bp); } else (void) putq(q, bp); @@ -2732,7 +2941,7 @@ rv: mutex_exit(&asy->asy_excl_hi); mutex_exit(&asy->asy_excl); asyerror(CE_NOTE, "asy%d: ring buffer overflow", - instance); + instance); mutex_enter(&asy->asy_excl); mutex_enter(&asy->asy_excl_hi); } @@ -2775,10 +2984,9 @@ async_restart(void *arg) if ((async->async_flags & ASYNC_BREAK) && !(async->async_flags & ASYNC_OUT_SUSPEND)) { mutex_enter(&asy->asy_excl_hi); - lcr = ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + LCR); + lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR); ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, - (lcr & ~SETBREAK)); + (lcr & ~SETBREAK)); mutex_exit(&asy->asy_excl_hi); } async->async_flags &= ~(ASYNC_DELAY|ASYNC_BREAK); @@ -2831,9 +3039,9 @@ async_nstart(struct asyncline *async, int mode) */ if (async->async_flags & (ASYNC_BREAK|ASYNC_BUSY)) { DEBUGCONT2((mode? ASY_DEBUG_OUT : 0), - "async%d_nstart: start %s.\n", - instance, - async->async_flags & ASYNC_BREAK ? "break" : "busy"); + "async%d_nstart: start %s.\n", + instance, + async->async_flags & ASYNC_BREAK ? "break" : "busy"); return; } @@ -2851,13 +3059,13 @@ async_nstart(struct asyncline *async, int mode) */ if (async->async_flags & ASYNC_DELAY) { DEBUGCONT1((mode? ASY_DEBUG_OUT : 0), - "async%d_nstart: start ASYNC_DELAY.\n", instance); + "async%d_nstart: start ASYNC_DELAY.\n", instance); return; } if ((q = async->async_ttycommon.t_writeq) == NULL) { DEBUGCONT1((mode? ASY_DEBUG_OUT : 0), - "async%d_nstart: start writeq is null.\n", instance); + "async%d_nstart: start writeq is null.\n", instance); return; /* not attached to a stream */ } @@ -2882,9 +3090,9 @@ async_nstart(struct asyncline *async, int mode) */ mutex_enter(&asy->asy_excl_hi); val = ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + LCR); - ddi_put8(asy->asy_iohandle, - asy->asy_ioaddr + LCR, (val | SETBREAK)); + asy->asy_ioaddr + LCR); + ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, + (val | SETBREAK)); mutex_exit(&asy->asy_excl_hi); async->async_flags |= ASYNC_BREAK; (void) timeout(async_restart, (caddr_t)async, @@ -2977,9 +3185,8 @@ async_nstart(struct asyncline *async, int mode) if (didsome) async->async_flags |= ASYNC_PROGRESS; DEBUGCONT2(ASY_DEBUG_BUSY, - "async%d_nstart: Set ASYNC_BUSY. async_ocnt=%d\n", - instance, - async->async_ocnt); + "async%d_nstart: Set ASYNC_BUSY. async_ocnt=%d\n", + instance, async->async_ocnt); async->async_flags |= ASYNC_BUSY; mutex_exit(&asy->asy_excl_hi); } @@ -3123,12 +3330,13 @@ async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp) * ioctls, so keep the others safe too. */ DEBUGCONT2(ASY_DEBUG_IOCTL, "async%d_ioctl: %s\n", - instance, - iocp->ioc_cmd == TIOCMGET ? "TIOCMGET" : - iocp->ioc_cmd == TIOCMSET ? "TIOCMSET" : - iocp->ioc_cmd == TIOCMBIS ? "TIOCMBIS" : - iocp->ioc_cmd == TIOCMBIC ? "TIOCMBIC" : - "other"); + instance, + iocp->ioc_cmd == TIOCMGET ? "TIOCMGET" : + iocp->ioc_cmd == TIOCMSET ? "TIOCMSET" : + iocp->ioc_cmd == TIOCMBIS ? "TIOCMBIS" : + iocp->ioc_cmd == TIOCMBIC ? "TIOCMBIC" : + "other"); + switch (iocp->ioc_cmd) { case TIOCMGET: case TIOCGPPS: @@ -3330,14 +3538,15 @@ async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp) * clock / (baud * 16) * 16 * 2. */ index = BAUDINDEX( - async->async_ttycommon.t_cflag); + async->async_ttycommon.t_cflag); async->async_flags |= ASYNC_BREAK; + while ((ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & XSRE) == 0) { mutex_exit(&asy->asy_excl_hi); mutex_exit(&asy->asy_excl); drv_usecwait( - 32*asyspdtab[index] & 0xfff); + 32*asyspdtab[index] & 0xfff); mutex_enter(&asy->asy_excl); mutex_enter(&asy->asy_excl_hi); } @@ -3348,23 +3557,23 @@ async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp) * "async_start" to grab the next message. */ val = ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + LCR); + asy->asy_ioaddr + LCR); ddi_put8(asy->asy_iohandle, - asy->asy_ioaddr + LCR, - (val | SETBREAK)); + asy->asy_ioaddr + LCR, + (val | SETBREAK)); mutex_exit(&asy->asy_excl_hi); (void) timeout(async_restart, (caddr_t)async, drv_usectohz(1000000)/4); } else { DEBUGCONT1(ASY_DEBUG_OUT, - "async%d_ioctl: wait for flush.\n", - instance); + "async%d_ioctl: wait for flush.\n", + instance); mutex_enter(&asy->asy_excl_hi); asy_waiteot(asy); mutex_exit(&asy->asy_excl_hi); DEBUGCONT1(ASY_DEBUG_OUT, - "async%d_ioctl: ldterm satisfied.\n", - instance); + "async%d_ioctl: ldterm satisfied.\n", + instance); } break; @@ -3409,7 +3618,7 @@ async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp) case TIOCMBIC: if (iocp->ioc_count != TRANSPARENT) { DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: " - "non-transparent\n", instance); + "non-transparent\n", instance); error = miocpullup(mp, sizeof (int)); if (error != 0) @@ -3417,14 +3626,14 @@ async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp) mutex_enter(&asy->asy_excl_hi); (void) asymctl(asy, - dmtoasy(*(int *)mp->b_cont->b_rptr), - iocp->ioc_cmd); + 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; } else { DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: " - "transparent\n", instance); + "transparent\n", instance); mcopyin(mp, NULL, sizeof (int), NULL); } break; @@ -3442,12 +3651,11 @@ async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp) if (iocp->ioc_count == TRANSPARENT) { DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: " - "transparent\n", instance); - mcopyout(mp, NULL, sizeof (int), NULL, - datamp); + "transparent\n", instance); + mcopyout(mp, NULL, sizeof (int), NULL, datamp); } else { DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: " - "non-transparent\n", instance); + "non-transparent\n", instance); mioc2ack(mp, datamp, sizeof (int), 0); } break; @@ -3458,7 +3666,7 @@ async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp) break; *(struct cons_polledio **)mp->b_cont->b_rptr = - &asy->polledio; + &asy->polledio; mp->b_datap->db_type = M_IOCACK; break; @@ -3498,7 +3706,7 @@ async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp) */ mcopyout(mp, NULL, sizeof (boolean_t), NULL, NULL); *(boolean_t *)mp->b_cont->b_rptr = - (asy->asy_flags & ASY_CONSOLE) != 0; + (asy->asy_flags & ASY_CONSOLE) != 0; break; default: @@ -3534,7 +3742,16 @@ asyrsrv(queue_t *q) } /* - * Put procedure for write queue. + * The ASYWPUTDO_NOT_SUSP macro indicates to asywputdo() whether it should + * handle messages as though the driver is operating normally or is + * suspended. In the suspended case, some or all of the processing may have + * to be delayed until the driver is resumed. + */ +#define ASYWPUTDO_NOT_SUSP(async, wput) \ + !((wput) && ((async)->async_flags & ASYNC_DDI_SUSPENDED)) + +/* + * Processing for write queue put procedure. * 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 @@ -3545,7 +3762,7 @@ asyrsrv(queue_t *q) * as we do in ldterm. */ static int -asywput(queue_t *q, mblk_t *mp) +asywputdo(queue_t *q, mblk_t *mp, boolean_t wput) { struct asyncline *async; struct asycom *asy; @@ -3555,6 +3772,7 @@ asywput(queue_t *q, mblk_t *mp) int error; async = (struct asyncline *)q->q_ptr; + #ifdef DEBUG instance = UNIT(async->async_dev); #endif @@ -3577,17 +3795,19 @@ asywput(queue_t *q, mblk_t *mp) 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); + if (ASYWPUTDO_NOT_SUSP(async, wput)) { + /* + * 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); @@ -3606,19 +3826,21 @@ asywput(queue_t *q, mblk_t *mp) if (*(int *)mp->b_cont->b_rptr != 0) { DEBUGCONT1(ASY_DEBUG_OUT, - "async%d_ioctl: flush request.\n", - instance); + "async%d_ioctl: flush request.\n", + instance); (void) putq(q, mp); - mutex_enter(&asy->asy_excl); - /* - * If an TIOCSBRK is in progress, - * clean it as TIOCCBRK does, - * then kick off output. - * If TIOCSBRK is not in progress, - * just kick off output. - */ - async_resume_utbrk(async); + mutex_enter(&asy->asy_excl); + if (ASYWPUTDO_NOT_SUSP(async, wput)) { + /* + * If an TIOCSBRK is in progress, + * clean it as TIOCCBRK does, + * then kick off output. + * If TIOCSBRK is not in progress, + * just kick off output. + */ + async_resume_utbrk(async); + } mutex_exit(&asy->asy_excl); break; } @@ -3636,16 +3858,18 @@ asywput(queue_t *q, mblk_t *mp) * start routine, just in case. */ (void) putq(q, mp); - mutex_enter(&asy->asy_excl); - /* - * If an TIOCSBRK is in progress, - * clean it as TIOCCBRK does. - * then kick off output. - * If TIOCSBRK is not in progress, - * just kick off output. - */ - async_resume_utbrk(async); + mutex_enter(&asy->asy_excl); + if (ASYWPUTDO_NOT_SUSP(async, wput)) { + /* + * If an TIOCSBRK is in progress, + * clean it as TIOCCBRK does. + * then kick off output. + * If TIOCSBRK is not in progress, + * just kick off output. + */ + async_resume_utbrk(async); + } mutex_exit(&asy->asy_excl); break; @@ -3653,7 +3877,14 @@ asywput(queue_t *q, mblk_t *mp) /* * Do it now. */ - async_ioctl(async, q, mp); + mutex_enter(&asy->asy_excl); + if (ASYWPUTDO_NOT_SUSP(async, wput)) { + mutex_exit(&asy->asy_excl); + async_ioctl(async, q, mp); + break; + } + async_put_suspq(asy, mp); + mutex_exit(&asy->asy_excl); break; } break; @@ -3667,13 +3898,20 @@ asywput(queue_t *q, mblk_t *mp) */ mutex_enter(&asy->asy_excl_hi); if (async->async_flags & ASYNC_BUSY) { - DEBUGCONT1(ASY_DEBUG_BUSY, "asy%dwput: " + DEBUGCONT1(ASY_DEBUG_BUSY, "asy%dwput: " "Clearing async_ocnt, " "leaving ASYNC_BUSY set\n", instance); async->async_ocnt = 0; async->async_flags &= ~ASYNC_BUSY; } /* if */ + + if (ASYWPUTDO_NOT_SUSP(async, wput)) { + /* Flush FIFO buffers */ + if (asy->asy_use_fifo == FIFO_ON) { + asy_reset_fifo(asy, FIFOTXFLSH); + } + } mutex_exit(&asy->asy_excl_hi); /* Flush FIFO buffers */ @@ -3693,9 +3931,11 @@ asywput(queue_t *q, mblk_t *mp) *mp->b_rptr &= ~FLUSHW; /* it has been flushed */ } if (*mp->b_rptr & FLUSHR) { - /* Flush FIFO buffers */ - if (asy->asy_use_fifo == FIFO_ON) { - asy_reset_fifo(asy, FIFORXFLSH); + if (ASYWPUTDO_NOT_SUSP(async, wput)) { + /* Flush FIFO buffers */ + if (asy->asy_use_fifo == FIFO_ON) { + asy_reset_fifo(asy, FIFORXFLSH); + } } flushq(RD(q), FLUSHDATA); qreply(q, mp); /* give the read queues a crack at it */ @@ -3707,9 +3947,11 @@ asywput(queue_t *q, mblk_t *mp) * We must make sure we process messages that survive the * write-side flush. */ - mutex_enter(&asy->asy_excl); - async_start(async); - mutex_exit(&asy->asy_excl); + if (ASYWPUTDO_NOT_SUSP(async, wput)) { + mutex_enter(&asy->asy_excl); + async_start(async); + mutex_exit(&asy->asy_excl); + } break; case M_BREAK: @@ -3720,44 +3962,64 @@ asywput(queue_t *q, mblk_t *mp) * and poke the start routine. */ (void) putq(q, mp); - mutex_enter(&asy->asy_excl); - async_start(async); - mutex_exit(&asy->asy_excl); + if (ASYWPUTDO_NOT_SUSP(async, wput)) { + mutex_enter(&asy->asy_excl); + async_start(async); + mutex_exit(&asy->asy_excl); + } break; case M_STOPI: mutex_enter(&asy->asy_excl); - mutex_enter(&asy->asy_excl_hi); - if (!(async->async_inflow_source & IN_FLOW_USER)) { - async_flowcontrol_hw_input(asy, FLOW_STOP, - IN_FLOW_USER); - (void) async_flowcontrol_sw_input(asy, FLOW_STOP, - IN_FLOW_USER); + if (ASYWPUTDO_NOT_SUSP(async, wput)) { + mutex_enter(&asy->asy_excl_hi); + if (!(async->async_inflow_source & IN_FLOW_USER)) { + async_flowcontrol_hw_input(asy, FLOW_STOP, + IN_FLOW_USER); + (void) async_flowcontrol_sw_input(asy, + FLOW_STOP, IN_FLOW_USER); + } + mutex_exit(&asy->asy_excl_hi); + mutex_exit(&asy->asy_excl); + freemsg(mp); + break; } - mutex_exit(&asy->asy_excl_hi); + async_put_suspq(asy, mp); mutex_exit(&asy->asy_excl); - freemsg(mp); break; case M_STARTI: mutex_enter(&asy->asy_excl); - mutex_enter(&asy->asy_excl_hi); - if (async->async_inflow_source & IN_FLOW_USER) { - async_flowcontrol_hw_input(asy, FLOW_START, - IN_FLOW_USER); - (void) async_flowcontrol_sw_input(asy, FLOW_START, - IN_FLOW_USER); + if (ASYWPUTDO_NOT_SUSP(async, wput)) { + mutex_enter(&asy->asy_excl_hi); + if (async->async_inflow_source & IN_FLOW_USER) { + async_flowcontrol_hw_input(asy, FLOW_START, + IN_FLOW_USER); + (void) async_flowcontrol_sw_input(asy, + FLOW_START, IN_FLOW_USER); + } + mutex_exit(&asy->asy_excl_hi); + mutex_exit(&asy->asy_excl); + freemsg(mp); + break; } - mutex_exit(&asy->asy_excl_hi); + async_put_suspq(asy, mp); 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); + mutex_enter(&asy->asy_excl); + if (ASYWPUTDO_NOT_SUSP(async, wput)) { + ((struct iocblk *)mp->b_rptr)->ioc_cmd = + MC_HAS_POSIX; + mutex_exit(&asy->asy_excl); + qreply(q, mp); + break; + } else { + async_put_suspq(asy, mp); + } } else { /* * These MC_SERVICE type messages are used by upper @@ -3784,7 +4046,14 @@ asywput(queue_t *q, mblk_t *mp) break; case M_IOCDATA: - async_iocdata(q, mp); + mutex_enter(&asy->asy_excl); + if (ASYWPUTDO_NOT_SUSP(async, wput)) { + mutex_exit(&asy->asy_excl); + async_iocdata(q, mp); + break; + } + async_put_suspq(asy, mp); + mutex_exit(&asy->asy_excl); break; default: @@ -3794,6 +4063,12 @@ asywput(queue_t *q, mblk_t *mp) return (0); } +static int +asywput(queue_t *q, mblk_t *mp) +{ + return (asywputdo(q, mp, B_TRUE)); +} + /* * Retry an "ioctl", now that "bufcall" claims we may be able to allocate * the buffer we need. @@ -3853,11 +4128,11 @@ async_iocdata(queue_t *q, mblk_t *mp) mutex_enter(&asy->asy_excl); DEBUGCONT2(ASY_DEBUG_MODEM, "async%d_iocdata: case %s\n", - instance, - csp->cp_cmd == TIOCMGET ? "TIOCMGET" : - csp->cp_cmd == TIOCMSET ? "TIOCMSET" : - csp->cp_cmd == TIOCMBIS ? "TIOCMBIS" : - "TIOCMBIC"); + instance, + csp->cp_cmd == TIOCMGET ? "TIOCMGET" : + csp->cp_cmd == TIOCMSET ? "TIOCMSET" : + csp->cp_cmd == TIOCMBIS ? "TIOCMBIS" : + "TIOCMBIC"); switch (csp->cp_cmd) { case TIOCMGET: @@ -3876,9 +4151,8 @@ async_iocdata(queue_t *q, mblk_t *mp) case TIOCMBIS: case TIOCMBIC: mutex_enter(&asy->asy_excl_hi); - (void) asymctl(asy, - dmtoasy(*(int *)mp->b_cont->b_rptr), - csp->cp_cmd); + (void) asymctl(asy, dmtoasy(*(int *)mp->b_cont->b_rptr), + csp->cp_cmd); mutex_exit(&asy->asy_excl_hi); mioc2ack(mp, NULL, 0, 0); break; @@ -3927,8 +4201,8 @@ asyischar(cons_polledio_arg_t arg) { struct asycom *asy = (struct asycom *)arg; - return ((ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + LSR) & RCA) != 0); + return ((ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & RCA) + != 0); } /* @@ -3941,8 +4215,7 @@ asygetchar(cons_polledio_arg_t arg) while (!asyischar(arg)) drv_usecwait(10); - return (ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + DAT)); + return (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + DAT)); } /* @@ -3964,19 +4237,19 @@ asymctl(struct asycom *asy, int bits, int how) case TIOCMSET: DEBUGCONT2(ASY_DEBUG_MODEM, - "asy%dmctl: TIOCMSET, bits = %x\n", instance, bits); + "asy%dmctl: TIOCMSET, bits = %x\n", instance, bits); mcr_r = bits; /* Set bits */ break; case TIOCMBIS: DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dmctl: TIOCMBIS, bits = %x\n", - instance, bits); + instance, bits); mcr_r |= bits; /* Mask in bits */ break; case TIOCMBIC: DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dmctl: TIOCMBIC, bits = %x\n", - instance, bits); + instance, bits); mcr_r &= ~bits; /* Mask out bits */ break; @@ -3990,17 +4263,17 @@ asymctl(struct asycom *asy, int bits, int how) asy->asy_ioaddr + ICR) & MIEN) { msr_r = asy->asy_msr; DEBUGCONT2(ASY_DEBUG_MODEM, - "asy%dmctl: TIOCMGET, read msr_r = %x\n", - instance, msr_r); + "asy%dmctl: TIOCMGET, read msr_r = %x\n", + instance, msr_r); } else { msr_r = ddi_get8(asy->asy_iohandle, - asy->asy_ioaddr + MSR); + asy->asy_ioaddr + MSR); DEBUGCONT2(ASY_DEBUG_MODEM, - "asy%dmctl: TIOCMGET, read MSR = %x\n", - instance, msr_r); + "asy%dmctl: TIOCMGET, read MSR = %x\n", + instance, msr_r); } DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dtodm: modem_lines = %x\n", - instance, asytodm(mcr_r, msr_r)); + instance, asytodm(mcr_r, msr_r)); return (asytodm(mcr_r, msr_r)); } diff --git a/usr/src/uts/common/io/audio/sada/drv/audio810/audio810.c b/usr/src/uts/common/io/audio/sada/drv/audio810/audio810.c index 19cc147773..5001635a43 100644 --- a/usr/src/uts/common/io/audio/sada/drv/audio810/audio810.c +++ b/usr/src/uts/common/io/audio/sada/drv/audio810/audio810.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -160,6 +160,8 @@ static uint_t audio810_intr(caddr_t); /* * Local Routine Prototypes */ +static void audio810_set_busy(audio810_state_t *); +static void audio810_set_idle(audio810_state_t *); static int audio810_codec_sync(audio810_state_t *); static int audio810_write_ac97(audio810_state_t *, int, uint16_t); static int audio810_read_ac97(audio810_state_t *, int, uint16_t *); @@ -569,17 +571,49 @@ audio810_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) switch (cmd) { case DDI_ATTACH: break; - - /* - * now, no suspend/resume supported. we'll do it in the future. - */ case DDI_RESUME: ATRACE("I810_attach() DDI_RESUME", NULL); - audio_sup_log(NULL, CE_WARN, - "%s%d: i810_attach() resume is not supported yet", - audio810_name, instance); - return (DDI_FAILURE); + if ((statep = ddi_get_soft_state(audio810_statep, instance)) == + NULL) { + audio_sup_log(NULL, CE_WARN, + "!attach() DDI_RESUME get soft state failed"); + return (DDI_FAILURE); + } + + ASSERT(dip == statep->dip); + + mutex_enter(&statep->inst_lock); + + ASSERT(statep->i810_suspended == I810_SUSPENDED); + + statep->i810_suspended = I810_NOT_SUSPENDED; + + /* Restore the audio810 chip's state */ + if (audio810_chip_init(statep, I810_INIT_RESTORE) != + AUDIO_SUCCESS) { + audio_sup_log(statep->audio_handle, CE_WARN, + "!attach() DDI_RESUME failed to init chip"); + mutex_exit(&statep->inst_lock); + return (DDI_FAILURE); + } + + mutex_exit(&statep->inst_lock); + + /* Resume playing and recording, if required */ + if (audio_sup_restore_state(statep->audio_handle, + AUDIO_ALL_DEVICES, AUDIO_BOTH) == AUDIO_FAILURE) { + audio_sup_log(statep->audio_handle, CE_WARN, + "!attach() DDI_RESUME audio restart failed"); + } + + mutex_enter(&statep->inst_lock); + cv_broadcast(&statep->i810_cv); /* let entry points continue */ + mutex_exit(&statep->inst_lock); + + ATRACE("audio810_attach() DDI_RESUME done", NULL); + + return (DDI_SUCCESS); default: audio_sup_log(NULL, CE_WARN, "!%s%d: attach() unknown command: 0x%x", @@ -638,7 +672,7 @@ audio810_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) /* set PCI command register */ cmdreg = pci_config_get16(statep->pci_conf_handle, PCI_CONF_COMM); pci_config_put16(statep->pci_conf_handle, PCI_CONF_COMM, - cmdreg | PCI_COMM_IO | PCI_COMM_MAE | PCI_COMM_ME); + cmdreg | PCI_COMM_IO | PCI_COMM_MAE | PCI_COMM_ME); if ((audio810_alloc_sample_buf(statep, I810_DMA_PCM_OUT, statep->play_buf_size) == AUDIO_FAILURE) || @@ -700,6 +734,7 @@ error_unmap: error_destroy: ATRACE("audio810_attach() error_destroy", statep); mutex_destroy(&statep->inst_lock); + cv_destroy(&statep->i810_cv); error_audiosup: ATRACE("audio810_attach() error_audiosup", statep); @@ -751,17 +786,33 @@ audio810_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) switch (cmd) { case DDI_DETACH: break; - - /* - * now, no suspend/resume supported. we'll do it in the future. - */ case DDI_SUSPEND: ATRACE("i810_detach() SUSPEND", statep); - audio_sup_log(NULL, CE_WARN, - "%s%d: i810_detach() suspend is not supported yet", - audio810_name, instance); - return (DDI_FAILURE); + mutex_enter(&statep->inst_lock); + + ASSERT(statep->i810_suspended == I810_NOT_SUSPENDED); + + statep->i810_suspended = I810_SUSPENDED; /* stop new ops */ + + /* wait for current operations to complete */ + while (statep->i810_busy_cnt != 0) + cv_wait(&statep->i810_cv, &statep->inst_lock); + + /* stop DMA engines */ + audio810_stop_dma(statep); + + if (audio_sup_save_state(statep->audio_handle, + AUDIO_ALL_DEVICES, AUDIO_BOTH) == AUDIO_FAILURE) { + audio_sup_log(statep->audio_handle, CE_WARN, + "!detach() DDI_SUSPEND audio save failed"); + } + + mutex_exit(&statep->inst_lock); + + ATRACE("audio810_detach() DDI_SUSPEND successful", statep); + + return (DDI_SUCCESS); default: ATRACE("i810_detach() unknown command", cmd); audio_sup_log(statep->audio_handle, CE_WARN, @@ -796,6 +847,7 @@ audio810_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) (void) audio_sup_unregister(statep->audio_handle); mutex_destroy(&statep->inst_lock); + cv_destroy(&statep->i810_cv); audio810_unmap_regs(statep); @@ -835,6 +887,13 @@ audio810_intr(caddr_t arg) statep = (audio810_state_t *)arg; mutex_enter(&statep->inst_lock); + + if (statep->i810_suspended == I810_SUSPENDED) { + ATRACE("audio810_intr() device suspended", NULL); + mutex_exit(&statep->inst_lock); + return (DDI_INTR_UNCLAIMED); + } + gsr = I810_BM_GET32(I810_REG_GSR); /* check if device is interrupting */ @@ -883,6 +942,97 @@ audio810_intr(caddr_t arg) } /* audio810_intr() */ +/* + * audio810_set_busy() + * + * Description: + * This routine is called whenever a routine needs to guarantee + * that it will not be suspended. It will also block any routine + * while a suspend is going on. + * + * CAUTION: This routine cannot be called by routines that will + * block. Otherwise DDI_SUSPEND will be blocked for a + * long time. And that is the wrong thing to do. + * + * Arguments: + * audio810_state_t *statep The device's state structure + * + * Returns: + * void + */ +static void +audio810_set_busy(audio810_state_t *statep) +{ + ATRACE("in audio810_set_busy()", statep); + + ASSERT(!mutex_owned(&statep->inst_lock)); + + /* get the lock so we are safe */ + mutex_enter(&statep->inst_lock); + + /* block if we are suspended */ + while (statep->i810_suspended == I810_SUSPENDED) { + cv_wait(&statep->i810_cv, &statep->inst_lock); + } + + /* + * Okay, we aren't suspended, so mark as busy. + * This will keep us from being suspended when we release the lock. + */ + ASSERT(statep->i810_busy_cnt >= 0); + statep->i810_busy_cnt++; + + mutex_exit(&statep->inst_lock); + + ATRACE("audio810_set_busy() done", statep); + + ASSERT(!mutex_owned(&statep->inst_lock)); + +} /* audio810_set_busy() */ + +/* + * audio810_set_idle() + * + * Description: + * This routine reduces the busy count. It then does a cv_broadcast() + * if the count is 0 so a waiting DDI_SUSPEND will continue forward. + * + * Arguments: + * audio810_state_t *state The device's state structure + * + * Returns: + * void + */ +static void +audio810_set_idle(audio810_state_t *statep) +{ + ATRACE("in audio810_set_idle()", statep); + + ASSERT(!mutex_owned(&statep->inst_lock)); + + /* get the lock so we are safe */ + mutex_enter(&statep->inst_lock); + + ASSERT(statep->i810_suspended == I810_NOT_SUSPENDED); + + /* decrement the busy count */ + ASSERT(statep->i810_busy_cnt > 0); + statep->i810_busy_cnt--; + + /* if no longer busy, then we wake up a waiting SUSPEND */ + if (statep->i810_busy_cnt == 0) { + cv_broadcast(&statep->i810_cv); + } + + /* we're done, so unlock */ + mutex_exit(&statep->inst_lock); + + ATRACE("audio810_set_idle() done", statep); + + ASSERT(!mutex_owned(&statep->inst_lock)); + +} /* audio810_set_idle() */ + /* *********************** Mixer Entry Point Routines ******************* */ /* * audio810_ad_set_config() @@ -926,6 +1076,8 @@ audio810_ad_set_config(audiohdl_t ahandle, int stream, int command, statep = audio_sup_get_private(ahandle); ASSERT(statep); + audio810_set_busy(statep); + mutex_enter(&statep->inst_lock); switch (command) { case AM_SET_GAIN: @@ -1030,6 +1182,8 @@ audio810_ad_set_config(audiohdl_t ahandle, int stream, int command, } mutex_exit(&statep->inst_lock); + audio810_set_idle(statep); + ATRACE_32("i810_ad_set_config() returning", rc); return (rc); @@ -1063,6 +1217,7 @@ audio810_ad_set_format(audiohdl_t ahandle, int stream, int dir, { audio810_state_t *statep; uint16_t val; + int rc = AUDIO_FAILURE; ASSERT(precision == AUDIO_PRECISION_16); ASSERT(channels == AUDIO_CHANNELS_STEREO); @@ -1083,6 +1238,8 @@ audio810_ad_set_format(audiohdl_t ahandle, int stream, int dir, statep = audio_sup_get_private(ahandle); ASSERT(statep); + audio810_set_busy(statep); + mutex_enter(&statep->inst_lock); if (statep->var_sr == B_FALSE) { @@ -1092,8 +1249,7 @@ audio810_ad_set_format(audiohdl_t ahandle, int stream, int dir, audio_sup_log(statep->audio_handle, CE_NOTE, "!ad_set_format() bad sample rate %d\n", sample_rate); - mutex_exit(&statep->inst_lock); - return (AUDIO_FAILURE); + goto done; } } else { switch (sample_rate) { @@ -1111,8 +1267,7 @@ audio810_ad_set_format(audiohdl_t ahandle, int stream, int dir, case I810_SAMPR48000: break; default: ATRACE_32("i810_ad_set_format() bad SR", sample_rate); - mutex_exit(&statep->inst_lock); - return (AUDIO_FAILURE); + goto done; } } @@ -1140,8 +1295,7 @@ audio810_ad_set_format(audiohdl_t ahandle, int stream, int dir, audio_sup_log(statep->audio_handle, CE_NOTE, "!set_format() bad output sample rate %d", sample_rate); - mutex_exit(&statep->inst_lock); - return (AUDIO_FAILURE); + goto done; } } @@ -1170,8 +1324,7 @@ audio810_ad_set_format(audiohdl_t ahandle, int stream, int dir, audio_sup_log(statep->audio_handle, CE_NOTE, "!set_format() bad input sample rate %d", sample_rate); - mutex_exit(&statep->inst_lock); - return (AUDIO_FAILURE); + goto done; } } @@ -1180,12 +1333,15 @@ audio810_ad_set_format(audiohdl_t ahandle, int stream, int dir, statep->i810_cprecision = precision; } + rc = AUDIO_SUCCESS; done: mutex_exit(&statep->inst_lock); - ATRACE_32("i810_ad_set_format() returning success", 0); + audio810_set_idle(statep); - return (AUDIO_SUCCESS); + ATRACE_32("i810_ad_set_format() returning", rc); + + return (rc); } /* audio810_ad_set_format() */ @@ -1215,6 +1371,8 @@ audio810_ad_start_play(audiohdl_t ahandle, int stream) statep = audio_sup_get_private(ahandle); ASSERT(statep); + audio810_set_busy(statep); + mutex_enter(&statep->inst_lock); if (statep->flags & I810_DMA_PLAY_PAUSED) { @@ -1239,6 +1397,9 @@ audio810_ad_start_play(audiohdl_t ahandle, int stream) done: mutex_exit(&statep->inst_lock); + + audio810_set_idle(statep); + return (rc); } /* audio810_ad_start_play() */ @@ -1269,17 +1430,20 @@ audio810_ad_pause_play(audiohdl_t ahandle, int stream) ATRACE("audio810_ad_pause_play() ", ahandle); ATRACE_32("i810_ad_pause_play() stream", stream); + audio810_set_busy(statep); + mutex_enter(&statep->inst_lock); - if ((statep->flags & I810_DMA_PLAY_STARTED) == 0) { - mutex_exit(&statep->inst_lock); - return; - } + if ((statep->flags & I810_DMA_PLAY_STARTED) == 0) + goto done; cr = I810_BM_GET8(I810_PCM_OUT_CR); cr &= ~I810_BM_CR_RUN; I810_BM_PUT8(I810_PCM_OUT_CR, cr); statep->flags |= I810_DMA_PLAY_PAUSED; +done: mutex_exit(&statep->inst_lock); + audio810_set_idle(statep); + } /* audio810_ad_pause_play() */ /* @@ -1308,6 +1472,8 @@ audio810_ad_stop_play(audiohdl_t ahandle, int stream) statep = audio_sup_get_private(ahandle); ASSERT(statep); + audio810_set_busy(statep); + mutex_enter(&statep->inst_lock); /* pause bus master */ @@ -1323,6 +1489,8 @@ audio810_ad_stop_play(audiohdl_t ahandle, int stream) mutex_exit(&statep->inst_lock); + audio810_set_idle(statep); + } /* audio810_ad_stop_play() */ /* @@ -1344,25 +1512,28 @@ static int audio810_ad_start_record(audiohdl_t ahandle, int stream) { audio810_state_t *statep; - int rc; + int rc = AUDIO_SUCCESS; ATRACE("audio810_ad_start_record() ", ahandle); ATRACE_32("i810_ad_start_record() stream", stream); statep = audio_sup_get_private(ahandle); ASSERT(statep); + audio810_set_busy(statep); + mutex_enter(&statep->inst_lock); - if (statep->flags & I810_DMA_RECD_STARTED) { - mutex_exit(&statep->inst_lock); - return (AUDIO_SUCCESS); - } + if (statep->flags & I810_DMA_RECD_STARTED) + goto done; rc = audio810_prepare_record_buf(statep); if (rc == AUDIO_SUCCESS) { statep->flags |= I810_DMA_RECD_STARTED; } +done: mutex_exit(&statep->inst_lock); + audio810_set_idle(statep); + return (rc); } /* audio810_ad_start_record() */ @@ -1393,6 +1564,8 @@ audio810_ad_stop_record(audiohdl_t ahandle, int stream) statep = audio_sup_get_private(ahandle); ASSERT(statep); + audio810_set_busy(statep); + mutex_enter(&statep->inst_lock); statep->flags &= ~I810_DMA_RECD_STARTED; @@ -1407,6 +1580,8 @@ audio810_ad_stop_record(audiohdl_t ahandle, int stream) mutex_exit(&statep->inst_lock); + audio810_set_idle(statep); + } /* audio810_ad_stop_record() */ /* *********************** Local Routines *************************** */ @@ -1585,6 +1760,7 @@ audio810_init_state(audio810_state_t *statep, dev_info_t *dip) return (AUDIO_FAILURE); } mutex_init(&statep->inst_lock, NULL, MUTEX_DRIVER, statep->intr_iblock); + cv_init(&statep->i810_cv, NULL, CV_DRIVER, NULL); /* fill in device info strings */ (void) strcpy(statep->i810_dev_info.name, I810_DEV_NAME); @@ -2419,6 +2595,14 @@ audio810_stop_dma(audio810_state_t *statep) statep->flags = 0; +/* + * XXXX Not sure what these declarations are for, but I brought them from + * the PM gate. + */ + statep->play_buf.io_started = B_FALSE; + + statep->record_buf.io_started = B_FALSE; + } /* audio810_stop_dma() */ /* diff --git a/usr/src/uts/common/io/fdc.c b/usr/src/uts/common/io/fdc.c index ab8d9b9c18..0e111658e0 100644 --- a/usr/src/uts/common/io/fdc.c +++ b/usr/src/uts/common/io/fdc.c @@ -2,9 +2,8 @@ * 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. + * Common Development and Distribution License (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. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -472,7 +471,7 @@ fdc_probe(dev_info_t *dip) } FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fdc_probe: dip %p", - (void*)dip)); + (void*)dip)); if (get_ioaddr(dip, &ioaddr) != DDI_SUCCESS) return (DDI_PROBE_FAILURE); @@ -497,7 +496,7 @@ fdc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) char name[MAXNAMELEN]; FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fdc_attach: dip %p", - (void*)dip)); + (void*)dip)); switch (cmd) { case DDI_ATTACH: @@ -600,6 +599,10 @@ fdc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) ddi_report_dev(dip); return (DDI_SUCCESS); + case DDI_RESUME: + return (DDI_SUCCESS); + /* break; */ + default: return (DDI_FAILURE); } @@ -826,11 +829,12 @@ static int fdc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { struct fdcntlr *fcp; + struct fcu_obj *fjp; int unit; int rval = 0; FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fdc_detach: dip %p", - (void*)dip)); + (void*)dip)); fcp = ddi_get_driver_private(dip); @@ -847,8 +851,8 @@ fdc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) if (ddi_dmae_release(fcp->c_dip, fcp->c_dmachan) != DDI_SUCCESS) cmn_err(CE_WARN, "fdc_detach: dma release failed, " - "dip %p, dmachan %x\n", - (void*)fcp->c_dip, fcp->c_dmachan); + "dip %p, dmachan %x\n", + (void*)fcp->c_dip, fcp->c_dmachan); ddi_prop_remove_all(fcp->c_dip); ddi_set_driver_private(fcp->c_dip, NULL); @@ -858,6 +862,35 @@ fdc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) sema_destroy(&fcp->c_selsem); ddi_soft_state_free(fdc_state_head, ddi_get_instance(dip)); break; + + case DDI_SUSPEND: + /* + * Following code causes the fdc (floppy controller) + * to suspend as long as there are no floppy drives + * attached to it. + * At present the floppy driver does not support + * SUSPEND/RESUME. + * + * Check if any FD units are attached + * + * For now, SUSPEND/RESUME is not supported + * if a floppy drive is present. + * So if any FD unit is attached return DDI_FAILURE + */ + for (unit = 0; unit < NFDUN; unit++) { + fjp = fcp->c_unit[unit]; + if (fjp->fj_flags & FUNIT_DRVATCH) { + cmn_err(CE_WARN, + "fdc_detach: fd attached, failing SUSPEND"); + return (DDI_FAILURE); + } + } + + cmn_err(CE_NOTE, "fdc_detach: SUSPEND fdc"); + + rval = DDI_SUCCESS; + break; + default: rval = EINVAL; break; @@ -892,9 +925,9 @@ fdc_abort(struct fcu_obj *fjp) if (ddi_dmae_stop(fcp->c_dip, fcp->c_dmachan) != DDI_SUCCESS) cmn_err(CE_WARN, - "fdc_detach: dma release failed, " - "dip %p, dmachan %x\n", - (void*)fcp->c_dip, fcp->c_dmachan); + "fdc_detach: dma release failed, " + "dip %p, dmachan %x\n", + (void*)fcp->c_dip, fcp->c_dmachan); } mutex_exit(&fcp->c_lock); drv_usecwait(500); @@ -923,7 +956,7 @@ fdc_dkinfo(struct fcu_obj *fjp, struct dk_cinfo *dcp) struct fdcntlr *fcp = fjp->fj_fdc; (void) strncpy((char *)&dcp->dki_cname, ddi_get_name(fcp->c_dip), - DK_DEVLEN); + DK_DEVLEN); dcp->dki_ctype = DKC_UNKNOWN; /* no code for generic PC/AT fdc */ dcp->dki_flags = DKI_FMTTRK; dcp->dki_addr = fcp->c_regbase; @@ -931,7 +964,7 @@ fdc_dkinfo(struct fcu_obj *fjp, struct dk_cinfo *dcp) dcp->dki_prio = fcp->c_intprio; dcp->dki_vec = fcp->c_intvec; (void) strncpy((char *)&dcp->dki_dname, ddi_driver_name(fjp->fj_dip), - DK_DEVLEN); + DK_DEVLEN); dcp->dki_slave = fjp->fj_unit & 3; dcp->dki_maxtransfer = maxphys / DEV_BSIZE; return (DDI_SUCCESS); @@ -1149,7 +1182,7 @@ fdrw(struct fcu_obj *fjp, int funit, int rw, int cyl, int head, dmar_flags |= (DDI_DMA_STREAMING | DDI_DMA_PARTIAL); if (ddi_dma_alloc_handle(fcp->c_dip, &fdc_dma_attr, DDI_DMA_SLEEP, - 0, &csb->csb_dmahandle) != DDI_SUCCESS) { + 0, &csb->csb_dmahandle) != DDI_SUCCESS) { rval = EINVAL; goto out; } @@ -1181,15 +1214,14 @@ fdrw(struct fcu_obj *fjp, int funit, int rw, int cyl, int head, } else if (rval == DDI_DMA_PARTIAL_MAP) { csb->csb_handle_bound = 1; if (ddi_dma_numwin(csb->csb_dmahandle, &csb->csb_dmawincnt) != - DDI_SUCCESS) { + DDI_SUCCESS) { cmn_err(CE_WARN, "fdrw: dma numwin failed\n"); rval = EINVAL; goto out; } } else { cmn_err(CE_WARN, - "fdrw: dma addr bind handle failed, rval = %d\n", - rval); + "fdrw: dma addr bind handle failed, rval = %d\n", rval); rval = EINVAL; goto out; } @@ -1269,7 +1301,7 @@ fdtrkformat(struct fcu_obj *fjp, int funit, int cyl, int head, int filldata) fmdatlen = 4 * numsctr; if (ddi_dma_alloc_handle(fcp->c_dip, &fdc_dma_attr, DDI_DMA_SLEEP, - 0, &csb->csb_dmahandle) != DDI_SUCCESS) { + 0, &csb->csb_dmahandle) != DDI_SUCCESS) { rval = EINVAL; goto out; } @@ -1310,15 +1342,15 @@ fdtrkformat(struct fcu_obj *fjp, int funit, int cyl, int head, int filldata) } else if (rval == DDI_DMA_PARTIAL_MAP) { csb->csb_handle_bound = 1; if (ddi_dma_numwin(csb->csb_dmahandle, &csb->csb_dmawincnt) != - DDI_SUCCESS) { + DDI_SUCCESS) { cmn_err(CE_WARN, "fdtrkformat: dma numwin failed\n"); rval = EINVAL; goto out; } } else { cmn_err(CE_WARN, - "fdtrkformat: dma buf bind handle failed, rval = %d\n", - rval); + "fdtrkformat: dma buf bind handle failed, rval = %d\n", + rval); rval = EINVAL; goto out; } @@ -1579,15 +1611,15 @@ fdcquiesce(struct fdcntlr *fcp) int unit; FCERRPRINT(FDEP_L2, FDEM_RESE, (CE_NOTE, "fdcquiesce fcp %p", - (void*)fcp)); + (void*)fcp)); ASSERT(MUTEX_HELD(&fcp->c_lock)); mutex_enter(&fcp->c_dorlock); if (ddi_dmae_stop(fcp->c_dip, fcp->c_dmachan) != DDI_SUCCESS) cmn_err(CE_WARN, "fdcquiesce: dmae stop failed, " - "dip %p, dmachan %x\n", - (void*)fcp->c_dip, fcp->c_dmachan); + "dip %p, dmachan %x\n", + (void*)fcp->c_dip, fcp->c_dmachan); fcp->c_digout = (fcp->c_digout & (FD_DMTREN | FD_DRSEL)) | FD_ENABLE; outb(fcp->c_regbase + FCR_DOR, fcp->c_digout); @@ -2158,7 +2190,7 @@ retry: */ mutex_enter(&fcp->c_dorlock); (void) fdc_motorsm(fjp, FMI_RSTARTCMD, - fjp->fj_drive->fdd_motoron); + fjp->fj_drive->fdd_motoron); /* * Return value ignored - fdcmotort deals with failure. */ @@ -2202,8 +2234,8 @@ retry: if (ddi_dmae_prog(fcp->c_dip, &dmaereq, &csb->csb_dmacookie, fcp->c_dmachan) != DDI_SUCCESS) cmn_err(CE_WARN, "fdc_exec: dmae prog failed, " - "dip %p, dmachan %x\n", - (void*)fcp->c_dip, fcp->c_dmachan); + "dip %p, dmachan %x\n", + (void*)fcp->c_dip, fcp->c_dmachan); } if ((fdc_statemach(fcp) == FXS_DOWT) && !sleep) { @@ -2400,7 +2432,9 @@ fdrecover(struct fdcntlr *fcp) (void*)fcp->c_dip, fcp->c_dmachan, residual); FCERRPRINT(FDEP_L2, FDEM_RECO, - (CE_NOTE, "fd unit %d: %s error: dma count=0x%lx residual=0x%x", + (CE_NOTE, + "fd unit %d: %s error: " + "dma count=0x%lx residual=0x%x", csb->csb_drive, fdcmds[*csb->csb_cmd & 0x1f].cmdname, csb->csb_dmacookie.dmac_size, residual)); @@ -2412,8 +2446,10 @@ fdrecover(struct fdcntlr *fcp) */ if (++csb->csb_ourtrys <= OURUN_TRIES) { FCERRPRINT(FDEP_L2, FDEM_RECO, -(CE_NOTE, "fd unit %d: %s error: over/under-run", -csb->csb_drive, fdcmds[*csb->csb_cmd & 0x1f].cmdname)); + (CE_NOTE, + "fd unit %d: %s error: over/under-run", + csb->csb_drive, + fdcmds[*csb->csb_cmd & 0x1f].cmdname)); return (0); } else /* @@ -2445,9 +2481,12 @@ csb->csb_drive, fdcmds[*csb->csb_cmd & 0x1f].cmdname)); if (csb->csb_opflags & (CSB_OFDMARD | CSB_OFDMAWT)) { FCERRPRINT(FDEP_L4, FDEM_RECO, -(CE_WARN, "fd unit %d: %s error: st0=0x%x st1=0x%x st2=0x%x", + (CE_WARN, + "fd unit %d: %s error: " + "st0=0x%x st1=0x%x st2=0x%x", csb->csb_drive, -fdcmds[*csb->csb_cmd & 0x1f].cmdname, + fdcmds[*csb->csb_cmd & + 0x1f].cmdname, *csb->csb_rslt, csb->csb_rslt[1], csb->csb_rslt[2])); } @@ -2681,8 +2720,8 @@ fdwatch(void *arg) if (fcp->c_flags & FCFLG_WAITING) { if (ddi_dmae_stop(fcp->c_dip, fcp->c_dmachan) != DDI_SUCCESS) cmn_err(CE_WARN, "fdwatch: dmae stop failed, " - "dip %p, dmachan %x\n", - (void*)fcp->c_dip, fcp->c_dmachan); + "dip %p, dmachan %x\n", + (void*)fcp->c_dip, fcp->c_dmachan); csb = &fcp->c_csb; FCERRPRINT(FDEP_L3, FDEM_WATC, (CE_WARN, "fdcwatch unit %d: xstate = %d", @@ -3168,7 +3207,7 @@ get_ioaddr(dev_info_t *dip, int *ioaddr) } *reglist; if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, - "reg", (caddr_t)®list, ®len) != DDI_PROP_SUCCESS) { + "reg", (caddr_t)®list, ®len) != DDI_PROP_SUCCESS) { cmn_err(CE_WARN, "fdc: reg property not found"); return (DDI_FAILURE); } diff --git a/usr/src/uts/common/io/i8042.c b/usr/src/uts/common/io/i8042.c index 42bcbb8d2a..9c9e568abc 100644 --- a/usr/src/uts/common/io/i8042.c +++ b/usr/src/uts/common/io/i8042.c @@ -382,6 +382,23 @@ static void i8042_send(struct i8042 *global, int reg, unsigned char cmd); unsigned int i8042_unclaimed_interrupts = 0; +static void +i8042_discard_junk_data(struct i8042 *global) +{ + /* Discard any junk data that may have been left around */ + for (;;) { + unsigned char stat; + + stat = ddi_get8(global->io_handle, + global->io_addr + I8042_STAT); + if (! (stat & I8042_STAT_OUTBF)) + break; + (void) ddi_get8(global->io_handle, + global->io_addr + I8042_DATA); + + } +} + static int i8042_cleanup(struct i8042 *global) { @@ -508,7 +525,7 @@ i8042_purge_outbuf(struct i8042 *global) if (i8042_wait_obf(global)) break; (void) ddi_get8(global->io_handle, - global->io_addr + I8042_DATA); + global->io_addr + I8042_DATA); } /* @@ -537,10 +554,9 @@ i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) switch (cmd) { case DDI_RESUME: -#ifdef __sparc global = (struct i8042 *)ddi_get_driver_private(dip); + i8042_discard_junk_data(global); i8042_write_command_byte(global, I8042_CMD_ENABLE_ALL); -#endif return (DDI_SUCCESS); case DDI_ATTACH: @@ -628,7 +644,7 @@ i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) global->iblock_cookies = NULL; mutex_init(&global->i8042_mutex, NULL, MUTEX_DRIVER, - (global->nintrs > 0) ? global->iblock_cookies[0] : NULL); + (global->nintrs > 0) ? global->iblock_cookies[0] : NULL); mutex_init(&global->i8042_out_mutex, NULL, MUTEX_DRIVER, NULL); @@ -816,8 +832,8 @@ i8042_map( } if (rnumber < 0 || rnumber >= iprop_len) { cmn_err(CE_WARN, "%s #%d: bad map request for %s@%s", - DRIVER_NAME(dip), ddi_get_instance(dip), - ddi_node_name(rdip), ddi_get_name_addr(rdip)); + DRIVER_NAME(dip), ddi_get_instance(dip), + ddi_node_name(rdip), ddi_get_name_addr(rdip)); return (DDI_FAILURE); } #endif @@ -838,9 +854,9 @@ i8042_map( default: #if defined(DEBUG) cmn_err(CE_WARN, "%s #%d: unknown map type %d for %s@%s", - DRIVER_NAME(dip), ddi_get_instance(dip), - mp->map_type, - ddi_node_name(rdip), ddi_get_name_addr(rdip)); + DRIVER_NAME(dip), ddi_get_instance(dip), + mp->map_type, + ddi_node_name(rdip), ddi_get_name_addr(rdip)); #endif return (DDI_FAILURE); } @@ -848,9 +864,9 @@ i8042_map( #if defined(DEBUG) if (offset != 0 || len != 0) { cmn_err(CE_WARN, - "%s #%d: partial mapping attempt for %s@%s ignored", - DRIVER_NAME(dip), ddi_get_instance(dip), - ddi_node_name(rdip), ddi_get_name_addr(rdip)); + "%s #%d: partial mapping attempt for %s@%s ignored", + DRIVER_NAME(dip), ddi_get_instance(dip), + ddi_node_name(rdip), ddi_get_name_addr(rdip)); } #endif @@ -901,7 +917,7 @@ i8042_map( default: cmn_err(CE_WARN, "%s: map operation %d not supported", - DRIVER_NAME(dip), mp->map_op); + DRIVER_NAME(dip), mp->map_op); return (DDI_FAILURE); } } @@ -986,7 +1002,7 @@ i8042_intr(caddr_t arg) #if defined(DEBUG) if (port->overruns % 50 == 1) { cmn_err(CE_WARN, "i8042/%d: %d overruns\n", - which_port, port->overruns); + which_port, port->overruns); } #endif mutex_exit(&global->i8042_mutex); @@ -1039,7 +1055,7 @@ i8042_send(struct i8042 *global, int reg, unsigned char val) /*CONSTANTCONDITION*/ while (1) { stat = ddi_get8(global->io_handle, - global->io_addr + I8042_STAT); + global->io_addr + I8042_STAT); if ((stat & I8042_STAT_INBF) == 0) { ddi_put8(global->io_handle, global->io_addr+reg, val); @@ -1113,7 +1129,7 @@ i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr) } else { #if defined(DEBUG) cmn_err(CE_WARN, - "i8042: Tried to read from empty buffer"); + "i8042: Tried to read from empty buffer"); #endif ret = 0; } @@ -1127,7 +1143,7 @@ i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr) case I8042_INT_OUTPUT_DATA: case I8042_POLL_OUTPUT_DATA: cmn_err(CE_WARN, "i8042: read of write-only register 0x%p", - (void *)addr); + (void *)addr); ret = 0; break; #endif @@ -1137,7 +1153,7 @@ i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr) return (B_TRUE); for (;;) { stat = ddi_get8(global->io_handle, - global->io_addr + I8042_STAT); + global->io_addr + I8042_STAT); if ((stat & I8042_STAT_OUTBF) == 0) return (B_FALSE); switch (port->which) { @@ -1151,13 +1167,13 @@ i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr) break; default: cmn_err(CE_WARN, "data from unknown port: %d", - port->which); + port->which); } /* * Data for wrong port pending; discard it. */ (void) ddi_get8(global->io_handle, - global->io_addr + I8042_DATA); + global->io_addr + I8042_DATA); } /* NOTREACHED */ @@ -1170,7 +1186,7 @@ i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr) } stat = ddi_get8(global->io_handle, - global->io_addr + I8042_STAT); + global->io_addr + I8042_STAT); if ((stat & I8042_STAT_OUTBF) == 0) { #if defined(DEBUG) prom_printf("I8042_POLL_INPUT_DATA: no data!\n"); @@ -1178,7 +1194,7 @@ i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr) return (0); } ret = ddi_get8(global->io_handle, - global->io_addr + I8042_DATA); + global->io_addr + I8042_DATA); switch (port->which) { case MAIN_PORT: if ((stat & I8042_STAT_AUXBF) == 0) @@ -1197,7 +1213,7 @@ i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr) default: #if defined(DEBUG) cmn_err(CE_WARN, "i8042: read of undefined register 0x%p", - (void *)addr); + (void *)addr); #endif ret = 0; break; @@ -1240,12 +1256,12 @@ i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr, uint8_t value) case I8042_POLL_INPUT_AVAIL: case I8042_POLL_INPUT_DATA: cmn_err(CE_WARN, "i8042: write of read-only register 0x%p", - (void *)addr); + (void *)addr); break; default: cmn_err(CE_WARN, "i8042: read of undefined register 0x%p", - (void *)addr); + (void *)addr); break; #endif } @@ -1391,7 +1407,7 @@ i8042_ctlops(dev_info_t *dip, dev_info_t *rdip, (void) sprintf(name, "%d", which_port); ddi_set_name_addr(child, name); ddi_set_parent_data(child, - (caddr_t)&global->i8042_ports[which_port]); + (caddr_t)&global->i8042_ports[which_port]); return (DDI_SUCCESS); case DDI_CTLOPS_UNINITCHILD: @@ -1402,8 +1418,8 @@ i8042_ctlops(dev_info_t *dip, dev_info_t *rdip, case DDI_CTLOPS_REPORTDEV: cmn_err(CE_CONT, "?8042 device: %s@%s, %s # %d\n", - ddi_node_name(rdip), ddi_get_name_addr(rdip), - DRIVER_NAME(rdip), ddi_get_instance(rdip)); + ddi_node_name(rdip), ddi_get_name_addr(rdip), + DRIVER_NAME(rdip), ddi_get_instance(rdip)); return (DDI_SUCCESS); default: diff --git a/usr/src/uts/common/io/kb8042/kb8042.c b/usr/src/uts/common/io/kb8042/kb8042.c index 6799e6d75a..0e8369b076 100644 --- a/usr/src/uts/common/io/kb8042/kb8042.c +++ b/usr/src/uts/common/io/kb8042/kb8042.c @@ -431,6 +431,14 @@ kb8042_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) kb8042->w_init = 0; kb8042_init(kb8042, B_TRUE); kb8042_setled(kb8042, leds, B_FALSE); + mutex_enter(&kb8042->w_hw_mutex); + kb8042->suspended = B_FALSE; + if (kb8042->w_qp != NULL) { + enableok(WR(kb8042->w_qp)); + qenable(WR(kb8042->w_qp)); + } + cv_broadcast(&kb8042->suspend_cv); + mutex_exit(&kb8042->w_hw_mutex); return (DDI_SUCCESS); case DDI_ATTACH: @@ -480,7 +488,8 @@ kb8042_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) } mutex_init(&kb8042->w_hw_mutex, NULL, MUTEX_DRIVER, kb8042->w_iblock); - + cv_init(&kb8042->ops_cv, NULL, CV_DRIVER, NULL); + cv_init(&kb8042->suspend_cv, NULL, CV_DRIVER, NULL); kb8042->init_state |= KB8042_HW_MUTEX_INITTED; kb8042_init(kb8042, B_FALSE); @@ -552,6 +561,12 @@ kb8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) switch (cmd) { case DDI_SUSPEND: + mutex_enter(&kb8042->w_hw_mutex); + ASSERT(kb8042->ops >= 0); + while (kb8042->ops > 0) + cv_wait(&kb8042->ops_cv, &kb8042->w_hw_mutex); + kb8042->suspended = B_TRUE; + mutex_exit(&kb8042->w_hw_mutex); return (DDI_SUCCESS); case DDI_DETACH: @@ -606,8 +621,11 @@ kb8042_cleanup(struct kb8042 *kb8042) { ASSERT(kb8042_dip != NULL); - if (kb8042->init_state & KB8042_HW_MUTEX_INITTED) + if (kb8042->init_state & KB8042_HW_MUTEX_INITTED) { + cv_destroy(&kb8042->suspend_cv); + cv_destroy(&kb8042->ops_cv); mutex_destroy(&kb8042->w_hw_mutex); + } if (kb8042->init_state & KB8042_INTR_ADDED) ddi_remove_intr(kb8042_dip, 0, kb8042->w_iblock); @@ -660,9 +678,19 @@ kb8042_open(queue_t *qp, dev_t *devp, int flag, int sflag, cred_t *credp) kb8042 = &Kdws; + mutex_enter(&kb8042->w_hw_mutex); + while (kb8042->suspended) { + if (cv_wait_sig(&kb8042->suspend_cv, &kb8042->w_hw_mutex) == + 0) { + mutex_exit(&kb8042->w_hw_mutex); + return (EINTR); + } + } + kb8042->w_dev = *devp; if (qp->q_ptr) { + mutex_exit(&kb8042->w_hw_mutex); return (0); } qp->q_ptr = (caddr_t)kb8042; @@ -670,6 +698,10 @@ kb8042_open(queue_t *qp, dev_t *devp, int flag, int sflag, cred_t *credp) if (!kb8042->w_qp) kb8042->w_qp = qp; + ASSERT(kb8042->ops >= 0); + kb8042->ops++; + mutex_exit(&kb8042->w_hw_mutex); + kb8042_get_initial_leds(kb8042, &initial_leds, &initial_led_mask); err = kbtrans_streams_init(qp, sflag, credp, (struct kbtrans_hardware *)kb8042, &kb8042_callbacks, @@ -700,6 +732,13 @@ kb8042_open(queue_t *qp, dev_t *devp, int flag, int sflag, cred_t *credp) kbtrans_streams_enable(kb8042->hw_kbtrans); + mutex_enter(&kb8042->w_hw_mutex); + ASSERT(kb8042->ops > 0); + kb8042->ops--; + if (kb8042->ops == 0) + cv_broadcast(&kb8042->ops_cv); + mutex_exit(&kb8042->w_hw_mutex); + return (0); } @@ -714,11 +753,31 @@ kb8042_close(queue_t *qp, int flag, cred_t *credp) kb8042 = (struct kb8042 *)qp->q_ptr; + mutex_enter(&kb8042->w_hw_mutex); + while (kb8042->suspended) { + if (cv_wait_sig(&kb8042->suspend_cv, &kb8042->w_hw_mutex) == + 0) { + mutex_exit(&kb8042->w_hw_mutex); + return (EINTR); + } + } + + ASSERT(kb8042->ops >= 0); + kb8042->ops++; + mutex_exit(&kb8042->w_hw_mutex); + (void) kbtrans_streams_fini(kb8042->hw_kbtrans); kb8042->w_qp = (queue_t *)NULL; qprocsoff(qp); + mutex_enter(&kb8042->w_hw_mutex); + ASSERT(kb8042->ops > 0); + kb8042->ops--; + if (kb8042->ops == 0) + cv_broadcast(&kb8042->ops_cv); + mutex_exit(&kb8042->w_hw_mutex); + return (0); } @@ -728,10 +787,27 @@ kb8042_wsrv(queue_t *qp) struct kb8042 *kb8042; mblk_t *mp; + boolean_t suspended; kb8042 = (struct kb8042 *)qp->q_ptr; + mutex_enter(&kb8042->w_hw_mutex); + suspended = kb8042->suspended; + ASSERT(kb8042->ops >= 0); + if (!suspended) + kb8042->ops++; + mutex_exit(&kb8042->w_hw_mutex); + +#ifdef NO_KB_DEBUG + while (!suspended && (mp = getq(qp)) != NULL) { +#else + /* + * Not taking keyboard input while suspending can make debugging + * difficult. However, we still do the ops counting so that we + * don't suspend at a bad time. + */ while ((mp = getq(qp))) { +#endif switch (kbtrans_streams_message(kb8042->hw_kbtrans, mp)) { case KBTRANS_MESSAGE_HANDLED: continue; @@ -765,6 +841,16 @@ kb8042_wsrv(queue_t *qp) continue; } } + + mutex_enter(&kb8042->w_hw_mutex); + if (!suspended) { + ASSERT(kb8042->ops > 0); + kb8042->ops--; + if (kb8042->ops == 0) + cv_broadcast(&kb8042->ops_cv); + } + mutex_exit(&kb8042->w_hw_mutex); + return (0); } diff --git a/usr/src/uts/common/io/kb8042/kb8042.h b/usr/src/uts/common/io/kb8042/kb8042.h index 33d849669e..6232d38279 100644 --- a/usr/src/uts/common/io/kb8042/kb8042.h +++ b/usr/src/uts/common/io/kb8042/kb8042.h @@ -2,9 +2,8 @@ * 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. + * Common Development and Distribution License (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. @@ -19,12 +18,13 @@ * * 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. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -111,6 +111,10 @@ struct kb8042 { int simulated_kbd_type; uint32_t init_state; int break_received; + boolean_t suspended; + int ops; + kcondvar_t suspend_cv; + kcondvar_t ops_cv; }; #define KB_COMMAND_STATE_IDLE 0 diff --git a/usr/src/uts/common/io/pci-ide/pci-ide.c b/usr/src/uts/common/io/pci-ide/pci-ide.c index c47157dd31..924aec38d7 100644 --- a/usr/src/uts/common/io/pci-ide/pci-ide.c +++ b/usr/src/uts/common/io/pci-ide/pci-ide.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -47,6 +47,7 @@ #include <sys/pci_intr_lib.h> int pciide_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); +int pciide_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); #define PCIIDE_NATIVE_MODE(dip) \ (!ddi_prop_exists(DDI_DEV_T_ANY, (dip), DDI_PROP_DONTPASS, \ @@ -154,7 +155,7 @@ struct dev_ops pciide_ops = { nulldev, /* identify */ nulldev, /* probe */ pciide_attach, /* attach */ - nodev, /* detach */ + pciide_detach, /* detach */ nodev, /* reset */ (struct cb_ops *)0, /* driver operations */ &pciide_bus_ops /* bus operations */ @@ -203,9 +204,8 @@ pciide_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) ddi_acc_handle_t conf_hdl = NULL; int rc; - - if (cmd == DDI_ATTACH) { - + switch (cmd) { + case DDI_ATTACH: /* * Make sure bus-mastering is enabled, even if * BIOS didn't. @@ -225,13 +225,55 @@ pciide_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) cmdreg | PCI_COMM_ME); } pci_config_teardown(&conf_hdl); + return (DDI_SUCCESS); + case DDI_RESUME: + /* Restore our PCI configuration header */ + if (pci_restore_config_regs(dip) != DDI_SUCCESS) { + /* + * XXXX + * This is a pretty bad thing. However, for some + * reason it always happens. To further complicate + * things, it appears if we just ignore this, we + * properly resume. For now, all I want to do is + * to generate this message so that it doesn't get + * forgotten. + */ + cmn_err(CE_WARN, + "Couldn't restore PCI config regs for %s(%p)", + ddi_node_name(dip), (void *) dip); + } +#ifdef DEBUG + /* Bus mastering should still be enabled */ + if (pci_config_setup(dip, &conf_hdl) != DDI_SUCCESS) + return (DDI_FAILURE); + cmdreg = pci_config_get16(conf_hdl, PCI_CONF_COMM); + ASSERT((cmdreg & PCI_COMM_ME) != 0); + pci_config_teardown(&conf_hdl); +#endif return (DDI_SUCCESS); - } else { - return (DDI_FAILURE); } + + return (DDI_FAILURE); } +/*ARGSUSED*/ +int +pciide_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + switch (cmd) { + case DDI_DETACH: + return (DDI_SUCCESS); + case DDI_SUSPEND: + /* Save our PCI configuration header */ + if (pci_save_config_regs(dip) != DDI_SUCCESS) { + /* Don't suspend if we cannot save config regs */ + return (DDI_FAILURE); + } + return (DDI_SUCCESS); + } + return (DDI_FAILURE); +} /*ARGSUSED*/ static int @@ -295,9 +337,9 @@ pciide_ddi_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop, old_rnumber = rnumber; new_rnumber - = pciide_pre26_rnumber_map(dip, old_rnumber); + = pciide_pre26_rnumber_map(dip, old_rnumber); PDBG(("pciide rnumber old %d new %d\n", - old_rnumber, new_rnumber)); + old_rnumber, new_rnumber)); rnumber = new_rnumber; } @@ -454,7 +496,7 @@ pciide_initchild(dev_info_t *mydip, dev_info_t *cdip) * property in the ata.conf file. */ vec = ddi_prop_get_int(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS, - "interrupts", -1); + "interrupts", -1); if (vec == -1) { /* setup compatibility mode interrupts */ if (dev == 0) { @@ -530,7 +572,7 @@ pciide_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, old_rnumber = mp->map_obj.rnumber; new_rnumber = pciide_pre26_rnumber_map(dip, old_rnumber); PDBG(("pciide rnumber old %d new %d\n", - old_rnumber, new_rnumber)); + old_rnumber, new_rnumber)); mp->map_obj.rnumber = new_rnumber; } @@ -545,7 +587,7 @@ pciide_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, */ pdip = ddi_get_parent(dip); rc = ((*(DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)) - (pdip, dip, mp, offset, len, vaddrp)); + (pdip, dip, mp, offset, len, vaddrp)); PDBG(("pciide_bus_map %s\n", rc == DDI_SUCCESS ? "okay" : "!ok")); @@ -751,7 +793,7 @@ pciide_compat_setup(dev_info_t *mydip, dev_info_t *cdip, int dev) if ((dev == 0 && !(class_code & PCI_IDE_IF_NATIVE_PRI)) || (dev == 1 && !(class_code & PCI_IDE_IF_NATIVE_SEC))) { rc = ddi_prop_update_int(DDI_DEV_T_NONE, cdip, - "compatibility-mode", 1); + "compatibility-mode", 1); if (rc != DDI_PROP_SUCCESS) cmn_err(CE_WARN, "pciide prop error %d compat-mode", rc); @@ -765,9 +807,9 @@ pciide_compat_setup(dev_info_t *mydip, dev_info_t *cdip, int dev) */ class_code &= 0x00ffff00; class_code |= PCI_IDE_IF_BM_CAP_MASK | - PCI_IDE_IF_NATIVE_PRI | PCI_IDE_IF_NATIVE_SEC; + PCI_IDE_IF_NATIVE_PRI | PCI_IDE_IF_NATIVE_SEC; rc = ddi_prop_update_int(DDI_DEV_T_NONE, mydip, - "class-code", class_code); + "class-code", class_code); if (rc != DDI_PROP_SUCCESS) cmn_err(CE_WARN, "pciide prop error %d class-code", rc); @@ -783,7 +825,7 @@ pciide_pre26_rnumber_map(dev_info_t *mydip, int rnumber) int class_code; class_code = ddi_prop_get_int(DDI_DEV_T_ANY, mydip, DDI_PROP_DONTPASS, - "class-code", 0); + "class-code", 0); pri_native = (class_code & PCI_IDE_IF_NATIVE_PRI) ? TRUE : FALSE; sec_native = (class_code & PCI_IDE_IF_NATIVE_SEC) ? TRUE : FALSE; diff --git a/usr/src/uts/common/io/pm.c b/usr/src/uts/common/io/pm.c index d4cb7f0da0..42847eecf7 100644 --- a/usr/src/uts/common/io/pm.c +++ b/usr/src/uts/common/io/pm.c @@ -56,10 +56,10 @@ #include <sys/policy.h> /* - * Minor number is instance<<8 + clone minor from range 1-255; (0 reserved - * for "original" + * Minor number is instance<<8 + clone minor from range 1-254; (0 reserved + * for "original") */ -#define PM_MINOR_TO_CLONE(minor) ((minor) & (PM_MAX_CLONE - 1)) +#define PM_MINOR_TO_CLONE(minor) ((minor) & (PM_MAX_CLONE -1)) #define PM_NUMCMPTS(dip) (DEVI(dip)->devi_pm_num_components) #define PM_IS_CFB(dip) (DEVI(dip)->devi_pm_flags & PMC_CONSOLE_FB) @@ -67,6 +67,8 @@ #define PM_RELE(dip) ddi_release_devi(dip) #define PM_IDLEDOWN_TIME 10 +#define MAXSMBIOSSTRLEN 64 /* from SMBIOS spec */ +#define MAXCOPYBUF (MAXSMBIOSSTRLEN + 1) extern kmutex_t pm_scan_lock; /* protects autopm_enable, pm_scans_disabled */ extern kmutex_t pm_clone_lock; /* protects pm_clones array */ @@ -77,6 +79,19 @@ extern int pm_system_idle_threshold; extern int pm_cpu_idle_threshold; extern kcondvar_t pm_clones_cv[PM_MAX_CLONE]; extern uint_t pm_poll_cnt[PM_MAX_CLONE]; +extern int autoS3_enabled; +extern void pm_record_thresh(pm_thresh_rec_t *); +extern void pm_register_watcher(int, dev_info_t *); +extern int pm_get_current_power(dev_info_t *, int, int *); +extern int pm_interest_registered(int); +extern void pm_all_to_default_thresholds(void); +extern int pm_current_threshold(dev_info_t *, int, int *); +extern void pm_deregister_watcher(int, dev_info_t *); +extern void pm_unrecord_threshold(char *); +extern int pm_S3_enabled; +extern int pm_ppm_searchlist(pm_searchargs_t *); +extern psce_t *pm_psc_clone_to_direct(int); +extern psce_t *pm_psc_clone_to_interest(int); /* * The soft state of the power manager. Since there will only @@ -181,7 +196,7 @@ pm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) pmstp->pm_instance = ddi_get_instance(dip); if (ddi_create_minor_node(dip, "pm", S_IFCHR, (pmstp->pm_instance << 8) + 0, - DDI_PSEUDO, 0) != DDI_SUCCESS) { + DDI_PSEUDO, 0) != DDI_SUCCESS) { return (DDI_FAILURE); } pmstp->pm_dip = dip; /* pm_init and getinfo depend on it */ @@ -271,6 +286,7 @@ pm_close_direct_pm_device(dev_info_t *dip, void *arg) #define NODEP 5 #define DEP 6 #define PM_PSC 7 +#define PM_SRCH 8 #define CHECKPERMS 0x001 #define SU 0x002 @@ -405,6 +421,8 @@ static struct pm_cmd_info pmci[] = { {PM_RESET_DEVICE_THRESHOLD, "PM_RESET_DEVICE_THRESHOLD", 1, PM_REQ, INWHO, DIP, NODEP, SU}, {PM_GET_PM_STATE, "PM_GET_PM_STATE", 1, NOSTRUCT}, + {PM_GET_AUTOS3_STATE, "PM_GET_AUTOS3_STATE", 1, NOSTRUCT}, + {PM_GET_S3_SUPPORT_STATE, "PM_GET_S3_SUPPORT_STATE", 1, NOSTRUCT}, {PM_GET_DEVICE_TYPE, "PM_GET_DEVICE_TYPE", 1, PM_REQ, INWHO, DIP, NODEP}, {PM_SET_COMPONENT_THRESHOLDS, "PM_SET_COMPONENT_THRESHOLDS", 1, PM_REQ, @@ -431,6 +449,14 @@ static struct pm_cmd_info pmci[] = { {PM_SET_CPU_THRESHOLD, "PM_SET_CPU_THRESHOLD", 1, NOSTRUCT, 0, 0, 0, SU}, {PM_GET_CPUPM_STATE, "PM_GET_CPUPM_STATE", 1, NOSTRUCT}, + {PM_START_AUTOS3, "PM_START_AUTOS3", 1, NOSTRUCT, 0, 0, 0, SU}, + {PM_STOP_AUTOS3, "PM_STOP_AUTOS3", 1, NOSTRUCT, 0, 0, 0, SU}, + {PM_ENABLE_S3, "PM_ENABLE_S3", 1, NOSTRUCT, 0, 0, 0, SU}, + {PM_DISABLE_S3, "PM_DISABLE_S3", 1, NOSTRUCT, 0, 0, 0, SU}, + {PM_ENTER_S3, "PM_ENTER_S3", 1, NOSTRUCT, 0, 0, 0, SU}, + {PM_SEARCH_LIST, "PM_SEARCH_LIST", 1, PM_SRCH, 0, 0, 0, SU}, + {PM_GET_CMD_NAME, "PM_GET_CMD_NAME", 1, PM_REQ, INDATAOUT, NODIP, + NODEP, 0}, {0, NULL} }; @@ -729,8 +755,6 @@ static void pm_discard_entries(int clone) { psce_t *pscep; - psce_t *pm_psc_clone_to_direct(int); - psce_t *pm_psc_clone_to_interest(int); int direct = 0; mutex_enter(&pm_clone_lock); @@ -901,26 +925,21 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) size_t wholen; /* copyinstr length */ size_t deplen = MAXNAMELEN; char *dep, i_dep_buf[MAXNAMELEN]; - char *pathbuf; + char pathbuf[MAXNAMELEN]; struct pm_component *cp; #ifdef _MULTI_DATAMODEL pm_state_change32_t *pscp32; pm_state_change32_t psc32; + pm_searchargs32_t psa32; size_t copysize32; #endif pm_state_change_t *pscp; pm_state_change_t psc; + pm_searchargs_t psa; + char listname[MAXCOPYBUF]; + char manufacturer[MAXCOPYBUF]; + char product[MAXCOPYBUF]; size_t copysize; - extern void pm_record_thresh(pm_thresh_rec_t *); - psce_t *pm_psc_clone_to_direct(int); - psce_t *pm_psc_clone_to_interest(int); - extern void pm_register_watcher(int, dev_info_t *); - extern int pm_get_current_power(dev_info_t *, int, int *); - extern int pm_interest_registered(int); - extern void pm_all_to_default_thresholds(void); - extern int pm_current_threshold(dev_info_t *, int, int *); - extern void pm_deregister_watcher(int, dev_info_t *); - extern void pm_unrecord_threshold(char *); PMD(PMD_IOCTL, ("ioctl: %s: begin\n", cmdstr)) @@ -955,6 +974,7 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) } switch (pcip->str_type) { case PM_REQ: + { #ifdef _MULTI_DATAMODEL if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) { pm_req32_t req32; @@ -979,9 +999,9 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) break; } req.physpath = who; + PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n", + cmdstr, req.physpath)) } - PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n", cmdstr, - req.physpath)) if (pcip->inargs & INDATA) { req.data = (void *)(uintptr_t)req32.data; req.datasize = req32.datasize; @@ -1053,9 +1073,8 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) ASSERT(!(pcip->inargs & INDATAINT)); ASSERT(pcip->deptype == DEP); if (req32.data != NULL) { - size_t dummy; if (copyinstr((void *)(uintptr_t) - req32.data, dep, deplen, &dummy)) { + req32.data, dep, deplen, NULL)) { PMD(PMD_ERROR, ("ioctl: %s: " "0x%p dep size %lx, EFAULT" "\n", cmdstr, @@ -1096,9 +1115,9 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) break; } req.physpath = who; + PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n", + cmdstr, req.physpath)) } - PMD(PMD_IOCTL, ("ioctl: %s: physpath=%s\n", cmdstr, - req.physpath)) if (!(pcip->inargs & INDATA)) { req.data = NULL; req.datasize = 0; @@ -1154,9 +1173,8 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) ASSERT(!(pcip->inargs & INDATAINT)); ASSERT(pcip->deptype == DEP); if (req.data != NULL) { - size_t dummy; if (copyinstr((caddr_t)req.data, - dep, deplen, &dummy)) { + dep, deplen, NULL)) { PMD(PMD_ERROR, ("ioctl: %s: " "0x%p dep size %lu, " "EFAULT\n", cmdstr, @@ -1222,6 +1240,7 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) } case PM_GET_DEVICE_THRESHOLD: + { PM_LOCK_DIP(dip); if (!PM_GET_PM_INFO(dip) || PM_ISBC(dip)) { PM_UNLOCK_DIP(dip); @@ -1234,6 +1253,7 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) PM_UNLOCK_DIP(dip); ret = 0; break; + } case PM_DIRECT_PM: { @@ -1248,11 +1268,9 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) * Check to see if we are there is a dependency on * this kept device, if so, return EBUSY. */ - pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP); (void) ddi_pathname(dip, pathbuf); pm_dispatch_to_dep_thread(PM_DEP_WK_CHECK_KEPT, NULL, pathbuf, PM_DEP_WAIT, &has_dep, 0); - kmem_free(pathbuf, MAXPATHLEN); if (has_dep) { PMD(PMD_ERROR | PMD_DPM, ("%s EBUSY\n", cmdstr)) @@ -1301,11 +1319,9 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) info->pmi_dev_pm_state &= ~PM_DIRECT; PM_UNLOCK_DIP(dip); /* Bring ourselves up if there is a keeper. */ - pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP); (void) ddi_pathname(dip, pathbuf); pm_dispatch_to_dep_thread(PM_DEP_WK_BRINGUP_SELF, NULL, pathbuf, PM_DEP_WAIT, NULL, 0); - kmem_free(pathbuf, MAXPATHLEN); pm_discard_entries(clone); pm_deregister_watcher(clone, dip); /* @@ -1426,6 +1442,7 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) } case PM_GET_CURRENT_POWER: + { if (pm_get_current_power(dip, req.component, rval_p) != DDI_SUCCESS) { PMD(PMD_ERROR | PMD_DPM, ("ioctl: %s " @@ -1440,6 +1457,7 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) else ret = 0; break; + } case PM_GET_TIME_IDLE: { @@ -1629,11 +1647,14 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) } case PM_GET_NUM_COMPONENTS: + { ret = 0; *rval_p = PM_NUMCMPTS(dip); break; + } case PM_GET_DEVICE_TYPE: + { ret = 0; if ((info = PM_GET_PM_INFO(dip)) == NULL) { PMD(PMD_ERROR, ("ioctl: %s: " @@ -1647,6 +1668,7 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) *rval_p = PM_AUTOPM; } break; + } case PM_SET_COMPONENT_THRESHOLDS: { @@ -1981,6 +2003,8 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) cmdstr, PM_DEVICE(dip), (void *)req.data)) ASSERT(!dipheld); + kmem_free(timestamp, + comps * sizeof (time_t)); return (EFAULT); } rvaddr = (caddr_t)req.data; @@ -1994,7 +2018,24 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) break; } + case PM_GET_CMD_NAME: + { + PMD(PMD_IOCTL, ("%s: %s\n", cmdstr, + pm_decode_cmd(req.value))) + if (ret = copyoutstr(pm_decode_cmd(req.value), + (char *)req.data, req.datasize, &lencopied)) { + PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) " + "copyoutstr %p failed--EFAULT\n", cmdstr, + PM_DEVICE(dip), (void *)req.data)) + break; + } + *rval_p = lencopied; + ret = 0; + break; + } + case PM_GET_COMPONENT_NAME: + { ASSERT(dip); if (!e_pm_valid_comp(dip, req.component, &cp)) { PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) " @@ -2014,6 +2055,7 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) *rval_p = lencopied; ret = 0; break; + } case PM_GET_POWER_NAME: { @@ -2118,6 +2160,7 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) case PM_GET_NUM_POWER_LEVELS: + { if (!e_pm_valid_comp(dip, req.component, &cp)) { PMD(PMD_ERROR, ("ioctl: %s: %s@%s(%s#%d) " "component %d > numcmpts - 1 %d--EINVAL\n", @@ -2129,8 +2172,10 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) *rval_p = cp->pmc_comp.pmc_numlevels; ret = 0; break; + } case PM_GET_DEVICE_THRESHOLD_BASIS: + { ret = 0; PM_LOCK_DIP(dip); if ((info = PM_GET_PM_INFO(dip)) == NULL) { @@ -2172,9 +2217,23 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) PM_UNLOCK_DIP(dip); break; } + default: + /* + * Internal error, invalid ioctl description + * force debug entry even if pm_debug not set + */ +#ifdef DEBUG + pm_log("invalid diptype %d for cmd %d (%s)\n", + pcip->diptype, cmd, pcip->name); +#endif + ASSERT(0); + return (EIO); + } break; + } case PM_PSC: + { /* * Commands that require pm_state_change_t as arg */ @@ -2461,7 +2520,7 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) { if (ddi_copyout(&psc32.component, &pscp32->component, copysize32, mode) - != 0) { + != 0) { PMD(PMD_ERROR, ("ioctl: %s: copyout " "failed--EFAULT\n", cmdstr)) ret = EFAULT; @@ -2482,14 +2541,128 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) break; } default: + /* + * Internal error, invalid ioctl description + * force debug entry even if pm_debug not set + */ +#ifdef DEBUG + pm_log("invalid diptype %d for cmd %d (%s)\n", + pcip->diptype, cmd, pcip->name); +#endif ASSERT(0); + return (EIO); } break; + } + + case PM_SRCH: /* command that takes a pm_searchargs_t arg */ + { + /* + * If no ppm, then there is nothing to search. + */ + if (DEVI(ddi_root_node())->devi_pm_ppm == NULL) { + ret = ENODEV; + break; + } + +#ifdef _MULTI_DATAMODEL + if ((mode & DATAMODEL_MASK) == DATAMODEL_ILP32) { + if (ddi_copyin((caddr_t)arg, &psa32, + sizeof (psa32), mode) != 0) { + PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin " + "EFAULT\n\n", cmdstr)) + return (EFAULT); + } + if (copyinstr((void *)(uintptr_t)psa32.pms_listname, + listname, MAXCOPYBUF, NULL)) { + PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF " + "%d, " "EFAULT\n", cmdstr, + (void *)(uintptr_t)psa32.pms_listname, + MAXCOPYBUF)) + ret = EFAULT; + break; + } + if (copyinstr((void *)(uintptr_t)psa32.pms_manufacturer, + manufacturer, MAXCOPYBUF, NULL)) { + PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF " + "%d, " "EFAULT\n", cmdstr, + (void *)(uintptr_t)psa32.pms_manufacturer, + MAXCOPYBUF)) + ret = EFAULT; + break; + } + if (copyinstr((void *)(uintptr_t)psa32.pms_product, + product, MAXCOPYBUF, NULL)) { + PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF " + "%d, " "EFAULT\n", cmdstr, + (void *)(uintptr_t)psa32.pms_product, + MAXCOPYBUF)) + ret = EFAULT; + break; + } + } else +#endif /* _MULTI_DATAMODEL */ + { + if (ddi_copyin((caddr_t)arg, &psa, + sizeof (psa), mode) != 0) { + PMD(PMD_ERROR, ("ioctl: %s: ddi_copyin " + "EFAULT\n\n", cmdstr)) + return (EFAULT); + } + if (copyinstr(psa.pms_listname, + listname, MAXCOPYBUF, NULL)) { + PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF " + "%d, " "EFAULT\n", cmdstr, + (void *)psa.pms_listname, MAXCOPYBUF)) + ret = EFAULT; + break; + } + if (copyinstr(psa.pms_manufacturer, + manufacturer, MAXCOPYBUF, NULL)) { + PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF " + "%d, " "EFAULT\n", cmdstr, + (void *)psa.pms_manufacturer, MAXCOPYBUF)) + ret = EFAULT; + break; + } + if (copyinstr(psa.pms_product, + product, MAXCOPYBUF, NULL)) { + PMD(PMD_ERROR, ("ioctl: %s: 0x%p MAXCOPYBUF " + "%d, " "EFAULT\n", cmdstr, + (void *)psa.pms_product, MAXCOPYBUF)) + ret = EFAULT; + break; + } + } + psa.pms_listname = listname; + psa.pms_manufacturer = manufacturer; + psa.pms_product = product; + switch (cmd) { + case PM_SEARCH_LIST: + ret = pm_ppm_searchlist(&psa); + break; + + default: + /* + * Internal error, invalid ioctl description + * force debug entry even if pm_debug not set + */ +#ifdef DEBUG + pm_log("invalid diptype %d for cmd %d (%s)\n", + pcip->diptype, cmd, pcip->name); +#endif + ASSERT(0); + return (EIO); + } + break; + } case NOSTRUCT: + { switch (cmd) { case PM_START_PM: case PM_START_CPUPM: + { mutex_enter(&pm_scan_lock); if ((cmd == PM_START_PM && autopm_enabled) || (cmd == PM_START_CPUPM && PM_CPUPM_ENABLED)) { @@ -2500,13 +2673,14 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) break; } if (cmd == PM_START_PM) - autopm_enabled = 1; + autopm_enabled = 1; else - cpupm = PM_CPUPM_ENABLE; + cpupm = PM_CPUPM_ENABLE; mutex_exit(&pm_scan_lock); ddi_walk_devs(ddi_root_node(), pm_start_pm_walk, &cmd); ret = 0; break; + } case PM_RESET_PM: case PM_STOP_PM: @@ -2523,13 +2697,16 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) ret = EINVAL; break; } - if (cmd == PM_STOP_PM) - autopm_enabled = 0; - else if (cmd == PM_STOP_CPUPM) - cpupm = PM_CPUPM_DISABLE; - else { - autopm_enabled = 0; - cpupm = PM_CPUPM_NOTSET; + if (cmd == PM_STOP_PM) { + autopm_enabled = 0; + pm_S3_enabled = 0; + autoS3_enabled = 0; + } else if (cmd == PM_STOP_CPUPM) { + cpupm = PM_CPUPM_DISABLE; + } else { + autopm_enabled = 0; + autoS3_enabled = 0; + cpupm = PM_CPUPM_NOTSET; } mutex_exit(&pm_scan_lock); @@ -2553,22 +2730,29 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) } case PM_GET_SYSTEM_THRESHOLD: + { *rval_p = pm_system_idle_threshold; ret = 0; break; + } case PM_GET_DEFAULT_SYSTEM_THRESHOLD: + { *rval_p = pm_default_idle_threshold; ret = 0; break; + } case PM_GET_CPU_THRESHOLD: + { *rval_p = pm_cpu_idle_threshold; ret = 0; break; + } case PM_SET_SYSTEM_THRESHOLD: case PM_SET_CPU_THRESHOLD: + { if ((int)arg < 0) { PMD(PMD_ERROR, ("ioctl: %s: arg 0x%x < 0" "--EINVAL\n", cmdstr, (int)arg)) @@ -2583,20 +2767,24 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) pm_cpu_idle_threshold = (int)arg; } ddi_walk_devs(ddi_root_node(), pm_set_idle_thresh_walk, - (void *) &cmd); + (void *) &cmd); ret = 0; break; + } case PM_IDLE_DOWN: + { if (pm_timeout_idledown() != 0) { ddi_walk_devs(ddi_root_node(), pm_start_idledown, (void *)PMID_IOC); } ret = 0; break; + } case PM_GET_PM_STATE: + { if (autopm_enabled) { *rval_p = PM_SYSTEM_PM_ENABLED; } else { @@ -2604,8 +2792,10 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) } ret = 0; break; + } case PM_GET_CPUPM_STATE: + { if (PM_CPUPM_ENABLED) *rval_p = PM_CPU_PM_ENABLED; else if (PM_CPUPM_DISABLED) @@ -2615,7 +2805,96 @@ pm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) ret = 0; break; } + + case PM_GET_AUTOS3_STATE: + { + if (autoS3_enabled) { + *rval_p = PM_AUTOS3_ENABLED; + } else { + *rval_p = PM_AUTOS3_DISABLED; + } + ret = 0; + break; + } + + case PM_GET_S3_SUPPORT_STATE: + { + if (pm_S3_enabled) { + *rval_p = PM_S3_SUPPORT_ENABLED; + } else { + *rval_p = PM_S3_SUPPORT_DISABLED; + } + ret = 0; + break; + } + + /* + * pmconfig tells us if the platform supports S3 + */ + case PM_ENABLE_S3: + { + mutex_enter(&pm_scan_lock); + if (pm_S3_enabled) { + mutex_exit(&pm_scan_lock); + PMD(PMD_ERROR, ("ioctl: %s: EBUSY\n", + cmdstr)) + ret = EBUSY; + break; + } + pm_S3_enabled = 1; + mutex_exit(&pm_scan_lock); + ret = 0; + break; + } + + case PM_DISABLE_S3: + { + mutex_enter(&pm_scan_lock); + pm_S3_enabled = 0; + mutex_exit(&pm_scan_lock); + ret = 0; + break; + } + + case PM_START_AUTOS3: + { + mutex_enter(&pm_scan_lock); + if (autoS3_enabled) { + mutex_exit(&pm_scan_lock); + PMD(PMD_ERROR, ("ioctl: %s: EBUSY\n", + cmdstr)) + ret = EBUSY; + break; + } + autoS3_enabled = 1; + mutex_exit(&pm_scan_lock); + ret = 0; + break; + } + + case PM_STOP_AUTOS3: + { + mutex_enter(&pm_scan_lock); + autoS3_enabled = 0; + mutex_exit(&pm_scan_lock); + ret = 0; + break; + } + + default: + /* + * Internal error, invalid ioctl description + * force debug entry even if pm_debug not set + */ +#ifdef DEBUG + pm_log("invalid diptype %d for cmd %d (%s)\n", + pcip->diptype, cmd, pcip->name); +#endif + ASSERT(0); + return (EIO); + } break; + } default: /* diff --git a/usr/src/uts/common/io/ppm/ppm.c b/usr/src/uts/common/io/ppm/ppm.c index b35a2e369d..e52ff63b78 100644 --- a/usr/src/uts/common/io/ppm/ppm.c +++ b/usr/src/uts/common/io/ppm/ppm.c @@ -206,8 +206,10 @@ int _init(void) { if (ddi_soft_state_init( - &ppm_statep, sizeof (ppm_unit_t), 1) != DDI_SUCCESS) + &ppm_statep, sizeof (ppm_unit_t), 1) != DDI_SUCCESS) { + PPMD(D_INIT, ("ppm: soft state init\n")) return (DDI_FAILURE); + } if (mod_install(&modlinkage) != DDI_SUCCESS) { ddi_soft_state_fini(&ppm_statep); @@ -220,7 +222,12 @@ _init(void) int _fini(void) { - return (mod_remove(&modlinkage)); + int error; + + if ((error = mod_remove(&modlinkage)) == DDI_SUCCESS) + ddi_soft_state_fini(&ppm_statep); + + return (error); } @@ -679,7 +686,7 @@ err_bydom: STRUCT_INIT(norm, mode); ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(norm), - STRUCT_SIZE(norm), mode); + STRUCT_SIZE(norm), mode); if (ret != 0) return (EFAULT); @@ -755,6 +762,10 @@ ppm_ctlops(dev_info_t *dip, dev_info_t *rdip, ppm_owned_t *owned; int mode; int ret = DDI_SUCCESS; + static int ppm_manage_sx(s3a_t *, int); + static int ppm_search_list(pm_searchargs_t *); + int *res = (int *)result; + s3a_t s3args; #ifdef DEBUG char *str = "ppm_ctlops"; @@ -765,8 +776,9 @@ ppm_ctlops(dev_info_t *dip, dev_info_t *rdip, str, ddi_binding_name(rdip), ctlstr)) #endif - if (ctlop != DDI_CTLOPS_POWER) + if (ctlop != DDI_CTLOPS_POWER) { return (DDI_FAILURE); + } unitp = (ppm_unit_t *)ddi_get_soft_state(ppm_statep, ppm_inst); @@ -779,8 +791,6 @@ ppm_ctlops(dev_info_t *dip, dev_info_t *rdip, ppm_manage_led(PPM_LED_BLINKING); else ppm_manage_led(PPM_LED_SOLIDON); - PPMD(D_LOWEST, ("%s: %sall devices are at lowest power \n", - str, mode ? "" : "not ")) return (DDI_SUCCESS); /* undo the claiming of 'rdip' at attach time */ @@ -984,9 +994,37 @@ ppm_ctlops(dev_info_t *dip, dev_info_t *rdip, return (DDI_FAILURE); } + case PMR_PPM_ENTER_SX: + case PMR_PPM_EXIT_SX: + s3args.s3a_state = reqp->req.ppm_power_enter_sx_req.sx_state; + s3args.s3a_test_point = + reqp->req.ppm_power_enter_sx_req.test_point; + s3args.s3a_wakephys = reqp->req.ppm_power_enter_sx_req.wakephys; + s3args.s3a_psr = reqp->req.ppm_power_enter_sx_req.psr; + ret = ppm_manage_sx(&s3args, + reqp->request_type == PMR_PPM_ENTER_SX); + if (ret) { + PPMD(D_CPR, ("ppm_manage_sx returns %d\n", ret)) + return (DDI_FAILURE); + } else { + return (DDI_SUCCESS); + } + + case PMR_PPM_SEARCH_LIST: + ret = ppm_search_list(reqp->req.ppm_search_list_req.searchlist); + reqp->req.ppm_search_list_req.result = ret; + *res = ret; + if (ret) { + PPMD(D_CPR, ("ppm_search_list returns %d\n", ret)) + return (DDI_FAILURE); + } else { + PPMD(D_CPR, ("ppm_search_list returns %d\n", ret)) + return (DDI_SUCCESS); + } + default: cmn_err(CE_WARN, "ppm_ctlops: unrecognized ctlops req(%d)", - reqp->request_type); + reqp->request_type); return (DDI_FAILURE); } } @@ -1246,7 +1284,7 @@ ppm_bringup_domains() } mutex_exit(&domp->lock); } - PPMD(D_CPR, ("%s[%d]: exit, ret=%d\n", str, ppmbringup, ret)) + PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmbringup)) return (ret); } @@ -1275,6 +1313,15 @@ ppm_sync_bookkeeping() mutex_exit(&domp->lock); continue; } + + /* + * skip NULL .devlist slot, for some may host pci device + * that can not tolerate clock off or not even participate + * in PM. + */ + if (domp->devlist == NULL) + continue; + switch (domp->model) { case PPMD_FET: ret = ppm_fetset(domp, PPMD_OFF); @@ -1291,7 +1338,7 @@ ppm_sync_bookkeeping() } mutex_exit(&domp->lock); } - PPMD(D_CPR, ("%s[%d]: exit, ret=%d\n", str, ppmsyncbp, ret)) + PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmsyncbp)) return (ret); } @@ -1655,14 +1702,14 @@ ppm_fetset(ppm_domain_t *domp, uint8_t value) * we might wait for longer than required */ PPMD(D_FET, ("%s : waiting %lu micro seconds " - "before on\n", domp->name, - delay - temp)) + "before on\n", domp->name, + delay - temp)); drv_usecwait(delay - temp); } } } switch (dc->method) { -#if !defined(__x86) +#ifdef sun4u case PPMDC_I2CKIO: { i2c_gpio_t i2c_req; i2c_req.reg_mask = dc->m_un.i2c.mask; @@ -1739,7 +1786,7 @@ ppm_fetget(ppm_domain_t *domp, uint8_t *lvl) } switch (dc->method) { -#if !defined(__x86) +#ifdef sun4u case PPMDC_I2CKIO: { i2c_gpio_t i2c_req; i2c_req.reg_mask = dc->m_un.i2c.mask; @@ -1773,7 +1820,7 @@ ppm_fetget(ppm_domain_t *domp, uint8_t *lvl) } off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.kio.val : - dc->next->m_un.kio.val; + dc->next->m_un.kio.val; *lvl = (kio_val == off_val) ? PPMD_OFF : PPMD_ON; PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name, @@ -2187,7 +2234,7 @@ ppm_gpioset(ppm_domain_t *domp, int key) } switch (dc->method) { -#if !defined(__x86) +#ifdef sun4u case PPMDC_I2CKIO: { i2c_gpio_t i2c_req; ppm_dev_t *pdev; @@ -2223,6 +2270,7 @@ ppm_gpioset(ppm_domain_t *domp, int key) break; } #endif + case PPMDC_KIO: ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr, (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred, @@ -2666,3 +2714,100 @@ ppm_power_down_domain(dev_info_t *dip) mutex_exit(&domp->lock); return (ret); } + +static int +ppm_manage_sx(s3a_t *s3ap, int enter) +{ + ppm_domain_t *domp = ppm_lookup_domain("domain_estar"); + ppm_dc_t *dc; + int ret = 0; + + if (domp == NULL) { + PPMD(D_CPR, ("ppm_manage_sx: can't find estar domain\n")) + return (ENODEV); + } + PPMD(D_CPR, ("ppm_manage_sx %x, enter %d\n", s3ap->s3a_state, + enter)) + switch (s3ap->s3a_state) { + case S3: + if (enter) { + dc = ppm_lookup_dc(domp, PPMDC_ENTER_S3); + } else { + dc = ppm_lookup_dc(domp, PPMDC_EXIT_S3); + } + ASSERT(dc && dc->method == PPMDC_KIO); + PPMD(D_CPR, + ("ppm_manage_sx: calling acpi driver (handle %p)" + " with %x\n", (void *)dc->lh, dc->m_un.kio.iowr)) + ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr, + (intptr_t)s3ap, FWRITE | FKIOCTL, kcred, NULL); + break; + + case S4: + /* S4 is not supported yet */ + return (EINVAL); + default: + ASSERT(0); + } + return (ret); +} + +/* + * Search enable/disable lists, which are encoded in ppm.conf as an array + * of char strings. + */ +static int +ppm_search_list(pm_searchargs_t *sl) +{ + int i; + int flags = DDI_PROP_DONTPASS; + ppm_unit_t *unitp = ddi_get_soft_state(ppm_statep, ppm_inst); + char **pp; + char *starp; + uint_t nelements; + char *manuf = sl->pms_manufacturer; + char *prod = sl->pms_product; + + if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, unitp->dip, flags, + sl->pms_listname, &pp, &nelements) != DDI_PROP_SUCCESS) { + PPMD(D_CPR, ("ppm_search_list prop lookup %s failed--EINVAL\n", + sl->pms_listname)) + return (EINVAL); + } + ASSERT((nelements & 1) == 0); /* must be even */ + + PPMD(D_CPR, ("ppm_search_list looking for %s, %s\n", manuf, prod)) + + for (i = 0; i < nelements; i += 2) { + PPMD(D_CPR, ("checking %s, %s", pp[i], pp[i+1])) + /* we support only a trailing '*' pattern match */ + if ((starp = strchr(pp[i], '*')) != NULL && *(starp + 1) == 0) { + /* LINTED - ptrdiff overflow */ + if (strncmp(manuf, pp[i], (starp - pp[i])) != 0) { + PPMD(D_CPR, (" no match %s with %s\n", + manuf, pp[i + 1])) + continue; + } + } + if ((starp = strchr(pp[i + 1], '*')) != NULL && + *(starp + 1) == 0) { + if (strncmp(prod, + /* LINTED - ptrdiff overflow */ + pp[i + 1], (starp - pp[i + 1])) != 0) { + PPMD(D_CPR, (" no match %s with %s\n", + prod, pp[i + 1])) + continue; + } + } + if (strcmp(manuf, pp[i]) == 0 && + (strcmp(prod, pp[i + 1]) == 0)) { + PPMD(D_CPR, (" match\n")) + ddi_prop_free(pp); + return (0); + } + PPMD(D_CPR, (" no match %s with %s or %s with %s\n", + manuf, pp[i], prod, pp[i + 1])) + } + ddi_prop_free(pp); + return (ENODEV); +} diff --git a/usr/src/uts/common/io/ppm/ppm_subr.c b/usr/src/uts/common/io/ppm/ppm_subr.c index 4bcd24c877..617ac3a380 100644 --- a/usr/src/uts/common/io/ppm/ppm_subr.c +++ b/usr/src/uts/common/io/ppm/ppm_subr.c @@ -207,6 +207,7 @@ ppm_lookup_hndl(int model, ppm_dc_t *key_dc) #define PPM_CTRL_PROP_SUFFIX "-control" struct ppm_domit ppm_domit_data[] = { + "SX", PPMD_SX, 0, PPMD_ON, "CPU", PPMD_CPU, PPMD_LOCK_ALL, PPMD_ON, "FET", PPMD_FET, PPMD_LOCK_ONE, PPMD_ON, "PCI", PPMD_PCI, PPMD_LOCK_ONE, PPMD_ON, @@ -401,11 +402,19 @@ ppm_lookup_dev(dev_info_t *dip) PPM_GET_PATHNAME(dip, path); for (domp = ppm_domain_p; domp; domp = domp->next) { - if (PPM_DOMAIN_UP(domp)) + if (PPM_DOMAIN_UP(domp)) { for (dbp = domp->conflist; dbp; dbp = dbp->next) { + /* + * allow claiming root without knowing + * its full name + */ + if (dip == ddi_root_node() && + strcmp(dbp->name, "/") == 0) + return (domp); if (ppm_match_devs(path, dbp) == 0) return (domp); } + } } return (NULL); @@ -514,7 +523,8 @@ ppm_match_devs(char *dev_path, ppm_db_t *dbp) /* "<exact match>*" */ if (dbp->name[dbp->wcpos[0] + 1] == 0) { cp = path + dbp->wcpos[0]; - while (*cp && (*cp++ != '/')); + while (*cp && (*cp++ != '/')) + ; return ((*cp == 0) ? 0 : -1); } @@ -761,6 +771,22 @@ ppm_init_cb(dev_info_t *dip) for (domp = ppm_domain_p; domp != NULL; domp = domp->next) { for (dc = domp->dc; dc; dc = dc->next) { + /* + * Warning: This code is rather confusing. + * + * It intends to ensure that ppm_init_lyr() is only + * called ONCE for a device that may be associated + * with more than one domain control. + * So, what it does is first to check to see if + * there is a handle, and then if not it goes on + * to call the init_lyr() routine. + * + * The non-obvious thing is that the ppm_init_lyr() + * routine, in addition to opening the device + * associated with the dc (domain control) in + * question, has the side-effect of creating the + * handle for that dc as well. + */ if (ppm_lookup_hndl(domp->model, dc) != NULL) continue; @@ -979,6 +1005,8 @@ struct ppm_confdefs { char *sym; int val; } ppm_confdefs_table[] = { + "ENTER_S3", PPMDC_ENTER_S3, + "EXIT_S3", PPMDC_EXIT_S3, "CPU_NEXT", PPMDC_CPU_NEXT, "PRE_CHNG", PPMDC_PRE_CHNG, "CPU_GO", PPMDC_CPU_GO, @@ -991,7 +1019,9 @@ struct ppm_confdefs { "LED_OFF", PPMDC_LED_OFF, "KIO", PPMDC_KIO, "VCORE", PPMDC_VCORE, +#ifdef sun4u "I2CKIO", PPMDC_I2CKIO, +#endif "CPUSPEEDKIO", PPMDC_CPUSPEEDKIO, "PRE_PWR_OFF", PPMDC_PRE_PWR_OFF, "PRE_PWR_ON", PPMDC_PRE_PWR_ON, @@ -1103,14 +1133,20 @@ ppm_parse_dc(char **dc_namep, ppm_dc_t *dc) (void) ppm_stoi(dclist[i], &dc->m_un.cpu.speeds); continue; } +#ifdef sun4u if (strstr(dclist[i], "mask=")) { (void) ppm_stoi(dclist[i], &dc->m_un.i2c.mask); continue; } +#endif /* This must be before the if statement for delay */ if (strstr(dclist[i], "post_delay=")) { +#ifdef sun4u ASSERT(dc->method == PPMDC_KIO || dc->method == PPMDC_I2CKIO); +#else + ASSERT(dc->method == PPMDC_KIO); +#endif /* * all delays are uint_t type instead of clock_t. * If the delay is too long, it might get truncated. @@ -1119,13 +1155,15 @@ ppm_parse_dc(char **dc_namep, ppm_dc_t *dc) switch (dc->method) { case PPMDC_KIO: (void) ppm_stoi(dclist[i], - &dc->m_un.kio.post_delay); + &dc->m_un.kio.post_delay); break; +#ifdef sun4u case PPMDC_I2CKIO: (void) ppm_stoi(dclist[i], - &dc->m_un.i2c.post_delay); + &dc->m_un.i2c.post_delay); break; +#endif default: break; @@ -1133,9 +1171,14 @@ ppm_parse_dc(char **dc_namep, ppm_dc_t *dc) continue; } if (strstr(dclist[i], "delay=")) { +#ifdef sun4u + ASSERT(dc->method == PPMDC_VCORE || + dc->method == PPMDC_KIO || + dc->method == PPMDC_I2CKIO); +#else ASSERT(dc->method == PPMDC_VCORE || - dc->method == PPMDC_KIO || - dc->method == PPMDC_I2CKIO); + dc->method == PPMDC_KIO); +#endif /* * all delays are uint_t type instead of clock_t. @@ -1148,9 +1191,11 @@ ppm_parse_dc(char **dc_namep, ppm_dc_t *dc) (void) ppm_stoi(dclist[i], &dc->m_un.kio.delay); break; +#ifdef sun4u case PPMDC_I2CKIO: (void) ppm_stoi(dclist[i], &dc->m_un.i2c.delay); break; +#endif case PPMDC_VCORE: (void) ppm_stoi(dclist[i], &dc->m_un.cpu.delay); @@ -1227,6 +1272,8 @@ ppm_lookup_dc(ppm_domain_t *domp, int cmd) case PPMDC_PWR_ON: case PPMDC_RESET_OFF: case PPMDC_RESET_ON: + case PPMDC_ENTER_S3: + case PPMDC_EXIT_S3: break; default: PPMD(D_PPMDC, ("%s: cmd(%d) unrecognized\n", str, cmd)) @@ -1234,9 +1281,11 @@ ppm_lookup_dc(ppm_domain_t *domp, int cmd) } for (dc = domp->dc; dc; dc = dc->next) { - if (dc->cmd == cmd) + if (dc->cmd == cmd) { return (dc); + } } + return (NULL); } @@ -1315,6 +1364,7 @@ ppm_get_ctlstr(int ctlop, uint_t mask) FLINTSTR(D_LOCKS, PMR_PPM_UNLOCK_POWER), FLINTSTR(D_LOCKS, PMR_PPM_TRY_LOCK_POWER), FLINTSTR(D_LOCKS, PMR_PPM_POWER_LOCK_OWNER), + FLINTSTR(D_CTLOPS1 | D_CTLOPS2, PMR_PPM_ENTER_SX), FLINTSTR(D_CTLOPS1 | D_CTLOPS2, PMR_UNKNOWN), }; @@ -1334,13 +1384,15 @@ ppm_print_dc(ppm_dc_t *dc) PPMD(D_PPMDC, ("\nAdds ppm_dc: path(%s),\n cmd(%x), " "method(%x), ", d->path, d->cmd, d->method)) - if (d->method == PPMDC_I2CKIO) { + if (d->method == PPMDC_KIO) { + PPMD(D_PPMDC, ("kio.iowr(%x), kio.val(0x%X)", + d->m_un.kio.iowr, d->m_un.kio.val)) +#ifdef sun4u + } else if (d->method == PPMDC_I2CKIO) { PPMD(D_PPMDC, ("i2c.iowr(%x), i2c.val(0x%X), " "i2c.mask(0x%X)", d->m_un.i2c.iowr, d->m_un.i2c.val, d->m_un.i2c.mask)) - } else if (d->method == PPMDC_KIO) { - PPMD(D_PPMDC, ("kio.iowr(%x), kio.val(0x%X)", - d->m_un.kio.iowr, d->m_un.kio.val)) +#endif } else if (d->method == PPMDC_VCORE) { PPMD(D_PPMDC, ("cpu: .iord(%x), .iowr(%x), .val(0x%X), " ".delay(0x%x)", diff --git a/usr/src/uts/common/io/sata/adapters/nv_sata/nv_sata.c b/usr/src/uts/common/io/sata/adapters/nv_sata/nv_sata.c index fa303ca9b5..f6d5870c5c 100644 --- a/usr/src/uts/common/io/sata/adapters/nv_sata/nv_sata.c +++ b/usr/src/uts/common/io/sata/adapters/nv_sata/nv_sata.c @@ -138,6 +138,7 @@ static void nv_read_signature(nv_port_t *nvp); static void mcp55_set_intr(nv_port_t *nvp, int flag); static void mcp04_set_intr(nv_port_t *nvp, int flag); static void nv_resume(nv_port_t *nvp); +static void nv_suspend(nv_port_t *nvp); static int nv_start_sync(nv_port_t *nvp, sata_pkt_t *spkt); static int nv_abort_active(nv_port_t *nvp, sata_pkt_t *spkt, int abort_reason); static void nv_copy_registers(nv_port_t *nvp, sata_device_t *sd, @@ -400,7 +401,7 @@ nv_get8(ddi_acc_handle_t handle, uint8_t *dev_addr) static int nv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { - int status, attach_state, intr_types, bar, i; + int status, attach_state, intr_types, bar, i, command; int inst = ddi_get_instance(dip); ddi_acc_handle_t pci_conf_handle; nv_ctl_t *nvc; @@ -439,6 +440,20 @@ nv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) attach_state |= ATTACH_PROGRESS_CONF_HANDLE; + /* + * If a device is attached after a suspend/resume, sometimes + * the command register is zero, as it might not be set by + * BIOS or a parent. Set it again here. + */ + command = pci_config_get16(pci_conf_handle, PCI_CONF_COMM); + + if (command == 0) { + cmn_err(CE_WARN, "nv_sata%d: restoring PCI command" + " register", inst); + pci_config_put16(pci_conf_handle, PCI_CONF_COMM, + PCI_COMM_IO|PCI_COMM_MAE|PCI_COMM_ME); + } + subclass = pci_config_get8(pci_conf_handle, PCI_CONF_SUBCLASS); if (subclass & PCI_MASS_RAID) { @@ -585,7 +600,6 @@ nv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) NVLOG((NVDBG_INIT, nvc, NULL, "nv_attach(): DDI_RESUME inst %d", inst)); - nvc->nvc_state &= ~NV_CTRL_SUSPEND; for (i = 0; i < NV_MAX_PORTS(nvc); i++) { @@ -716,6 +730,11 @@ nv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) * the current state. */ NVLOG((NVDBG_INIT, nvc, NULL, "nv_detach: DDI_SUSPEND")); + + for (i = 0; i < NV_MAX_PORTS(nvc); i++) { + nv_suspend(&(nvc->nvc_port[i])); + } + nvc->nvc_state |= NV_CTRL_SUSPEND; return (DDI_SUCCESS); @@ -1226,7 +1245,7 @@ nv_start_sync(nv_port_t *nvp, sata_pkt_t *spkt) (*(nvc->nvc_set_intr))(nvp, NV_INTR_ENABLE); NVLOG((NVDBG_SYNC, nvp->nvp_ctlp, nvp, "nv_sata_satapkt_sync:" - " done % reason %d", ret)); + " done % reason %d", ret)); return (ret); } @@ -2725,7 +2744,7 @@ mcp55_dma_setup_intr(nv_ctl_t *nvc, nv_port_t *nvp) MCP_SATA_AE_NCQ_SDEV_DMA_SETUP_TAG_SHIFT}; nv_cmn_err(CE_PANIC, nvc, nvp, - "this is should not be executed at all until NCQ"); + "this is should not be executed at all until NCQ"); mutex_enter(&nvp->nvp_mutex); @@ -4576,6 +4595,38 @@ nv_resume(nv_port_t *nvp) * nv_reset(nvp); */ + nv_reset(nvp); + + mutex_exit(&nvp->nvp_mutex); +} + +/* + * The PM functions for suspend and resume are incomplete and need additional + * work. It may or may not work in the current state. + */ +static void +nv_suspend(nv_port_t *nvp) +{ + NVLOG((NVDBG_INIT, nvp->nvp_ctlp, nvp, "nv_suspend()")); + + mutex_enter(&nvp->nvp_mutex); + + if (nvp->nvp_state & NV_PORT_INACTIVE) { + mutex_exit(&nvp->nvp_mutex); + + return; + } + + (*(nvp->nvp_ctlp->nvc_set_intr))(nvp, NV_INTR_DISABLE); + + /* + * power may have been removed to the port and the + * drive, and/or a drive may have been added or removed. + * Force a reset which will cause a probe and re-establish + * any state needed on the drive. + * nv_reset(nvp); + */ + mutex_exit(&nvp->nvp_mutex); } diff --git a/usr/src/uts/common/io/srn.c b/usr/src/uts/common/io/srn.c new file mode 100755 index 0000000000..cb2888871d --- /dev/null +++ b/usr/src/uts/common/io/srn.c @@ -0,0 +1,563 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * srn Provide apm-like interfaces to Xorg + */ + +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/modctl.h> +#include <sys/conf.h> /* driver flags and functions */ +#include <sys/open.h> /* OTYP_CHR definition */ +#include <sys/stat.h> /* S_IFCHR definition */ +#include <sys/pathname.h> /* name -> dev_info xlation */ +#include <sys/kmem.h> /* memory alloc stuff */ +#include <sys/debug.h> +#include <sys/pm.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/epm.h> +#include <sys/vfs.h> +#include <sys/mode.h> +#include <sys/mkdev.h> +#include <sys/promif.h> +#include <sys/consdev.h> +#include <sys/ddi_impldefs.h> +#include <sys/poll.h> +#include <sys/note.h> +#include <sys/taskq.h> +#include <sys/policy.h> +#include <sys/srn.h> + +/* + * Minor number is instance<<8 + clone minor from range 1-255; + * But only one will be allocated + */ +#define SRN_MINOR_TO_CLONE(minor) ((minor) & (SRN_MAX_CLONE - 1)) +#define SU 0x002 +#define SG 0x004 + +extern kmutex_t srn_clone_lock; /* protects srn_clones array */ +extern kcondvar_t srn_clones_cv[SRN_MAX_CLONE]; +extern uint_t srn_poll_cnt[SRN_MAX_CLONE]; + +/* + * The soft state of the srn driver. Since there will only be + * one of these, just reference it through a static struct. + */ +static struct srnstate { + dev_info_t *srn_dip; /* ptr to our dev_info node */ + int srn_instance; /* for ddi_get_instance() */ + uchar_t srn_clones[SRN_MAX_CLONE]; /* unique opens */ + struct cred *srn_cred[SRN_MAX_CLONE]; /* cred for each open */ + int srn_type[SRN_MAX_CLONE]; /* type of handshake */ + int srn_delivered[SRN_MAX_CLONE]; + srn_event_info_t srn_pending[SRN_MAX_CLONE]; +} srn = { NULL, -1}; +typedef struct srnstate *srn_state_t; + +kcondvar_t srn_clones_cv[SRN_MAX_CLONE]; +uint_t srn_poll_cnt[SRN_MAX_CLONE]; /* count of events for poll */ +int srn_apm_count; +int srn_autosx_count; +struct pollhead srn_pollhead[SRN_MAX_CLONE]; + +static int srn_open(dev_t *, int, int, cred_t *); +static int srn_close(dev_t, int, int, cred_t *); +static int srn_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); +static int srn_chpoll(dev_t, short, int, short *, struct pollhead **); + +static struct cb_ops srn_cb_ops = { + srn_open, /* open */ + srn_close, /* close */ + nodev, /* strategy */ + nodev, /* print */ + nodev, /* dump */ + nodev, /* read */ + nodev, /* write */ + srn_ioctl, /* ioctl */ + nodev, /* devmap */ + nodev, /* mmap */ + nodev, /* segmap */ + srn_chpoll, /* poll */ + ddi_prop_op, /* prop_op */ + NULL, /* streamtab */ + D_NEW | D_MP /* driver compatibility flag */ +}; + +static int srn_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, + void **result); +static int srn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); +static int srn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); +static void srn_notify(int type, int event); + +static struct dev_ops srn_ops = { + DEVO_REV, /* devo_rev */ + 0, /* refcnt */ + srn_getinfo, /* info */ + nulldev, /* identify */ + nulldev, /* probe */ + srn_attach, /* attach */ + srn_detach, /* detach */ + nodev, /* reset */ + &srn_cb_ops, /* driver operations */ + NULL, /* bus operations */ + NULL /* power */ +}; + +static struct modldrv modldrv = { + &mod_driverops, + "srn driver v1.4", + &srn_ops +}; + +static struct modlinkage modlinkage = { + MODREV_1, &modldrv, 0 +}; + +/* Local functions */ + +int +_init(void) +{ + return (mod_install(&modlinkage)); +} + +int +_fini(void) +{ + return (mod_remove(&modlinkage)); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} + +static int +srn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + int i; + extern void (*srn_signal)(int, int); + + switch (cmd) { + + case DDI_ATTACH: + if (srn.srn_instance != -1) /* Only allow one instance */ + return (DDI_FAILURE); + srn.srn_instance = ddi_get_instance(dip); + if (ddi_create_minor_node(dip, "srn", S_IFCHR, + (srn.srn_instance << 8) + 0, DDI_PSEUDO, 0) + != DDI_SUCCESS) { + return (DDI_FAILURE); + } + srn.srn_dip = dip; /* srn_init and getinfo depend on it */ + + for (i = 0; i < SRN_MAX_CLONE; i++) + cv_init(&srn_clones_cv[i], NULL, CV_DEFAULT, NULL); + + srn.srn_instance = ddi_get_instance(dip); + mutex_enter(&srn_clone_lock); + srn_signal = srn_notify; + mutex_exit(&srn_clone_lock); + ddi_report_dev(dip); + return (DDI_SUCCESS); + + default: + return (DDI_FAILURE); + } +} + +/* ARGSUSED */ +static int +srn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + int i; + extern int srn_inuse; + extern void (*srn_signal)(int, int); + + switch (cmd) { + case DDI_DETACH: + + mutex_enter(&srn_clone_lock); + while (srn_inuse) { + mutex_exit(&srn_clone_lock); + delay(1); + mutex_enter(&srn_clone_lock); + } + srn_signal = NULL; + mutex_exit(&srn_clone_lock); + + for (i = 0; i < SRN_MAX_CLONE; i++) + cv_destroy(&srn_clones_cv[i]); + + ddi_remove_minor_node(dip, NULL); + srn.srn_instance = -1; + return (DDI_SUCCESS); + + default: + return (DDI_FAILURE); + } +} + + +#ifdef DEBUG +char *srn_cmd_string; +int srn_cmd; +#endif + +/* + * Returns true if permission granted by credentials + * XXX + */ +static int +srn_perms(int perm, cred_t *cr) +{ + if ((perm & SU) && secpolicy_power_mgmt(cr) == 0) /* privileged? */ + return (1); + if ((perm & SG) && (crgetgid(cr) == 0)) /* group 0 is ok */ + return (1); + return (0); +} + +static int +srn_chpoll(dev_t dev, short events, int anyyet, short *reventsp, + struct pollhead **phpp) +{ + extern struct pollhead srn_pollhead[]; /* common/os/sunpm.c */ + int clone; + + clone = SRN_MINOR_TO_CLONE(getminor(dev)); + if ((events & (POLLIN | POLLRDNORM)) && srn_poll_cnt[clone]) { + *reventsp |= (POLLIN | POLLRDNORM); + } else { + *reventsp = 0; + if (!anyyet) { + *phpp = &srn_pollhead[clone]; + } + } + return (0); +} + +/*ARGSUSED*/ +static int +srn_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) +{ + dev_t dev; + int instance; + + switch (infocmd) { + case DDI_INFO_DEVT2DEVINFO: + if (srn.srn_instance == -1) + return (DDI_FAILURE); + *result = srn.srn_dip; + return (DDI_SUCCESS); + + case DDI_INFO_DEVT2INSTANCE: + dev = (dev_t)arg; + instance = getminor(dev) >> 8; + *result = (void *)(uintptr_t)instance; + return (DDI_SUCCESS); + + default: + return (DDI_FAILURE); + } +} + + +/*ARGSUSED1*/ +static int +srn_open(dev_t *devp, int flag, int otyp, cred_t *cr) +{ + int clone; + + if (otyp != OTYP_CHR) + return (EINVAL); + + mutex_enter(&srn_clone_lock); + for (clone = 1; clone < SRN_MAX_CLONE - 1; clone++) + if (!srn.srn_clones[clone]) + break; + + if (clone == SRN_MAX_CLONE) { + mutex_exit(&srn_clone_lock); + return (ENXIO); + } + srn.srn_cred[clone] = cr; + ASSERT(srn_apm_count >= 0); + srn_apm_count++; + srn.srn_type[clone] = SRN_TYPE_APM; + crhold(cr); + + *devp = makedevice(getmajor(*devp), (srn.srn_instance << 8) + + clone); + srn.srn_clones[clone] = 1; + srn.srn_cred[clone] = cr; + crhold(cr); + mutex_exit(&srn_clone_lock); + PMD(PMD_SX, ("srn open OK\n")) + return (0); +} + +/*ARGSUSED1*/ +static int +srn_close(dev_t dev, int flag, int otyp, cred_t *cr) +{ + int clone; + + if (otyp != OTYP_CHR) + return (EINVAL); + + clone = SRN_MINOR_TO_CLONE(getminor(dev)); + PMD(PMD_SX, ("srn_close: minor %x, clone %x\n", getminor(dev), + clone)) + mutex_enter(&srn_clone_lock); + crfree(srn.srn_cred[clone]); + srn.srn_cred[clone] = 0; + srn_poll_cnt[clone] = 0; + if (srn.srn_pending[clone].ae_type || srn.srn_delivered[clone]) { + srn.srn_pending[clone].ae_type = 0; + srn.srn_delivered[clone] = 0; + cv_signal(&srn_clones_cv[clone]); + } + switch (srn.srn_type[clone]) { + case SRN_TYPE_AUTOSX: + ASSERT(srn_autosx_count); + srn_autosx_count--; + break; + case SRN_TYPE_APM: + ASSERT(srn_apm_count); + srn_apm_count--; + break; + default: + ASSERT(0); + return (EINVAL); + } + srn.srn_clones[clone] = 0; + mutex_exit(&srn_clone_lock); + return (0); +} + +/*ARGSUSED*/ +static int +srn_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p) +{ + int clone = SRN_MINOR_TO_CLONE(getminor(dev)); + + PMD(PMD_SX, ("ioctl: %x: begin\n", cmd)) + + switch (cmd) { + case SRN_IOC_NEXTEVENT: + case SRN_IOC_SUSPEND: + case SRN_IOC_RESUME: + case SRN_IOC_AUTOSX: + break; + default: + return (ENOTTY); + } + + if (!srn_perms(SU | SG, srn.srn_cred[clone])) { + return (EPERM); + } + switch (cmd) { + case SRN_IOC_AUTOSX: + PMD(PMD_SX, ("SRN_IOC_AUTOSX entered\n")) + mutex_enter(&srn_clone_lock); + if (!srn.srn_clones[clone]) { + PMD(PMD_SX, (" ioctl !srn_clones--EINVAL\n")) + mutex_exit(&srn_clone_lock); + return (EINVAL); + } + if (srn.srn_pending[clone].ae_type) { + PMD(PMD_SX, ("AUTOSX while pending--EBUSY\n")) + mutex_exit(&srn_clone_lock); + return (EBUSY); + } + if (srn.srn_type[clone] == SRN_TYPE_AUTOSX) { + PMD(PMD_SX, ("AUTOSX already--EBUSY\n")) + mutex_exit(&srn_clone_lock); + return (EBUSY); + } + ASSERT(srn.srn_type[clone] == SRN_TYPE_APM); + srn.srn_type[clone] = SRN_TYPE_AUTOSX; + srn_apm_count--; + ASSERT(srn_apm_count >= 0); + ASSERT(srn_autosx_count >= 0); + srn_autosx_count++; + mutex_exit(&srn_clone_lock); + PMD(PMD_SX, ("SRN_IOC_AUTOSX returns success\n")) + return (0); + + case SRN_IOC_NEXTEVENT: + /* + * return the next suspend or resume event; there should + * be one, cause we only get called if we've signalled a + * poll data completion + * then wake up the kernel thread sleeping for the delivery + */ + PMD(PMD_SX, ("SRN_IOC_NEXTEVENT entered\n")) + mutex_enter(&srn_clone_lock); + if (srn_poll_cnt[clone] == 0) { + mutex_exit(&srn_clone_lock); + PMD(PMD_SX, ("SRN_IOC_NEXTEVENT clone %d " + "EWOULDBLOCK\n", clone)) + return (EWOULDBLOCK); + } + ASSERT(srn.srn_pending[clone].ae_type); + if (ddi_copyout(&srn.srn_pending[clone], (void *)arg, + sizeof (srn_event_info_t), mode) != 0) { + mutex_exit(&srn_clone_lock); + PMD(PMD_SX, ("SRN_IOC_NEXTEVENT clone %d EFAULT\n", + clone)) + return (EFAULT); + } + if (srn.srn_type[clone] == SRN_TYPE_APM) + srn.srn_delivered[clone] = + srn.srn_pending[clone].ae_type; + PMD(PMD_SX, ("SRN_IOC_NEXTEVENT clone %d delivered %x\n", + clone, srn.srn_pending[clone].ae_type)) + srn_poll_cnt[clone] = 0; + mutex_exit(&srn_clone_lock); + return (0); + + case SRN_IOC_SUSPEND: + /* ack suspend */ + PMD(PMD_SX, ("SRN_IOC_SUSPEND entered clone %d\n", clone)) + mutex_enter(&srn_clone_lock); + if (srn.srn_delivered[clone] != SRN_SUSPEND_REQ) { + mutex_exit(&srn_clone_lock); + PMD(PMD_SX, ("SRN_IOC_SUSPEND EINVAL\n")) + return (EINVAL); + } + srn.srn_delivered[clone] = 0; + srn.srn_pending[clone].ae_type = 0; + /* notify the kernel suspend thread to continue */ + PMD(PMD_SX, ("SRN_IOC_SUSPEND clone %d ok\n", clone)) + cv_signal(&srn_clones_cv[clone]); + mutex_exit(&srn_clone_lock); + return (0); + + case SRN_IOC_RESUME: + /* ack resume */ + PMD(PMD_SX, ("SRN_IOC_RESUME entered clone %d\n", clone)) + mutex_enter(&srn_clone_lock); + if (srn.srn_delivered[clone] != SRN_NORMAL_RESUME) { + mutex_exit(&srn_clone_lock); + PMD(PMD_SX, ("SRN_IOC_RESUME EINVAL\n")) + return (EINVAL); + } + srn.srn_delivered[clone] = 0; + srn.srn_pending[clone].ae_type = 0; + /* notify the kernel resume thread to continue */ + PMD(PMD_SX, ("SRN_IOC_RESUME ok for clone %d\n", clone)) + cv_signal(&srn_clones_cv[clone]); + mutex_exit(&srn_clone_lock); + return (0); + + default: + PMD(PMD_SX, ("srn_ioctl unknown cmd EINVAL\n")) + return (EINVAL); + } +} +/* + * A very simple handshake with the srn driver, + * only one outstanding event at a time. + * The OS delivers the event and depending on type, + * either blocks waiting for the ack, or drives on + */ +void +srn_notify(int type, int event) +{ + int clone, count; + PMD(PMD_SX, ("srn_notify entered with type %d, event 0x%x\n", + type, event)); + ASSERT(mutex_owned(&srn_clone_lock)); + switch (type) { + case SRN_TYPE_APM: + if (srn_apm_count == 0) { + PMD(PMD_SX, ("no apm types\n")) + return; + } + count = srn_apm_count; + break; + case SRN_TYPE_AUTOSX: + if (srn_autosx_count == 0) { + PMD(PMD_SX, ("no autosx types\n")) + return; + } + count = srn_autosx_count; + break; + default: + ASSERT(0); + break; + } + ASSERT(count > 0); + PMD(PMD_SX, ("count %d\n", count)) + for (clone = 0; clone < SRN_MAX_CLONE; clone++) { + if (srn.srn_type[clone] == type) { + if (type == SRN_TYPE_APM) { + ASSERT(srn.srn_pending[clone].ae_type == 0); + ASSERT(srn_poll_cnt[clone] == 0); + ASSERT(srn.srn_delivered[clone] == 0); + } + srn.srn_pending[clone].ae_type = event; + srn_poll_cnt[clone] = 1; + PMD(PMD_SX, ("pollwake %d\n", clone)) + pollwakeup(&srn_pollhead[clone], (POLLRDNORM | POLLIN)); + count--; + if (count == 0) + break; + } + } + if (type == SRN_TYPE_AUTOSX) { /* we don't wait */ + PMD(PMD_SX, ("Not waiting for AUTOSX ack\n")) + return; + } + ASSERT(type == SRN_TYPE_APM); + /* otherwise wait for acks */ +restart: + /* + * We wait untill all of the pending events are cleared. + * We have to start over every time we do a cv_wait because + * we give up the mutex and can be re-entered + */ + for (clone = 1; clone < SRN_MAX_CLONE; clone++) { + if (srn.srn_clones[clone] == 0 || + srn.srn_type[clone] != SRN_TYPE_APM) + continue; + if (srn.srn_pending[clone].ae_type) { + PMD(PMD_SX, ("srn_notify waiting for ack for clone %d, " + "event %x\n", clone, event)) + cv_wait(&srn_clones_cv[clone], &srn_clone_lock); + goto restart; + } + } + PMD(PMD_SX, ("srn_notify done with %x\n", event)) +} diff --git a/usr/src/uts/common/io/srn.conf b/usr/src/uts/common/io/srn.conf new file mode 100755 index 0000000000..7db6545647 --- /dev/null +++ b/usr/src/uts/common/io/srn.conf @@ -0,0 +1,27 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +name="srn" parent="pseudo" instance=0; diff --git a/usr/src/uts/common/io/usb/hcd/ehci/ehci.c b/usr/src/uts/common/io/usb/hcd/ehci/ehci.c index 3b986e1723..93b7815a7d 100644 --- a/usr/src/uts/common/io/usb/hcd/ehci/ehci.c +++ b/usr/src/uts/common/io/usb/hcd/ehci/ehci.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -65,6 +65,11 @@ uint_t ehci_errmask = (uint_t)PRINT_MASK_ALL; uint_t ehci_errlevel = USB_LOG_L2; uint_t ehci_instance_debug = (uint_t)-1; +/* + * Tunable to ensure host controller goes off even if a keyboard is attached. + */ +int force_ehci_off = 1; + /* Enable all workarounds for VIA VT62x2 */ uint_t ehci_vt62x2_workaround = EHCI_VIA_WORKAROUNDS; diff --git a/usr/src/uts/common/io/usb/hcd/ehci/ehci_util.c b/usr/src/uts/common/io/usb/hcd/ehci/ehci_util.c index bc760adfc8..44e6688a4b 100644 --- a/usr/src/uts/common/io/usb/hcd/ehci/ehci_util.c +++ b/usr/src/uts/common/io/usb/hcd/ehci/ehci_util.c @@ -54,6 +54,7 @@ extern void *ehci_statep; extern void ehci_handle_endpoint_reclaimation(ehci_state_t *); extern uint_t ehci_vt62x2_workaround; +extern int force_ehci_off; /* Adjustable variables for the size of the pools */ int ehci_qh_pool_size = EHCI_QH_POOL_SIZE; @@ -2006,7 +2007,7 @@ ehci_cpr_suspend(ehci_state_t *ehcip) * Stop the ehci host controller * if usb keyboard is not connected. */ - if (ehcip->ehci_polled_kbd_count == 0) { + if (ehcip->ehci_polled_kbd_count == 0 || force_ehci_off != 0) { Set_OpReg(ehci_command, Get_OpReg(ehci_command) & ~EHCI_CMD_HOST_CTRL_RUN); } diff --git a/usr/src/uts/common/io/usb/hcd/openhci/ohci.c b/usr/src/uts/common/io/usb/hcd/openhci/ohci.c index 2cb62f57ee..bbfef3dff2 100644 --- a/usr/src/uts/common/io/usb/hcd/openhci/ohci.c +++ b/usr/src/uts/common/io/usb/hcd/openhci/ohci.c @@ -47,6 +47,8 @@ /* Pointer to the state structure */ static void *ohci_statep; +int force_ohci_off = 1; + /* Number of instances */ #define OHCI_INSTS 1 @@ -2353,7 +2355,7 @@ ohci_cpr_suspend(ohci_state_t *ohcip) * Suspend the ohci host controller * if usb keyboard is not connected. */ - if (ohcip->ohci_polled_kbd_count == 0) { + if (ohcip->ohci_polled_kbd_count == 0 || force_ohci_off != 0) { Set_OpReg(hcr_control, HCR_CONTROL_SUSPD); } |