Imported Upstream version 2.5.1
[debian/amanda] / changer-src / scsi-cam.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-2000 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: scsi-cam.c,v 1.15 2006/05/25 01:47:07 johnfranks Exp $
28  *
29  * Interface to execute SCSI commands on an system with cam support
30  * Current support is for FreeBSD 4.x
31  *
32  * Copyright (c) Thomes Hepper th@ant.han.de
33  */
34
35
36 #include <amanda.h>
37
38 #ifdef HAVE_CAM_LIKE_SCSI
39
40 /*
41 #ifdef HAVE_STDIO_H
42 */
43 #include <stdio.h>
44 /*
45 #endif
46 */
47 #ifdef HAVE_SYS_TYPES_H
48 #include <sys/types.h>
49 #endif
50 #ifdef HAVE_SYS_STAT_H
51 #include <sys/stat.h>
52 #endif
53 #ifdef HAVE_FCNTL_H
54 #include <fcntl.h>
55 #endif
56
57 #ifdef HAVE_CAMLIB_H
58 # include <camlib.h>
59 #endif
60
61 #include <cam/scsi/scsi_message.h>
62
63 #ifdef HAVE_SYS_MTIO_H
64 #include <sys/mtio.h>
65 #endif
66
67 #include <scsi-defs.h>
68
69 extern OpenFiles_T *pChangerDev;
70 extern OpenFiles_T *pTapeDev;
71 extern OpenFiles_T *pTapeDevCtl;
72 extern FILE *debug_file;
73
74
75 void SCSI_OS_Version()
76 {
77 #ifndef lint
78    static char rcsid[] = "$Id: scsi-cam.c,v 1.15 2006/05/25 01:47:07 johnfranks Exp $";
79    DebugPrint(DEBUG_INFO, SECTION_INFO, "scsi-os-layer: %s\n",rcsid);
80 #endif
81 }
82
83 /* parse string of format 1:2:3 and fill in path, target, lun
84    returns 0  if it doesn't look like btl
85    returns 1  if parse successful
86    calls ChgExit if it looks like btl but formatted improperly
87 */
88
89 int parse_btl(char *DeviceName, 
90           path_id_t *path, target_id_t *target, lun_id_t *lun) 
91 {
92   char *p;
93   if (strstr(DeviceName, ":") == NULL) 
94     return 0;
95
96   p = strtok(DeviceName, ":");
97   if (sscanf(p,"%d", path) != 1) {
98       free(DeviceName);
99       ChgExit("SCSI_OpenDevice",
100         "Path conversion error. Digits expected", FATAL);
101   }
102           
103   if ((p = strtok(NULL,":")) == NULL) {
104       free(DeviceName);
105       ChgExit("SCSI_OpenDevice", "target in Device Name not found", FATAL);
106   }
107
108   if (sscanf(p,"%d", target) != 1) {
109       free(DeviceName);
110       ChgExit("SCSI_OpenDevice",
111         "Target conversion error. Digits expected", FATAL);
112   }
113
114   if ((p = strtok(NULL,":")) == NULL) {
115       free(DeviceName);
116       ChgExit("SCSI_OpenDevice", "lun in Device Name not found", FATAL);
117   }
118   if (sscanf(p,"%d", lun) != 1) {
119       free(DeviceName);
120       ChgExit("SCSI_OpenDevice",
121         "LUN conversion error. Digits expected", FATAL);
122   }
123
124   return 1;
125 }
126
127 /*
128  * Check if the device is already open,
129  * if no open it and save it in the list 
130  * of open files.
131  * DeviceName can be an device name, /dev/nrsa0 for example
132  * or an bus:target:lun path, 0:4:0 for bus 0 target 4 lun 0
133  */
134
135 int SCSI_OpenDevice(int ip)
136 {
137   extern OpenFiles_T *pDev;
138   char *DeviceName;
139   int DeviceFD;
140   int i;
141   path_id_t path;
142   target_id_t target;
143   lun_id_t lun;
144
145   DeviceName = stralloc(pDev[ip].dev);
146
147   if (pDev[ip].inqdone == 0) {
148     pDev[ip].inqdone = 1;
149     pDev[ip].SCSI = 0;
150     pDev[ip].inquiry = (SCSIInquiry_T *)malloc(INQUIRY_SIZE);
151     if (parse_btl(DeviceName, &path, &target, &lun)) 
152         pDev[ip].curdev = cam_open_btl(path, target, lun, O_RDWR, NULL);
153     else
154         pDev[ip].curdev = cam_open_device(DeviceName, O_RDWR);
155
156     free(DeviceName);
157     if (pDev[ip].curdev) {
158       pDev[ip].avail = 1;
159       pDev[ip].SCSI = 1;
160       pDev[ip].devopen = 1;
161       if (SCSI_Inquiry(ip, pDev[ip].inquiry, INQUIRY_SIZE) == 0) {
162         if (pDev[ip].inquiry->type == TYPE_TAPE || pDev[ip].inquiry->type == TYPE_CHANGER) {
163           for (i=0;i < 16;i++)
164             pDev[ip].ident[i] = pDev[ip].inquiry->prod_ident[i];
165
166           for (i=15; i >= 0 && !isalnum(pDev[ip].ident[i]); i--) {
167             pDev[ip].ident[i] = '\0';
168           }
169           pDev[ip].SCSI = 1;
170
171           if (pDev[ip].inquiry->type == TYPE_TAPE)
172           {
173                   pDev[ip].type = stralloc("tape");
174           }
175
176           if (pDev[ip].inquiry->type == TYPE_CHANGER)
177           {
178                   pDev[ip].type = stralloc("changer");
179           }
180
181           PrintInquiry(pDev[ip].inquiry);
182           return(1);
183         } else {
184           free(pDev[ip].inquiry);
185           return(0);
186         }
187       } else {
188         pDev[ip].SCSI = 0;
189         free(pDev[ip].inquiry);
190         pDev[ip].inquiry = NULL;
191         return(1);
192       }
193     } else { /* Device open failed */
194       DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice open failed\n");
195       return(0);
196     }
197   }
198   if (parse_btl(DeviceName, &path, &target, &lun))
199       pDev[ip].curdev = cam_open_btl(path, target, lun, O_RDWR, NULL);
200   else
201       pDev[ip].curdev = cam_open_device(DeviceName, O_RDWR);
202
203   free(DeviceName);
204
205   if (pDev[ip].curdev) {
206     pDev[ip].devopen = 1;
207     return(1);
208   } else  {
209     return(0);
210   }
211 }
212
213 int SCSI_CloseDevice(int DeviceFD)
214 {
215   int ret;
216   extern OpenFiles_T *pDev;
217
218   if (pDev[DeviceFD].SCSI == 1)
219     {
220       cam_close_device(pDev[DeviceFD].curdev);
221       pDev[DeviceFD].devopen = 0;
222     } else {
223       close(pDev[DeviceFD].fd);
224     }
225   return(0);
226 }
227
228 int SCSI_ExecuteCommand(int DeviceFD,
229                         Direction_T Direction,
230                         CDB_T CDB,
231                         size_t CDB_Length,
232                         void *DataBuffer,
233                         size_t DataBufferLength,
234                         RequestSense_T *pRequestSense,
235                         size_t RequestSenseLength)
236 {
237   ExtendedRequestSense_T ExtendedRequestSense;
238   extern OpenFiles_T *pDev;
239   union ccb *ccb;
240   int ret;
241   uint32_t ccb_flags;
242   OpenFiles_T *pwork = NULL;
243
244   /* Basic sanity checks */
245   assert(CDB_Length <= UCHAR_MAX);
246   assert(RequestSenseLength <= UCHAR_MAX);
247
248   /* Clear buffer for cases where sense is not returned */
249   memset(pRequestSense, 0, RequestSenseLength);
250
251   if (pDev[DeviceFD].avail == 0)
252     {
253       return(SCSI_ERROR);
254     }
255
256   /* 
257    * CLear the SENSE buffer
258    */
259   bzero(pRequestSense, RequestSenseLength);
260
261   DecodeSCSI(CDB, "SCSI_ExecuteCommand : ");
262
263   ccb = cam_getccb(pDev[DeviceFD].curdev);
264
265   /* Build the CCB */
266   bzero(&(&ccb->ccb_h)[1], SIZEOF(struct ccb_scsiio));
267   bcopy(&CDB[0], &ccb->csio.cdb_io.cdb_bytes, CDB_Length);
268
269   switch (Direction)
270     {
271     case Input:
272       if (DataBufferLength == 0)
273         {
274           ccb_flags = CAM_DIR_NONE;
275         } else {
276           ccb_flags = CAM_DIR_IN;
277         }
278       break;
279     case Output:
280       if (DataBufferLength == 0)
281         {
282           ccb_flags = CAM_DIR_NONE;
283         } else {     
284           ccb_flags = CAM_DIR_OUT;
285         }
286       break;
287     default:
288       ccb_flags = CAM_DIR_NONE;
289       break;
290     }
291   
292   cam_fill_csio(&ccb->csio,
293                 /* retires */ 1,
294                 /* cbfncp */ NULL,
295                 /* flags */ ccb_flags,
296                 /* tag_action */ MSG_SIMPLE_Q_TAG,
297                 /* data_ptr */ (u_int8_t*)DataBuffer,
298                 /* dxfer_len */ DataBufferLength,
299                 /* sense_len */ SSD_FULL_SIZE,
300                 /* cdb_len */ CDB_Length,
301                 /* timeout */ 600 * 1000);
302   
303
304   if (pDev[DeviceFD].devopen == 0)
305     {
306       if (SCSI_OpenDevice(DeviceFD) == 0)
307         {
308            cam_freeccb(ccb);
309            return(SCSI_ERROR);
310         }
311     }
312   
313   ret = cam_send_ccb(pDev[DeviceFD].curdev, ccb);
314   SCSI_CloseDevice(DeviceFD);
315
316   if ( ret == -1)
317     {
318       cam_freeccb(ccb);
319       return(SCSI_ERROR);
320     }
321   
322   /* 
323    * copy the SENSE data to the Sense Buffer !!
324    */
325   memcpy(pRequestSense, &ccb->csio.sense_data, RequestSenseLength);
326   
327   /* ToDo add error handling */
328   if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
329     {
330       dbprintf(("SCSI_ExecuteCommand return %d\n", (ccb->ccb_h.status & CAM_STATUS_MASK)));
331       return(SCSI_ERROR);
332     }
333
334   cam_freeccb(ccb);
335   return(SCSI_OK);
336 }
337
338 /*
339  * Send the command to the device with the
340  * ioctl interface
341  */
342 int Tape_Ioctl( int DeviceFD, int command)
343 {
344   extern OpenFiles_T *pDev;
345   struct mtop mtop;
346   int ret = 0;
347
348   if (pDev[DeviceFD].devopen == 0)
349       if (SCSI_OpenDevice(DeviceFD) == 0)
350           return(-1);
351
352   switch (command)
353     {
354     case IOCTL_EJECT:
355       mtop.mt_op = MTOFFL;
356       mtop.mt_count = 1;
357       break;
358     default:
359       break;
360     }
361
362   if (ioctl(pDev[DeviceFD].fd , MTIOCTOP, &mtop) != 0)
363     {
364       dbprintf(("Tape_Ioctl error ioctl %s\n", strerror(errno)));
365       SCSI_CloseDevice(DeviceFD);
366       return(-1);
367     }
368
369   SCSI_CloseDevice(DeviceFD);
370   return(ret);  
371 }
372
373 int Tape_Status( int DeviceFD)
374 {
375   extern OpenFiles_T *pDev;
376   struct mtget mtget;
377   int ret = 0;
378   
379   if (pDev[DeviceFD].devopen == 0)
380       if (SCSI_OpenDevice(DeviceFD) == 0)
381           return(-1);
382
383   if (ioctl(pDev[DeviceFD].fd , MTIOCGET, &mtget) != 0)
384   {
385      dbprintf(("Tape_Status error ioctl %s\n", strerror(errno)));
386      SCSI_CloseDevice(DeviceFD);
387      return(-1);
388   }
389
390   dbprintf(("ioctl -> mtget.mt_dsreg %lX\n",mtget.mt_dsreg));
391   dbprintf(("ioctl -> mtget.mt_erreg %lX\n",mtget.mt_erreg));
392
393   /*
394    * I have no idea what is the meaning of the bits in mt_erreg
395    * I assume that nothing set is tape loaded
396    * 0x2 is no tape online
397    */
398   if (mtget.mt_erreg == 0)
399     {
400       ret = ret | TAPE_ONLINE;
401     }
402
403   if (mtget.mt_erreg & 0x2)
404     {
405       ret = ret | TAPE_NOT_LOADED;
406     }
407   
408   SCSI_CloseDevice(DeviceFD);
409   return(ret);
410 }
411
412 /*
413  * Scans the bus for device with the type 'tape' or 'robot'
414  *
415  */
416 int ScanBus(int print)
417 {
418   int bus,target,lun;
419   int count = 0;
420   extern OpenFiles_T *pDev;
421   
422   for (bus = 0;bus < 3; bus++)
423     {
424       pDev[count].dev = malloc(10);
425       for (target = 0;target < 8; target++)
426         {
427           for (lun = 0; lun < 8; lun++)
428             {
429               sprintf(pDev[count].dev, "%d:%d:%d", bus, target, lun);
430               pDev[count].inqdone = 0;
431               if (OpenDevice(count, pDev[count].dev, "Scan", NULL))
432                 {
433                   if (pDev[count].inquiry->type == TYPE_TAPE ||
434                       pDev[count].inquiry->type == TYPE_CHANGER)
435                     {
436                       count++;
437                       pDev[count].dev = malloc(10);
438                     } else {
439                       if (print)
440                         {
441                           printf("bus:target:lun -> %s == ",pDev[count].dev);
442                           
443                           switch (pDev[count].inquiry->type)
444                             {
445                             case TYPE_DISK:
446                               printf("Disk");
447                               break;
448                             case TYPE_TAPE:
449                               printf("Tape");
450                               break;
451                             case TYPE_PRINTER:
452                               printf("Printer");
453                               break;
454                             case TYPE_PROCESSOR:
455                               printf("Processor");
456                               break;
457                             case TYPE_WORM:
458                               printf("Worm");
459                               break;
460                             case TYPE_CDROM:
461                               printf("Cdrom");
462                               break;
463                             case TYPE_SCANNER:
464                               printf("Scanner");
465                               break;
466                             case TYPE_OPTICAL:
467                               printf("Optical");
468                               break;
469                             case TYPE_CHANGER:
470                               printf("Changer");
471                               break;
472                             case TYPE_COMM:
473                               printf("Comm");
474                               break;
475                             default:
476                               printf("unknown %d",pDev[count].inquiry->type);
477                               break;
478                             }
479                           printf("\n");
480                         }
481                     } 
482                 }
483             }
484         }
485     }
486     return 0;
487 }
488
489 #endif
490 /*
491  * Local variables:
492  * indent-tabs-mode: nil
493  * c-file-style: gnu
494  * End:
495  */