diff options
| author | mbalmer <mbalmer@NetBSD.org> | 2013-10-16 19:44:57 +0000 |
|---|---|---|
| committer | mbalmer <mbalmer@NetBSD.org> | 2013-10-16 19:44:57 +0000 |
| commit | e0fe45ce9c35c204a7c19d5a5291ca64b47b2452 (patch) | |
| tree | e871dc37f16f51aff76180bb26119b0ced6630a4 /sys/modules/lua/lua.c | |
| parent | a1bd524499a4e93e3f6f934dc1e6f644595e0a89 (diff) | |
welcome lua(4), a devide driver that can create and control Lua states inside the kernel
Diffstat (limited to 'sys/modules/lua/lua.c')
| -rw-r--r-- | sys/modules/lua/lua.c | 862 |
1 files changed, 862 insertions, 0 deletions
diff --git a/sys/modules/lua/lua.c b/sys/modules/lua/lua.c new file mode 100644 index 00000000000..7fd0001a186 --- /dev/null +++ b/sys/modules/lua/lua.c @@ -0,0 +1,862 @@ +/* $NetBSD */ + +/* + * Copyright (c) 2011 by Marc Balmer <mbalmer@NetBSD.org>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the Author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Lua device driver */ + +#include <sys/param.h> +#include <sys/fcntl.h> +#include <sys/conf.h> +#include <sys/condvar.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/lock.h> +#include <sys/lua.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/namei.h> +#include <sys/queue.h> +#include <sys/sysctl.h> +#include <sys/vnode.h> + +#include <lauxlib.h> + +#include "luavar.h" + +struct lua_softc { + device_t sc_dev; + + kmutex_t sc_lock; + kcondvar_t sc_inuse_cv; + bool sc_inuse; + + /* Locking access to state queues */ + kmutex_t sc_state_lock; + kcondvar_t sc_state_cv; + bool sc_state; + + struct sysctllog *sc_log; +}; + +static device_t sc_self; +static bool lua_autoload_on = true; +static bool lua_require_on = true; +static bool lua_bytecode_on = false; +static int lua_verbose; +static int lua_max_instr; + +static LIST_HEAD(, lua_state) lua_states; +static LIST_HEAD(, lua_module) lua_modules; + +static int lua_match(device_t, cfdata_t, void *); +static void lua_attach(device_t, device_t, void *); +static int lua_detach(device_t, int); +static klua_State *klua_find(const char *); +static const char *lua_reader(lua_State *, void *, size_t *); +static void lua_maxcount(lua_State *, lua_Debug *); + +static int lua_require(lua_State *); + +CFATTACH_DECL_NEW(lua, sizeof(struct lua_softc), + lua_match, lua_attach, lua_detach, NULL); + +dev_type_open(luaopen); +dev_type_close(luaclose); +dev_type_ioctl(luaioctl); + +const struct cdevsw lua_cdevsw = { + luaopen, luaclose, noread, nowrite, luaioctl, nostop, notty, + nopoll, nommap, nokqfilter, D_OTHER | D_MPSAFE +}; + +struct lua_loadstate { + struct vnode *vp; + size_t size; + off_t off; +}; + +extern struct cfdriver lua_cd; + +static int +lua_match(device_t parent, cfdata_t match, void *aux) +{ + return 1; +} + +static void +lua_attach(device_t parent, device_t self, void *aux) +{ + struct lua_softc *sc; + const struct sysctlnode *node; + + if (sc_self) + return; + + sc = device_private(self); + sc->sc_dev = self; + sc_self = self; + + mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM); + cv_init(&sc->sc_inuse_cv, "luactl"); + + mutex_init(&sc->sc_state_lock, MUTEX_DEFAULT, IPL_VM); + cv_init(&sc->sc_state_cv, "luastate"); + + pmf_device_register(self, NULL, NULL); + + /* Sysctl to provide some control over behaviour */ + sysctl_createv(NULL, 0, NULL, NULL, + CTLFLAG_PERMANENT, + CTLTYPE_NODE, "kern", NULL, + NULL, 0, NULL, 0, + CTL_KERN, CTL_EOL); + sysctl_createv(&sc->sc_log, 0, NULL, &node, + CTLFLAG_OWNDESC, + CTLTYPE_NODE, "lua", + SYSCTL_DESCR("Lua options"), + NULL, 0, NULL, 0, + CTL_KERN, CTL_CREATE, CTL_EOL); + + if (node == NULL) { + printf(": can't create sysctl node\n"); + return; + } + + /* + * XXX Some of the sysctl values must not be changed after the + * securelevel has been raised. + */ + sysctl_createv(&sc->sc_log, 0, &node, NULL, + CTLFLAG_READWRITE | CTLFLAG_OWNDESC, + CTLTYPE_BOOL, "require", + SYSCTL_DESCR("Enable the require command"), + NULL, 0, &lua_require_on, 0, + CTL_CREATE, CTL_EOL); + + sysctl_createv(&sc->sc_log, 0, &node, NULL, + CTLFLAG_READWRITE | CTLFLAG_OWNDESC, + CTLTYPE_BOOL, "autoload", + SYSCTL_DESCR("Enable automatic load of modules"), + NULL, 0, &lua_autoload_on, 0, + CTL_CREATE, CTL_EOL); + + sysctl_createv(&sc->sc_log, 0, &node, NULL, + CTLFLAG_READWRITE | CTLFLAG_OWNDESC, + CTLTYPE_BOOL, "bytecode", + SYSCTL_DESCR("Enable loading of bytecode"), + NULL, 0, &lua_bytecode_on, 0, + CTL_CREATE, CTL_EOL); + + sysctl_createv(&sc->sc_log, 0, &node, NULL, + CTLFLAG_READWRITE | CTLFLAG_OWNDESC, + CTLTYPE_INT, "verbose", + SYSCTL_DESCR("Enable verbose output"), + NULL, 0, &lua_verbose, 0, + CTL_CREATE, CTL_EOL); + + sysctl_createv(&sc->sc_log, 0, &node, NULL, + CTLFLAG_READWRITE | CTLFLAG_OWNDESC, + CTLTYPE_INT, "maxcount", + SYSCTL_DESCR("Limit maximum instruction count"), + NULL, 0, &lua_max_instr, 0, + CTL_CREATE, CTL_EOL); + + aprint_normal_dev(self, "%s %s\n", LUA_RELEASE, LUA_COPYRIGHT); +} + +static int +lua_detach(device_t self, int flags) +{ + struct lua_softc *sc; + struct lua_state *s; + + sc = device_private(self); + pmf_device_deregister(self); + + if (sc->sc_log != NULL) { + sysctl_teardown(&sc->sc_log); + sc->sc_log = NULL; + } + + /* Traverse the list of states and close them */ + while ((s = LIST_FIRST(&lua_states)) != NULL) { + LIST_REMOVE(s, lua_next); + klua_close(s->K); + if (lua_verbose) + device_printf(self, "state %s destroyed\n", + s->lua_name); + free(s, NULL); + } + mutex_destroy(&sc->sc_lock); + cv_destroy(&sc->sc_inuse_cv); + mutex_destroy(&sc->sc_state_lock); + cv_destroy(&sc->sc_state_cv); + sc_self = NULL; + return 0; +} + +int +luaopen(dev_t dev, int flag, int mode, struct lwp *l) +{ + struct lua_softc *sc; + int error; + + if (minor(dev) > 0) + return ENXIO; + + sc = device_lookup_private(&lua_cd, minor(dev)); + if (sc == NULL) + return ENXIO; + + mutex_enter(&sc->sc_lock); + while (sc->sc_inuse == true) { + error = cv_wait_sig(&sc->sc_inuse_cv, &sc->sc_lock); + if (error) + break; + } + if (!error) + sc->sc_inuse = true; + mutex_exit(&sc->sc_lock); + + if (error) + return error; + return 0; +} + +int +luaclose(dev_t dev, int flag, int mode, struct lwp *l) +{ + struct lua_softc *sc; + + if (minor(dev) > 0) + return ENXIO; + sc = device_lookup_private(&lua_cd, minor(dev)); + mutex_enter(&sc->sc_lock); + sc->sc_inuse = false; + cv_signal(&sc->sc_inuse_cv); + mutex_exit(&sc->sc_lock); + return 0; +} + +int +luaioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) +{ + struct lua_softc *sc; + struct lua_info *info; + struct lua_create *create; + struct lua_require *require; + struct lua_load *load; + struct lua_state *s; + struct lua_module *m; + kauth_cred_t cred; + struct nameidata nd; + struct pathbuf *pb; + struct vattr va; + struct lua_loadstate ls; + int error, n; + klua_State *K; + + sc = device_lookup_private(&lua_cd, minor(dev)); + if (!device_is_active(sc->sc_dev)) + return EBUSY; + + switch (cmd) { + case LUAINFO: + info = data; + if (info->states == NULL) { + info->num_states = 0; + LIST_FOREACH(s, &lua_states, lua_next) + info->num_states++; + } else { + n = 0; + LIST_FOREACH(s, &lua_states, lua_next) { + if (n > info->num_states) + break; + copyoutstr(s->lua_name, info->states[n].name, + MAX_LUA_NAME, NULL); + copyoutstr(s->lua_desc, info->states[n].desc, + MAX_LUA_DESC, NULL); + info->states[n].user = s->K->ks_user; + n++; + } + info->num_states = n; + } + break; + case LUACREATE: + create = data; + + if (*create->name == '_') { + if (lua_verbose) + device_printf(sc->sc_dev, "names of user " + "created states must not begin with '_'"); + return ENXIO; + } + LIST_FOREACH(s, &lua_states, lua_next) + if (!strcmp(s->lua_name, create->name)) { + if (lua_verbose) + device_printf(sc->sc_dev, + "state %s exists\n", create->name); + return EBUSY; + } + + K = klua_newstate(lua_alloc, NULL, create->name, + create->desc); + K->ks_user = true; + + if (K == NULL) + return ENOMEM; + if (lua_verbose) + device_printf(sc->sc_dev, "state %s created\n", + create->name); + break; + case LUADESTROY: + create = data; + + K = klua_find(create->name); + + if (K != NULL && (K->ks_user == true)) { + klua_close(K); + return 0; + } + return EBUSY; + case LUAREQUIRE: /* 'require' a module in a State */ + require = data; + LIST_FOREACH(s, &lua_states, lua_next) + if (!strcmp(s->lua_name, require->state)) + LIST_FOREACH(m, &lua_modules, mod_next) + if (!strcmp(m->mod_name, + require->module)) { + if (lua_verbose) + device_printf( + sc->sc_dev, + "requiring module " + "%s to state %s\n", + m->mod_name, + s->lua_name); + m->open(s->K->L); + m->refcount++; + LIST_INSERT_HEAD( + &s->lua_modules, m, + mod_next); + return 0; + } + return ENXIO; + case LUALOAD: + load = data; + if (strrchr(load->path, '/') == NULL) + return ENXIO; + + LIST_FOREACH(s, &lua_states, lua_next) + if (!strcmp(s->lua_name, load->state)) { + if (lua_verbose) + device_printf(sc->sc_dev, + "loading %s into state %s\n", + load->path, s->lua_name); + cred = kauth_cred_get(); + pb = pathbuf_create(load->path); + if (pb == NULL) + return ENOMEM; + NDINIT(&nd, LOOKUP, FOLLOW | NOCHROOT, pb); + pathbuf_destroy(pb); + error = vn_open(&nd, FREAD, 0); + if (error) { + if (lua_verbose) + device_printf(sc->sc_dev, + "error vn_open %d\n", + error); + return error; + } + error = VOP_GETATTR(nd.ni_vp, &va, + kauth_cred_get()); + if (error) { + VOP_UNLOCK(nd.ni_vp); + vn_close(nd.ni_vp, FREAD, + kauth_cred_get()); + if (lua_verbose) + device_printf(sc->sc_dev, + "erro VOP_GETATTR %d\n", + error); + return error; + } + if (va.va_type != VREG) { + VOP_UNLOCK(nd.ni_vp); + vn_close(nd.ni_vp, FREAD, + kauth_cred_get()); + return EINVAL; + } + ls.vp = nd.ni_vp; + ls.off = 0L; + ls.size = va.va_size; + VOP_UNLOCK(nd.ni_vp); + error = lua_load(s->K->L, lua_reader, &ls, + strrchr(load->path, '/') + 1); + vn_close(nd.ni_vp, FREAD, cred); + switch (error) { + case 0: /* no error */ + break; + case LUA_ERRSYNTAX: + if (lua_verbose) + device_printf(sc->sc_dev, + "syntax error\n"); + return EINVAL; + case LUA_ERRMEM: + if (lua_verbose) + device_printf(sc->sc_dev, + "memory error\n"); + return ENOMEM; + default: + if (lua_verbose) + device_printf(sc->sc_dev, + "load error %d: %s\n", + error, + lua_tostring(s->K->L, -1)); + return EINVAL; + } + if (lua_max_instr > 0) + lua_sethook(s->K->L, lua_maxcount, + LUA_MASKCOUNT, lua_max_instr); + error = lua_pcall(s->K->L, 0, LUA_MULTRET, 0); + if (error) { + if (lua_verbose) { + device_printf(sc->sc_dev, + "execution error: %s\n", + lua_tostring(s->K->L, -1)); + } + return EINVAL; + } + return 0; + } + return ENXIO; + } + return 0; +} + +static int +lua_require(lua_State *L) +{ + struct lua_state *s; + struct lua_module *m, *md; + const char *module; + char name[MAXPATHLEN]; + + module = lua_tostring(L, -1); + md = NULL; + LIST_FOREACH(m, &lua_modules, mod_next) + if (!strcmp(m->mod_name, module)) { + md = m; + break; + } + + if (md == NULL && lua_autoload_on && strchr(module, '/') == NULL) { + snprintf(name, sizeof name, "lua%s", module); + if (lua_verbose) + device_printf(sc_self, "autoload %s\n", name); + module_autoload(name, MODULE_CLASS_LUA_BINDING); + LIST_FOREACH(m, &lua_modules, mod_next) + if (!strcmp(m->mod_name, module)) { + md = m; + break; + } + } + + if (md != NULL) + LIST_FOREACH(s, &lua_states, lua_next) + if (s->K->L == L) { + if (lua_verbose) + device_printf(sc_self, + "require module %s\n", + md->mod_name); + md->open(L); + md->refcount++; + LIST_INSERT_HEAD(&s->lua_modules, md, mod_next); + return 0; + } + + lua_pushstring(L, "module not found"); + return lua_error(L); +} + +void * +lua_alloc(void *ud, void *ptr, size_t osize, size_t nsize) +{ + if (nsize == 0) { + if (ptr != NULL) + free(ptr, NULL); + return NULL; + } else + return realloc(ptr, nsize, ud, 0); +} + +static const char * +lua_reader(lua_State *L, void *data, size_t *size) +{ + struct lua_loadstate *ls; + static char buf[1024]; + size_t rsiz; + + ls = data; + if (ls->size < sizeof(buf)) + rsiz = ls->size; + else + rsiz = sizeof(buf); + vn_rdwr(UIO_READ, ls->vp, buf, rsiz, ls->off, UIO_SYSSPACE, + 0, curlwp->l_cred, NULL, curlwp); + if (ls->off == 0L && lua_bytecode_on == false && buf[0] == 0x1b) { + *size = 0L; + lua_pushstring(L, "loading of bytecode is not allowed"); + lua_error(L); + return NULL; + } else { + *size = rsiz; + ls->off += *size; + ls->size -= *size; + } + return buf; +} + +static void +lua_maxcount(lua_State *L, lua_Debug *d) +{ + lua_pushstring(L, "maximum instruction count exceeded"); + lua_error(L); +} + +int +lua_mod_register(const char *name, int (*open)(void *)) +{ + struct lua_module *m; + + LIST_FOREACH(m, &lua_modules, mod_next) + if (!strcmp(m->mod_name, name)) + return EBUSY; + m = malloc(sizeof(struct lua_module), NULL, M_ZERO); + if (m == NULL) + return ENOMEM; + strlcpy(m->mod_name, name, LUA_MAX_MODNAME); + m->open = open; + m->refcount = 0; + LIST_INSERT_HEAD(&lua_modules, m, mod_next); + if (lua_verbose) + device_printf(sc_self, "registered lua module %s\n", name); + return 0; +} + +int +lua_mod_unregister(const char *name) +{ + struct lua_module *m; + + LIST_FOREACH(m, &lua_modules, mod_next) + if (!strcmp(m->mod_name, name)) { + if (m->refcount == 0) { + LIST_REMOVE(m, mod_next); + free(m, NULL); + if (lua_verbose) + device_printf(sc_self, + "unregistered lua module %s\n", + name); + return 0; + } else + return EBUSY; + } + return 0; +} + +klua_State * +klua_newstate(lua_Alloc f, void *ud, const char *name, const char *desc) +{ + klua_State *K; + struct lua_state *s; + struct lua_softc *sc; + int error; + + s = malloc(sizeof(struct lua_state), NULL, M_ZERO); + if (s == NULL) + return NULL; + + sc = device_private(sc_self); + mutex_enter(&sc->sc_state_lock); + while (sc->sc_state == true) { + error = cv_wait_sig(&sc->sc_state_cv, &sc->sc_state_lock); + if (error) + break; + } + if (!error) + sc->sc_state = true; + mutex_exit(&sc->sc_state_lock); + + if (error) + return NULL; + + K = malloc(sizeof(klua_State), NULL, M_ZERO); + if (K == NULL) + goto finish; + K->L = lua_newstate(f, ud); + K->ks_user = false; + if (K->L == NULL) { + free(K, NULL); + K = NULL; + goto finish; + } + + strlcpy(s->lua_name, name, MAX_LUA_NAME); + strlcpy(s->lua_desc, desc, MAX_LUA_DESC); + s->K = K; + + if (lua_require_on || lua_autoload_on) { + lua_pushcfunction(K->L, lua_require); + lua_setglobal(K->L, "require"); + } + LIST_INSERT_HEAD(&lua_states, s, lua_next); + + mutex_init(&K->ks_lock, MUTEX_DEFAULT, IPL_VM); + cv_init(&K->ks_inuse_cv, "luainuse"); + +finish: + mutex_enter(&sc->sc_state_lock); + sc->sc_state = false; + cv_signal(&sc->sc_state_cv); + mutex_exit(&sc->sc_state_lock); + return K; +} + +void +klua_close(klua_State *K) +{ + struct lua_state *s; + struct lua_softc *sc; + struct lua_module *m; + int error; + + /* Notify the Lua state that it is about to be closed */ + if (klua_lock(K)) + return; /* Nothing we can do about */ + + lua_getglobal(K->L, "onClose"); + if (lua_isfunction(K->L, -1)) + lua_pcall(K->L, -1, 0, 0); + + /* + * Don't unlock, make sure no one uses the state until it is destroyed + * klua_unlock(K); + */ + + sc = device_private(sc_self); + mutex_enter(&sc->sc_state_lock); + while (sc->sc_state == true) { + error = cv_wait_sig(&sc->sc_state_cv, &sc->sc_state_lock); + if (error) + break; + } + if (!error) + sc->sc_state = true; + mutex_exit(&sc->sc_state_lock); + + if (error) + return; /* Nothing we can do... */ + + LIST_FOREACH(s, &lua_states, lua_next) + if (s->K == K) { + LIST_REMOVE(s, lua_next); + LIST_FOREACH(m, &s->lua_modules, mod_next) + m->refcount--; + free(s, NULL); + } + + lua_close(K->L); + cv_destroy(&K->ks_inuse_cv); + mutex_destroy(&K->ks_lock); + free(K, NULL); + + mutex_enter(&sc->sc_state_lock); + sc->sc_state = false; + cv_signal(&sc->sc_state_cv); + mutex_exit(&sc->sc_state_lock); +} + +static klua_State * +klua_find(const char *name) +{ + struct lua_state *s; + struct lua_softc *sc; + klua_State *K; + int error; + + K = NULL; + sc = device_private(sc_self); + mutex_enter(&sc->sc_state_lock); + while (sc->sc_state == true) { + error = cv_wait_sig(&sc->sc_state_cv, &sc->sc_state_lock); + if (error) + break; + } + if (!error) + sc->sc_state = true; + mutex_exit(&sc->sc_state_lock); + + if (error) + return NULL; + + LIST_FOREACH(s, &lua_states, lua_next) + if (!strcmp(s->lua_name, name)) { + K = s->K; + break; + } + + mutex_enter(&sc->sc_state_lock); + sc->sc_state = false; + cv_signal(&sc->sc_state_cv); + mutex_exit(&sc->sc_state_lock); + return K; +} + +int +klua_lock(klua_State *K) +{ + int error; + + error = 0; + mutex_enter(&K->ks_lock); + while (K->ks_inuse == true) { + error = cv_wait_sig(&K->ks_inuse_cv, &K->ks_lock); + if (error) + break; + } + if (!error) + K->ks_inuse = true; + mutex_exit(&K->ks_lock); + + if (error) + return error; + return 0; +} + +void +klua_unlock(klua_State *K) +{ + mutex_enter(&K->ks_lock); + K->ks_inuse = false; + cv_signal(&K->ks_inuse_cv); + mutex_exit(&K->ks_lock); +} + +MODULE(MODULE_CLASS_MISC, lua, NULL); + +#ifdef _MODULE +static const struct cfiattrdata luabus_iattrdata = { + "luabus", 0, { { NULL, NULL, 0 },} +}; +static const struct cfiattrdata *const lua_attrs[] = { + &luabus_iattrdata, NULL +}; +CFDRIVER_DECL(lua, DV_DULL, lua_attrs); +extern struct cfattach lua_ca; +static int lualoc[] = { + -1, + -1, + -1 +}; +static struct cfdata lua_cfdata[] = { + { + .cf_name = "lua", + .cf_atname = "lua", + .cf_unit = 0, + .cf_fstate = FSTATE_STAR, + .cf_loc = lualoc, + .cf_flags = 0, + .cf_pspec = NULL, + }, + { NULL, NULL, 0, FSTATE_NOTFOUND, NULL, 0, NULL } +}; +#endif + +static int +lua_modcmd(modcmd_t cmd, void *opaque) +{ +#ifdef _MODULE + devmajor_t cmajor, bmajor; + int error = 0; + + cmajor = bmajor = NODEVMAJOR; +#endif + switch (cmd) { + case MODULE_CMD_INIT: +#ifdef _MODULE + error = config_cfdriver_attach(&lua_cd); + if (error) + return error; + + error = config_cfattach_attach(lua_cd.cd_name, + &lua_ca); + if (error) { + config_cfdriver_detach(&lua_cd); + aprint_error("%s: unable to register cfattach\n", + lua_cd.cd_name); + return error; + } + error = config_cfdata_attach(lua_cfdata, 1); + if (error) { + config_cfattach_detach(lua_cd.cd_name, + &lua_ca); + config_cfdriver_detach(&lua_cd); + aprint_error("%s: unable to register cfdata\n", + lua_cd.cd_name); + return error; + } + error = devsw_attach(lua_cd.cd_name, NULL, &bmajor, + &lua_cdevsw, &cmajor); + if (error) { + aprint_error("%s: unable to register devsw\n", + lua_cd.cd_name); + config_cfattach_detach(lua_cd.cd_name, &lua_ca); + config_cfdriver_detach(&lua_cd); + return error; + } + config_attach_pseudo(lua_cfdata); +#endif + return 0; + case MODULE_CMD_FINI: +#ifdef _MODULE + error = config_cfdata_detach(lua_cfdata); + if (error) + return error; + + config_cfattach_detach(lua_cd.cd_name, &lua_ca); + config_cfdriver_detach(&lua_cd); + devsw_detach(NULL, &lua_cdevsw); +#endif + return 0; + case MODULE_CMD_AUTOUNLOAD: + /* no auto-unload */ + return EBUSY; + default: + return ENOTTY; + } +} |
