/* * USB device driver framework. * * This is in charge of providing access to actual HCIs * and providing I/O to the various endpoints of devices. * A separate user program (usbd) is in charge of * enumerating the bus, setting up endpoints and * starting devices (also user programs). * * The interface provided is a violation of the standard: * you're welcome. * * The interface consists of a root directory with several files * plus a directory (epN.M) with two files per endpoint. * A device is represented by its first endpoint, which * is a control endpoint automatically allocated for each device. * Device control endpoints may be used to create new endpoints. * Devices corresponding to hubs may also allocate new devices, * perhaps also hubs. Initially, a hub device is allocated for * each controller present, to represent its root hub. Those can * never be removed. * * All endpoints refer to the first endpoint (epN.0) of the device, * which keeps per-device information, and also to the HCI used * to reach them. Although all endpoints cache that information. * * epN.M/data files permit I/O and are considered DMEXCL. * epN.M/ctl files provide status info and accept control requests. * * Endpoints may be given file names to be listed also at #u, * for those drivers that have nothing to do after configuring the * device and its endpoints. * * Drivers for different controllers are kept at usb[oue]hci.c * It's likely we could factor out much from controllers into * a generic controller driver, the problem is that details * regarding how to handle toggles, tokens, Tds, etc. will * get in the way. Thus, code is probably easier the way it is. */ #include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"#include "../port/error.h"#include "../port/usb.h" typedef struct Hcitype Hcitype; enum{ /* Qid numbers */ Qdir = 0, /* #u */ Qusbdir, /* #u/usb */ Qctl, /* #u/usb/ctl - control requests */ Qep0dir, /* #u/usb/ep0.0 - endpoint 0 dir */ Qep0io, /* #u/usb/ep0.0/data - endpoint 0 I/O */ Qep0ctl, /* #u/usb/ep0.0/ctl - endpoint 0 ctl. */ Qep0dummy, /* give 4 qids to each endpoint */ Qepdir = 0, /* (qid-qep0dir)&3 is one of these */ Qepio, /* to identify which file for the endpoint */ Qepctl, /* ... */ /* Usb ctls. */ CMdebug = 0, /* debug on|off */ CMdump, /* dump (data structures for debug) */ /* Ep. ctls */ CMnew = 0, /* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */ CMnewdev, /* newdev full|low|high portnb (allocate new devices) */ CMhub, /* hub (set the device as a hub) */ CMspeed, /* speed full|low|high|no */ CMmaxpkt, /* maxpkt size */ CMntds, /* ntds nb (max nb. of tds per µframe) */ CMclrhalt, /* clrhalt (halt was cleared on endpoint) */ CMpollival, /* pollival interval (interrupt/iso) */ CMhz, /* hz n (samples/sec; iso) */ CMsamplesz, /* samplesz n (sample size; iso) */ CMinfo, /* info infostr (ke.ep info for humans) */ CMdetach, /* detach (abort I/O forever on this ep). */ CMaddress, /* address (address is assigned) */ CMdebugep, /* debug n (set/clear debug for this ep) */ CMname, /* name str (show up as #u/name as well) */ CMtmout, /* timeout n (activate timeouts for ep) */ CMpreset, /* reset the port */ /* Hub feature selectors */ Rportenable = 1, Rportreset = 4, }; struct Hcitype{ char* type; int (*reset)(Hci*);}; #define QID(q) ((int)(q).path) static char Edetach[] = "device is detached";static char Enotconf[] = "endpoint not configured";char Estalled[] = "endpoint stalled"; static Cmdtab usbctls[] ={ {CMdebug, "debug", 2}, {CMdump, "dump", 1},}; static Cmdtab epctls[] ={ {CMnew, "new", 4}, {CMnewdev, "newdev", 3}, {CMhub, "hub", 1}, {CMspeed, "speed", 2}, {CMmaxpkt, "maxpkt", 2}, {CMntds, "ntds", 2}, {CMpollival, "pollival", 2}, {CMsamplesz, "samplesz", 2}, {CMhz, "hz", 2}, {CMinfo, "info", 0}, {CMdetach, "detach", 1}, {CMaddress, "address", 1}, {CMdebugep, "debug", 2}, {CMclrhalt, "clrhalt", 1}, {CMname, "name", 2}, {CMtmout, "timeout", 2}, {CMpreset, "reset", 1},}; static Dirtab usbdir[] ={ "ctl", {Qctl}, 0, 0666,}; char *usbmodename[] ={ [OREAD] "r", [OWRITE] "w", [ORDWR] "rw",}; static char *ttname[] ={ [Tnone] "none", [Tctl] "control", [Tiso] "iso", [Tintr] "interrupt", [Tbulk] "bulk",}; static char *spname[] ={ [Fullspeed] "full", [Lowspeed] "low", [Highspeed] "high", [Nospeed] "no",}; static int debug;static Hcitype hcitypes[Nhcis];static Hci* hcis[Nhcis];static QLock epslck; /* add, del, lookup endpoints */static Ep* eps[Neps]; /* all endpoints known */static int epmax; /* 1 + last endpoint index used */static int usbidgen; /* device address generator */ /* * Is there something like this in a library? should it be? */char*seprintdata(char *s, char *se, uchar *d, int n){ int i, l; s = seprint(s, se, " %#p[%d]: ", d, n); l = n; if(l > 10) l = 10; for(i=0; i<l; i++) s = seprint(s, se, " %2.2ux", d[i]); if(l < n) s = seprint(s, se, "..."); return s;} static intname2speed(char *name){ int i; for(i = 0; i < nelem(spname); i++) if(strcmp(name, spname[i]) == 0) return i; return Nospeed;} static intname2ttype(char *name){ int i; for(i = 0; i < nelem(ttname); i++) if(strcmp(name, ttname[i]) == 0) return i; /* may be a std. USB ep. type */ i = strtol(name, nil, 0); switch(i+1){ case Tctl: case Tiso: case Tbulk: case Tintr: return i+1; default: return Tnone; }} static intname2mode(char *mode){ int i; for(i = 0; i < nelem(usbmodename); i++) if(strcmp(mode, usbmodename[i]) == 0) return i; return -1;} static intqid2epidx(int q){ q = (q-Qep0dir)/4; if(q < 0 || q >= epmax || eps[q] == nil) return -1; return q;} static intisqtype(int q, int type){ if(q < Qep0dir) return 0; q -= Qep0dir; return (q & 3) == type;} voidaddhcitype(char* t, int (*r)(Hci*)){ static int ntype; if(ntype == Nhcis) panic("too many USB host interface types"); hcitypes[ntype].type = t; hcitypes[ntype].reset = r; ntype++;} static char*seprintep(char *s, char *se, Ep *ep, int all){ static char* dsnames[] = { "config", "enabled", "detached", "reset" }; Udev *d; int i; int di; d = ep->dev; qlock(ep); if(waserror()){ qunlock(ep); nexterror(); } di = ep->dev->nb; if(all) s = seprint(s, se, "dev %d ep %d ", di, ep->nb); s = seprint(s, se, "%s", dsnames[ep->dev->state]); s = seprint(s, se, " %s", ttname[ep->ttype]); assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR); s = seprint(s, se, " %s", usbmodename[ep->mode]); s = seprint(s, se, " speed %s", spname[d->speed]); s = seprint(s, se, " maxpkt %ld", ep->maxpkt); s = seprint(s, se, " pollival %ld", ep->pollival); s = seprint(s, se, " samplesz %ld", ep->samplesz); s = seprint(s, se, " hz %ld", ep->hz); s = seprint(s, se, " hub %d", ep->dev->hub); s = seprint(s, se, " port %d", ep->dev->port); if(ep->inuse) s = seprint(s, se, " busy"); else s = seprint(s, se, " idle"); if(all){ s = seprint(s, se, " load %uld", ep->load); s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep); s = seprint(s, se, " idx %d", ep->idx); if(ep->name != nil) s = seprint(s, se, " name '%s'", ep->name); if(ep->tmout != 0) s = seprint(s, se, " tmout"); if(ep == ep->ep0){ s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno); s = seprint(s, se, " eps:"); for(i = 0; i < nelem(d->eps); i++) if(d->eps[i] != nil) s = seprint(s, se, " ep%d.%d", di, i); } } if(ep->info != nil) s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type); else s = seprint(s, se, "\n"); qunlock(ep); poperror(); return s;} static Ep*epalloc(Hci *hp){ Ep *ep; int i; ep = smalloc(sizeof(Ep)); ep->ref = 1; qlock(&epslck); for(i = 0; i < Neps; i++) if(eps[i] == nil) break; if(i == Neps){ qunlock(&epslck); free(ep); print("usb: bug: too few endpoints.\n"); return nil; } ep->idx = i; if(epmax <= i) epmax = i+1; eps[i] = ep; ep->hp = hp; ep->maxpkt = 8; ep->ntds = 1; ep->samplesz = ep->pollival = ep->hz = 0; /* make them void */ qunlock(&epslck); return ep;} static Ep*getep(int i){ Ep *ep; if(i < 0 || i >= epmax || eps[i] == nil) return nil; qlock(&epslck); ep = eps[i]; if(ep != nil) incref(ep); qunlock(&epslck); return ep;} static voidputep(Ep *ep){ Udev *d; if(ep != nil && decref(ep) == 0){ d = ep->dev; deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep); qlock(&epslck); eps[ep->idx] = nil; if(ep->idx == epmax-1) epmax--; if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen) usbidgen--; qunlock(&epslck); if(d != nil){ qlock(ep->ep0); d->eps[ep->nb] = nil; qunlock(ep->ep0); } if(ep->ep0 != ep){ putep(ep->ep0); ep->ep0 = nil; } free(ep->info); free(ep->name); free(ep); }} static voiddumpeps(void){ int i; static char buf[512]; char *s; char *e; Ep *ep; print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps); for(i = 0; i < epmax; i++){ s = buf; e = buf+sizeof(buf); ep = getep(i); if(ep != nil){ if(waserror()){ putep(ep); nexterror(); } s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb); seprintep(s, e, ep, 1); print("%s", buf); ep->hp->seprintep(buf, e, ep); print("%s", buf); poperror(); putep(ep); } } print("usb dump hcis:\n"); for(i = 0; i < Nhcis; i++) if(hcis[i] != nil) hcis[i]->dump(hcis[i]);} static intnewusbid(Hci *){ int id; qlock(&epslck); id = ++usbidgen; if(id >= 0x7F) print("#u: too many device addresses; reuse them more\n"); qunlock(&epslck); return id;} /* * Create endpoint 0 for a new device */static Ep*newdev(Hci *hp, int ishub, int isroot){ Ep *ep; Udev *d; ep = epalloc(hp); d = ep->dev = smalloc(sizeof(Udev)); d->nb = newusbid(hp); d->eps[0] = ep; ep->nb = 0; ep->toggle[0] = ep->toggle[1] = 0; d->ishub = ishub; d->isroot = isroot; if(hp->highspeed != 0) d->speed = Highspeed; else d->speed = Fullspeed; d->state = Dconfig; /* address not yet set */ ep->dev = d; ep->ep0 = ep; /* no ref counted here */ ep->ttype = Tctl; ep->tmout = Xfertmout; ep->mode = ORDWR; dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep); return ep;} /* * Create a new endpoint for the device * accessed via the given endpoint 0. */static Ep*newdevep(Ep *ep, int i, int tt, int mode){ Ep *nep; Udev *d; d = ep->dev; if(d->eps[i] != nil) error("endpoint already in use"); nep = epalloc(ep->hp); incref(ep); d->eps[i] = nep; nep->nb = i; nep->toggle[0] = nep->toggle[1] = 0; nep->ep0 = ep; nep->dev = ep->dev; nep->mode = mode; nep->ttype = tt; nep->debug = ep->debug;Lines 1–500 of 1479. Load more
/* * USB device driver framework. * * This is in charge of providing access to actual HCIs * and providing I/O to the various endpoints of devices. * A separate user program (usbd) is in charge of * enumerating the bus, setting up endpoints and * starting devices (also user programs). * * The interface provided is a violation of the standard: * you're welcome. * * The interface consists of a root directory with several files * plus a directory (epN.M) with two files per endpoint. * A device is represented by its first endpoint, which * is a control endpoint automatically allocated for each device. * Device control endpoints may be used to create new endpoints. * Devices corresponding to hubs may also allocate new devices, * perhaps also hubs. Initially, a hub device is allocated for * each controller present, to represent its root hub. Those can * never be removed. * * All endpoints refer to the first endpoint (epN.0) of the device, * which keeps per-device information, and also to the HCI used * to reach them. Although all endpoints cache that information. * * epN.M/data files permit I/O and are considered DMEXCL. * epN.M/ctl files provide status info and accept control requests. * * Endpoints may be given file names to be listed also at #u, * for those drivers that have nothing to do after configuring the * device and its endpoints. * * Drivers for different controllers are kept at usb[oue]hci.c * It's likely we could factor out much from controllers into * a generic controller driver, the problem is that details * regarding how to handle toggles, tokens, Tds, etc. will * get in the way. Thus, code is probably easier the way it is. */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" #include "../port/usb.h" typedef struct Hcitype Hcitype; enum { /* Qid numbers */ Qdir = 0, /* #u */ Qusbdir, /* #u/usb */ Qctl, /* #u/usb/ctl - control requests */ Qep0dir, /* #u/usb/ep0.0 - endpoint 0 dir */ Qep0io, /* #u/usb/ep0.0/data - endpoint 0 I/O */ Qep0ctl, /* #u/usb/ep0.0/ctl - endpoint 0 ctl. */ Qep0dummy, /* give 4 qids to each endpoint */ Qepdir = 0, /* (qid-qep0dir)&3 is one of these */ Qepio, /* to identify which file for the endpoint */ Qepctl, /* ... */ /* Usb ctls. */ CMdebug = 0, /* debug on|off */ CMdump, /* dump (data structures for debug) */ /* Ep. ctls */ CMnew = 0, /* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */ CMnewdev, /* newdev full|low|high portnb (allocate new devices) */ CMhub, /* hub (set the device as a hub) */ CMspeed, /* speed full|low|high|no */ CMmaxpkt, /* maxpkt size */ CMntds, /* ntds nb (max nb. of tds per µframe) */ CMclrhalt, /* clrhalt (halt was cleared on endpoint) */ CMpollival, /* pollival interval (interrupt/iso) */ CMhz, /* hz n (samples/sec; iso) */ CMsamplesz, /* samplesz n (sample size; iso) */ CMinfo, /* info infostr (ke.ep info for humans) */ CMdetach, /* detach (abort I/O forever on this ep). */ CMaddress, /* address (address is assigned) */ CMdebugep, /* debug n (set/clear debug for this ep) */ CMname, /* name str (show up as #u/name as well) */ CMtmout, /* timeout n (activate timeouts for ep) */ CMpreset, /* reset the port */ /* Hub feature selectors */ Rportenable = 1, Rportreset = 4, }; struct Hcitype { char* type; int (*reset)(Hci*); }; #define QID(q) ((int)(q).path) static char Edetach[] = "device is detached"; static char Enotconf[] = "endpoint not configured"; char Estalled[] = "endpoint stalled"; static Cmdtab usbctls[] = { {CMdebug, "debug", 2}, {CMdump, "dump", 1}, }; static Cmdtab epctls[] = { {CMnew, "new", 4}, {CMnewdev, "newdev", 3}, {CMhub, "hub", 1}, {CMspeed, "speed", 2}, {CMmaxpkt, "maxpkt", 2}, {CMntds, "ntds", 2}, {CMpollival, "pollival", 2}, {CMsamplesz, "samplesz", 2}, {CMhz, "hz", 2}, {CMinfo, "info", 0}, {CMdetach, "detach", 1}, {CMaddress, "address", 1}, {CMdebugep, "debug", 2}, {CMclrhalt, "clrhalt", 1}, {CMname, "name", 2}, {CMtmout, "timeout", 2}, {CMpreset, "reset", 1}, }; static Dirtab usbdir[] = { "ctl", {Qctl}, 0, 0666, }; char *usbmodename[] = { [OREAD] "r", [OWRITE] "w", [ORDWR] "rw", }; static char *ttname[] = { [Tnone] "none", [Tctl] "control", [Tiso] "iso", [Tintr] "interrupt", [Tbulk] "bulk", }; static char *spname[] = { [Fullspeed] "full", [Lowspeed] "low", [Highspeed] "high", [Nospeed] "no", }; static int debug; static Hcitype hcitypes[Nhcis]; static Hci* hcis[Nhcis]; static QLock epslck; /* add, del, lookup endpoints */ static Ep* eps[Neps]; /* all endpoints known */ static int epmax; /* 1 + last endpoint index used */ static int usbidgen; /* device address generator */ /* * Is there something like this in a library? should it be? */ char* seprintdata(char *s, char *se, uchar *d, int n) { int i, l; s = seprint(s, se, " %#p[%d]: ", d, n); l = n; if(l > 10) l = 10; for(i=0; i<l; i++) s = seprint(s, se, " %2.2ux", d[i]); if(l < n) s = seprint(s, se, "..."); return s; } static int name2speed(char *name) { int i; for(i = 0; i < nelem(spname); i++) if(strcmp(name, spname[i]) == 0) return i; return Nospeed; } static int name2ttype(char *name) { int i; for(i = 0; i < nelem(ttname); i++) if(strcmp(name, ttname[i]) == 0) return i; /* may be a std. USB ep. type */ i = strtol(name, nil, 0); switch(i+1){ case Tctl: case Tiso: case Tbulk: case Tintr: return i+1; default: return Tnone; } } static int name2mode(char *mode) { int i; for(i = 0; i < nelem(usbmodename); i++) if(strcmp(mode, usbmodename[i]) == 0) return i; return -1; } static int qid2epidx(int q) { q = (q-Qep0dir)/4; if(q < 0 || q >= epmax || eps[q] == nil) return -1; return q; } static int isqtype(int q, int type) { if(q < Qep0dir) return 0; q -= Qep0dir; return (q & 3) == type; } void addhcitype(char* t, int (*r)(Hci*)) { static int ntype; if(ntype == Nhcis) panic("too many USB host interface types"); hcitypes[ntype].type = t; hcitypes[ntype].reset = r; ntype++; } static char* seprintep(char *s, char *se, Ep *ep, int all) { static char* dsnames[] = { "config", "enabled", "detached", "reset" }; Udev *d; int i; int di; d = ep->dev; qlock(ep); if(waserror()){ qunlock(ep); nexterror(); } di = ep->dev->nb; if(all) s = seprint(s, se, "dev %d ep %d ", di, ep->nb); s = seprint(s, se, "%s", dsnames[ep->dev->state]); s = seprint(s, se, " %s", ttname[ep->ttype]); assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR); s = seprint(s, se, " %s", usbmodename[ep->mode]); s = seprint(s, se, " speed %s", spname[d->speed]); s = seprint(s, se, " maxpkt %ld", ep->maxpkt); s = seprint(s, se, " pollival %ld", ep->pollival); s = seprint(s, se, " samplesz %ld", ep->samplesz); s = seprint(s, se, " hz %ld", ep->hz); s = seprint(s, se, " hub %d", ep->dev->hub); s = seprint(s, se, " port %d", ep->dev->port); if(ep->inuse) s = seprint(s, se, " busy"); else s = seprint(s, se, " idle"); if(all){ s = seprint(s, se, " load %uld", ep->load); s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep); s = seprint(s, se, " idx %d", ep->idx); if(ep->name != nil) s = seprint(s, se, " name '%s'", ep->name); if(ep->tmout != 0) s = seprint(s, se, " tmout"); if(ep == ep->ep0){ s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno); s = seprint(s, se, " eps:"); for(i = 0; i < nelem(d->eps); i++) if(d->eps[i] != nil) s = seprint(s, se, " ep%d.%d", di, i); } } if(ep->info != nil) s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type); else s = seprint(s, se, "\n"); qunlock(ep); poperror(); return s; } static Ep* epalloc(Hci *hp) { Ep *ep; int i; ep = smalloc(sizeof(Ep)); ep->ref = 1; qlock(&epslck); for(i = 0; i < Neps; i++) if(eps[i] == nil) break; if(i == Neps){ qunlock(&epslck); free(ep); print("usb: bug: too few endpoints.\n"); return nil; } ep->idx = i; if(epmax <= i) epmax = i+1; eps[i] = ep; ep->hp = hp; ep->maxpkt = 8; ep->ntds = 1; ep->samplesz = ep->pollival = ep->hz = 0; /* make them void */ qunlock(&epslck); return ep; } static Ep* getep(int i) { Ep *ep; if(i < 0 || i >= epmax || eps[i] == nil) return nil; qlock(&epslck); ep = eps[i]; if(ep != nil) incref(ep); qunlock(&epslck); return ep; } static void putep(Ep *ep) { Udev *d; if(ep != nil && decref(ep) == 0){ d = ep->dev; deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep); qlock(&epslck); eps[ep->idx] = nil; if(ep->idx == epmax-1) epmax--; if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen) usbidgen--; qunlock(&epslck); if(d != nil){ qlock(ep->ep0); d->eps[ep->nb] = nil; qunlock(ep->ep0); } if(ep->ep0 != ep){ putep(ep->ep0); ep->ep0 = nil; } free(ep->info); free(ep->name); free(ep); } } static void dumpeps(void) { int i; static char buf[512]; char *s; char *e; Ep *ep; print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps); for(i = 0; i < epmax; i++){ s = buf; e = buf+sizeof(buf); ep = getep(i); if(ep != nil){ if(waserror()){ putep(ep); nexterror(); } s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb); seprintep(s, e, ep, 1); print("%s", buf); ep->hp->seprintep(buf, e, ep); print("%s", buf); poperror(); putep(ep); } } print("usb dump hcis:\n"); for(i = 0; i < Nhcis; i++) if(hcis[i] != nil) hcis[i]->dump(hcis[i]); } static int newusbid(Hci *) { int id; qlock(&epslck); id = ++usbidgen; if(id >= 0x7F) print("#u: too many device addresses; reuse them more\n"); qunlock(&epslck); return id; } /* * Create endpoint 0 for a new device */ static Ep* newdev(Hci *hp, int ishub, int isroot) { Ep *ep; Udev *d; ep = epalloc(hp); d = ep->dev = smalloc(sizeof(Udev)); d->nb = newusbid(hp); d->eps[0] = ep; ep->nb = 0; ep->toggle[0] = ep->toggle[1] = 0; d->ishub = ishub; d->isroot = isroot; if(hp->highspeed != 0) d->speed = Highspeed; else d->speed = Fullspeed; d->state = Dconfig; /* address not yet set */ ep->dev = d; ep->ep0 = ep; /* no ref counted here */ ep->ttype = Tctl; ep->tmout = Xfertmout; ep->mode = ORDWR; dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep); return ep; } /* * Create a new endpoint for the device * accessed via the given endpoint 0. */ static Ep* newdevep(Ep *ep, int i, int tt, int mode) { Ep *nep; Udev *d; d = ep->dev; if(d->eps[i] != nil) error("endpoint already in use"); nep = epalloc(ep->hp); incref(ep); d->eps[i] = nep; nep->nb = i; nep->toggle[0] = nep->toggle[1] = 0; nep->ep0 = ep; nep->dev = ep->dev; nep->mode = mode; nep->ttype = tt; nep->debug = ep->debug;