|
|
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.