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;
}

Reply via email to