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

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

unix.superglobalmegacorp.com

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