Annotation of hatari/src/nf_scsidrv.c, revision 1.1.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.