e159b45facf8ab30b2f46051e2badf4f1deef105
[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.14 2005/10/15 13:20:47 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.14 2005/10/15 13:20:47 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       if (SCSI_OpenDevice(DeviceFD) == 0)
287         {
288            cam_freeccb(ccb);
289            return(SCSI_ERROR);
290         }
291     }
292   
293   ret = cam_send_ccb(pDev[DeviceFD].curdev, ccb);
294   SCSI_CloseDevice(DeviceFD);
295
296   if ( ret == -1)
297     {
298       cam_freeccb(ccb);
299       return(SCSI_ERROR);
300     }
301   
302   /* 
303    * copy the SENSE data to the Sense Buffer !!
304    */
305   memcpy(pRequestSense, &ccb->csio.sense_data, RequestSenseLength);
306   
307   /* ToDo add error handling */
308   if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
309     {
310       dbprintf(("SCSI_ExecuteCommand return %d\n", (ccb->ccb_h.status & CAM_STATUS_MASK)));
311       return(SCSI_ERROR);
312     }
313
314   cam_freeccb(ccb);
315   return(SCSI_OK);
316 }
317
318 /*
319  * Send the command to the device with the
320  * ioctl interface
321  */
322 int Tape_Ioctl( int DeviceFD, int command)
323 {
324   extern OpenFiles_T *pDev;
325   struct mtop mtop;
326   int ret = 0;
327
328   if (pDev[DeviceFD].devopen == 0)
329       if (SCSI_OpenDevice(DeviceFD) == 0)
330           return(-1);
331
332   switch (command)
333     {
334     case IOCTL_EJECT:
335       mtop.mt_op = MTOFFL;
336       mtop.mt_count = 1;
337       break;
338     default:
339       break;
340     }
341
342   if (ioctl(pDev[DeviceFD].fd , MTIOCTOP, &mtop) != 0)
343     {
344       dbprintf(("Tape_Ioctl error ioctl %d\n",errno));
345       SCSI_CloseDevice(DeviceFD);
346       return(-1);
347     }
348
349   SCSI_CloseDevice(DeviceFD);
350   return(ret);  
351 }
352
353 int Tape_Status( int DeviceFD)
354 {
355   extern OpenFiles_T *pDev;
356   struct mtget mtget;
357   int ret = 0;
358   
359   if (pDev[DeviceFD].devopen == 0)
360       if (SCSI_OpenDevice(DeviceFD) == 0)
361           return(-1);
362
363   if (ioctl(pDev[DeviceFD].fd , MTIOCGET, &mtget) != 0)
364   {
365      dbprintf(("Tape_Status error ioctl %d\n",errno));
366      SCSI_CloseDevice(DeviceFD);
367      return(-1);
368   }
369
370   dbprintf(("ioctl -> mtget.mt_dsreg %lX\n",mtget.mt_dsreg));
371   dbprintf(("ioctl -> mtget.mt_erreg %lX\n",mtget.mt_erreg));
372
373   /*
374    * I have no idea what is the meaning of the bits in mt_erreg
375    * I assume that nothing set is tape loaded
376    * 0x2 is no tape online
377    */
378   if (mtget.mt_erreg == 0)
379     {
380       ret = ret | TAPE_ONLINE;
381     }
382
383   if (mtget.mt_erreg & 0x2)
384     {
385       ret = ret | TAPE_NOT_LOADED;
386     }
387   
388   SCSI_CloseDevice(DeviceFD);
389   return(ret);
390 }
391
392 /*
393  * Scans the bus for device with the type 'tape' or 'robot'
394  *
395  */
396 int ScanBus(int print)
397 {
398   int bus,target,lun;
399   int count = 0;
400   extern OpenFiles_T *pDev;
401   
402   for (bus = 0;bus < 3; bus++)
403     {
404       pDev[count].dev = malloc(10);
405       for (target = 0;target < 8; target++)
406         {
407           for (lun = 0; lun < 8; lun++)
408             {
409               sprintf(pDev[count].dev, "%d:%d:%d", bus, target, lun);
410               pDev[count].inqdone = 0;
411               if (OpenDevice(count, pDev[count].dev, "Scan", NULL))
412                 {
413                   if (pDev[count].inquiry->type == TYPE_TAPE ||
414                       pDev[count].inquiry->type == TYPE_CHANGER)
415                     {
416                       count++;
417                       pDev[count].dev = malloc(10);
418                     } else {
419                       if (print)
420                         {
421                           printf("bus:target:lun -> %s == ",pDev[count].dev);
422                           
423                           switch (pDev[count].inquiry->type)
424                             {
425                             case TYPE_DISK:
426                               printf("Disk");
427                               break;
428                             case TYPE_TAPE:
429                               printf("Tape");
430                               break;
431                             case TYPE_PRINTER:
432                               printf("Printer");
433                               break;
434                             case TYPE_PROCESSOR:
435                               printf("Processor");
436                               break;
437                             case TYPE_WORM:
438                               printf("Worm");
439                               break;
440                             case TYPE_CDROM:
441                               printf("Cdrom");
442                               break;
443                             case TYPE_SCANNER:
444                               printf("Scanner");
445                               break;
446                             case TYPE_OPTICAL:
447                               printf("Optical");
448                               break;
449                             case TYPE_CHANGER:
450                               printf("Changer");
451                               break;
452                             case TYPE_COMM:
453                               printf("Comm");
454                               break;
455                             default:
456                               printf("unknown %d",pDev[count].inquiry->type);
457                               break;
458                             }
459                           printf("\n");
460                         }
461                     } 
462                 }
463             }
464         }
465     }
466 }
467
468 #endif
469 /*
470  * Local variables:
471  * indent-tabs-mode: nil
472  * c-file-style: gnu
473  * End:
474  */