for the curious, here's a simple implementation of a vnd like driver for
vscsi. it's not really intended to replace vnd, but it's a good
starting point if you want to try something more interesting with vscsi.
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <scsi/scsi_all.h>
#include <scsi/scsi_disk.h>
#include <scsi/scsiconf.h>
#include <dev/vscsivar.h>
#include <err.h>
#include <errno.h>
#include <event.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
int filefd;
void
readdata(void *data, int64_t blk, int64_t len)
{
int64_t start = blk * 512;
lseek(filefd, start, SEEK_SET);
read(filefd, data, len);
}
void
writedata(void *data, int64_t blk, int64_t len)
{
int64_t start = blk * 512;
lseek(filefd, start, SEEK_SET);
write(filefd, data, len);
}
void
doinquiry(int fd, struct vscsi_ioc_i2t *i2t)
{
struct vscsi_ioc_t2i t2i;
struct vscsi_ioc_data vdata;
struct scsi_inquiry *inq;
struct scsi_inquiry_data inqd;
memset(&t2i, 0, sizeof(t2i));
t2i.tag = i2t->tag;
memset(&vdata, 0, sizeof(vdata));
vdata.tag = i2t->tag;
vdata.datalen = i2t->datalen;
inq = (void *)&i2t->cmd;
if (inq->flags & SI_EVPD) {
t2i.status = VSCSI_STAT_ERR;
if (ioctl(fd, VSCSI_T2I, &t2i) == -1)
err(1, "ioctl");
return;
}
memset(&inqd, 0, sizeof(inqd));
inqd.device = T_DIRECT;
inqd.version = 2;
inqd.response_format = 2;
inqd.additional_length = 32; /* what does 32 mean? */
snprintf(inqd.vendor, sizeof(inqd.vendor), "ven");
snprintf(inqd.product, sizeof(inqd.product), "prod");
snprintf(inqd.revision, sizeof(inqd.revision), "rev");
vdata.data = &inqd;
if (ioctl(fd, VSCSI_DATA_READ, &vdata) == -1)
err(1, "ioctl");
t2i.status = VSCSI_STAT_DONE;
if (ioctl(fd, VSCSI_T2I, &t2i) == -1)
err(1, "ioctl");
}
void
doreadcap(int fd, struct vscsi_ioc_i2t *i2t)
{
struct vscsi_ioc_t2i t2i;
struct vscsi_ioc_data vdata;
struct scsi_read_cap_data rcd;
struct stat sb;
memset(&t2i, 0, sizeof(t2i));
t2i.tag = i2t->tag;
memset(&vdata, 0, sizeof(vdata));
vdata.tag = i2t->tag;
vdata.datalen = i2t->datalen;
fstat(filefd, &sb);
memset(&rcd, 0, sizeof(rcd));
_lto4b(sb.st_size / 512, rcd.addr);
_lto4b(512, rcd.length);
vdata.data = &rcd;
if (ioctl(fd, VSCSI_DATA_READ, &vdata) == -1)
err(1, "ioctl");
t2i.status = VSCSI_STAT_DONE;
if (ioctl(fd, VSCSI_T2I, &t2i) == -1)
err(1, "ioctl");
}
void
vscsi_op(int fd, short event, void *arg)
{
struct vscsi_ioc_i2t i2t;
struct vscsi_ioc_t2i t2i;
struct vscsi_ioc_data vdata;
struct scsi_generic *cmd;
struct scsi_rw_big *srwb;
int64_t blk;
if (!(event & EV_READ)) {
return;
}
if (ioctl(fd, VSCSI_I2T, &i2t) == -1)
err(1, "ioctl");
cmd = &i2t.cmd;
memset(&t2i, 0, sizeof(t2i));
t2i.tag = i2t.tag;
memset(&vdata, 0, sizeof(vdata));
vdata.tag = i2t.tag;
vdata.datalen = i2t.datalen;
switch (cmd->opcode) {
case TEST_UNIT_READY:
t2i.status = VSCSI_STAT_DONE;
if (ioctl(fd, VSCSI_T2I, &t2i) == -1)
err(1, "ioctl");
break;
case INQUIRY:
doinquiry(fd, &i2t);
break;
case START_STOP:
t2i.status = VSCSI_STAT_DONE;
if (ioctl(fd, VSCSI_T2I, &t2i) == -1)
err(1, "ioctl");
break;
case READ_CAPACITY:
doreadcap(fd, &i2t);
break;
case READ_BIG:
srwb = (void *)cmd;
blk = _4btol(srwb->addr);
vdata.data = malloc(vdata.datalen);
readdata(vdata.data, blk, vdata.datalen);
if (ioctl(fd, VSCSI_DATA_READ, &vdata) == -1)
err(1, "ioctl");
t2i.status = VSCSI_STAT_DONE;
if (ioctl(fd, VSCSI_T2I, &t2i) == -1)
err(1, "ioctl");
free(vdata.data);
break;
case WRITE_BIG:
srwb = (void *)cmd;
blk = _4btol(srwb->addr);
vdata.data = malloc(vdata.datalen);
if (ioctl(fd, VSCSI_DATA_WRITE, &vdata) == -1)
err(1, "ioctl");
writedata(vdata.data, blk, vdata.datalen);
t2i.status = VSCSI_STAT_DONE;
if (ioctl(fd, VSCSI_T2I, &t2i) == -1)
err(1, "ioctl");
free(vdata.data);
break;
case SYNCHRONIZE_CACHE:
fsync(filefd);
t2i.status = VSCSI_STAT_DONE;
if (ioctl(fd, VSCSI_T2I, &t2i) == -1)
err(1, "ioctl");
break;
case MODE_SENSE:
case MODE_SENSE_BIG:
t2i.status = VSCSI_STAT_ERR;
if (ioctl(fd, VSCSI_T2I, &t2i) == -1)
err(1, "ioctl");
break;
default:
t2i.status = VSCSI_STAT_ERR;
if (ioctl(fd, VSCSI_T2I, &t2i) == -1)
err(1, "ioctl");
printf("unhandled opcode 0x%x\n", cmd->opcode);
}
}
int
vscsi_open(char *devname)
{
struct vscsi_ioc_devevent probe;
int fd;
if ((fd = open(devname, O_RDWR)) == -1)
err(1, "%s", devname);
memset(&probe, 0, sizeof(probe));
probe.target = 1;
probe.lun = 2;
if (ioctl(fd, VSCSI_REQPROBE, &probe) == -1)
err(1, "ioctl");
return fd;
}
int
main(int argc, char **argv)
{
int vfd;
struct event vev;
char *devname = "/dev/vscsi0";
char *filename;
if (argc != 2)
exit(1);
filename = argv[1];
filefd = open(filename, O_RDWR | O_CREAT);
if (filefd == -1)
err(1, "open");
event_init();
memset(&vev, 0, sizeof(vev));
vfd = vscsi_open(devname);
event_set(&vev, vfd, EV_READ|EV_PERSIST, vscsi_op, NULL);
event_add(&vev, NULL);
event_dispatch();
return 0;
}