/*
* 简单‘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 int
echo_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);
}
int
echo_open(dev_t dev, int oflags, int devtype, struct proc *p)
{
int err = 0;
uprintf("Opened device \"echo\" successfully.\n");
return(err);
}
int
echo_close(dev_t dev, int fflag, int devtype, struct proc *p)
{
uprintf("Closing device \"echo.\"\n");
return(0);
}
/*
* read函数接受由echo_write()存储的buf,并将其返回到用户空间,
* 以供其他函数访问。
* uio(9)
*/
int
echo_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接受一个字符串并将它保存到缓冲区,用于以后的访问。
*/
int
echo_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);