diff options
-rw-r--r-- | utils/imxtools/scsitools/Makefile | 4 | ||||
-rw-r--r-- | utils/scsi/Makefile | 17 | ||||
-rw-r--r-- | utils/scsi/rbscsi.c | 313 | ||||
-rw-r--r-- | utils/scsi/rbscsi.h | 83 |
4 files changed, 415 insertions, 2 deletions
diff --git a/utils/imxtools/scsitools/Makefile b/utils/imxtools/scsitools/Makefile index ed4bc88d0b..4bb6e585fe 100644 --- a/utils/imxtools/scsitools/Makefile +++ b/utils/imxtools/scsitools/Makefile | |||
@@ -1,6 +1,6 @@ | |||
1 | DEFINES= | 1 | DEFINES= |
2 | CC=gcc | 2 | CC=$(PREFIX)gcc |
3 | LD=gcc | 3 | LD=$(PREFIX)gcc |
4 | CFLAGS=-g -std=c99 -W -Wall $(DEFINES) | 4 | CFLAGS=-g -std=c99 -W -Wall $(DEFINES) |
5 | LDFLAGS=-lsgutils2 | 5 | LDFLAGS=-lsgutils2 |
6 | BINS=scsitool | 6 | BINS=scsitool |
diff --git a/utils/scsi/Makefile b/utils/scsi/Makefile new file mode 100644 index 0000000000..2591bf2416 --- /dev/null +++ b/utils/scsi/Makefile | |||
@@ -0,0 +1,17 @@ | |||
1 | PREFIX?= | ||
2 | CC=$(PREFIX)gcc | ||
3 | AR=$(PREFIX)ar | ||
4 | CFLAGS=-g -std=c99 -Wall | ||
5 | BINS=librbscsi.a | ||
6 | ARFLAGS=rcs | ||
7 | |||
8 | all: $(BINS) | ||
9 | |||
10 | %.o: %.c | ||
11 | $(CC) $(CFLAGS) -c -o $@ $< | ||
12 | |||
13 | librbscsi.a: rbscsi.o | ||
14 | $(AR) $(ARFLAGS) -o $@ $^ | ||
15 | |||
16 | clean: | ||
17 | rm -fr *.o $(BINS) | ||
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 | |||
26 | static 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> | ||
40 | typedef 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 | ||
52 | typedef int rb_scsi_handle_t; | ||
53 | #else | ||
54 | typedef void *rb_scsi_handle_t; | ||
55 | #endif | ||
56 | |||
57 | struct 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 | ||
67 | rb_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 | |||
87 | int 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 | |||
146 | void 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) | ||
154 | rb_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 | |||
181 | int 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 | |||
250 | void rb_scsi_close(rb_scsi_device_t dev) | ||
251 | { | ||
252 | CloseHandle(dev->handle); | ||
253 | free(dev); | ||
254 | } | ||
255 | |||
256 | /* other targets */ | ||
257 | #else | ||
258 | rb_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 | |||
269 | int rb_scsi_raw_xfer(rb_scsi_device_t dev, struct rb_scsi_raw_cmd_t *raw) | ||
270 | { | ||
271 | return RB_SCSI_ERROR; | ||
272 | } | ||
273 | |||
274 | void rb_scsi_close(rb_scsi_device_t dev) | ||
275 | { | ||
276 | free(dev); | ||
277 | } | ||
278 | #endif | ||
279 | |||
280 | void 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 | } | ||
diff --git a/utils/scsi/rbscsi.h b/utils/scsi/rbscsi.h new file mode 100644 index 0000000000..2b56aabad2 --- /dev/null +++ b/utils/scsi/rbscsi.h | |||
@@ -0,0 +1,83 @@ | |||
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 | #ifndef __RB_SCSI_H__ | ||
22 | #define __RB_SCSI_H__ | ||
23 | |||
24 | #ifdef __cplusplus | ||
25 | extern "C" { | ||
26 | #endif | ||
27 | |||
28 | struct rb_scsi_device_t; | ||
29 | typedef struct rb_scsi_device_t *rb_scsi_device_t; | ||
30 | |||
31 | typedef void (*rb_scsi_printf_t)(void *user, const char *fmt, ...); | ||
32 | |||
33 | /* flags for rb_scsi_open */ | ||
34 | #define RB_SCSI_READ_ONLY (1 << 0) | ||
35 | #define RB_SCSI_DEBUG (1 << 1) | ||
36 | |||
37 | /* transfer direction */ | ||
38 | #define RB_SCSI_NONE 0 | ||
39 | #define RB_SCSI_READ 1 | ||
40 | #define RB_SCSI_WRITE 2 | ||
41 | |||
42 | /* most common status */ | ||
43 | #define RB_SCSI_GOOD 0 | ||
44 | #define RB_SCSI_CHECK_CONDITION 2 | ||
45 | #define RB_SCSI_COMMAND_TERMINATED 0x22 | ||
46 | |||
47 | /* return codes */ | ||
48 | #define RB_SCSI_OK 0 /* Everything worked */ | ||
49 | #define RB_SCSI_STATUS 1 /* Device returned an error in status */ | ||
50 | #define RB_SCSI_SENSE 2 /* Device returned sense data */ | ||
51 | #define RB_SCSI_OS_ERROR 3 /* Transfer failed, got OS error */ | ||
52 | #define RB_SCSI_ERROR 4 /* Transfer failed, got transfer/host error */ | ||
53 | |||
54 | /* structure for raw transfers */ | ||
55 | struct rb_scsi_raw_cmd_t | ||
56 | { | ||
57 | int dir; /* direction: none, read or write */ | ||
58 | int cdb_len; /* command buffer length */ | ||
59 | void *cdb; /* command buffer */ | ||
60 | int buf_len; /* data buffer length (will be overwritten with actual count) */ | ||
61 | void *buf; /* buffer */ | ||
62 | int sense_len; /* sense buffer length (will be overwritten with actual count) */ | ||
63 | void *sense; /* sense buffer */ | ||
64 | int tmo; /* timeout (in seconds) */ | ||
65 | int status; /* status returned by device (STATUS) or errno (OS_ERROR) or other error (ERROR) */ | ||
66 | }; | ||
67 | |||
68 | /* open a device, returns a handle or NULL on error | ||
69 | * the caller can optionally provide an error printing function */ | ||
70 | rb_scsi_device_t rb_scsi_open(const char *path, unsigned flags, void *user, | ||
71 | rb_scsi_printf_t printf); | ||
72 | /* performs a raw transfer, returns !=0 on error */ | ||
73 | int rb_scsi_raw_xfer(rb_scsi_device_t dev, struct rb_scsi_raw_cmd_t *raw); | ||
74 | /* decode sense and print information if debug flag is set */ | ||
75 | void rb_scsi_decode_sense(rb_scsi_device_t dev, void *sense, int sense_len); | ||
76 | /* close a device */ | ||
77 | void rb_scsi_close(rb_scsi_device_t dev); | ||
78 | |||
79 | #ifdef __cplusplus | ||
80 | } | ||
81 | #endif | ||
82 | |||
83 | #endif /* __RB_SCSI_H__ */ | ||