/* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * This file contains interface code to make the kernel look it has * an svr4.2 ddi/ddk. It also adds a little other system dependent * functionality that is useful for drivers lower than nsctl. */ #include #ifndef DS_DDICT #include /* only DDI compliant as of 5.9 */ #endif #include #include #include #include #include #include #include #include #ifndef DS_DDICT #include #endif #include #include #include "nsc_thread.h" #ifdef DS_DDICT #include #endif #include #include #include "nskernd.h" #include "nsc_list.h" kmutex_t _nskern_lock; void _nsc_stop_proc(void); void _nsc_start_proc(void); /* * Solaris specific driver module interface code. */ static struct cb_ops nskern_cb_ops = { nulldev, /* open */ nulldev, /* close */ nodev, /* strategy */ nodev, /* print */ nodev, /* dump */ nodev, /* read */ nodev, /* write */ nodev, /* ioctl */ nodev, /* devmap routine */ nodev, /* mmap routine */ nodev, /* segmap */ nochpoll, /* chpoll */ ddi_prop_op, 0, /* not a STREAMS driver, no cb_str routine */ D_NEW | D_MP | D_64BIT, /* safe for multi-thread/multi-processor */ CB_REV, nodev, /* aread */ nodev, /* awrite */ }; static int _nskern_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); static int _nskern_attach(dev_info_t *, ddi_attach_cmd_t); static int _nskern_detach(dev_info_t *, ddi_detach_cmd_t); static struct dev_ops nskern_ops = { DEVO_REV, /* Driver build version */ 0, /* device reference count */ _nskern_getinfo, nulldev, /* identify */ nulldev, /* probe */ _nskern_attach, _nskern_detach, nodev, /* reset */ &nskern_cb_ops, (struct bus_ops *)NULL }; static struct modldrv nskern_ldrv = { &mod_driverops, "nws:Kernel Interface:" ISS_VERSION_STR, &nskern_ops }; static dev_info_t *nskern_dip; static struct modlinkage nskern_modlinkage = { MODREV_1, &nskern_ldrv, NULL }; /* * Solaris module load time code */ int _init(void) { void nskern_init(); int err; mutex_init(&_nskern_lock, NULL, MUTEX_DRIVER, NULL); err = mod_install(&nskern_modlinkage); if (err) { mutex_destroy(&_nskern_lock); cmn_err(CE_WARN, "nskern_init: mod_install err %d", err); return (err); } nskern_init(); return (DDI_SUCCESS); } /* * Solaris module unload time code */ int _fini(void) { int err; if ((err = mod_remove(&nskern_modlinkage)) == 0) { nskernd_stop(); _nsc_stop_proc(); nskernd_deinit(); mutex_destroy(&_nskern_lock); } return (err); } int _info(struct modinfo *modinfop) { return (mod_info(&nskern_modlinkage, modinfop)); } /* * Attach an instance of the device. This happens before an open * can succeed. */ static int _nskern_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { if (cmd == DDI_ATTACH) { nskern_dip = dip; return (DDI_SUCCESS); } else { return (DDI_FAILURE); } } /* ARGSUSED */ static int _nskern_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { if (cmd == DDI_DETACH) { nskern_dip = NULL; return (DDI_SUCCESS); } else { return (DDI_FAILURE); } } /* ARGSUSED */ static int _nskern_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) { int rc = DDI_FAILURE; switch (cmd) { case DDI_INFO_DEVT2DEVINFO: *result = nskern_dip; rc = DDI_SUCCESS; break; case DDI_INFO_DEVT2INSTANCE: /* single instance */ *result = 0; rc = DDI_SUCCESS; break; } return (rc); } /* ARGSUSED */ int _nskern_print(dev_t dev, char *s) { cmn_err(CE_WARN, "nskern:%s", s); return (0); } /* * nskern_init - initialize the nskern layer at module load time. */ void nskern_init(void) { _nsc_start_proc(); nskernd_init(); (void) nst_startup(); } #if (defined(DS_DDICT)) static clock_t nskern_lbolt(void) { #ifdef _SunOS_5_6 clock_t lbolt; if (drv_getparm(LBOLT, &lbolt) == 0) return (lbolt); return (0); #else return (ddi_get_lbolt()); #endif } #endif /* ddict */ /* * nsc_usec() * - return the value of the "microsecond timer emulation". * * Pre-SunOS 5.9: * Actually this is a fake free running counter based on the lbolt value. * * SunOS 5.9+ * This is based on the gethrtime(9f) DDI facility. */ #if (defined(DS_DDICT)) /* these two #defines need to match! */ #define USEC_SHIFT 16 #define INCR_TYPE uint16_t #endif /* ! _SunOS_5_9+ */ clock_t nsc_usec(void) { /* avoid divide by zero */ return (gethrtime() / 1000); } /* * nsc_yield - yield the cpu. */ void nsc_yield(void) { /* can't call yield() unless there is an lwp context */ /* do this for now */ delay(2); } /* * void * ls_ins_before(ls_elt_t *, ls_elt_t *) * Link new into list before old. * * Calling/Exit State: * None. */ #ifdef lint void nsc_ddi_ls_ins_before(ls_elt_t *old, ls_elt_t *new) #else void ls_ins_before(ls_elt_t *old, ls_elt_t *new) #endif { new->ls_prev = old->ls_prev; new->ls_next = old; new->ls_prev->ls_next = new; new->ls_next->ls_prev = new; } /* * void * ls_ins_after(ls_elt_t *, ls_elt_t *) * Link new into list after old. * * Calling/Exit State: * None. */ #ifdef lint void nsc_ddi_ls_ins_after(ls_elt_t *old, ls_elt_t *new) #else void ls_ins_after(ls_elt_t *old, ls_elt_t *new) #endif { new->ls_next = old->ls_next; new->ls_prev = old; new->ls_next->ls_prev = new; new->ls_prev->ls_next = new; } /* * ls_elt_t * * ls_remque(ls_elt_t *) * Unlink first element in the specified list. * * Calling/Exit State: * Returns the element's address or 0 if list is empty. * Resets elements pointers to empty list state. */ ls_elt_t * ls_remque(ls_elt_t *p) { ls_elt_t *result = 0; if (!LS_ISEMPTY(p)) { result = p->ls_next; result->ls_prev->ls_next = result->ls_next; result->ls_next->ls_prev = result->ls_prev; LS_INIT(result); } return (result); } /* * void * ls_remove(ls_elt_t *) * Unlink donated element for list. * * Calling/Exit State: * Resets elements pointers to empty list state. */ #ifdef lint void nsc_ddi_ls_remove(ls_elt_t *p) #else void ls_remove(ls_elt_t *p) #endif { p->ls_prev->ls_next = p->ls_next; p->ls_next->ls_prev = p->ls_prev; LS_INIT(p); }