Annotation of hatari/src/nf_scsidrv.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Hatari - nf_scsidrv.c
        !             3:  *
        !             4:  * Copyright (C) 2015-2016 by Uwe Seimet
        !             5:  *
        !             6:  * This file is distributed under the GNU General Public License, version 2
        !             7:  * or at your option any later version. Read the file gpl.txt for details.
        !             8:  *
        !             9:  * nf_scsidrv.c - Implementation of the host system part of a SCSI Driver
        !            10:  * (Linux only), based on the Linux SG driver version 3. The corresponding
        !            11:  * TOS binary and its source code can be downloaded from
        !            12:  * http://hddriver.seimet.de/en/downloads.html, where you can also find
        !            13:  * information on the open SCSI Driver standard.
        !            14:  */
        !            15: const char NfScsiDrv_fileid[] = "Hatari nf_scsidrv.c : " __DATE__ " " __TIME__;
        !            16: 
        !            17: #if defined(__linux__)
        !            18: 
        !            19: #include "config.h"
        !            20: #include <stdlib.h>
        !            21: #include <unistd.h>
        !            22: #include <fcntl.h>
        !            23: #if HAVE_UDEV
        !            24: #include <libudev.h>
        !            25: #endif
        !            26: #include <sys/ioctl.h>
        !            27: #include <scsi/sg.h>
        !            28: #include "stMemory.h"
        !            29: #include "log.h"
        !            30: #include "gemdos_defines.h"
        !            31: #include "nf_scsidrv.h"
        !            32: 
        !            33: // The driver interface version, 1.02
        !            34: #define INTERFACE_VERSION 0x0102
        !            35: // Maximum is 20 characters
        !            36: #define BUS_NAME "Linux Generic SCSI"
        !            37: // The SG driver supports cAllCmds
        !            38: #define BUS_FEATURES 0x02
        !            39: // The transfer length may depend on the device, 65536 should always be safe
        !            40: #define BUS_TRANSFER_LEN 65536
        !            41: // The maximum number of SCSI Driver handles, must be the same as in the stub
        !            42: #define SCSI_MAX_HANDLES 32
        !            43: 
        !            44: 
        !            45: typedef struct
        !            46: {
        !            47:        int fd;
        !            48:        int id_lo;
        !            49:        int error;
        !            50: } HANDLE_META_DATA;
        !            51: 
        !            52: static HANDLE_META_DATA handle_meta_data[SCSI_MAX_HANDLES];
        !            53: 
        !            54: #if HAVE_UDEV
        !            55: static struct udev *udev;
        !            56: static struct udev_monitor *mon;
        !            57: static int udev_mon_fd;
        !            58: static struct timeval tv;
        !            59: #endif
        !            60: 
        !            61: static Uint32 read_stack_long(Uint32 *stack)
        !            62: {
        !            63:        Uint32 value = STMemory_ReadLong(*stack);
        !            64: 
        !            65:        *stack += SIZE_LONG;
        !            66: 
        !            67:        return value;
        !            68: }
        !            69: 
        !            70: static void *read_stack_pointer(Uint32 *stack)
        !            71: {
        !            72:        Uint32 ptr = read_stack_long(stack);
        !            73:        return ptr ? STMemory_STAddrToPointer(ptr) : 0;
        !            74: }
        !            75: 
        !            76: static void write_long(Uint32 addr, Uint32 value)
        !            77: {
        !            78:        STMemory_WriteLong(addr, value);
        !            79: }
        !            80: 
        !            81: static void write_word(Uint32 addr, Uint16 value)
        !            82: {
        !            83:        STMemory_WriteWord(addr, value);
        !            84: }
        !            85: 
        !            86: // Sets the error status
        !            87: static void set_error(Uint32 handle, int errbit)
        !            88: {
        !            89:        Uint32 i;
        !            90:        for (i = 0; i < SCSI_MAX_HANDLES; i++)
        !            91:        {
        !            92:                if (handle != i && handle_meta_data[i].fd &&
        !            93:                    handle_meta_data[i].id_lo == handle_meta_data[handle].id_lo)
        !            94:                {
        !            95:                        handle_meta_data[i].error |= errbit;
        !            96:                }
        !            97:        }
        !            98: }
        !            99: 
        !           100: // udev-based check for media change. When udev is active media change messages
        !           101: // are handled globally by udev, i.e. that media changes cannot directly be
        !           102: // detected by the SCSI Driver. The SCSI Driver has to query udev instead.
        !           103: static bool check_mchg_udev(void)
        !           104: {
        !           105:        bool changed = false;
        !           106: 
        !           107: #if HAVE_UDEV
        !           108:        fd_set udevFds;
        !           109:        int ret;
        !           110: 
        !           111:        FD_ZERO(&udevFds);
        !           112:        FD_SET(udev_mon_fd, &udevFds);
        !           113: 
        !           114:        ret = select(udev_mon_fd + 1, &udevFds, 0, 0, &tv);
        !           115:        if (ret > 0 && FD_ISSET(udev_mon_fd, &udevFds))
        !           116:        {
        !           117:                struct udev_device *dev = udev_monitor_receive_device(mon);
        !           118:                while (dev)
        !           119:                {
        !           120:                        if (!changed)
        !           121:                        {
        !           122:                                const char *dev_type = udev_device_get_devtype(dev);
        !           123:                                const char *action = udev_device_get_action(dev);
        !           124:                                if (!strcmp("disk", dev_type) && !strcmp("change", action))
        !           125:                                {
        !           126:                                        LOG_TRACE(TRACE_SCSIDRV, ": %s has been changed",
        !           127:                                                  udev_device_get_devnode(dev));
        !           128: 
        !           129:                                        // TODO Determine sg device name from block device name and
        !           130:                                        // only report media change for the actually affected device
        !           131: 
        !           132:                                        changed = true;
        !           133:                                }
        !           134:                        }
        !           135: 
        !           136:                        // Process all pending events
        !           137:                        dev = udev_monitor_receive_device(mon);
        !           138:                }
        !           139:        }
        !           140: #endif
        !           141: 
        !           142:        return changed;
        !           143: }
        !           144: 
        !           145: // Checks whether a device exists by checking for the device file name
        !           146: static int check_device_file(Uint32 id)
        !           147: {
        !           148:        char device_file[16];
        !           149:        sprintf(device_file, "/dev/sg%d", id);
        !           150: 
        !           151:        if (!access(device_file, R_OK | W_OK))
        !           152:        {
        !           153:                LOG_TRACE(TRACE_SCSIDRV, ", device file %s is accessible",
        !           154:                          device_file);
        !           155: 
        !           156:                return 0;
        !           157:        }
        !           158:        else
        !           159:        {
        !           160:                LOG_TRACE(TRACE_SCSIDRV, ", device file %s is inaccessible",
        !           161:                          device_file);
        !           162: 
        !           163:                return -1;
        !           164:        }
        !           165: }
        !           166: 
        !           167: static int scsidrv_interface_version(Uint32 stack)
        !           168: {
        !           169:        return INTERFACE_VERSION;
        !           170: }
        !           171: 
        !           172: static int scsidrv_interface_features(Uint32 stack)
        !           173: {
        !           174:        char *busName = read_stack_pointer(&stack);
        !           175:        Uint32 features = read_stack_long(&stack);
        !           176:        Uint32 transferLen = read_stack_long(&stack);
        !           177: 
        !           178:        strncpy(busName, BUS_NAME, 20);
        !           179:        write_word(features, BUS_FEATURES);
        !           180:        write_long(transferLen, BUS_TRANSFER_LEN);
        !           181: 
        !           182:        return 0;
        !           183: }
        !           184: 
        !           185: // SCSI Driver: InquireBus()
        !           186: static int scsidrv_inquire_bus(Uint32 stack)
        !           187: {
        !           188:        Uint32 id = read_stack_long(&stack);
        !           189:        char device_file[16];
        !           190: 
        !           191:        LOG_TRACE(TRACE_SCSIDRV, "scsidrv_inquire_bus: id=%d", id);
        !           192: 
        !           193:        sprintf(device_file, "/dev/sg%d", id);
        !           194: 
        !           195:        while (!access(device_file, F_OK))
        !           196:        {
        !           197:                if (!check_device_file(id))
        !           198:                {
        !           199:                        return id;
        !           200:                }
        !           201: 
        !           202:                sprintf(device_file, "/dev/sg%d", ++id);
        !           203:        }
        !           204: 
        !           205:        return -1;
        !           206: }
        !           207: 
        !           208: // SCSI Driver: Open()
        !           209: static int scsidrv_open(Uint32 stack)
        !           210: {
        !           211:        char device_file[16];
        !           212:        Uint32 handle;
        !           213:        Uint32 id;
        !           214:        int fd;
        !           215: 
        !           216: #if HAVE_UDEV
        !           217:        if (!udev)
        !           218:        {
        !           219:                udev = udev_new();
        !           220:                if (!udev)
        !           221:                {
        !           222:                        return -1;
        !           223:                }
        !           224: 
        !           225:                mon = udev_monitor_new_from_netlink(udev, "udev");
        !           226:                udev_monitor_filter_add_match_subsystem_devtype(mon, "block", NULL);
        !           227:                udev_monitor_enable_receiving(mon);
        !           228:                udev_mon_fd = udev_monitor_get_fd(mon);
        !           229: 
        !           230:                tv.tv_sec = 0;
        !           231:                tv.tv_usec = 0;
        !           232:        }
        !           233: #endif
        !           234: 
        !           235:        handle = read_stack_long(&stack);
        !           236:        id = read_stack_long(&stack);
        !           237: 
        !           238:        LOG_TRACE(TRACE_SCSIDRV, "scsidrv_open: handle=%d, id=%d", handle, id);
        !           239: 
        !           240:        if (handle >= SCSI_MAX_HANDLES || handle_meta_data[handle].fd ||
        !           241:            check_device_file(id))
        !           242:        {
        !           243:                return GEMDOS_ENHNDL;
        !           244:        }
        !           245: 
        !           246:        sprintf(device_file, "/dev/sg%d", id);
        !           247: 
        !           248:        fd = open(device_file, O_RDWR | O_NONBLOCK);
        !           249:        if (fd < 0)
        !           250:        {
        !           251:                return fd;
        !           252:        }
        !           253: 
        !           254:        handle_meta_data[handle].fd = fd;
        !           255:        handle_meta_data[handle].id_lo = id;
        !           256:        handle_meta_data[handle].error = 0;
        !           257: 
        !           258:        return 0;
        !           259: }
        !           260: 
        !           261: // SCSI Driver: Close()
        !           262: static int scsidrv_close(Uint32 stack)
        !           263: {
        !           264:        Uint32 handle = read_stack_long(&stack);
        !           265: 
        !           266:        LOG_TRACE(TRACE_SCSIDRV, "scsidrv_close: handle=%d", handle);
        !           267: 
        !           268:        if (handle >= SCSI_MAX_HANDLES || !handle_meta_data[handle].fd)
        !           269:        {
        !           270:                return GEMDOS_ENHNDL;
        !           271:        }
        !           272: 
        !           273:        close(handle_meta_data[handle].fd);
        !           274: 
        !           275:        handle_meta_data[handle].fd = 0;
        !           276: 
        !           277:        return 0;
        !           278: }
        !           279: 
        !           280: // SCSI Driver: In() and Out()
        !           281: static int scsidrv_inout(Uint32 stack)
        !           282: {
        !           283:        Uint32 handle = read_stack_long(&stack);
        !           284:        Uint32 dir = read_stack_long(&stack);
        !           285:        unsigned char *cmd = read_stack_pointer(&stack);
        !           286:        Uint32 cmd_len = read_stack_long(&stack);
        !           287:        unsigned char *buffer = read_stack_pointer(&stack);
        !           288:        Uint32 transfer_len = read_stack_long(&stack);
        !           289:        unsigned char *sense_buffer = read_stack_pointer(&stack);
        !           290:        Uint32 timeout;
        !           291:        int status;
        !           292: 
        !           293:        if (sense_buffer)
        !           294:        {
        !           295:                memset(sense_buffer, 0, 18);
        !           296:        }
        !           297:        timeout = read_stack_long(&stack);
        !           298: 
        !           299:        if (LOG_TRACE_LEVEL(TRACE_SCSIDRV))
        !           300:        {
        !           301:                LOG_TRACE_PRINT(
        !           302:                    "scsidrv_inout: handle=%d, dir=%d, cmd_len=%d, buffer=%p,\n"
        !           303:                    "               transfer_len=%d, sense_buffer=%p, timeout=%d,\n"
        !           304:                    "               cmd=",
        !           305:                    handle, dir, cmd_len, buffer, transfer_len, sense_buffer,
        !           306:                    timeout);
        !           307: 
        !           308:                Uint32 i;
        !           309:                for (i = 0; i < cmd_len; i++)
        !           310:                {
        !           311:                        char str[8];
        !           312:                        sprintf(str, i ? ":$%02X" : "$%02X", cmd[i]);
        !           313:                        LOG_TRACE_PRINT("%s", str);
        !           314:                }
        !           315:        }
        !           316: 
        !           317:        if (handle >= SCSI_MAX_HANDLES || !handle_meta_data[handle].fd)
        !           318:        {
        !           319:                return GEMDOS_ENHNDL;
        !           320:        }
        !           321: 
        !           322:        // No explicit LUN support, the SG driver maps LUNs to device files
        !           323:        if (cmd[1] & 0xe0)
        !           324:        {
        !           325:                if (sense_buffer)
        !           326:                {
        !           327:                        // Sense Key and ASC
        !           328:                        sense_buffer[2] = 0x05;
        !           329:                        sense_buffer[12] = 0x25;
        !           330: 
        !           331:                        LOG_TRACE(TRACE_SCSIDRV,
        !           332:                                  "\n               Sense Key=$%02X, ASC=$%02X, ASCQ=$00",
        !           333:                                  sense_buffer[2], sense_buffer[12]);
        !           334:                }
        !           335: 
        !           336:                return 2;
        !           337:        }
        !           338: 
        !           339:        if (check_mchg_udev())
        !           340:        {
        !           341:                // cErrMediach for all open handles
        !           342:                Uint32 i;
        !           343:                for (i = 0; i < SCSI_MAX_HANDLES; i++)
        !           344:                {
        !           345:                        if (handle_meta_data[i].fd)
        !           346:                        {
        !           347:                                handle_meta_data[i].error |= 1;
        !           348:                        }
        !           349:                }
        !           350: 
        !           351:                if (sense_buffer)
        !           352:                {
        !           353:                        // Sense Key and ASC
        !           354:                        sense_buffer[2] = 0x06;
        !           355:                        sense_buffer[12] = 0x28;
        !           356:                }
        !           357: 
        !           358:                status = 2;
        !           359:        }
        !           360:        else
        !           361:        {
        !           362:                struct sg_io_hdr io_hdr;
        !           363:                memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
        !           364: 
        !           365:                io_hdr.interface_id = 'S';
        !           366: 
        !           367:                io_hdr.dxfer_direction = dir ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV;
        !           368:                if (!transfer_len)
        !           369:                {
        !           370:                        io_hdr.dxfer_direction = SG_DXFER_NONE;
        !           371:                }
        !           372: 
        !           373:                io_hdr.dxferp = buffer;
        !           374:                io_hdr.dxfer_len = transfer_len;
        !           375: 
        !           376:                io_hdr.sbp = sense_buffer;
        !           377:                io_hdr.mx_sb_len = 18;
        !           378: 
        !           379:                io_hdr.cmdp = cmd;
        !           380:                io_hdr.cmd_len = cmd_len;
        !           381: 
        !           382:                io_hdr.timeout = timeout;
        !           383: 
        !           384:                status = ioctl(handle_meta_data[handle].fd,
        !           385:                               SG_IO, &io_hdr) < 0 ? -1 : io_hdr.status;
        !           386:        }
        !           387: 
        !           388:        if (status > 0 && sense_buffer)
        !           389:        {
        !           390:                LOG_TRACE(TRACE_SCSIDRV,
        !           391:                          "\n               Sense Key=$%02X, ASC=$%02X, ASCQ=$%02X",
        !           392:                          sense_buffer[2], sense_buffer[12], sense_buffer[13]);
        !           393: 
        !           394:                if (status == 2)
        !           395:                {
        !           396:                        // Automatic media change and reset handling for
        !           397:                        // SCSI Driver version 1.0.1
        !           398:                        if ((sense_buffer[2] & 0x0f) && !sense_buffer[13])
        !           399:                        {
        !           400:                                if (sense_buffer[12] == 0x28)
        !           401:                                {
        !           402:                                        // cErrMediach
        !           403:                                        set_error(handle, 1);
        !           404:                                }
        !           405:                                else if (sense_buffer[12] == 0x29)
        !           406:                                {
        !           407:                                        // cErrReset
        !           408:                                        set_error(handle, 2);
        !           409:                                }
        !           410:                        }
        !           411:                }
        !           412:        }
        !           413: 
        !           414:        return status;
        !           415: }
        !           416: 
        !           417: // SCSI Driver: Error()
        !           418: static int scsidrv_error(Uint32 stack)
        !           419: {
        !           420:        Uint32 handle = read_stack_long(&stack);
        !           421:        Uint32 rwflag = read_stack_long(&stack);
        !           422:        Uint32 errnum = read_stack_long(&stack);
        !           423:        int errbit;
        !           424: 
        !           425:        LOG_TRACE(TRACE_SCSIDRV, "scsidrv_error: handle=%d, rwflag=%d, errno=%d",
        !           426:                  handle, rwflag, errnum);
        !           427: 
        !           428:        if (handle >= SCSI_MAX_HANDLES || !handle_meta_data[handle].fd)
        !           429:        {
        !           430:                return GEMDOS_ENHNDL;
        !           431:        }
        !           432: 
        !           433:        errbit = 1 << errnum;
        !           434: 
        !           435:        if (rwflag)
        !           436:        {
        !           437:                set_error(handle, errbit);
        !           438: 
        !           439:                return 0;
        !           440:        }
        !           441:        else
        !           442:        {
        !           443:                int status = handle_meta_data[handle].error & errbit;
        !           444:                handle_meta_data[handle].error &= ~errbit;
        !           445: 
        !           446:                return status;
        !           447:        }
        !           448: }
        !           449: 
        !           450: // SCSI Driver: CheckDev()
        !           451: static int scsidrv_check_dev(Uint32 stack)
        !           452: {
        !           453:        Uint32 id = read_stack_long(&stack);
        !           454: 
        !           455:        LOG_TRACE(TRACE_SCSIDRV, "scsidrv_check_dev: id=%d", id);
        !           456: 
        !           457:        return check_device_file(id);
        !           458: }
        !           459: 
        !           460: static const struct
        !           461: {
        !           462:        int (*cb)(Uint32 stack);
        !           463: } operations[] =
        !           464: {
        !           465:        { scsidrv_interface_version },
        !           466:        { scsidrv_interface_features },
        !           467:        { scsidrv_inquire_bus },
        !           468:        { scsidrv_open },
        !           469:        { scsidrv_close },
        !           470:        { scsidrv_inout },
        !           471:        { scsidrv_error },
        !           472:        { scsidrv_check_dev }
        !           473: };
        !           474: 
        !           475: bool nf_scsidrv(Uint32 stack, Uint32 subid, Uint32 *retval)
        !           476: {
        !           477:        if (subid >= ARRAY_SIZE(operations))
        !           478:        {
        !           479:                *retval = -1;
        !           480: 
        !           481:                LOG_TRACE(TRACE_SCSIDRV,
        !           482:                          "ERROR: Invalid SCSI Driver operation %d requested\n", subid);
        !           483:        }
        !           484:        else
        !           485:        {
        !           486:                *retval = operations[subid].cb(stack);
        !           487: 
        !           488:                LOG_TRACE(TRACE_SCSIDRV, " -> %d\n", *retval);
        !           489:        }
        !           490: 
        !           491:        return true;
        !           492: }
        !           493: 
        !           494: void nf_scsidrv_reset()
        !           495: {
        !           496:        int i;
        !           497:        for (i = 0; i < SCSI_MAX_HANDLES; i++)
        !           498:        {
        !           499:                if (handle_meta_data[i].fd)
        !           500:                {
        !           501:                        close(handle_meta_data[i].fd);
        !           502: 
        !           503:                        handle_meta_data[i].fd = 0;
        !           504:                }
        !           505:        }
        !           506: }
        !           507: 
        !           508: #endif

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.