/* * 简单‘echo’伪设备KLD * * Murray Stokely */#define MIN(a,b) (((a) < (b)) ? (a) : (b))#include <sys/types.h>#include <sys/module.h>#include <sys/systm.h> /* uprintf */#include <sys/errno.h>#include <sys/param.h> /* kernel.h中用到的定义 */#include <sys/kernel.h> /* 模块初始化中使用的类型 */#include <sys/conf.h> /* cdevsw结构 */#include <sys/uio.h> /* uio结构 */#include <sys/malloc.h>#define BUFFERSIZE 256/* 函数原型 */d_open_t echo_open;d_close_t echo_close;d_read_t echo_read;d_write_t echo_write;/* 字符设备入口点 */static struct cdevsw echo_cdevsw = { echo_open, echo_close, echo_read, echo_write, noioctl, nopoll, nommap, nostrategy, "echo", 33, /* 为lkms保留 - /usr/src/sys/conf/majors */ nodump, nopsize, D_TTY, -1};typedef struct s_echo { char msg[BUFFERSIZE]; int len;} t_echo;/* 变量 */static dev_t sdev;static int count;static t_echo *echomsg;MALLOC_DECLARE(M_ECHOBUF);MALLOC_DEFINE(M_ECHOBUF, "echobuffer", "buffer for echo module");/* * 这个函数被kld[un]load(2)系统调用来调用, * 以决定加载和卸载模块时需要采取的动作。 */static intecho_loader(struct module *m, int what, void *arg){ int err = 0; switch (what) { case MOD_LOAD: /* kldload */ sdev = make_dev(&echo_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "echo"); /* kmalloc分配供驱动程序使用的内存 */ MALLOC(echomsg, t_echo *, sizeof(t_echo), M_ECHOBUF, M_WAITOK); printf("Echo device loaded.\n"); break; case MOD_UNLOAD: destroy_dev(sdev); FREE(echomsg,M_ECHOBUF); printf("Echo device unloaded.\n"); break; default: err = EOPNOTSUPP; break; } return(err);}intecho_open(dev_t dev, int oflags, int devtype, struct proc *p){ int err = 0; uprintf("Opened device \"echo\" successfully.\n"); return(err);}intecho_close(dev_t dev, int fflag, int devtype, struct proc *p){ uprintf("Closing device \"echo.\"\n"); return(0);}/* * read函数接受由echo_write()存储的buf,并将其返回到用户空间, * 以供其他函数访问。 * uio(9) */intecho_read(dev_t dev, struct uio *uio, int ioflag){ int err = 0; int amt; /* * 这个读操作有多大? * 与用户请求的大小一样,或者等于剩余数据的大小。 */ amt = MIN(uio->uio_resid, (echomsg->len - uio->uio_offset > 0) ? echomsg->len - uio->uio_offset : 0); if ((err = uiomove(echomsg->msg + uio->uio_offset,amt,uio)) != 0) { uprintf("uiomove failed!\n"); } return(err);}/* * echo_write接受一个字符串并将它保存到缓冲区,用于以后的访问。 */intecho_write(dev_t dev, struct uio *uio, int ioflag){ int err = 0; /* 将字符串从用户空间的内存复制到内核空间 */ err = copyin(uio->uio_iov->iov_base, echomsg->msg, MIN(uio->uio_iov->iov_len, BUFFERSIZE - 1)); /* 现在需要以null结束字符串,并记录长度 */ *(echomsg->msg + MIN(uio->uio_iov->iov_len, BUFFERSIZE - 1)) = 0; echomsg->len = MIN(uio->uio_iov->iov_len, BUFFERSIZE); if (err != 0) { uprintf("Write failed: bad address!\n"); } count++; return(err);}DEV_MODULE(echo,echo_loader,NULL);