1 /* $Id: scsi.c,v 1.3 2007/01/07 23:27:50 fredette Exp $ */
2 
3 /* generic/scsi.c - generic SCSI implementation support: */
4 
5 /*
6  * Copyright (c) 2003 Matt Fredette
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed by Matt Fredette.
20  * 4. The name of the author may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #include <tme/common.h>
37 _TME_RCSID("$Id: scsi.c,v 1.3 2007/01/07 23:27:50 fredette Exp $");
38 
39 /* includes: */
40 #include <tme/generic/scsi.h>
41 #include <tme/scsi/scsi-cdb.h>
42 #include <tme/scsi/scsi-msg.h>
43 #include <stdlib.h>
44 
45 /* this scores a SCSI connection: */
46 int
tme_scsi_connection_score(struct tme_connection * conn,unsigned int * _score)47 tme_scsi_connection_score(struct tme_connection *conn, unsigned int *_score)
48 {
49   struct tme_scsi_connection *conn_scsi;
50   struct tme_scsi_connection *conn_scsi_other;
51 
52   /* both sides must be SCSI connections: */
53   assert(conn->tme_connection_type == TME_CONNECTION_SCSI);
54   assert(conn->tme_connection_other->tme_connection_type == TME_CONNECTION_SCSI);
55 
56   /* you cannot connect a bus to a bus, or a device to a device: */
57   conn_scsi
58     = (struct tme_scsi_connection *) conn;
59   conn_scsi_other
60     = (struct tme_scsi_connection *) conn->tme_connection_other;
61   /* XXX we need a way to distinguish a bus from a device: */
62   *_score
63     = 1;
64   return (TME_OK);
65 }
66 
67 /* this parses a SCSI ID: */
68 int
tme_scsi_id_parse(const char * id_string)69 tme_scsi_id_parse(const char *id_string)
70 {
71   unsigned long id;
72   char *p1;
73 
74   /* catch a NULL string: */
75   if (id_string == NULL) {
76     return (-1);
77   }
78 
79   /* convert the string: */
80   id = strtoul(id_string, &p1, 0);
81   if (p1 == id_string
82       || *p1 != '\0') {
83     return (-1);
84   }
85   return (id);
86 }
87 
88 /* this implements state machines that determine the residual in a
89    SCSI command or message phase: */
90 tme_uint32_t
tme_scsi_phase_resid(tme_scsi_control_t scsi_control,tme_uint32_t * _state,const tme_shared tme_uint8_t * bytes,unsigned long count)91 tme_scsi_phase_resid(tme_scsi_control_t scsi_control,
92 		     tme_uint32_t *_state,
93 		     const tme_shared tme_uint8_t *bytes,
94 		     unsigned long count)
95 {
96 #define _TME_SCSI_PHASE_RESID_STATE_COUNT_MASK		(4095)
97   tme_uint32_t state;
98   tme_uint32_t transferred;
99   tme_uint32_t seen;
100   tme_uint32_t skip;
101   tme_uint32_t resid;
102   tme_uint8_t byte;
103 
104   /* get the opaque state, which must not be the value zero (the
105      opaque stop state): */
106   state = *_state;
107   assert (state != 0);
108 
109   /* decompose the opaque state.  NB since the opaque start state for
110      all SCSI bus phases is the value one, and we use internal state
111      zero to represent the stop state, we subtract one from the bytes
112      transferred value and add one to the internal state number: */
113   transferred = (state - 1) & _TME_SCSI_PHASE_RESID_STATE_COUNT_MASK;
114   state /= (_TME_SCSI_PHASE_RESID_STATE_COUNT_MASK + 1);
115   seen = state & _TME_SCSI_PHASE_RESID_STATE_COUNT_MASK;
116   state = (state / (_TME_SCSI_PHASE_RESID_STATE_COUNT_MASK + 1)) + 1;
117 
118   /* we can't have transferred more than we've seen: */
119   assert (transferred <= seen);
120 
121   /* start with a residual of the number of bytes that we have already
122      seen, and skip past those bytes in the buffer: */
123   resid = seen - transferred;
124   skip = TME_MIN(resid, count);
125   bytes += skip;
126   count -= skip;
127 
128   /* loop over the bytes: */
129   for (; count > 0; ) {
130 
131     /* get this next byte: */
132     byte = *(bytes++);
133     count--;
134     seen++;
135 
136     /* dispatch on the SCSI bus phase: */
137     switch (TME_SCSI_PHASE(scsi_control)) {
138 
139       /* an unknown bus phase: */
140     default: assert(FALSE);
141 
142     case TME_SCSI_PHASE_COMMAND:
143 
144       /* we only have state one.  transfer the number of bytes in the
145 	 CDB and move to the stop state: */
146       assert (state == 1);
147       switch (byte & TME_SCSI_CDB_GROUP_MASK) {
148       default: abort();
149       case TME_SCSI_CDB_GROUP_0: resid += TME_SCSI_CDB_GROUP_0_LEN; break;
150       case TME_SCSI_CDB_GROUP_1: resid += TME_SCSI_CDB_GROUP_1_LEN; break;
151       case TME_SCSI_CDB_GROUP_2: resid += TME_SCSI_CDB_GROUP_2_LEN; break;
152       case TME_SCSI_CDB_GROUP_4: resid += TME_SCSI_CDB_GROUP_4_LEN; break;
153       case TME_SCSI_CDB_GROUP_5: resid += TME_SCSI_CDB_GROUP_5_LEN; break;
154       }
155       state = 0;
156       break;
157 
158     case TME_SCSI_PHASE_MESSAGE_IN:
159     case TME_SCSI_PHASE_MESSAGE_OUT:
160 
161       /* dispatch on the state: */
162       switch (state) {
163       default: assert(FALSE);
164 
165 	/* state one is the first byte of the message: */
166       case 1:
167 
168 	/* if this is an extended message: */
169 	if (byte == TME_SCSI_MSG_EXTENDED) {
170 
171 	  /* transfer this first message byte and move to state two: */
172 	  resid += 1;
173 	  state = 2;
174 	}
175 
176 	/* otherwise, if this is a two-byte message: */
177 	else if (TME_SCSI_MSG_IS_2(byte)) {
178 
179 	  /* transfer the two message bytes and move to state zero: */
180 	  resid += 2;
181 	  state = 0;
182 	}
183 
184 	/* otherwise, this is a one-byte message: */
185 	else {
186 
187 	  /* transfer the one message byte and move to state zero: */
188 	  resid += 1;
189 	  state = 0;
190 	}
191 	break;
192 
193 	/* the second byte of an extended message: */
194       case 2:
195 
196 	/* transfer this second message byte, followed by the extended
197 	   message itself, and move to state zero: */
198 	resid += (byte == 0 ? 1 + 256 : 1 + byte);
199 	state = 0;
200 	break;
201       }
202       break;
203     }
204 
205     /* if we've reached state zero, return the opaque stop state and
206        the detected residual: */
207     if (state == 0) {
208       *_state = 0;
209       return (resid);
210     }
211   }
212 
213   /* compose the updated opaque state.  NB since the opaque start
214      state for all SCSI bus phases is the value one, and we use
215      internal state zero to represent the stop state, we add one from
216      the bytes transferred value and subtract one to the internal
217      state number: */
218   state = (state - 1) * (_TME_SCSI_PHASE_RESID_STATE_COUNT_MASK + 1);
219   state += seen;
220   state *= (_TME_SCSI_PHASE_RESID_STATE_COUNT_MASK + 1);
221   state += (transferred + 1) & _TME_SCSI_PHASE_RESID_STATE_COUNT_MASK;
222   *_state = state;
223   return (0);
224 #undef _TME_SCSI_PHASE_RESID_STATE_COUNT_MASK
225 }
226