Imported Debian patch 1.3.11-1
[debian/mtx] / scsi_win32.c
1 /* Copyright 2006 Robert Nelson <robertn@the-nelsons.org>
2
3 $Date: 2007-03-24 18:14:01 -0700 (Sat, 24 Mar 2007) $
4 $Revision: 166 $
5
6   This program is free software; you may redistribute and/or modify it under
7   the terms of the GNU General Public License Version 2 as published by the
8   Free Software Foundation.
9
10   This program is distributed in the hope that it will be useful, but
11   WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
12   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13   for complete details.
14
15 */
16
17 /*
18  * This is the SCSI commands for Windows.
19  */
20
21 #include <stdio.h>
22 #include <windows.h>
23
24 #ifdef _MSC_VER
25 #include <ntddscsi.h>
26 #else
27 #include <ddk/ntddscsi.h>
28 #endif
29
30 #define SCSI_DEFAULT_TIMEOUT    300             /* 1 minutes */
31 #define SCSI_MAX_TIMEOUT                108000  /* 30 hours */
32
33 typedef struct  _HANDLE_ENTRY
34 {
35         HANDLE  hDevice;
36         UCHAR   PortId;
37         UCHAR   PathId;
38         UCHAR   TargetId;
39         UCHAR   Lun;
40 } HANDLE_ENTRY, *PHANDLE_ENTRY;
41
42 PHANDLE_ENTRY   HandleTable = NULL;
43 int                             nEntries = 0;
44
45 DEVICE_TYPE SCSI_OpenDevice(char *DeviceName)
46 {
47         int             DeviceIndex;
48         TCHAR   szDevicePath[256];
49
50         int             nColons = 0;
51         int             index;
52
53         int             port, path, target, lun;
54
55         for (DeviceIndex = 0; DeviceIndex < nEntries; DeviceIndex++)
56         {
57                 if (HandleTable[DeviceIndex].hDevice == INVALID_HANDLE_VALUE)
58                         break;
59         }
60
61         if (DeviceIndex >= nEntries)
62         {
63                 PHANDLE_ENTRY pNewTable;
64
65                 nEntries += 4;
66
67                 if (HandleTable == NULL)
68                 {
69                         pNewTable = (PHANDLE_ENTRY)malloc(nEntries * sizeof(HANDLE_ENTRY));
70                 }
71                 else
72                 {
73                         pNewTable = (PHANDLE_ENTRY)realloc(HandleTable, nEntries * sizeof(HANDLE_ENTRY));
74                 }
75
76                 if (pNewTable == NULL)
77                 {
78                         FatalError("cannot open SCSI device '%s' - %m\n", DeviceName);
79                 }
80
81                 HandleTable = pNewTable;
82         }
83
84         for (index = 0; DeviceName[index] != '\0'; index++)
85         {
86                 if (DeviceName[index] == ':')
87                         nColons++;
88                 else if (DeviceName[index] < '0' || DeviceName[index] > '9')
89                         break;
90         }
91
92         if (DeviceName[index] == '\0' && nColons == 3 && 
93                 sscanf(DeviceName, "%d:%d:%d:%d", &port, &path, &target, &lun) == 4)
94         {
95                 HandleTable[DeviceIndex].PortId = (UCHAR)port;
96                 HandleTable[DeviceIndex].PathId = (UCHAR)path;
97                 HandleTable[DeviceIndex].TargetId = (UCHAR)target;
98                 HandleTable[DeviceIndex].Lun = (UCHAR)lun;
99
100                 sprintf(szDevicePath, "\\\\.\\scsi%d:", port);
101         }
102         else 
103         {
104                 int nPrefixLength = 0;
105
106                 if (DeviceName[0] != '\\') {
107                         memcpy(szDevicePath, "\\\\.\\", 4 * sizeof(TCHAR));
108                         nPrefixLength = 4;
109                 }
110
111                 HandleTable[DeviceIndex].PortId = 0;
112                 HandleTable[DeviceIndex].PathId = 0;
113                 HandleTable[DeviceIndex].TargetId = 0;
114                 HandleTable[DeviceIndex].Lun = 0;
115
116                 strncpy(&szDevicePath[nPrefixLength], 
117                                 DeviceName, 
118                                 sizeof(szDevicePath) / sizeof(TCHAR) - nPrefixLength - 1);
119
120                 szDevicePath[sizeof(szDevicePath) / sizeof(TCHAR) - 1] = '\0';
121         }
122
123         HandleTable[DeviceIndex].hDevice = CreateFile(szDevicePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
124
125         if (HandleTable[DeviceIndex].hDevice == INVALID_HANDLE_VALUE)
126         {
127                 DWORD dwError = GetLastError();
128
129 #if DEBUG
130                 LPSTR lpszMessage;
131
132                 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, 0, (LPSTR)&lpszMessage, 0, NULL);
133                 fputs(lpszMessage, stderr);
134 #endif
135
136                 switch (dwError)
137                 {
138                 case ERROR_FILE_NOT_FOUND:
139                 case ERROR_PATH_NOT_FOUND:
140                         errno = ENOENT;
141                         break;
142
143                 case ERROR_TOO_MANY_OPEN_FILES:
144                         errno =  EMFILE;
145                         break;
146
147                 default:
148                 case ERROR_ACCESS_DENIED:
149                 case ERROR_SHARING_VIOLATION:
150                 case ERROR_LOCK_VIOLATION:
151                 case ERROR_INVALID_NAME:
152                         errno = EACCES;
153                         break;
154
155                 case ERROR_FILE_EXISTS:
156                         errno = EEXIST;
157                         break;
158
159                 case ERROR_INVALID_PARAMETER:
160                         errno = EINVAL;
161                         break;
162                 }
163
164                 FatalError("cannot open SCSI device '%s' - %m\n", DeviceName);
165         }
166
167         return DeviceIndex;
168 }
169
170 static int scsi_timeout = SCSI_DEFAULT_TIMEOUT;
171
172 void SCSI_Set_Timeout(int secs)
173 {
174         if (secs > SCSI_MAX_TIMEOUT)
175         {
176                 secs = SCSI_MAX_TIMEOUT;
177         }
178
179         scsi_timeout = secs;
180 }
181  
182 void SCSI_Default_Timeout(void)
183 {
184         scsi_timeout = SCSI_DEFAULT_TIMEOUT;
185 }
186
187 void SCSI_CloseDevice(char *DeviceName, DEVICE_TYPE DeviceFD)
188 {
189         if (DeviceFD < nEntries)
190         {
191                 CloseHandle(HandleTable[DeviceFD].hDevice);
192                 HandleTable[DeviceFD].hDevice = INVALID_HANDLE_VALUE;
193         }
194         else
195         {
196                 errno = EBADF;
197                 FatalError("cannot close SCSI device '%s' - %m\n", DeviceName);
198         }
199 }
200
201
202 /* Get the SCSI ID and LUN... */
203 scsi_id_t *SCSI_GetIDLun(DEVICE_TYPE fd)
204 {
205         scsi_id_t *             retval;
206
207         SCSI_ADDRESS    ScsiAddress;
208         BOOL                    bResult;
209         DWORD                   dwBytesReturned;
210
211         if (fd < nEntries)
212         {
213                 retval = (scsi_id_t *)xmalloc(sizeof(scsi_id_t));
214                 retval->id = HandleTable[fd].TargetId;
215                 retval->lun = HandleTable[fd].Lun;
216
217 #ifdef DEBUG
218                 fprintf(stderr,"SCSI:ID=%d LUN=%d\n", retval->id, retval->lun);
219 #endif
220                 return retval;
221         }
222         else
223         {
224                 errno = EBADF;
225                 FatalError("cannot close SCSI device - %m\n");
226         }
227
228         memset(&ScsiAddress, 0, sizeof(ScsiAddress));
229
230         ScsiAddress.Length = sizeof(ScsiAddress);
231
232         bResult = DeviceIoControl(      HandleTable[fd].hDevice, 
233                                                                 IOCTL_SCSI_GET_ADDRESS, 
234                                                                 &ScsiAddress, sizeof(ScsiAddress), 
235                                                                 &ScsiAddress, sizeof(ScsiAddress), 
236                                                                 &dwBytesReturned, 
237                                                                 NULL);
238
239         if (!bResult)
240         {
241                 return NULL;
242         }
243
244         retval = (scsi_id_t *)xmalloc(sizeof(scsi_id_t));
245         retval->id = ScsiAddress.TargetId;
246         retval->lun = ScsiAddress.Lun;
247
248 #ifdef DEBUG
249         fprintf(stderr,"SCSI:ID=%d LUN=%d\n",retval->id,retval->lun);
250 #endif
251         return retval;
252 }
253
254 int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
255                                                 Direction_T Direction,
256                                                 CDB_T *CDB,
257                                                 int CDB_Length,
258                                                 void *DataBuffer,
259                                                 int DataBufferLength,
260                                                 RequestSense_T *RequestSense)
261 {
262         PSCSI_PASS_THROUGH ScsiPassThrough;
263
264         const DWORD     dwDataBufferOffset = sizeof(SCSI_PASS_THROUGH) + (sizeof(RequestSense_T) + 3) / 4 * 4;
265         const DWORD     dwBufferSize = dwDataBufferOffset + DataBufferLength;
266
267         BOOL            bResult;
268         DWORD           dwBytesReturned;
269         DWORD           dwInputLength;
270         DWORD           dwOutputLength;
271
272         if (DeviceFD >= nEntries || HandleTable[DeviceFD].hDevice == INVALID_HANDLE_VALUE)
273         {
274                 errno = EBADF;
275                 return -1;
276         }
277
278         ScsiPassThrough = (PSCSI_PASS_THROUGH)malloc(dwBufferSize);
279
280         memset(ScsiPassThrough, 0, dwDataBufferOffset);
281
282         ScsiPassThrough->Length = sizeof(SCSI_PASS_THROUGH);
283
284         ScsiPassThrough->PathId = HandleTable[DeviceFD].PathId;
285         ScsiPassThrough->TargetId = HandleTable[DeviceFD].TargetId;
286         ScsiPassThrough->Lun = HandleTable[DeviceFD].Lun;
287         ScsiPassThrough->CdbLength = (UCHAR)CDB_Length;
288         ScsiPassThrough->DataIn = Direction == Input;
289         ScsiPassThrough->DataBufferOffset = dwDataBufferOffset;
290         ScsiPassThrough->DataTransferLength = DataBufferLength;
291         ScsiPassThrough->SenseInfoOffset = sizeof(SCSI_PASS_THROUGH);
292         ScsiPassThrough->SenseInfoLength = sizeof(RequestSense_T);
293         ScsiPassThrough->TimeOutValue = scsi_timeout;
294
295         memcpy(ScsiPassThrough->Cdb, CDB, CDB_Length);
296         dwBytesReturned = 0;
297
298         if (Direction == Output)
299         {
300                 memcpy((void *)(((char *)ScsiPassThrough) + dwDataBufferOffset), DataBuffer, DataBufferLength);
301                 dwInputLength = dwBufferSize;
302                 dwOutputLength = dwDataBufferOffset;
303         }
304         else
305         {
306                 dwInputLength = sizeof(SCSI_PASS_THROUGH);
307                 dwOutputLength = dwBufferSize;
308         }
309
310         bResult = DeviceIoControl(      HandleTable[DeviceFD].hDevice, 
311                                                                 IOCTL_SCSI_PASS_THROUGH, 
312                                                                 ScsiPassThrough, dwInputLength, 
313                                                                 ScsiPassThrough, dwOutputLength, 
314                                                                 &dwBytesReturned, 
315                                                                 NULL);
316         if (bResult)
317         {
318                 if (ScsiPassThrough->ScsiStatus != 0)
319                 {
320                         memcpy(RequestSense, &ScsiPassThrough[1], sizeof(RequestSense_T));
321 #if DEBUG
322                         fprintf(stderr, "Command failed - ScsiStatus = %d\n", ScsiPassThrough->ScsiStatus);
323                         PrintRequestSense(RequestSense);
324 #endif
325                         bResult = false;
326                 }
327                 else
328                 {
329                         if (Direction == Input)
330                         {
331                                 memcpy( DataBuffer, 
332                                                 (void *)(((char *)ScsiPassThrough) + dwDataBufferOffset),
333                                                 DataBufferLength);
334                         }
335                 }
336         }
337         else
338         {
339 #if DEBUG
340                 DWORD   dwError = GetLastError();
341                 LPSTR   lpszMessage;
342
343                 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, 0, (LPSTR)&lpszMessage, 0, NULL);
344                 fputs(lpszMessage, stderr);
345                 LocalFree(lpszMessage);
346 #endif
347
348                 memset(RequestSense, 0, sizeof(RequestSense_T));
349         }
350
351         free(ScsiPassThrough);
352
353         return bResult ? 0 : -1;
354 }