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