Imported Upstream version 1.1.11
[platform/upstream/cdrkit.git] / libusal / scsi-linux-pg.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-linux-pg.c  1.43 04/01/15 Copyright 1997 J. Schilling */
14 /*
15  *      Interface for the Linux PARIDE implementation.
16  *
17  *      We emulate the functionality of the usal driver, via the pg driver.
18  *
19  *      Warning: you may change this source, but if you do that
20  *      you need to change the _usal_version and _usal_auth* string below.
21  *      You may not return "schily" for an SCG_AUTHOR request anymore.
22  *      Choose your name instead of "schily" and make clear that the version
23  *      string is related to a modified source.
24  *
25  *      Copyright (c) 1997  J. Schilling
26  *      Copyright (c) 1998  Grant R. Guenther   <grant@torque.net>
27  *                          Under the terms of the GNU public license.
28  */
29 /*
30  * This program is free software; you can redistribute it and/or modify
31  * it under the terms of the GNU General Public License version 2
32  * as published by the Free Software Foundation.
33  *
34  * This program is distributed in the hope that it will be useful,
35  * but WITHOUT ANY WARRANTY; without even the implied warranty of
36  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37  * GNU General Public License for more details.
38  *
39  * You should have received a copy of the GNU General Public License along with
40  * this program; see the file COPYING.  If not, write to the Free Software
41  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
42  */
43
44 #include <string.h>
45 #ifdef  HAVE_LINUX_PG_H
46 #include <linux/pg.h>
47 #else
48 #include "pg.h"         /* Use local version as Linux sometimes doesn't have */
49 #endif                  /* installed. Now libusal always supports PP SCSI    */
50
51 /*
52  *      Warning: you may change this source, but if you do that
53  *      you need to change the _usal_version and _usal_auth* string below.
54  *      You may not return "schily" for an SCG_AUTHOR request anymore.
55  *      Choose your name instead of "schily" and make clear that the version
56  *      string is related to a modified source.
57  */
58 static  char    _usal_trans_version_pg[] = "scsi-linux-pg.c-1.43";      /* The version for this transport*/
59
60 #ifdef  USE_PG_ONLY
61
62 #define MAX_SCG         1       /* Max # of SCSI controllers */
63 #define MAX_TGT         8
64 #define MAX_LUN         8
65
66 struct usal_local {
67         short   usalfiles[MAX_SCG][MAX_TGT][MAX_LUN];
68         short   buscookies[MAX_SCG];
69         int     pgbus;
70         char    *SCSIbuf;
71 };
72 #define usallocal(p)    ((struct usal_local *)((p)->local))
73
74 #else
75
76 #define usalo_version   pg_version
77 #define usalo_help      pg_help
78 #define usalo_open      pg_open
79 #define usalo_close     pg_close
80 #define usalo_send      pg_send
81 #define usalo_maxdma    pg_maxdma
82 #define usalo_initiator_id pg_initiator_id
83 #define usalo_isatapi   pg_isatapi
84 #define usalo_reset     pg_reset
85
86 static  char    *pg_version(SCSI *usalp, int what);
87 static  int     pg_help(SCSI *usalp, FILE *f);
88 static  int     pg_open(SCSI *usalp, char *device);
89 static  int     pg_close(SCSI *usalp);
90 static  long    pg_maxdma(SCSI *usalp, long amt);
91 static  int     pg_initiator_id(SCSI *usalp);
92 static  int     pg_isatapi(SCSI *usalp);
93 static  int     pg_reset(SCSI *usalp, int what);
94 static  int     pg_send(SCSI *usalp);
95
96 #endif
97
98 static  int     do_usal_cmd(SCSI *usalp, struct usal_cmd *sp);
99 static  int     do_usal_sense(SCSI *usalp, struct usal_cmd *sp);
100
101
102 /*
103  * Return version information for the low level SCSI transport code.
104  * This has been introduced to make it easier to trace down problems
105  * in applications.
106  */
107 static char *
108 usalo_version(SCSI *usalp, int what)
109 {
110         if (usalp != (SCSI *)0) {
111                 switch (what) {
112
113                 case SCG_VERSION:
114                         return (_usal_trans_version_pg);
115                 /*
116                  * If you changed this source, you are not allowed to
117                  * return "schily" for the SCG_AUTHOR request.
118                  */
119                 case SCG_AUTHOR:
120                         return (_usal_auth_cdrkit);
121                 case SCG_SCCS_ID:
122                         return (___sccsid);
123                 }
124         }
125         return ((char *)0);
126 }
127
128 static int
129 usalo_help(SCSI *usalp, FILE *f)
130 {
131         __usal_help(f, "pg", "SCSI transport for ATAPI over Parallel Port",
132                 "", "bus,target,lun", "1,2,0", TRUE, FALSE);
133         return (0);
134 }
135
136 static int
137 usalo_open(SCSI *usalp, char *device)
138 {
139                 int     busno   = usal_scsibus(usalp);
140                 int     tgt     = usal_target(usalp);
141                 int     tlun    = usal_lun(usalp);
142         register int    f;
143         register int    b;
144 #ifdef  USE_PG_ONLY
145         register int    t;
146         register int    l;
147 #endif
148         register int    nopen = 0;
149         char            devname[32];
150
151         if (busno >= MAX_SCG || tgt >= MAX_TGT || tlun >= MAX_LUN) {
152                 errno = EINVAL;
153                 if (usalp->errstr)
154                         snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
155                                 "Illegal value for busno, target or lun '%d,%d,%d'",
156                                 busno, tgt, tlun);
157                 return (-1);
158         }
159
160 #ifndef USE_PG_ONLY
161         /*
162          * We need to find a fake bus number for the parallel port interface.
163          * Unfortunatly, the buscookie array may contain holes if
164          * SCSI_IOCTL_GET_BUS_NUMBER works, so we are searching backwards
165          * for some place for us.
166          * XXX Should add extra space in buscookies for a "PP bus".
167          */
168
169         if (usallocal(usalp)->buscookies[MAX_SCG-1] != (short)-1)
170                 return (0);                     /* No space for pgbus */
171
172         for (b = MAX_SCG-1; b >= 0; b--) {
173                 if (usallocal(usalp)->buscookies[b] != (short)-1) {
174                         usallocal(usalp)->pgbus = ++b;
175                         break;
176                 }
177         }
178         if (usalp->debug > 0) {
179                 fprintf((FILE *)usalp->errfile,
180                         "PP Bus: %d\n", usallocal(usalp)->pgbus);
181         }
182 #else
183         if (usalp->local == NULL) {
184                 usalp->local = malloc(sizeof (struct usal_local));
185                 if (usalp->local == NULL)
186                         return (0);
187
188                 usallocal(usalp)->pgbus = -2;
189                 usallocal(usalp)->SCSIbuf = (char *)-1;
190
191                 for (b = 0; b < MAX_SCG; b++) {
192                         for (t = 0; t < MAX_TGT; t++) {
193                                 for (l = 0; l < MAX_LUN; l++)
194                                         usallocal(usalp)->usalfiles[b][t][l] = (short)-1;
195                         }
196                 }
197         }
198 #endif
199         if (usallocal(usalp)->pgbus < 0)
200                 usallocal(usalp)->pgbus = 0;
201
202         if ((device != NULL && *device != '\0') || (busno == -2 && tgt == -2))
203                 goto openbydev;
204
205         if (busno >= 0 && tgt >= 0 && tlun >= 0) {
206 #ifndef USE_PG_ONLY
207                 if (usallocal(usalp)->pgbus != busno)
208                         return (0);
209 #endif
210                 snprintf(devname, sizeof (devname), "/dev/pg%d", tgt);
211                 f = sg_open_excl(devname, O_RDWR | O_NONBLOCK);
212                 if (f < 0) {
213                         if (usalp->errstr)
214                                 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
215                                                 "Cannot open '%s'", devname);
216                         return (0);
217                 }
218                 usallocal(usalp)->usalfiles[busno][tgt][tlun] = f;
219                 return (1);
220         } else {
221                 tlun = 0;
222                 for (tgt = 0; tgt < MAX_TGT; tgt++) {
223                         snprintf(devname, sizeof (devname), "/dev/pg%d", tgt);
224                         f = sg_open_excl(devname, O_RDWR | O_NONBLOCK);
225                         if (f < 0) {
226                                 /*
227                                  * Set up error string but let us clear it later
228                                  * if at least one open succeeded.
229                                  */
230                                 if (usalp->errstr)
231                                         snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
232                                                         "Cannot open '/dev/pg*'");
233                                 if (errno != ENOENT && errno != ENXIO && errno != ENODEV) {
234                                         if (usalp->errstr)
235                                                 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
236                                                         "Cannot open '%s'", devname);
237                                         return (0);
238                                 }
239                         } else {
240                                 usallocal(usalp)->usalfiles[usallocal(usalp)->pgbus][tgt][tlun] = f;
241                                 nopen++;
242                         }
243                 }
244         }
245         if (nopen > 0 && usalp->errstr)
246                 usalp->errstr[0] = '\0';
247
248 openbydev:
249         if (device != NULL && *device != '\0') {
250                 char    *p;
251
252                 if (tlun < 0)
253                         return (0);
254                 f = open(device, O_RDWR | O_NONBLOCK);
255 /*              if (f < 0 && errno == ENOENT) {*/
256                 if (f < 0) {
257                         if (usalp->errstr)
258                                 snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
259                                         "Cannot open '%s'",
260                                         device);
261                         return (0);
262                 }
263
264                 p = device + strlen(device) -1;
265                 tgt = *p - '0';
266                 if (tgt < 0 || tgt > 9)
267                         return (0);
268                 usallocal(usalp)->usalfiles[usallocal(usalp)->pgbus][tgt][tlun] = f;
269                 usal_settarget(usalp, usallocal(usalp)->pgbus, tgt, tlun);
270
271                 return (++nopen);
272         }
273         return (nopen);
274 }
275
276 static int
277 usalo_close(SCSI *usalp)
278 {
279         register int    f;
280         register int    b;
281         register int    t;
282         register int    l;
283
284         if (usalp->local == NULL)
285                 return (-1);
286         if (usallocal(usalp)->pgbus < 0)
287                 return (0);
288         b = usallocal(usalp)->pgbus;
289         usallocal(usalp)->buscookies[b] = (short)-1;
290
291         for (t = 0; t < MAX_TGT; t++) {
292                 for (l = 0; l < MAX_LUN; l++) {
293                         f = usallocal(usalp)->usalfiles[b][t][l];
294                         if (f >= 0)
295                                 close(f);
296                         usallocal(usalp)->usalfiles[b][t][l] = (short)-1;
297                 }
298         }
299         return (0);
300 }
301
302 static long
303 usalo_maxdma(SCSI *usalp, long amt)
304 {
305         return (PG_MAX_DATA);
306 }
307
308 #ifdef  USE_PG_ONLY
309
310 static void *
311 usalo_getbuf(SCSI *usalp, long amt)
312 {
313         char    *ret;
314
315         if (usalp->debug > 0) {
316                 fprintf((FILE *)usalp->errfile,
317                         "usalo_getbuf: %ld bytes\n", amt);
318         }
319         ret = valloc((size_t)(amt+getpagesize()));
320         if (ret == NULL)
321                 return (ret);
322         usalp->bufbase = ret;
323         ret += getpagesize();
324         usallocal(usalp)->SCSIbuf = ret;
325         return ((void *)ret);
326
327 }
328
329 static void
330 usalo_freebuf(SCSI *usalp)
331 {
332         if (usalp->bufbase)
333                 free(usalp->bufbase);
334         usalp->bufbase = NULL;
335 }
336
337 static BOOL
338 usalo_havebus(SCSI *usalp, int busno)
339 {
340         register int    t;
341         register int    l;
342
343         if (busno < 0 || busno >= MAX_SCG)
344                 return (FALSE);
345
346         if (usalp->local == NULL)
347                 return (FALSE);
348
349         for (t = 0; t < MAX_TGT; t++) {
350                 for (l = 0; l < MAX_LUN; l++)
351                         if (usallocal(usalp)->usalfiles[busno][t][l] >= 0)
352                                 return (TRUE);
353         }
354         return (FALSE);
355 }
356
357 static int
358 usalo_fileno(SCSI *usalp, int busno, int tgt, int tlun)
359 {
360         if (busno < 0 || busno >= MAX_SCG ||
361             tgt < 0 || tgt >= MAX_TGT ||
362             tlun < 0 || tlun >= MAX_LUN)
363                 return (-1);
364
365         if (usalp->local == NULL)
366                 return (-1);
367
368         return ((int)usallocal(usalp)->usalfiles[busno][tgt][tlun]);
369 }
370 #endif  /* USE_PG_ONLY */
371
372 static int
373 usalo_initiator_id(SCSI *usalp)
374 {
375         return (-1);
376 }
377
378 static int
379 usalo_isatapi(SCSI *usalp)
380 {
381         return (TRUE);
382 }
383
384 static int
385 usalo_reset(SCSI *usalp, int what)
386 {
387         struct pg_write_hdr hdr = {PG_MAGIC, PG_RESET, 0};
388
389         if (what == SCG_RESET_NOP)
390                 return (0);
391         if (what != SCG_RESET_BUS) {
392                 errno = EINVAL;
393                 return (-1);
394         }
395         /*
396          * XXX Does this reset TGT or BUS ???
397          */
398         return (write(usalp->fd, (char *)&hdr, sizeof (hdr)));
399
400 }
401
402 #ifndef MAX
403 #define MAX(a, b)       ((a) > (b) ? (a):(b))
404 #endif
405
406 #define RHSIZE  sizeof (struct pg_read_hdr)
407 #define WHSIZE  sizeof (struct pg_write_hdr)
408 #define LEAD    MAX(RHSIZE, WHSIZE)
409
410 static int
411 do_usal_cmd(SCSI *usalp, struct usal_cmd *sp)
412 {
413
414         char    local[LEAD+PG_MAX_DATA];
415         int     use_local, i, r;
416         int     inward = (sp->flags & SCG_RECV_DATA);
417
418         struct pg_write_hdr *whp;
419         struct pg_read_hdr  *rhp;
420         char                *dbp;
421
422         if (sp->cdb_len > 12)
423                 comerrno(EX_BAD, "Can't do %d byte command.\n", sp->cdb_len);
424
425         if (sp->addr == usallocal(usalp)->SCSIbuf) {
426                 use_local = 0;
427                 dbp = sp->addr;
428         } else {
429                 use_local = 1;
430                 dbp = &local[LEAD];
431                 if (!inward)
432                         movebytes(sp->addr, dbp, sp->size);
433         }
434
435         whp = (struct pg_write_hdr *)(dbp - WHSIZE);
436         rhp = (struct pg_read_hdr *)(dbp - RHSIZE);
437
438         whp->magic   = PG_MAGIC;
439         whp->func    = PG_COMMAND;
440         whp->dlen    = sp->size;
441         whp->timeout = sp->timeout;
442
443         for (i = 0; i < 12; i++) {
444                 if (i < sp->cdb_len)
445                         whp->packet[i] = sp->cdb.cmd_cdb[i];
446                 else
447                         whp->packet[i] = 0;
448         }
449
450         i = WHSIZE;
451         if (!inward)
452                 i += sp->size;
453
454         r = write(usalp->fd, (char *)whp, i);
455
456         if (r < 0) {                            /* command was not sent */
457                 sp->ux_errno = geterrno();
458                 if (sp->ux_errno == ETIME) {
459                         /*
460                          * If the previous command timed out, we cannot send
461                          * any further command until the command in the drive
462                          * is ready. So we behave as if the drive did not
463                          * respond to the command.
464                          */
465                         sp->error = SCG_FATAL;
466                         return (0);
467                 }
468                 return (-1);
469         }
470
471         if (r != i)
472                 errmsg("usalo_send(%s) wrote %d bytes (expected %d).\n",
473                         usalp->cmdname, r, i);
474
475         sp->ux_errno = 0;
476         sp->sense_count = 0;
477
478         r = read(usalp->fd, (char *)rhp, RHSIZE+sp->size);
479
480         if (r < 0) {
481                 sp->ux_errno = geterrno();
482                 if (sp->ux_errno == ETIME) {
483                         sp->error = SCG_TIMEOUT;
484                         return (0);
485                 }
486                 sp->error = SCG_FATAL;
487                 return (-1);
488         }
489
490         i = rhp->dlen;
491         if (i > sp->size) {
492                 /*
493                  * "DMA overrun" should be handled in the kernel.
494                  * However this may happen with flaky PP adapters.
495                  */
496                 errmsgno(EX_BAD,
497                         "DMA (read) overrun by %d bytes (requested %d bytes).\n",
498                         i - sp->size, sp->size);
499                 sp->resid = sp->size - i;
500                 sp->error = SCG_RETRYABLE;
501                 i = sp->size;
502         } else {
503                 sp->resid = sp->size - i;
504         }
505
506         if (use_local && inward)
507                 movebytes(dbp, sp->addr, i);
508
509         fillbytes(&sp->scb, sizeof (sp->scb), '\0');
510         fillbytes(&sp->u_sense.cmd_sense, sizeof (sp->u_sense.cmd_sense), '\0');
511
512         sp->error = SCG_NO_ERROR;
513         i = rhp->scsi?2:0;
514 /*      i = rhp->scsi;*/
515         sp->u_scb.cmd_scb[0] = i;
516         if (i & 2) {
517                 if (sp->ux_errno == 0)
518                         sp->ux_errno = EIO;
519                 /*
520                  * If there is no DMA overrun and there is a
521                  * SCSI Status byte != 0 then the SCSI cdb transport was OK
522                  * and sp->error must be SCG_NO_ERROR.
523                  */
524 /*              sp->error = SCG_RETRYABLE;*/
525         }
526
527         return (0);
528
529 }
530
531 static int
532 do_usal_sense(SCSI *usalp, struct usal_cmd *sp)
533 {
534         int             ret;
535         struct usal_cmd         s_cmd;
536
537         fillbytes((caddr_t)&s_cmd, sizeof (s_cmd), '\0');
538         s_cmd.addr = (caddr_t)sp->u_sense.cmd_sense;
539         s_cmd.size = sp->sense_len;
540         s_cmd.flags = SCG_RECV_DATA|SCG_DISRE_ENA;
541         s_cmd.cdb_len = SC_G0_CDBLEN;
542         s_cmd.sense_len = CCS_SENSE_LEN;
543         s_cmd.cdb.g0_cdb.cmd = SC_REQUEST_SENSE;
544         s_cmd.cdb.g0_cdb.lun = sp->cdb.g0_cdb.lun;
545         s_cmd.cdb.g0_cdb.count = sp->sense_len;
546         ret = do_usal_cmd(usalp, &s_cmd);
547
548         if (ret < 0)
549                 return (ret);
550
551         sp->sense_count = sp->sense_len - s_cmd.resid;
552         return (ret);
553 }
554
555 static int
556 usalo_send(SCSI *usalp)
557 {
558         struct usal_cmd *sp = usalp->scmd;
559         int     ret;
560
561         if (usalp->fd < 0) {
562                 sp->error = SCG_FATAL;
563                 return (0);
564         }
565         ret = do_usal_cmd(usalp, sp);
566         if (ret < 0)
567                 return (ret);
568         if (sp->u_scb.cmd_scb[0] & 2)
569                 ret = do_usal_sense(usalp, sp);
570         return (ret);
571 }
572
573 /* end of scsi-linux-pg.c */
574
575 #ifndef USE_PG_ONLY
576
577 #undef  usalo_version
578 #undef  usalo_help
579 #undef  usalo_open
580 #undef  usalo_close
581 #undef  usalo_send
582 #undef  usalo_maxdma
583 #undef  usalo_initiator_id
584 #undef  usalo_isatapi
585 #undef  usalo_reset
586
587 #endif