#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pollpal.c,v 1.4 2022/03/28 12:33:22 riastradh Exp $");
#include <sys/module.h>
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/lwp.h>
#include <sys/condvar.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/mutex.h>
#include <sys/kmem.h>
#include <sys/poll.h>
#include <sys/select.h>
dev_type_open(pollpal_open);
static struct cdevsw pollpal_cdevsw = {
.d_open = pollpal_open,
.d_close = noclose,
.d_read = noread,
.d_write = nowrite,
.d_ioctl = noioctl,
.d_stop = nostop,
.d_tty = notty,
.d_poll = nopoll,
.d_mmap = nommap,
.d_kqfilter = nokqfilter,
.d_discard = nodiscard,
.d_flag = D_OTHER
};
static int pollpal_nopen = 0;
static int pollpal_close(file_t *);
static int pollpal_write(file_t *, off_t *, struct uio *, kauth_cred_t,
int);
static int pollpal_read(file_t *, off_t *, struct uio *, kauth_cred_t,
int);
static int pollpal_poll(file_t *, int);
const struct fileops pollpal_fileops = {
.fo_read = pollpal_read,
.fo_write = pollpal_write,
.fo_ioctl = fbadop_ioctl,
.fo_fcntl = fnullop_fcntl,
.fo_poll = pollpal_poll,
.fo_stat = fbadop_stat,
.fo_close = pollpal_close,
.fo_kqfilter = fnullop_kqfilter,
.fo_restart = fnullop_restart,
};
typedef struct pollpal_softc {
kmutex_t lock;
kcondvar_t sc_cv;
struct selinfo psel;
char *buf;
size_t buf_len;
enum states {
READ_WAITING = 0,
WRITE_WAITING = 1
} sc_state;
} pal_t;
static int
check_pal(const char *str, size_t len)
{
size_t n;
n = 0;
while (n <= len / 2) {
if (str[n] != str[len - n - 1])
return 0;
n++;
}
return 1;
}
int
pollpal_open(dev_t dev, int flag, int mode, struct lwp *l __unused)
{
struct file *fp;
int error, fd;
pal_t *pl;
error = fd_allocfile(&fp, &fd);
if (error)
return error;
++pollpal_nopen;
pl = kmem_zalloc(sizeof(*pl), KM_SLEEP);
pl->sc_state = READ_WAITING;
mutex_init(&pl->lock, MUTEX_DEFAULT, IPL_NONE);
cv_init(&pl->sc_cv, "sc_cv");
selinit(&pl->psel);
return fd_clone(fp, fd, flag, &pollpal_fileops, pl);
}
int
pollpal_close(file_t * fp)
{
pal_t * pl = fp->f_data;
KASSERT(pl != NULL);
if (pl->buf != NULL)
kmem_free(pl->buf, pl->buf_len);
seldestroy(&pl->psel);
cv_destroy(&pl->sc_cv);
mutex_destroy(&pl->lock);
kmem_free(pl, sizeof(*pl));
--pollpal_nopen;
return 0;
}
int
pollpal_write(file_t *fp, off_t *offset, struct uio *uio, kauth_cred_t cred,
int flag)
{
pal_t *pl = fp->f_data;
int error;
error = 0;
mutex_enter(&pl->lock);
cv_broadcast(&pl->sc_cv);
switch (pl->sc_state) {
case READ_WAITING:
pl->sc_state = WRITE_WAITING;
selnotify(&pl->psel, POLLOUT | POLLWRNORM, 0);
while (pl->sc_state == WRITE_WAITING) {
if (pl->buf) {
error = cv_wait_sig(&pl->sc_cv, &pl->lock);
if (error)
goto ret;
}
pl->buf_len = uio->uio_iov->iov_len;
pl->buf = kmem_alloc(pl->buf_len, KM_SLEEP);
uiomove(pl->buf, pl->buf_len, uio);
printf("Use cat to know the result.\n");
break;
}
break;
case WRITE_WAITING:
printf("State: WRITE_WAITING\n");
break;
}
ret:
mutex_exit(&pl->lock);
return error;
}
int
pollpal_read(file_t *fp, off_t *offset, struct uio *uio, kauth_cred_t cred,
int flags)
{
pal_t *pl = fp->f_data;
int error;
error = 0;
mutex_enter(&pl->lock);
cv_broadcast(&pl->sc_cv);
switch (pl->sc_state) {
case READ_WAITING:
printf("State: READ_WAITING\n");
printf("You need to write something to the module first!\n");
goto ret;
case WRITE_WAITING:
pl->sc_state = READ_WAITING;
selnotify(&pl->psel, POLLIN | POLLRDNORM, 0);
while (pl->sc_state == READ_WAITING) {
if (!pl->buf) {
error = cv_wait_sig(&pl->sc_cv, &pl->lock);
if (error)
goto ret;
}
printf("The string you entered was: ");
uiomove(pl->buf, pl->buf_len, uio);
printf("\n");
if (check_pal(pl->buf, pl->buf_len))
printf("String '%s' was a palindrome.\n",
pl->buf);
else
printf("String '%s' was not a palindrome.\n",
pl->buf);
break;
}
break;
}
kmem_free(pl->buf, pl->buf_len);
pl->buf = NULL;
ret:
mutex_exit(&pl->lock);
return error;
}
int
pollpal_poll(struct file *fp, int events)
{
pal_t *pl = fp->f_data;
int revents;
revents = 0;
mutex_enter(&pl->lock);
switch (pl->sc_state) {
case READ_WAITING:
if (events & (POLLOUT | POLLWRNORM)) {
revents |= POLLOUT | POLLWRNORM;
} else {
selrecord(curlwp, &pl->psel);
}
break;
case WRITE_WAITING:
if (events & (POLLIN | POLLRDNORM)) {
revents |= POLLIN | POLLRDNORM;
} else {
selrecord(curlwp, &pl->psel);
}
break;
}
mutex_exit(&pl->lock);
return revents;
}
MODULE(MODULE_CLASS_MISC, pollpal, NULL);
static int
pollpal_modcmd(modcmd_t cmd, void *arg __unused)
{
int cmajor = 351, bmajor = -1;
switch (cmd) {
case MODULE_CMD_INIT:
if (devsw_attach("pollpal", NULL, &bmajor, &pollpal_cdevsw,
&cmajor))
return ENXIO;
return 0;
case MODULE_CMD_FINI:
if (pollpal_nopen != 0)
return EBUSY;
devsw_detach(NULL, &pollpal_cdevsw);
return 0;
default:
return ENOTTY;
}
}