summaryrefslogtreecommitdiff
path: root/utils/scsi/rbscsi.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/scsi/rbscsi.c')
-rw-r--r--utils/scsi/rbscsi.c313
1 files changed, 313 insertions, 0 deletions
diff --git a/utils/scsi/rbscsi.c b/utils/scsi/rbscsi.c
new file mode 100644
index 0000000000..aa62ba0118
--- /dev/null
+++ b/utils/scsi/rbscsi.c
@@ -0,0 +1,313 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2014 by Amaury Pouly
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include <stdlib.h>
22#include <stdio.h>
23#include <stdarg.h>
24#include "rbscsi.h"
25
26static void misc_std_printf(void *user, const char *fmt, ...)
27{
28 (void) user;
29 va_list args;
30 va_start(args, fmt);
31 vprintf(fmt, args);
32 va_end(args);
33}
34
35#if defined(_WIN32) || defined(__WIN32__)
36#define RB_SCSI_WINDOWS
37#include <windows.h>
38#include <ntddscsi.h>
39#include <stdint.h>
40typedef HANDLE rb_scsi_handle_t;
41#elif defined(__linux) || defined(__linux__) || defined(linux)
42#include <unistd.h>
43#include <fcntl.h>
44#include <stdio.h>
45#include <string.h>
46#include <errno.h>
47#include <sys/ioctl.h>
48#include <scsi/sg.h>
49#include <scsi/sg_lib.h>
50#include <scsi/sg_io_linux.h>
51#define RB_SCSI_LINUX
52typedef int rb_scsi_handle_t;
53#else
54typedef void *rb_scsi_handle_t;
55#endif
56
57struct rb_scsi_device_t
58{
59 rb_scsi_handle_t handle;
60 void *user;
61 rb_scsi_printf_t printf;
62 unsigned flags;
63};
64
65/* Linux */
66#ifdef RB_SCSI_LINUX
67rb_scsi_device_t rb_scsi_open(const char *path, unsigned flags, void *user,
68 rb_scsi_printf_t printf)
69{
70 if(printf == NULL)
71 printf = misc_std_printf;
72 int fd = open(path, (flags & RB_SCSI_READ_ONLY) ? O_RDONLY : O_RDWR);
73 if(fd < 0)
74 {
75 if(flags & RB_SCSI_DEBUG)
76 printf(user, "rb_scsi: open failed: %s\n", strerror(errno));
77 return NULL;
78 }
79 rb_scsi_device_t dev = malloc(sizeof(struct rb_scsi_device_t));
80 dev->flags = flags;
81 dev->handle = fd;
82 dev->user = user;
83 dev->printf = printf ? printf : misc_std_printf;
84 return dev;
85}
86
87int rb_scsi_raw_xfer(rb_scsi_device_t dev, struct rb_scsi_raw_cmd_t *raw)
88{
89#define rb_printf(...) \
90 do{ if(dev->flags & RB_SCSI_DEBUG) dev->printf(dev->user, __VA_ARGS__); }while(0)
91 sg_io_hdr_t hdr;
92 memset(&hdr, 0, sizeof(hdr));
93 /* prepare transfer descriptor */
94 hdr.interface_id = 'S';
95 switch(raw->dir)
96 {
97 case RB_SCSI_NONE: hdr.dxfer_direction = SG_DXFER_NONE; break;
98 case RB_SCSI_READ: hdr.dxfer_direction = SG_DXFER_FROM_DEV; break;
99 case RB_SCSI_WRITE: hdr.dxfer_direction = SG_DXFER_TO_DEV; break;
100 default: rb_printf("rb_scsi: invalid direction"); return -1;
101 }
102 hdr.cmd_len = raw->cdb_len;
103 hdr.mx_sb_len = raw->sense_len;
104 hdr.dxfer_len = raw->buf_len;
105 hdr.dxferp = raw->buf;
106 hdr.cmdp = raw->cdb;
107 hdr.sbp = raw->sense;
108 hdr.timeout = raw->tmo * 1000;
109 hdr.flags = SG_FLAG_LUN_INHIBIT;
110 /* do raw command */
111 if(ioctl(dev->handle, SG_IO, &hdr) < 0)
112 {
113 raw->status = errno;
114 rb_printf("rb_scsi: ioctl failed: %s\n", strerror(raw->status));
115 return RB_SCSI_OS_ERROR;
116 }
117 /* error handling is weird: host status has the priority */
118 if(hdr.host_status)
119 {
120 rb_printf("rb_scsi: host error: %d\n", hdr.host_status);
121 raw->status = hdr.host_status;
122 return RB_SCSI_ERROR;
123 }
124 raw->status = hdr.status & 0x7e;
125 raw->buf_len -= hdr.resid;
126 raw->sense_len = hdr.sb_len_wr;
127 /* driver error can report sense */
128 if((hdr.driver_status & 0xf) == DRIVER_SENSE)
129 {
130 rb_printf("rb_scsi: driver status reported sense\n");
131 return RB_SCSI_SENSE;
132 }
133 /* otherwise errors */
134 if(hdr.driver_status)
135 {
136 rb_printf("rb_scsi: driver error: %d\n", hdr.driver_status);
137 return RB_SCSI_ERROR;
138 }
139 /* scsi status can imply sense */
140 if(raw->status == RB_SCSI_CHECK_CONDITION || raw->status == RB_SCSI_COMMAND_TERMINATED)
141 return RB_SCSI_SENSE;
142 return raw->status ? RB_SCSI_STATUS : RB_SCSI_OK;
143#undef rb_printf
144}
145
146void rb_scsi_close(rb_scsi_device_t dev)
147{
148 close(dev->handle);
149 free(dev);
150}
151
152/* Windpws */
153#elif defined(RB_SCSI_WINDOWS)
154rb_scsi_device_t rb_scsi_open(const char *path, unsigned flags, void *user,
155 rb_scsi_printf_t printf)
156{
157 if(printf == NULL)
158 printf = misc_std_printf;
159 HANDLE h = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
160 NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL);
161 if(h == INVALID_HANDLE_VALUE)
162 {
163 if(flags & RB_SCSI_DEBUG)
164 {
165 LPSTR msg = NULL;
166 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
167 NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&msg, 0, NULL);
168 printf(user, "rb_scsi: open failed: %s\n", msg);
169 LocalFree(msg);
170 }
171 return NULL;
172 }
173 rb_scsi_device_t dev = malloc(sizeof(struct rb_scsi_device_t));
174 dev->flags = flags;
175 dev->handle = h;
176 dev->user = user;
177 dev->printf = printf ? printf : misc_std_printf;
178 return dev;
179}
180
181int rb_scsi_raw_xfer(rb_scsi_device_t dev, struct rb_scsi_raw_cmd_t *raw)
182{
183#define rb_printf(...) \
184 do{ if(dev->flags & RB_SCSI_DEBUG) dev->printf(dev->user, __VA_ARGS__); }while(0)
185 /* we need to allocate memory for SCSI_PASS_THROUGH_WITH_BUFFERS and the
186 * data and sense */
187 DWORD length = sizeof(SCSI_PASS_THROUGH) + raw->buf_len + raw->sense_len;
188 DWORD returned = 0;
189 SCSI_PASS_THROUGH *spt = malloc(length);
190 memset(spt, 0, length);
191 spt->Length = sizeof(SCSI_PASS_THROUGH);
192 spt->PathId = 0;
193 spt->TargetId = 0;
194 spt->Lun = 0;
195 spt->CdbLength = raw->cdb_len;
196 memcpy(spt->Cdb, raw->cdb, raw->cdb_len);
197 spt->SenseInfoLength = raw->sense_len;
198 spt->SenseInfoOffset = spt->Length; /* Sense after header */
199 switch(raw->dir)
200 {
201 case RB_SCSI_NONE: spt->DataIn = SCSI_IOCTL_DATA_UNSPECIFIED; break;
202 case RB_SCSI_READ: spt->DataIn = SCSI_IOCTL_DATA_IN; break;
203 case RB_SCSI_WRITE: spt->DataIn = SCSI_IOCTL_DATA_OUT; break;
204 default:
205 free(spt);
206 rb_printf("rb_scsi: invalid direction"); return -1;
207 }
208 spt->DataTransferLength = raw->buf_len;
209 spt->DataBufferOffset = spt->SenseInfoOffset + spt->SenseInfoLength; /* Data after Sense */
210 spt->TimeOutValue = raw->tmo;
211 /* on write, copy data to buffer */
212 if(raw->dir == RB_SCSI_WRITE)
213 memcpy((BYTE *)spt + spt->DataBufferOffset, raw->buf, raw->buf_len);
214 BOOL status = DeviceIoControl(dev->handle, IOCTL_SCSI_PASS_THROUGH,
215 spt, length, spt, length, &returned, FALSE);
216 if(!status)
217 {
218 if(dev->flags & RB_SCSI_DEBUG)
219 {
220 LPSTR msg = NULL;
221 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
222 NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&msg, 0, NULL);
223 printf(dev->user, "rb_scsi: ioctl failed: %s\n", msg);
224 LocalFree(msg);
225 }
226 free(spt);
227 return RB_SCSI_OS_ERROR;
228 }
229 /* on read, copy data from buffer */
230 if(raw->dir == RB_SCSI_READ)
231 {
232 raw->buf_len = spt->DataTransferLength;
233 memcpy(raw->buf, (BYTE *)spt + spt->DataBufferOffset, raw->buf_len);
234 }
235 /* check status */
236 raw->status = spt->ScsiStatus;
237 if(raw->status == RB_SCSI_CHECK_CONDITION || raw->status == RB_SCSI_COMMAND_TERMINATED)
238 {
239 memcpy(raw->sense, (BYTE *)spt + spt->SenseInfoOffset, raw->sense_len);
240 free(spt);
241 return RB_SCSI_SENSE;
242 }
243 else
244 raw->sense_len = 0;
245 free(spt);
246 return raw->status ? RB_SCSI_STATUS : RB_SCSI_OK;
247#undef rb_printf
248}
249
250void rb_scsi_close(rb_scsi_device_t dev)
251{
252 CloseHandle(dev->handle);
253 free(dev);
254}
255
256/* other targets */
257#else
258rb_scsi_device_t rb_scsi_open(const char *path, unsigned flags, void *user,
259 rb_scsi_printf_t printf)
260{
261 if(printf == NULL)
262 printf = misc_std_printf;
263 (void) path;
264 if(flags & RB_SCSI_DEBUG)
265 printf(user, "rb_scsi: unimplemented on this platform\n");
266 return NULL;
267}
268
269int rb_scsi_raw_xfer(rb_scsi_device_t dev, struct rb_scsi_raw_cmd_t *raw)
270{
271 return RB_SCSI_ERROR;
272}
273
274void rb_scsi_close(rb_scsi_device_t dev)
275{
276 free(dev);
277}
278#endif
279
280void rb_scsi_decode_sense(rb_scsi_device_t dev, void *_sense, int sense_len)
281{
282#define rb_printf(...) \
283 do{ if(dev->flags & RB_SCSI_DEBUG) dev->printf(dev->user, __VA_ARGS__); }while(0)
284
285 uint8_t *sense = _sense;
286 uint8_t type = sense[0] & 0x7f;
287 switch(type)
288 {
289 case 0x70: case 0x73: rb_printf("Current sense: "); break;
290 case 0x71: case 0x72: rb_printf("Previous sense: "); break;
291 default: rb_printf("Unknown sense\n"); return;
292 }
293 unsigned key = sense[2] & 0xf;
294 switch(key)
295 {
296 case 0: rb_printf("no sense"); break;
297 case 1: rb_printf("recovered error"); break;
298 case 2: rb_printf("not ready"); break;
299 case 3: rb_printf("medium error"); break;
300 case 4: rb_printf("hardware error"); break;
301 case 5: rb_printf("illegal request"); break;
302 case 6: rb_printf("unit attention"); break;
303 case 7: rb_printf("data protect"); break;
304 case 8: rb_printf("blank check"); break;
305 case 9: rb_printf("vendor specific"); break;
306 case 10: rb_printf("copy aborted"); break;
307 case 11: rb_printf("aborted command"); break;
308 default: rb_printf("unknown key"); break;
309 }
310 rb_printf("\n");
311
312#undef rb_printf
313}