|
|
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
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.