Imported Upstream version 2.4.5
[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.10.4.1.2.3.2.1 2004/04/29 20:47:21 martinea 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.10.4.1.2.3.2.1 2004/04/29 20:47:21 martinea 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   sscanf(p,"%d", path);
98           
99   if ((p = strtok(NULL,":")) == NULL) {
100       free(DeviceName);
101       ChgExit("SCSI_OpenDevice", "target in Device Name not found", FATAL);
102   }
103   sscanf(p,"%d", target);
104   if ((p = strtok(NULL,":")) == NULL) {
105       free(DeviceName);
106       ChgExit("SCSI_OpenDevice", "lun in Device Name not found", FATAL);
107   }
108   sscanf(p,"%d", lun);
109   return 1;
110 }
111
112 /*
113  * Check if the device is already open,
114  * if no open it and save it in the list 
115  * of open files.
116  * DeviceName can be an device name, /dev/nrsa0 for example
117  * or an bus:target:lun path, 0:4:0 for bus 0 target 4 lun 0
118  */
119
120 int SCSI_OpenDevice(int ip)
121 {
122   extern OpenFiles_T *pDev;
123   char *DeviceName;
124   int DeviceFD;
125   int i;
126   path_id_t path;
127   target_id_t target;
128   lun_id_t lun;
129
130   DeviceName = stralloc(pDev[ip].dev);
131
132   if (pDev[ip].inqdone == 0) {
133     pDev[ip].inqdone = 1;
134     pDev[ip].SCSI = 0;
135     pDev[ip].inquiry = (SCSIInquiry_T *)malloc(INQUIRY_SIZE);
136     if (parse_btl(DeviceName, &path, &target, &lun)) 
137         pDev[ip].curdev = cam_open_btl(path, target, lun, O_RDWR, NULL);
138     else
139         pDev[ip].curdev = cam_open_device(DeviceName, O_RDWR);
140
141     free(DeviceName);
142     if (pDev[ip].curdev) {
143       pDev[ip].avail = 1;
144       pDev[ip].SCSI = 1;
145       pDev[ip].devopen = 1;
146       if (SCSI_Inquiry(ip, pDev[ip].inquiry, INQUIRY_SIZE) == 0) {
147         if (pDev[ip].inquiry->type == TYPE_TAPE || pDev[ip].inquiry->type == TYPE_CHANGER) {
148           for (i=0;i < 16;i++)
149             pDev[ip].ident[i] = pDev[ip].inquiry->prod_ident[i];
150
151           for (i=15; i >= 0 && !isalnum(pDev[ip].ident[i]); i--) {
152             pDev[ip].ident[i] = '\0';
153           }
154           pDev[ip].SCSI = 1;
155
156           if (pDev[ip].inquiry->type == TYPE_TAPE)
157           {
158                   pDev[ip].type = stralloc("tape");
159           }
160
161           if (pDev[ip].inquiry->type == TYPE_CHANGER)
162           {
163                   pDev[ip].type = stralloc("changer");
164           }
165
166           PrintInquiry(pDev[ip].inquiry);
167           return(1);
168         } else {
169           free(pDev[ip].inquiry);
170           return(0);
171         }
172       } else {
173         pDev[ip].SCSI = 0;
174         free(pDev[ip].inquiry);
175         pDev[ip].inquiry = NULL;
176         return(1);
177       }
178     } else { /* Device open failed */
179       DebugPrint(DEBUG_INFO, SECTION_SCSI,"##### STOP SCSI_OpenDevice open failed\n");
180       return(0);
181     }
182   } else {
183     if (parse_btl(DeviceName, &path, &target, &lun))
184         pDev[ip].curdev = cam_open_btl(path, target, lun, O_RDWR, NULL);
185     else
186         pDev[ip].curdev = cam_open_device(DeviceName, O_RDWR);
187
188     free(DeviceName);
189
190     if (pDev[ip].curdev) {
191       pDev[ip].devopen = 1;
192       return(1);
193     } else  {
194       return(0);
195     }
196   }
197   return(0); 
198 }
199
200 int SCSI_CloseDevice(int DeviceFD)
201 {
202   int ret;
203   extern OpenFiles_T *pDev;
204
205   if (pDev[DeviceFD].SCSI == 1)
206     {
207       cam_close_device(pDev[DeviceFD].curdev);
208       pDev[DeviceFD].devopen = 0;
209     } else {
210       close(pDev[DeviceFD].fd);
211     }
212   return(0);
213 }
214
215 int SCSI_ExecuteCommand(int DeviceFD,
216                         Direction_T Direction,
217                         CDB_T CDB,
218                         int CDB_Length,
219                         void *DataBuffer,
220                         int DataBufferLength,
221                         char *pRequestSense,
222                         int RequestSenseLength)
223 {
224   ExtendedRequestSense_T ExtendedRequestSense;
225   extern OpenFiles_T *pDev;
226   union ccb *ccb;
227   int ret;
228   uint32_t ccb_flags;
229   OpenFiles_T *pwork = NULL;
230
231   if (pDev[DeviceFD].avail == 0)
232     {
233       return(SCSI_ERROR);
234     }
235
236   /* 
237    * CLear the SENSE buffer
238    */
239   bzero(pRequestSense, RequestSenseLength);
240
241   DecodeSCSI(CDB, "SCSI_ExecuteCommand : ");
242
243   ccb = cam_getccb(pDev[DeviceFD].curdev);
244
245   /* Build the CCB */
246   bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio));
247   bcopy(&CDB[0], &ccb->csio.cdb_io.cdb_bytes, CDB_Length);
248
249   switch (Direction)
250     {
251     case Input:
252       if (DataBufferLength == 0)
253         {
254           ccb_flags = CAM_DIR_NONE;
255         } else {
256           ccb_flags = CAM_DIR_IN;
257         }
258       break;
259     case Output:
260       if (DataBufferLength == 0)
261         {
262           ccb_flags = CAM_DIR_NONE;
263         } else {     
264           ccb_flags = CAM_DIR_OUT;
265         }
266       break;
267     default:
268       ccb_flags = CAM_DIR_NONE;
269       break;
270     }
271   
272   cam_fill_csio(&ccb->csio,
273                 /* retires */ 1,
274                 /* cbfncp */ NULL,
275                 /* flags */ ccb_flags,
276                 /* tag_action */ MSG_SIMPLE_Q_TAG,
277                 /* data_ptr */ (u_int8_t*)DataBuffer,
278                 /* dxfer_len */ DataBufferLength,
279                 /* sense_len */ SSD_FULL_SIZE,
280                 /* cdb_len */ CDB_Length,
281                 /* timeout */ 600 * 1000);
282   
283
284   if (pDev[DeviceFD].devopen == 0)
285     {
286       SCSI_OpenDevice(DeviceFD);
287     }
288   
289   ret = cam_send_ccb(pDev[DeviceFD].curdev, ccb);
290   SCSI_CloseDevice(DeviceFD);
291
292   if ( ret == -1)
293     {
294       cam_freeccb(ccb);
295       return(SCSI_ERROR);
296     }
297   
298   /* 
299    * copy the SENSE data to the Sense Buffer !!
300    */
301   memcpy(pRequestSense, &ccb->csio.sense_data, RequestSenseLength);
302   
303   /* ToDo add error handling */
304   if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
305     {
306       dbprintf(("SCSI_ExecuteCommand return %d\n", (ccb->ccb_h.status & CAM_STATUS_MASK)));
307       return(SCSI_ERROR);
308     }
309
310   cam_freeccb(ccb);
311   return(SCSI_OK);
312 }
313
314 /*
315  * Send the command to the device with the
316  * ioctl interface
317  */
318 int Tape_Ioctl( int DeviceFD, int command)
319 {
320   extern OpenFiles_T *pDev;
321   struct mtop mtop;
322   int ret = 0;
323
324   if (pDev[DeviceFD].devopen == 0)
325     {
326       SCSI_OpenDevice(DeviceFD);
327     }
328
329   switch (command)
330     {
331     case IOCTL_EJECT:
332       mtop.mt_op = MTOFFL;
333       mtop.mt_count = 1;
334       break;
335     default:
336       break;
337     }
338
339   if (ioctl(pDev[DeviceFD].fd , MTIOCTOP, &mtop) != 0)
340     {
341       dbprintf(("Tape_Ioctl error ioctl %d\n",errno));
342       SCSI_CloseDevice(DeviceFD);
343       return(-1);
344     }
345
346   SCSI_CloseDevice(DeviceFD);
347   return(ret);  
348 }
349
350 int Tape_Status( int DeviceFD)
351 {
352   extern OpenFiles_T *pDev;
353   struct mtget mtget;
354   int ret = 0;
355   
356   if (pDev[DeviceFD].devopen == 0)
357     {
358       SCSI_OpenDevice(DeviceFD);
359     }
360
361   if (ioctl(pDev[DeviceFD].fd , MTIOCGET, &mtget) != 0)
362   {
363      dbprintf(("Tape_Status error ioctl %d\n",errno));
364      SCSI_CloseDevice(DeviceFD);
365      return(-1);
366   }
367
368   dbprintf(("ioctl -> mtget.mt_dsreg %lX\n",mtget.mt_dsreg));
369   dbprintf(("ioctl -> mtget.mt_erreg %lX\n",mtget.mt_erreg));
370
371   /*
372    * I have no idea what is the meaning of the bits in mt_erreg
373    * I assume that nothing set is tape loaded
374    * 0x2 is no tape online
375    */
376   if (mtget.mt_erreg == 0)
377     {
378       ret = ret | TAPE_ONLINE;
379     }
380
381   if (mtget.mt_erreg & 0x2)
382     {
383       ret = ret | TAPE_NOT_LOADED;
384     }
385   
386   SCSI_CloseDevice(DeviceFD);
387   return(ret);
388 }
389
390 /*
391  * Scans the bus for device with the type 'tape' or 'robot'
392  *
393  */
394 int ScanBus(int print)
395 {
396   int bus,target,lun;
397   int count = 0;
398   extern OpenFiles_T *pDev;
399   
400   for (bus = 0;bus < 3; bus++)
401     {
402       pDev[count].dev = malloc(10);
403       for (target = 0;target < 8; target++)
404         {
405           for (lun = 0; lun < 8; lun++)
406             {
407               sprintf(pDev[count].dev, "%d:%d:%d", bus, target, lun);
408               pDev[count].inqdone = 0;
409               if (OpenDevice(count, pDev[count].dev, "Scan", NULL))
410                 {
411                   if (pDev[count].inquiry->type == TYPE_TAPE ||
412                       pDev[count].inquiry->type == TYPE_CHANGER)
413                     {
414                       count++;
415                       pDev[count].dev = malloc(10);
416                     } else {
417                       if (print)
418                         {
419                           printf("bus:target:lun -> %s == ",pDev[count].dev);
420                           
421                           switch (pDev[count].inquiry->type)
422                             {
423                             case TYPE_DISK:
424                               printf("Disk");
425                               break;
426                             case TYPE_TAPE:
427                               printf("Tape");
428                               break;
429                             case TYPE_PRINTER:
430                               printf("Printer");
431                               break;
432                             case TYPE_PROCESSOR:
433                               printf("Processor");
434                               break;
435                             case TYPE_WORM:
436                               printf("Worm");
437                               break;
438                             case TYPE_CDROM:
439                               printf("Cdrom");
440                               break;
441                             case TYPE_SCANNER:
442                               printf("Scanner");
443                               break;
444                             case TYPE_OPTICAL:
445                               printf("Optical");
446                               break;
447                             case TYPE_CHANGER:
448                               printf("Changer");
449                               break;
450                             case TYPE_COMM:
451                               printf("Comm");
452                               break;
453                             default:
454                               printf("unknown %d",pDev[count].inquiry->type);
455                               break;
456                             }
457                           printf("\n");
458                         }
459                     } 
460                 }
461             }
462         }
463     }
464 }
465
466 #endif
467 /*
468  * Local variables:
469  * indent-tabs-mode: nil
470  * c-file-style: gnu
471  * End:
472  */