1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 /*
30 * s1394_fcp.c
31 * 1394 Services Layer FCP Support Routines
32 */
33
34 #include <sys/conf.h>
35 #include <sys/ddi.h>
36 #include <sys/sunddi.h>
37 #include <sys/cmn_err.h>
38 #include <sys/types.h>
39 #include <sys/kmem.h>
40 #include <sys/tnf_probe.h>
41
42 #include <sys/1394/t1394.h>
43 #include <sys/1394/s1394.h>
44 #include <sys/1394/h1394.h>
45
46 static int s1394_fcp_register_common(s1394_target_t *target,
47 t1394_fcp_evts_t *evts, s1394_fa_type_t type, s1394_fa_descr_t *descr);
48 static int s1394_fcp_unregister_common(s1394_target_t *target,
49 s1394_fa_type_t type);
50 static void s1394_fcp_resp_recv_write_request(cmd1394_cmd_t *req);
51 static void s1394_fcp_cmd_recv_write_request(cmd1394_cmd_t *req);
52 static void s1394_fcp_recv_write_request(cmd1394_cmd_t *req,
53 s1394_fa_type_t type);
54 static void s1394_fcp_recv_write_unclaimed(s1394_hal_t *hal,
55 cmd1394_cmd_t *req);
56
57
58 /*
59 * number of retries to notify registered targets in case target list
60 * changes while the list rwlock is dropped for the time of callback
61 */
62 uint_t s1394_fcp_notify_retry_cnt = 3;
63
64 s1394_fa_descr_t s1394_fcp_ctl_descr = {
65 IEC61883_FCP_RESP_ADDR,
66 IEC61883_FCP_RESP_SIZE,
67 T1394_ADDR_WRENBL,
68 { NULL, s1394_fcp_resp_recv_write_request, NULL },
69 IEC61883_FCP_CMD_ADDR
70 };
71
72 s1394_fa_descr_t s1394_fcp_tgt_descr = {
73 IEC61883_FCP_CMD_ADDR,
74 IEC61883_FCP_CMD_SIZE,
75 T1394_ADDR_WRENBL,
76 { NULL, s1394_fcp_cmd_recv_write_request, NULL },
77 IEC61883_FCP_RESP_ADDR
78 };
79
80
81 int
s1394_fcp_hal_init(s1394_hal_t * hal)82 s1394_fcp_hal_init(s1394_hal_t *hal)
83 {
84 int ret = DDI_SUCCESS;
85
86 TNF_PROBE_0_DEBUG(s1394_fcp_hal_init_enter, S1394_TNF_SL_FCP_STACK, "");
87
88 if ((ddi_prop_exists(DDI_DEV_T_ANY, hal->halinfo.dip, DDI_PROP_DONTPASS,
89 "h1394-fcp-claim-on-demand")) == 0) {
90 /* if not on-demand, claim addresses now */
91 ret = s1394_fa_claim_addr(hal, S1394_FA_TYPE_FCP_CTL,
92 &s1394_fcp_ctl_descr);
93 if (ret == DDI_SUCCESS) {
94 ret = s1394_fa_claim_addr(hal, S1394_FA_TYPE_FCP_TGT,
95 &s1394_fcp_tgt_descr);
96 if (ret != DDI_SUCCESS) {
97 s1394_fa_free_addr(hal, S1394_FA_TYPE_FCP_CTL);
98 }
99 }
100 }
101
102 TNF_PROBE_0_DEBUG(s1394_fcp_hal_init_exit, S1394_TNF_SL_FCP_STACK, "");
103 return (ret);
104 }
105
106 int
s1394_fcp_register_ctl(s1394_target_t * target,t1394_fcp_evts_t * evts)107 s1394_fcp_register_ctl(s1394_target_t *target, t1394_fcp_evts_t *evts)
108 {
109 return (s1394_fcp_register_common(target, evts, S1394_FA_TYPE_FCP_CTL,
110 &s1394_fcp_ctl_descr));
111 }
112
113 int
s1394_fcp_register_tgt(s1394_target_t * target,t1394_fcp_evts_t * evts)114 s1394_fcp_register_tgt(s1394_target_t *target, t1394_fcp_evts_t *evts)
115 {
116 return (s1394_fcp_register_common(target, evts, S1394_FA_TYPE_FCP_TGT,
117 &s1394_fcp_tgt_descr));
118 }
119
120 int
s1394_fcp_unregister_ctl(s1394_target_t * target)121 s1394_fcp_unregister_ctl(s1394_target_t *target)
122 {
123 return (s1394_fcp_unregister_common(target, S1394_FA_TYPE_FCP_CTL));
124 }
125
126 int
s1394_fcp_unregister_tgt(s1394_target_t * target)127 s1394_fcp_unregister_tgt(s1394_target_t *target)
128 {
129 return (s1394_fcp_unregister_common(target, S1394_FA_TYPE_FCP_TGT));
130 }
131
132
133 static int
s1394_fcp_register_common(s1394_target_t * target,t1394_fcp_evts_t * evts,s1394_fa_type_t type,s1394_fa_descr_t * descr)134 s1394_fcp_register_common(s1394_target_t *target, t1394_fcp_evts_t *evts,
135 s1394_fa_type_t type, s1394_fa_descr_t *descr)
136 {
137 s1394_hal_t *hal = target->on_hal;
138 s1394_fcp_target_t *fcp;
139
140 rw_enter(&hal->target_list_rwlock, RW_WRITER);
141
142 if (s1394_fa_list_is_empty(hal, type)) {
143 if (s1394_fa_claim_addr(hal, type, descr) != DDI_SUCCESS) {
144 rw_exit(&hal->target_list_rwlock);
145 return (DDI_FAILURE);
146 }
147 }
148
149 /* Add on the target list */
150 s1394_fa_list_add(hal, target, type);
151
152 fcp = &target->target_fa[type].fat_u.fcp;
153 fcp->fc_evts = *evts;
154
155 rw_exit(&hal->target_list_rwlock);
156
157 return (DDI_SUCCESS);
158 }
159
160 static int
s1394_fcp_unregister_common(s1394_target_t * target,s1394_fa_type_t type)161 s1394_fcp_unregister_common(s1394_target_t *target, s1394_fa_type_t type)
162 {
163 s1394_hal_t *hal = target->on_hal;
164 int result;
165
166 rw_enter(&hal->target_list_rwlock, RW_WRITER);
167
168 result = s1394_fa_list_remove(hal, target, type);
169 if (result == DDI_SUCCESS) {
170 if (s1394_fa_list_is_empty(hal, type)) {
171 s1394_fa_free_addr(hal, type);
172 }
173 } else {
174 TNF_PROBE_0(s1394_fcp_unregister_common_error_list,
175 S1394_TNF_SL_FCP_ERROR, "");
176 }
177
178 rw_exit(&hal->target_list_rwlock);
179
180 return (result);
181 }
182
183 /*
184 * s1394_fcp_write_check_cmd()
185 * Check if an FCP command is formed correctly;
186 * set cmd_result and return DDI_FAILURE if not.
187 */
188 int
s1394_fcp_write_check_cmd(cmd1394_cmd_t * cmd)189 s1394_fcp_write_check_cmd(cmd1394_cmd_t *cmd)
190 {
191 int len;
192
193 /* 4-byte writes must be quadlet writes */
194 if (cmd->cmd_type == CMD1394_ASYNCH_WR_BLOCK) {
195 len = cmd->cmd_u.b.blk_length;
196 if (len == 4) {
197 cmd->cmd_result = CMD1394_ETYPE_ERROR;
198 TNF_PROBE_0(t1394_write_error_type,
199 S1394_TNF_SL_FCP_ERROR, "");
200 return (DDI_FAILURE);
201 }
202 } else {
203 len = 4;
204 }
205
206 /*
207 * request must be within FCP range. we avoid extra checks by
208 * using the fact that command and response are of the same size
209 */
210 if ((cmd->cmd_addr & IEEE1394_ADDR_OFFSET_MASK) + len >
211 IEC61883_FCP_CMD_SIZE) {
212 cmd->cmd_result = CMD1394_EADDRESS_ERROR;
213 TNF_PROBE_0(t1394_write_error_addr, S1394_TNF_SL_FCP_ERROR, "");
214 return (DDI_FAILURE);
215 }
216
217 /* some options don't make sense for FCP commands */
218 if (cmd->cmd_options & CMD1394_OVERRIDE_ADDR) {
219 cmd->cmd_result = CMD1394_EINVALID_COMMAND;
220 TNF_PROBE_0(t1394_write_error_opt, S1394_TNF_SL_FCP_ERROR, "");
221 return (DDI_FAILURE);
222 }
223
224 return (DDI_SUCCESS);
225 }
226
227 static void
s1394_fcp_resp_recv_write_request(cmd1394_cmd_t * req)228 s1394_fcp_resp_recv_write_request(cmd1394_cmd_t *req)
229 {
230 s1394_fcp_recv_write_request(req, S1394_FA_TYPE_FCP_CTL);
231 }
232
233 static void
s1394_fcp_cmd_recv_write_request(cmd1394_cmd_t * req)234 s1394_fcp_cmd_recv_write_request(cmd1394_cmd_t *req)
235 {
236 s1394_fcp_recv_write_request(req, S1394_FA_TYPE_FCP_TGT);
237 }
238
239 /*
240 * s1394_fcp_recv_write_request()
241 * Common write request handler
242 */
243 static void
s1394_fcp_recv_write_request(cmd1394_cmd_t * req,s1394_fa_type_t type)244 s1394_fcp_recv_write_request(cmd1394_cmd_t *req, s1394_fa_type_t type)
245 {
246 s1394_hal_t *hal = (s1394_hal_t *)req->cmd_callback_arg;
247 s1394_target_t *target;
248 s1394_fa_target_t *fat;
249 uint_t saved_gen;
250 int num_retries = 0;
251 int (*cb)(cmd1394_cmd_t *req);
252 boolean_t restored = B_FALSE;
253 int ret = T1394_REQ_UNCLAIMED;
254
255 TNF_PROBE_0_DEBUG(s1394_fcp_recv_write_request_enter,
256 S1394_TNF_SL_FCP_STACK, "");
257
258 rw_enter(&hal->target_list_rwlock, RW_READER);
259
260 start:
261 target = hal->hal_fa[type].fal_head;
262
263 if (target) {
264 s1394_fa_restore_cmd(hal, req);
265 restored = B_TRUE;
266
267 /* Find a target that claims the request */
268 do {
269 fat = &target->target_fa[type];
270
271 cb = fat->fat_u.fcp.fc_evts.fcp_write_request;
272 if (cb == NULL) {
273 continue;
274 }
275 req->cmd_callback_arg = fat->fat_u.fcp.fc_evts.fcp_arg;
276
277 saved_gen = s1394_fa_list_gen(hal, type);
278
279 rw_exit(&hal->target_list_rwlock);
280 ret = cb(req);
281 rw_enter(&hal->target_list_rwlock, RW_READER);
282
283 if (ret == T1394_REQ_CLAIMED) {
284 break;
285 }
286
287 /*
288 * List could change while we dropped the lock. In such
289 * case, start all over again, because missing a write
290 * request can have more serious consequences for a
291 * target than receiving same request more than once
292 */
293 if (saved_gen != s1394_fa_list_gen(hal, type)) {
294 TNF_PROBE_2(s1394_fcp_recv_write_request_error,
295 S1394_TNF_SL_FCP_ERROR, "",
296 tnf_string, msg, "list gen changed",
297 tnf_opaque, num_retries, num_retries);
298 num_retries++;
299 if (num_retries <= s1394_fcp_notify_retry_cnt) {
300 goto start;
301 } else {
302 break;
303 }
304 }
305
306 target = fat->fat_next;
307 } while (target != NULL);
308 }
309
310 rw_exit(&hal->target_list_rwlock);
311
312 if (ret != T1394_REQ_CLAIMED) {
313 TNF_PROBE_0(s1394_fcp_recv_write_request_error_unclaimed,
314 S1394_TNF_SL_FCP_ERROR, "");
315 if (restored) {
316 s1394_fa_convert_cmd(hal, req);
317 }
318 s1394_fcp_recv_write_unclaimed(hal, req);
319 }
320
321 TNF_PROBE_0_DEBUG(s1394_fcp_recv_write_request_exit,
322 S1394_TNF_SL_FCP_STACK, "");
323 }
324
325 /*
326 * none of the targets claimed the request - send an appropriate response
327 */
328 static void
s1394_fcp_recv_write_unclaimed(s1394_hal_t * hal,cmd1394_cmd_t * req)329 s1394_fcp_recv_write_unclaimed(s1394_hal_t *hal, cmd1394_cmd_t *req)
330 {
331 req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR;
332 (void) s1394_send_response(hal, req);
333 }
334