|
|
| version 1.10, 2004/05/13 23:49:23 | version 1.11, 2004/05/19 22:52:58 |
|---|---|
| Line 43 | Line 43 |
| #include <sys/thread2.h> | #include <sys/thread2.h> |
| #include <sys/msgport2.h> | #include <sys/msgport2.h> |
| static struct cdevsw *cdevsw[NUMCDEVSW]; | static struct cdevlink *cdevbase[NUMCDEVSW]; |
| static struct lwkt_port *cdevport[NUMCDEVSW]; | |
| static int cdevsw_putport(lwkt_port_t port, lwkt_msg_t msg); | static int cdevsw_putport(lwkt_port_t port, lwkt_msg_t msg); |
| struct cdevsw dead_cdevsw; | |
| /* | /* |
| * Initialize a message port to serve as the default message-handling port | * Initialize a message port to serve as the default message-handling port |
| * for device operations. This message port provides compatibility with | * for device operations. This message port provides compatibility with |
| Line 69 int | Line 70 int |
| cdevsw_putport(lwkt_port_t port, lwkt_msg_t lmsg) | cdevsw_putport(lwkt_port_t port, lwkt_msg_t lmsg) |
| { | { |
| cdevallmsg_t msg = (cdevallmsg_t)lmsg; | cdevallmsg_t msg = (cdevallmsg_t)lmsg; |
| struct cdevsw *csw = msg->am_msg.csw; | struct cdevsw *devsw = msg->am_msg.dev->si_devsw; |
| int error; | int error; |
| /* | /* |
| Line 78 cdevsw_putport(lwkt_port_t port, lwkt_ms | Line 79 cdevsw_putport(lwkt_port_t port, lwkt_ms |
| */ | */ |
| switch(msg->am_lmsg.ms_cmd.cm_op) { | switch(msg->am_lmsg.ms_cmd.cm_op) { |
| case CDEV_CMD_OPEN: | case CDEV_CMD_OPEN: |
| error = csw->old_open( | error = devsw->old_open( |
| msg->am_open.msg.dev, | msg->am_open.msg.dev, |
| msg->am_open.oflags, | msg->am_open.oflags, |
| msg->am_open.devtype, | msg->am_open.devtype, |
| msg->am_open.td); | msg->am_open.td); |
| break; | break; |
| case CDEV_CMD_CLOSE: | case CDEV_CMD_CLOSE: |
| error = csw->old_close( | error = devsw->old_close( |
| msg->am_close.msg.dev, | msg->am_close.msg.dev, |
| msg->am_close.fflag, | msg->am_close.fflag, |
| msg->am_close.devtype, | msg->am_close.devtype, |
| msg->am_close.td); | msg->am_close.td); |
| break; | break; |
| case CDEV_CMD_STRATEGY: | case CDEV_CMD_STRATEGY: |
| csw->old_strategy(msg->am_strategy.bp); | devsw->old_strategy(msg->am_strategy.bp); |
| error = 0; | error = 0; |
| break; | break; |
| case CDEV_CMD_IOCTL: | case CDEV_CMD_IOCTL: |
| error = csw->old_ioctl( | error = devsw->old_ioctl( |
| msg->am_ioctl.msg.dev, | msg->am_ioctl.msg.dev, |
| msg->am_ioctl.cmd, | msg->am_ioctl.cmd, |
| msg->am_ioctl.data, | msg->am_ioctl.data, |
| Line 104 cdevsw_putport(lwkt_port_t port, lwkt_ms | Line 105 cdevsw_putport(lwkt_port_t port, lwkt_ms |
| msg->am_ioctl.td); | msg->am_ioctl.td); |
| break; | break; |
| case CDEV_CMD_DUMP: | case CDEV_CMD_DUMP: |
| error = csw->old_dump(msg->am_ioctl.msg.dev); | error = devsw->old_dump( |
| msg->am_dump.msg.dev, | |
| msg->am_dump.count, | |
| msg->am_dump.blkno, | |
| msg->am_dump.secsize); | |
| break; | break; |
| case CDEV_CMD_PSIZE: | case CDEV_CMD_PSIZE: |
| msg->am_psize.result = csw->old_psize(msg->am_psize.msg.dev); | msg->am_psize.result = devsw->old_psize(msg->am_psize.msg.dev); |
| error = 0; /* XXX */ | error = 0; /* XXX */ |
| break; | break; |
| case CDEV_CMD_READ: | case CDEV_CMD_READ: |
| error = csw->old_read( | error = devsw->old_read( |
| msg->am_read.msg.dev, | msg->am_read.msg.dev, |
| msg->am_read.uio, | msg->am_read.uio, |
| msg->am_read.ioflag); | msg->am_read.ioflag); |
| break; | break; |
| case CDEV_CMD_WRITE: | case CDEV_CMD_WRITE: |
| error = csw->old_write( | error = devsw->old_write( |
| msg->am_read.msg.dev, | msg->am_read.msg.dev, |
| msg->am_read.uio, | msg->am_read.uio, |
| msg->am_read.ioflag); | msg->am_read.ioflag); |
| break; | break; |
| case CDEV_CMD_POLL: | case CDEV_CMD_POLL: |
| msg->am_poll.events = csw->old_poll( | msg->am_poll.events = devsw->old_poll( |
| msg->am_poll.msg.dev, | msg->am_poll.msg.dev, |
| msg->am_poll.events, | msg->am_poll.events, |
| msg->am_poll.td); | msg->am_poll.td); |
| error = 0; | error = 0; |
| break; | break; |
| case CDEV_CMD_KQFILTER: | case CDEV_CMD_KQFILTER: |
| msg->am_kqfilter.result = csw->old_kqfilter( | msg->am_kqfilter.result = devsw->old_kqfilter( |
| msg->am_kqfilter.msg.dev, | msg->am_kqfilter.msg.dev, |
| msg->am_kqfilter.kn); | msg->am_kqfilter.kn); |
| error = 0; | error = 0; |
| break; | break; |
| case CDEV_CMD_MMAP: | case CDEV_CMD_MMAP: |
| msg->am_mmap.result = csw->old_mmap( | msg->am_mmap.result = devsw->old_mmap( |
| msg->am_mmap.msg.dev, | msg->am_mmap.msg.dev, |
| msg->am_mmap.offset, | msg->am_mmap.offset, |
| msg->am_mmap.nprot); | msg->am_mmap.nprot); |
| Line 150 cdevsw_putport(lwkt_port_t port, lwkt_ms | Line 155 cdevsw_putport(lwkt_port_t port, lwkt_ms |
| return(error); | return(error); |
| } | } |
| /* | |
| * These device dispatch functions provide convenient entry points for | |
| * any code wishing to make a dev call. | |
| * | |
| * YYY we ought to be able to optimize the port lookup by caching it in | |
| * the dev_t structure itself. | |
| */ | |
| static __inline | |
| struct cdevsw * | |
| _devsw(dev_t dev) | |
| { | |
| if (dev == NULL) | |
| return(NULL); | |
| if (dev->si_devsw) | |
| return (dev->si_devsw); | |
| return(cdevsw[major(dev)]); | |
| } | |
| static __inline | static __inline |
| lwkt_port_t | lwkt_port_t |
| _init_cdevmsg(dev_t dev, cdevmsg_t msg, int cmd) | _init_cdevmsg(dev_t dev, cdevmsg_t msg, int cmd) |
| { | { |
| struct cdevsw *csw; | |
| lwkt_initmsg_simple(&msg->msg, cmd); | lwkt_initmsg_simple(&msg->msg, cmd); |
| msg->dev = dev; | msg->dev = dev; |
| msg->csw = csw = _devsw(dev); | return(dev->si_port); |
| if (csw != NULL) { /* YYY too hackish */ | |
| KKASSERT(csw->d_port); /* YYY too hackish */ | |
| if (cdevport[major(dev)]) /* YYY too hackish */ | |
| return(cdevport[major(dev)]); | |
| return(csw->d_port); | |
| } | |
| return(NULL); | |
| } | } |
| int | int |
| Line 244 dev_dioctl(dev_t dev, u_long cmd, caddr_ | Line 222 dev_dioctl(dev_t dev, u_long cmd, caddr_ |
| return(lwkt_domsg(port, &msg.msg.msg)); | return(lwkt_domsg(port, &msg.msg.msg)); |
| } | } |
| /* | |
| * note: the disk layer is expected to set count, blkno, and secsize before | |
| * forwarding the message. | |
| */ | |
| int | int |
| dev_ddump(dev_t dev) | dev_ddump(dev_t dev) |
| { | { |
| Line 253 dev_ddump(dev_t dev) | Line 235 dev_ddump(dev_t dev) |
| port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_DUMP); | port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_DUMP); |
| if (port == NULL) | if (port == NULL) |
| return(ENXIO); | return(ENXIO); |
| msg.count = 0; | |
| msg.blkno = 0; | |
| msg.secsize = 0; | |
| return(lwkt_domsg(port, &msg.msg.msg)); | return(lwkt_domsg(port, &msg.msg.msg)); |
| } | } |
| Line 353 dev_dmmap(dev_t dev, vm_offset_t offset, | Line 338 dev_dmmap(dev_t dev, vm_offset_t offset, |
| return(-1); | return(-1); |
| } | } |
| int | |
| dev_port_dopen(lwkt_port_t port, dev_t dev, int oflags, int devtype, thread_t td) | |
| { | |
| struct cdevmsg_open msg; | |
| _init_cdevmsg(dev, &msg.msg, CDEV_CMD_OPEN); | |
| if (port == NULL) | |
| return(ENXIO); | |
| msg.oflags = oflags; | |
| msg.devtype = devtype; | |
| msg.td = td; | |
| return(lwkt_domsg(port, &msg.msg.msg)); | |
| } | |
| int | |
| dev_port_dclose(lwkt_port_t port, dev_t dev, int fflag, int devtype, thread_t td) | |
| { | |
| struct cdevmsg_close msg; | |
| _init_cdevmsg(dev, &msg.msg, CDEV_CMD_CLOSE); | |
| if (port == NULL) | |
| return(ENXIO); | |
| msg.fflag = fflag; | |
| msg.devtype = devtype; | |
| msg.td = td; | |
| return(lwkt_domsg(port, &msg.msg.msg)); | |
| } | |
| void | |
| dev_port_dstrategy(lwkt_port_t port, dev_t dev, struct buf *bp) | |
| { | |
| struct cdevmsg_strategy msg; | |
| _init_cdevmsg(dev, &msg.msg, CDEV_CMD_STRATEGY); | |
| KKASSERT(port); /* 'nostrategy' function is NULL YYY */ | |
| msg.bp = bp; | |
| lwkt_domsg(port, &msg.msg.msg); | |
| } | |
| int | |
| dev_port_dioctl(lwkt_port_t port, dev_t dev, u_long cmd, caddr_t data, int fflag, thread_t td) | |
| { | |
| struct cdevmsg_ioctl msg; | |
| _init_cdevmsg(dev, &msg.msg, CDEV_CMD_IOCTL); | |
| if (port == NULL) | |
| return(ENXIO); | |
| msg.cmd = cmd; | |
| msg.data = data; | |
| msg.fflag = fflag; | |
| msg.td = td; | |
| return(lwkt_domsg(port, &msg.msg.msg)); | |
| } | |
| int | |
| dev_port_ddump(lwkt_port_t port, dev_t dev) | |
| { | |
| struct cdevmsg_dump msg; | |
| _init_cdevmsg(dev, &msg.msg, CDEV_CMD_DUMP); | |
| if (port == NULL) | |
| return(ENXIO); | |
| return(lwkt_domsg(port, &msg.msg.msg)); | |
| } | |
| int | |
| dev_port_dpsize(lwkt_port_t port, dev_t dev) | |
| { | |
| struct cdevmsg_psize msg; | |
| int error; | |
| _init_cdevmsg(dev, &msg.msg, CDEV_CMD_PSIZE); | |
| if (port == NULL) | |
| return(-1); | |
| error = lwkt_domsg(port, &msg.msg.msg); | |
| if (error == 0) | |
| return(msg.result); | |
| return(-1); | |
| } | |
| int | |
| dev_port_dread(lwkt_port_t port, dev_t dev, struct uio *uio, int ioflag) | |
| { | |
| struct cdevmsg_read msg; | |
| _init_cdevmsg(dev, &msg.msg, CDEV_CMD_READ); | |
| if (port == NULL) | |
| return(ENXIO); | |
| msg.uio = uio; | |
| msg.ioflag = ioflag; | |
| return(lwkt_domsg(port, &msg.msg.msg)); | |
| } | |
| int | |
| dev_port_dwrite(lwkt_port_t port, dev_t dev, struct uio *uio, int ioflag) | |
| { | |
| struct cdevmsg_write msg; | |
| _init_cdevmsg(dev, &msg.msg, CDEV_CMD_WRITE); | |
| if (port == NULL) | |
| return(ENXIO); | |
| msg.uio = uio; | |
| msg.ioflag = ioflag; | |
| return(lwkt_domsg(port, &msg.msg.msg)); | |
| } | |
| int | |
| dev_port_dpoll(lwkt_port_t port, dev_t dev, int events, thread_t td) | |
| { | |
| struct cdevmsg_poll msg; | |
| int error; | |
| _init_cdevmsg(dev, &msg.msg, CDEV_CMD_POLL); | |
| if (port == NULL) | |
| return(ENXIO); | |
| msg.events = events; | |
| msg.td = td; | |
| error = lwkt_domsg(port, &msg.msg.msg); | |
| if (error == 0) | |
| return(msg.events); | |
| return(seltrue(dev, msg.events, td)); | |
| } | |
| int | |
| dev_port_dkqfilter(lwkt_port_t port, dev_t dev, struct knote *kn) | |
| { | |
| struct cdevmsg_kqfilter msg; | |
| int error; | |
| _init_cdevmsg(dev, &msg.msg, CDEV_CMD_KQFILTER); | |
| if (port == NULL) | |
| return(ENXIO); | |
| msg.kn = kn; | |
| error = lwkt_domsg(port, &msg.msg.msg); | |
| if (error == 0) | |
| return(msg.result); | |
| return(ENODEV); | |
| } | |
| int | |
| dev_port_dmmap(lwkt_port_t port, dev_t dev, vm_offset_t offset, int nprot) | |
| { | |
| struct cdevmsg_mmap msg; | |
| int error; | |
| _init_cdevmsg(dev, &msg.msg, CDEV_CMD_MMAP); | |
| if (port == NULL) | |
| return(-1); | |
| msg.offset = offset; | |
| msg.nprot = nprot; | |
| error = lwkt_domsg(port, &msg.msg.msg); | |
| if (error == 0) | |
| return(msg.result); | |
| return(-1); | |
| } | |
| const char * | const char * |
| dev_dname(dev_t dev) | dev_dname(dev_t dev) |
| { | { |
| struct cdevsw *csw; | return(dev->si_devsw->d_name); |
| if ((csw = _devsw(dev)) != NULL) | |
| return(csw->d_name); | |
| return(NULL); | |
| } | } |
| int | int |
| dev_dflags(dev_t dev) | dev_dflags(dev_t dev) |
| { | { |
| struct cdevsw *csw; | return(dev->si_devsw->d_flags); |
| if ((csw = _devsw(dev)) != NULL) | |
| return(csw->d_flags); | |
| return(0); | |
| } | } |
| int | int |
| dev_dmaj(dev_t dev) | dev_dmaj(dev_t dev) |
| { | { |
| struct cdevsw *csw; | return(dev->si_devsw->d_maj); |
| if ((csw = _devsw(dev)) != NULL) | |
| return(csw->d_maj); | |
| return(0); | |
| } | } |
| lwkt_port_t | lwkt_port_t |
| dev_dport(dev_t dev) | dev_dport(dev_t dev) |
| { | { |
| struct cdevsw *csw; | return(dev->si_port); |
| if ((csw = _devsw(dev)) != NULL) { | |
| if (cdevport[major(dev)]) /* YYY too hackish */ | |
| return(cdevport[major(dev)]); | |
| return(csw->d_port); | |
| } | |
| return(NULL); | |
| } | } |
| #if 0 | |
| /* | |
| * cdevsw[] array functions, moved from kern/kern_conf.c | |
| */ | |
| struct cdevsw * | |
| devsw(dev_t dev) | |
| { | |
| return(_devsw(dev)); | |
| } | |
| #endif | |
| /* | /* |
| * Convert a cdevsw template into the real thing, filling in fields the | * Convert a cdevsw template into the real thing, filling in fields the |
| * device left empty with appropriate defaults. | * device left empty with appropriate defaults. |
| Line 600 compile_devsw(struct cdevsw *devsw) | Line 399 compile_devsw(struct cdevsw *devsw) |
| if (devsw->d_port == NULL) | if (devsw->d_port == NULL) |
| devsw->d_port = &devsw_compat_port; | devsw->d_port = &devsw_compat_port; |
| if (devsw->d_clone == NULL) | |
| devsw->d_clone = noclone; | |
| } | } |
| /* | /* |
| * Add a cdevsw entry | * This makes a cdevsw entry visible to userland (e.g /dev/<blah>). |
| * | |
| * The kernel can overload a major number with multiple cdevsw's but only | |
| * the one installed in cdevbase[] is visible to userland. make_dev() does | |
| * not automatically call cdevsw_add() (nor do we want it to, since | |
| * partition-managed disk devices are overloaded on top of the raw device). | |
| */ | */ |
| int | int |
| cdevsw_add(struct cdevsw *newentry) | cdevsw_add(struct cdevsw *devsw, u_int mask, u_int match) |
| { | { |
| compile_devsw(newentry); | int maj; |
| if (newentry->d_maj < 0 || newentry->d_maj >= NUMCDEVSW) { | struct cdevlink *link; |
| compile_devsw(devsw); | |
| maj = devsw->d_maj; | |
| if (maj < 0 || maj >= NUMCDEVSW) { | |
| printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n", | printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n", |
| newentry->d_name, newentry->d_maj); | devsw->d_name, maj); |
| return (EINVAL); | return (EINVAL); |
| } | } |
| if (cdevsw[newentry->d_maj]) { | for (link = cdevbase[maj]; link; link = link->next) { |
| printf("WARNING: \"%s\" is usurping \"%s\"'s cdevsw[]\n", | /* |
| newentry->d_name, cdevsw[newentry->d_maj]->d_name); | * If we get an exact match we usurp the target, but we only print |
| * a warning message if a different device switch is installed. | |
| */ | |
| if (link->mask == mask && link->match == match) { | |
| if (link->devsw != devsw) { | |
| printf("WARNING: \"%s\" (%p) is usurping \"%s\"'s (%p)" | |
| " cdevsw[]\n", | |
| devsw->d_name, devsw, | |
| link->devsw->d_name, link->devsw); | |
| link->devsw = devsw; | |
| ++devsw->d_refs; | |
| } | |
| return(0); | |
| } | |
| /* | |
| * XXX add additional warnings for overlaps | |
| */ | |
| } | } |
| cdevsw[newentry->d_maj] = newentry; | |
| return (0); | link = malloc(sizeof(struct cdevlink), M_DEVBUF, M_INTWAIT|M_ZERO); |
| link->mask = mask; | |
| link->match = match; | |
| link->devsw = devsw; | |
| link->next = cdevbase[maj]; | |
| cdevbase[maj] = link; | |
| ++devsw->d_refs; | |
| return(0); | |
| } | } |
| /* | /* |
| * Add a cdevsw entry and override the port. | * Should only be used by udev2dev(). |
| * | |
| * If the minor number is -1, we match the first cdevsw we find for this | |
| * major. | |
| * | |
| * Note that this function will return NULL if the minor number is not within | |
| * the bounds of the installed mask(s). | |
| */ | */ |
| lwkt_port_t | struct cdevsw * |
| cdevsw_add_override(struct cdevsw *newentry, lwkt_port_t port) | cdevsw_get(int x, int y) |
| { | { |
| int error; | struct cdevlink *link; |
| if ((error = cdevsw_add(newentry)) == 0) | if (x < 0 || x >= NUMCDEVSW) |
| cdevport[newentry->d_maj] = port; | return(NULL); |
| return(newentry->d_port); | for (link = cdevbase[x]; link; link = link->next) { |
| if (y == -1 || (link->mask & y) == link->match) | |
| return(link->devsw); | |
| } | |
| return(NULL); | |
| } | |
| /* | |
| * Use the passed cdevsw as a template to create our intercept cdevsw, | |
| * and install and return ours. | |
| */ | |
| struct cdevsw * | |
| cdevsw_add_override(dev_t backing_dev, u_int mask, u_int match) | |
| { | |
| struct cdevsw *devsw; | |
| struct cdevsw *bsw = backing_dev->si_devsw; | |
| devsw = malloc(sizeof(struct cdevsw), M_DEVBUF, M_INTWAIT|M_ZERO); | |
| devsw->d_name = bsw->d_name; | |
| devsw->d_maj = bsw->d_maj; | |
| devsw->d_flags = bsw->d_flags; | |
| compile_devsw(devsw); | |
| cdevsw_add(devsw, mask, match); | |
| return(devsw); | |
| } | } |
| /* | |
| * Override a device's port, returning the previously installed port. This | |
| * is XXX very dangerous. | |
| */ | |
| lwkt_port_t | lwkt_port_t |
| cdevsw_dev_override(dev_t dev, lwkt_port_t port) | cdevsw_dev_override(dev_t dev, lwkt_port_t port) |
| { | { |
| struct cdevsw *csw; | lwkt_port_t oport; |
| KKASSERT(major(dev) >= 0 && major(dev) < NUMCDEVSW); | oport = dev->si_port; |
| if ((csw = _devsw(dev)) != NULL) { | dev->si_port = port; |
| cdevport[major(dev)] = port; | return(oport); |
| return(csw->d_port); | |
| } | |
| return(NULL); | |
| } | } |
| /* | /* |
| * Remove a cdevsw entry | * Remove a cdevsw entry from the cdevbase[] major array so no new user opens |
| * can be performed, and destroy all devices installed in the hash table | |
| * which are associated with this cdevsw. (see destroy_all_dev()). | |
| */ | */ |
| int | int |
| cdevsw_remove(struct cdevsw *oldentry) | cdevsw_remove(struct cdevsw *devsw, u_int mask, u_int match) |
| { | { |
| if (oldentry->d_maj < 0 || oldentry->d_maj >= NUMCDEVSW) { | int maj = devsw->d_maj; |
| struct cdevlink *link; | |
| struct cdevlink **plink; | |
| if (maj < 0 || maj >= NUMCDEVSW) { | |
| printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n", | printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n", |
| oldentry->d_name, oldentry->d_maj); | devsw->d_name, maj); |
| return EINVAL; | return EINVAL; |
| } | } |
| cdevsw[oldentry->d_maj] = NULL; | if (devsw != &dead_cdevsw) |
| cdevport[oldentry->d_maj] = NULL; | destroy_all_dev(devsw, mask, match); |
| for (plink = &cdevbase[maj]; (link = *plink) != NULL; plink = &link->next) { | |
| if (link->mask == mask && link->match == match) { | |
| if (link->devsw == devsw) | |
| break; | |
| printf("%s: ERROR: cannot remove from cdevsw[], its major" | |
| " number %d was stolen by %s\n", | |
| devsw->d_name, maj, | |
| link->devsw->d_name | |
| ); | |
| } | |
| } | |
| if (link == NULL) { | |
| printf("%s(%d): WARNING: cdevsw removed multiple times!\n", | |
| devsw->d_name, maj); | |
| } else { | |
| *plink = link->next; | |
| --devsw->d_refs; /* XXX cdevsw_release() / record refs */ | |
| free(link, M_DEVBUF); | |
| } | |
| if (devsw->d_refs != 0) { | |
| printf("%s: Warning: cdevsw_remove() called while %d device refs" | |
| " still exist! (major %d)\n", | |
| devsw->d_name, | |
| devsw->d_refs, | |
| maj); | |
| } else { | |
| printf("%s: cdevsw removed\n", devsw->d_name); | |
| } | |
| return 0; | return 0; |
| } | } |
| /* | |
| * Release a cdevsw entry. When the ref count reaches zero, recurse | |
| * through the stack. | |
| */ | |
| void | |
| cdevsw_release(struct cdevsw *devsw) | |
| { | |
| --devsw->d_refs; | |
| if (devsw->d_refs == 0) { | |
| /* XXX */ | |
| } | |
| } | |