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