Imported Upstream version 1.1.11
[platform/upstream/cdrkit.git] / libusal / scsi-apollo.c
1 /*
2  * This file has been modified for the cdrkit suite.
3  *
4  * The behaviour and appearence of the program code below can differ to a major
5  * extent from the version distributed by the original author(s).
6  *
7  * For details, see Changelog file distributed with the cdrkit package. If you
8  * received this file from another source then ask the distributing person for
9  * a log of modifications.
10  *
11  */
12
13 /* @(#)scsi-apollo.c    1.5 04/01/14 Copyright 1997,2000 J. Schilling */
14 /*
15  *      Code to support Apollo Domain/OS 10.4.1
16  *
17  *      Copyright (c) 1997,2000 J. Schilling
18  *      Apollo support code written by Paul Walker.
19  */
20 /*
21  * This program is free software; you can redistribute it and/or modify
22  * it under the terms of the GNU General Public License version 2
23  * as published by the Free Software Foundation.
24  *
25  * This program is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28  * GNU General Public License for more details.
29  *
30  * You should have received a copy of the GNU General Public License along with
31  * this program; see the file COPYING.  If not, write to the Free Software
32  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
33  */
34
35 #include <apollo/base.h>
36 #include <apollo/scsi.h>
37 #include <assert.h>
38 #define DomainScsiTimeout       100000
39
40 /*
41  *      Warning: you may change this source, but if you do that
42  *      you need to change the _usal_version and _usal_auth* string below.
43  *      You may not return "schily" for an SCG_AUTHOR request anymore.
44  *      Choose your name instead of "schily" and make clear that the version
45  *      string is related to a modified source.
46  */
47 static  char    _usal_trans_version[] = "scsi-apollo.c-1.5";    /* The version for this transport */
48
49
50 #define MAX_SCG         1       /* Max # of SCSI controllers */
51 #define MAX_TGT         1       /* Max # of SCSI targets */
52 #define MAX_LUN         1       /* Max # of SCSI logical units */
53
54 struct usal_local {
55         scsi_$handle_t  handle;
56         unsigned char   *DomainSensePointer;
57         short           usalfiles[MAX_SCG][MAX_TGT][MAX_LUN];
58 };
59
60 #define usallocal(p)    ((struct usal_local *)((p)->local))
61
62 #ifndef SG_MAX_SENSE
63 #define SG_MAX_SENSE    16      /* Too small for CCS / SCSI-2    */
64 #endif                          /* But cannot be changed         */
65
66 /*
67  * Return version information for the low level SCSI transport code.
68  * This has been introduced to make it easier to trace down problems
69  * in applications.
70  */
71 static char *
72 usalo_version(SCSI *usalp, int what)
73 {
74         if (usalp != (SCSI *)0) {
75                 switch (what) {
76
77                 case SCG_VERSION:
78                         return (_usal_trans_version);
79                 /*
80                  * If you changed this source, you are not allowed to
81                  * return "schily" for the SCG_AUTHOR request.
82                  */
83                 case SCG_AUTHOR:
84                         return ("Paul Walker");
85                 case SCG_SCCS_ID:
86                         return (__sccsid);
87                 }
88         }
89         return ((char *)0);
90 }
91
92 static int
93 usalo_help(SCSI *usalp, FILE *f)
94 {
95         __usal_help(f, "scsi_$do_command_2", "SCSI transport from Apollo DomainOS drivers",
96                 "", "DomainOS driver name", "A DomainOS device node name", FALSE, TRUE);
97         return (0);
98 }
99
100 static int
101 usalo_open(SCSI *usalp, char *device)
102 {
103         register int    nopen = 0;
104         status_$t       status;
105
106         if (usalp->debug > 1)
107                 printf("Entered scsi_open, usalp=%p, device='%s'\n", usalp, device);
108         if (usalp->local == NULL) {
109                 usalp->local = malloc(sizeof (struct usal_local));
110                 if (usalp->local == NULL)
111                         return (0);
112         }
113         if (device == NULL || *device == '\0') {
114                 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE, "Must give device name");
115                 return (0);
116         }
117
118         scsi_$acquire(device, strlen(device), &usallocal(usalp)->handle, &status);
119         if (status.all) {
120                 if (usalp->errstr)
121                         snprintf(usalp->errstr, SCSI_ERRSTR_SIZE, "Cannot open '%s', status %08x", device, status.all);
122                 return (0);
123         }
124         /*
125          * Allocate the sense buffer
126          */
127         usallocal(usalp)->DomainSensePointer = (Uchar *)valloc((size_t) (SCG_MAX_SENSE + getpagesize()));
128         assert(status.all == 0);
129         /*
130          * Wire the sense buffer
131          */
132         scsi_$wire(usallocal(usalp)->handle, (caddr_t)(usallocal(usalp)->DomainSensePointer), SCG_MAX_SENSE, &status);
133         assert(status.all == 0);
134
135         if (usallocal(usalp)->usalfiles[0][0][0] == -1)
136                 usallocal(usalp)->usalfiles[0][0][0] = 1;
137         usal_settarget(usalp, 0, 0, 0);
138         return (++nopen);
139 }
140
141 static int
142 usalo_close(SCSI *usalp)
143 {
144         status_$t       status;
145
146         if (usalp->debug > 1)
147                 printf("Entering scsi_close\n");
148         scsi_$release(usallocal(usalp)->handle, &status);
149         /*
150          * should also unwire the sense buffer
151          */
152         return (status.all);
153 }
154
155
156 static long
157 usalo_maxdma(SCSI *usalp, long amt)
158 {
159         status_$t       status;
160         scsi_$info_t    info;
161
162         scsi_$get_info(usallocal(usalp)->handle, sizeof (info), &info, &status);
163         if (status.all) {
164                 /*
165                  * Should have some better error handling here
166                  */
167                 printf("scsi_$get_info returned %08x\n", status.all);
168                 return (0);
169         }
170         return (info.max_xfer);
171 }
172
173
174 static void *
175 usalo_getbuf(SCSI *usalp, long amt)
176 {
177         void    *ret;
178
179         if (usalp->debug > 1)
180                 printf("scsi_getbuf: %ld bytes\n", amt);
181         ret = valloc((size_t)amt);
182         if (ret == NULL)
183                 return (ret);
184         usalp->bufbase = ret;
185         return (ret);
186 }
187
188 static void
189 usalo_freebuf(SCSI *usalp)
190 {
191         if (usalp->debug > 1)
192                 printf("Entering scsi_freebuf\n");
193
194         if (usalp->bufbase)
195                 free(usalp->bufbase);
196         usalp->bufbase = NULL;
197 }
198
199 static BOOL
200 usalo_havebus(SCSI *usalp, int busno)
201 {
202         register int    t;
203         register int    l;
204
205         if (usalp->debug > 1)
206                 printf("Entered scsi_havebus:  usalp=%p, busno=%d\n", usalp, busno);
207
208         if (busno < 0 || busno >= MAX_SCG)
209                 return (FALSE);
210
211         if (usalp->local == NULL)
212                 return (FALSE);
213
214         for (t = 0; t < MAX_TGT; t++) {
215                 for (l = 0; l < MAX_LUN; l++)
216                         if (usallocal(usalp)->usalfiles[busno][t][l] >= 0)
217                                 return (TRUE);
218         }
219         return (FALSE);
220 }
221
222 static int
223 usalo_fileno(SCSI *usalp, int busno, int tgt, int tlun)
224 {
225         if (usalp->debug > 1)
226                 printf("Entered scsi_fileno:  usalp=%p, busno=%d, tgt=%d, tlun=%d\n", usalp, busno, tgt, tlun);
227         if (busno < 0 || busno >= MAX_SCG ||
228                 tgt < 0 || tgt >= MAX_TGT ||
229                 tlun < 0 || tlun >= MAX_LUN)
230                 return (-1);
231
232         if (usalp->local == NULL)
233                 return (-1);
234         if (usalp->debug > 1)
235                 printf("exiting scsi_fileno, returning %d\n", usallocal(usalp)->usalfiles[busno][tgt][tlun]);
236         return ((int) usallocal(usalp)->usalfiles[busno][tgt][tlun]);
237 }
238
239 static int
240 usalo_initiator_id(SCSI *usalp)
241 {
242         if (usalp->debug > 1)
243                 printf("Entering scsi_initiator\n");
244
245         return (-1);
246 }
247
248 static int
249 usalo_isatapi(SCSI *usalp)
250 {
251         return (FALSE);
252 }
253
254 static int
255 usalo_reset(SCSI *usalp, int what)
256 {
257         status_$t       status;
258
259         if (usalp->debug > 0)
260                 printf("Entering scsi_reset\n");
261
262         if (what == SCG_RESET_NOP)
263                 return (0);
264
265         if (what == SCG_RESET_TGT) {
266                 scsi_$reset_device(usallocal(usalp)->handle, &status);
267                 if (status.all)
268                         printf("Error - scsi_$reset_device failed, status is 0x%08x\n", status.all);
269                 return (status.all);
270         } else {
271                 errno = EINVAL;
272                 return (-1);
273         }
274 }
275
276 static void
277 scsi_do_sense(SCSI *usalp, struct usal_cmd *sp)
278 {
279         scsi_$op_status_t       op_status;
280         static scsi_$cdb_t      sense_cdb;
281         static linteger         sense_cdb_size;
282         static linteger         sense_buffer_size;
283         static scsi_$operation_id_t sense_op_id;
284         static status_$t        sense_status;
285         static pinteger         sense_return_count;
286                 int             i;
287
288         /*
289          * Issue the sense command (wire, issue, wait, unwire
290          */
291         sense_buffer_size = sp->sense_len;
292         sense_cdb_size = SC_G0_CDBLEN;
293         memset(sense_cdb.all, 0, sense_cdb_size);       /* Assuming Apollo sense */
294                                                         /* structure is correct */
295                                                         /* size */
296         sense_cdb.g0.cmd = SC_REQUEST_SENSE;
297         sense_cdb.g0.lun = sp->cdb.g0_cdb.lun;
298         sense_cdb.g0.len = sp->sense_len;
299         scsi_$do_command_2(usallocal(usalp)->handle, sense_cdb, sense_cdb_size, (caddr_t) (usallocal(usalp)->DomainSensePointer), sense_buffer_size, scsi_read, &sense_op_id, &sense_status);
300         if (sense_status.all) {
301                 printf("Error executing sense command, status is 0x%08x\n", sense_status.all);
302         }
303         scsi_$wait(usallocal(usalp)->handle, DomainScsiTimeout, true, sense_op_id, 1, &op_status, &sense_return_count, &sense_status);
304         /*
305          * Print the sense information if debug is on, or if the information is
306          * "unusual"
307          */
308         if (usalp->debug > 0 ||
309                 /*
310                  * I don't prinqqt info for sense codes 0, 2, 5, 6 because
311                  * they aren't interesting
312                  */
313                 (((u_char *) usallocal(usalp)->DomainSensePointer)[2] == 1) ||
314                 (((u_char *) usallocal(usalp)->DomainSensePointer)[2] == 3) ||
315                 (((u_char *) usallocal(usalp)->DomainSensePointer)[2] == 4) ||
316                 (((u_char *) usallocal(usalp)->DomainSensePointer)[2] >= 7)) {
317                 printf(" Sense dump:\n");
318                 for (i = 0; i < sp->sense_len; i++)
319                         printf(" %02x", ((u_char *) usallocal(usalp)->DomainSensePointer)[i]);
320                 printf("\n");
321         }
322         if (((u_char *) usallocal(usalp)->DomainSensePointer)[2] == 5) {
323                 /*
324                  * Illegal command
325                  */
326                 printf("Illegal command detected, ASC=0x%02x, ASQ=0x%02x\n", ((u_char *) usallocal(usalp)->DomainSensePointer)[12], ((u_char *) usallocal(usalp)->DomainSensePointer)[13]);
327         }
328         /*
329          * Copy the sense information to the driver
330          */
331         memcpy(sp->u_sense.cmd_sense, usallocal(usalp)->DomainSensePointer, sp->sense_len);
332         sp->sense_count = sp->sense_len;
333 }
334
335
336 static int
337 usalo_send(SCSI *usalp)
338 {
339         linteger        buffer_length;
340         linteger        cdb_len;
341         scsi_$operation_id_t operation;
342         scsi_$wait_index_t wait_index;
343         scsi_$op_status_t op_status;
344         pinteger        return_count;
345         status_$t       status;
346         char    *ascii_wait_status;
347         int             i;
348         struct usal_cmd *sp = usalp->scmd;
349
350         if (usalp->fd < 0) {
351                 sp->error = SCG_FATAL;
352                 return (0);
353         }
354
355         if (usalp->debug > 0) {
356                 printf("Entered usalo_send, usalp=%p, sp=%p\n", usalp, sp);
357                 printf("usalcmd dump:\n");
358                 printf("  addr=%p\n", sp->addr);
359                 printf("  size=0x%x\n", sp->size);
360                 printf("  flags=0x%x\n", sp->flags);
361                 printf("  cdb_len=%d\n", sp->cdb_len);
362                 printf("  sense_len=%d\n", sp->sense_len);
363                 printf("  timeout=%d\n", sp->timeout);
364                 printf("  kdebug=%d\n", sp->kdebug);
365                 printf("  CDB:");
366                 for (i = 0; i < sp->cdb_len; i++)
367                                 printf(" %02x", sp->cdb.cmd_cdb[i]);
368                 printf("\n");
369         }
370
371         /*
372          * Assume complete transfer, so residual count = 0
373          */
374         sp->resid = 0;
375         buffer_length = sp->size;
376         if (sp->addr) {
377                 if (usalp->debug > 0)
378                         printf(" wiring 0x%x bytes at 0x%x\n", buffer_length, sp->addr);
379                 scsi_$wire(usallocal(usalp)->handle, sp->addr, buffer_length, &status);
380                 if (status.all) {
381                         /*
382                          * Need better error handling
383                          */
384                         printf("scsi_$wire failed, 0x%08x\n", status.all);
385                 }
386         }
387         cdb_len = sp->cdb_len;
388         scsi_$do_command_2(usallocal(usalp)->handle,            /* device handle*/
389                         *(scsi_$cdb_t *) &(sp->cdb.cmd_cdb[0]), /* SCSI CDB     */
390                         cdb_len,                                /* CDB len      */
391                         sp->addr,                               /* DMA buf      */
392                         buffer_length,                          /* DMA len      */
393                         (sp->flags & SCG_RECV_DATA) ? scsi_read : scsi_write,
394                         &operation,                             /* OP ID        */
395                         &status);                               /* Status ret   */
396
397         if (status.all) {
398                 /*
399                  * Need better error handling
400                  */
401                 printf("scsi_$do_command failed, 0x%08x\n", status.all);
402                 sp->error = SCG_FATAL;
403                 sp->ux_errno = EIO;
404                 return (0);
405         } else if (usalp->debug > 0) {
406                 printf("Command submitted, operation=0x%x\n", operation);
407         }
408         wait_index = scsi_$wait(usallocal(usalp)->handle,               /* device handle*/
409                                 sp->timeout * 1000,             /* timeout      */
410                                 0,                              /* async enable */
411                                 operation,                      /* OP ID        */
412                                 1,                              /* max count    */
413                                 &op_status,                     /* status list  */
414                                 &return_count,                  /* count ret    */
415                                 &status);                       /* Status ret   */
416         if (status.all) {
417                 /*
418                  * Need better error handling
419                  */
420                 printf("scsi_$wait failed, 0x%08x\n", status.all);
421                 sp->error = SCG_FATAL;
422                 sp->ux_errno = EIO;
423                 return (0);
424         } else {
425                 if (usalp->debug > 0) {
426                         printf("wait_index=%d, return_count=%d, op_status: op=0x%x, cmd_status=0x%x, op_status=0x%x\n",
427                                 wait_index, return_count, op_status.op, op_status.cmd_status, op_status.op_status);
428                 }
429                 switch (wait_index) {
430
431                 case scsi_device_advance:
432                         ascii_wait_status = "scsi_device_advance";
433                         break;
434                 case scsi_timeout:
435                         ascii_wait_status = "scsi_timeout";
436                         break;
437                 case scsi_async_fault:
438                         ascii_wait_status = "scsi_async_fault";
439                         break;
440                 default:
441                         ascii_wait_status = "unknown";
442                         break;
443                 }
444                 /*
445                  * See if the scsi_$wait terminated "abnormally"
446                  */
447                 if (wait_index != scsi_device_advance) {
448                         printf("scsi_$wait terminated abnormally, status='%s'\n", ascii_wait_status);
449                         sp->error = SCG_FATAL;
450                         sp->ux_errno = EIO;
451                         return (0);
452                 }
453                 /*
454                  * Normal termination, what's the scoop?
455                  */
456                 assert(return_count == 1);
457                 switch (op_status.cmd_status.all) {
458
459                 case status_$ok:
460                         switch (op_status.op_status) {
461
462                         case scsi_good_status:
463                                 sp->error = SCG_NO_ERROR;
464                                 sp->ux_errno = 0;
465                                 break;
466                         case scsi_busy:
467                                 sp->error = SCG_NO_ERROR;
468                                 sp->ux_errno = 0;
469                                 break;
470                         case scsi_check_condition:
471                                 if (usalp->debug > 0)
472                                         printf("SCSI ERROR - CheckCondition\n");
473                                 scsi_do_sense(usalp, sp);
474                                 /*
475                                  * If this was a media error, then call it retryable,
476                                  * instead of no error
477                                  */
478                                 if ((((u_char *) usallocal(usalp)->DomainSensePointer)[0] == 0xf0) &&
479                                         (((u_char *) usallocal(usalp)->DomainSensePointer)[2] == 0x03)) {
480                                         if (usalp->debug > 0)
481                                                 printf("  (retryable)\n");
482                                         sp->error = SCG_RETRYABLE;
483                                         sp->ux_errno = EIO;
484                                 } else {
485                                 /* printf("  (no error)\n"); */
486                                         sp->error = SCG_NO_ERROR;
487                                         sp->ux_errno = 0;
488                                 }
489                                 break;
490                         default:
491                                 /*
492                                  * I fault out in this case because I want to know
493                                  * about this error, and this guarantees that it will
494                                  * get attention.
495                                  */
496                                 printf("Unhandled Domain/OS op_status error:  status=%08x\n",
497                                                                         op_status.op_status);
498                                 exit(EXIT_FAILURE);
499                         }
500                         break;
501                 /*
502                  * Handle recognized error conditions by copying the error
503                  * code over
504                  */
505                 case scsi_$operation_timeout:
506                         printf("SCSI ERROR - Timeout\n");
507                         scsi_do_sense(usalp, sp);
508                         sp->error = SCG_TIMEOUT;
509                         sp->ux_errno = EIO;
510                         break;
511                 case scsi_$dma_underrun:
512                         /*
513                          * This condition seems to occur occasionaly.  I no longer
514                          *  complain because it doesn't seem to matter.
515                          */
516                         if (usalp->debug > 0)
517                                 printf("SCSI ERROR - Underrun\n");
518                         scsi_do_sense(usalp, sp);
519                         sp->resid = sp->size;   /* We don't have the right number */
520                         sp->error = SCG_RETRYABLE;
521                         sp->ux_errno = EIO;
522                         break;
523                 case scsi_$hdwr_failure:        /* received when both scanners were active */
524                         printf("SCSI ERROR - Hardware Failure\n");
525                         scsi_do_sense(usalp, sp);
526                         sp->error = SCG_RETRYABLE;
527                         sp->ux_errno = EIO;
528                         break;
529                 default:
530                         printf("\nUnhandled Domain/OS cmd_status error:  status=%08x\n", op_status.cmd_status.all);
531                         error_$print(op_status.cmd_status);
532                         exit(EXIT_FAILURE);
533                 }
534         }
535         if (sp->addr) {
536                 if (usalp->debug > 0)
537                         printf(" unwiring buffer\n");
538                 scsi_$unwire(usallocal(usalp)->handle, sp->addr, buffer_length, sp->flags & SCG_RECV_DATA, &status);
539                 if (status.all) {
540                         /*
541                          * Need better error handling
542                          */
543                         printf("scsi_$unwire failed, 0x%08x\n", status.all);
544                         sp->error = SCG_FATAL;
545                         sp->ux_errno = EIO;
546                         return (0);
547                 }
548         }
549         return (0);
550 }