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 /*
28 * s1394_fcp.c
29 * 1394 Services Layer FCP Support Routines
30 */
31
32 #include <sys/conf.h>
33 #include <sys/ddi.h>
34 #include <sys/sunddi.h>
35 #include <sys/cmn_err.h>
36 #include <sys/types.h>
37 #include <sys/kmem.h>
38
39 #include <sys/1394/t1394.h>
40 #include <sys/1394/s1394.h>
41 #include <sys/1394/h1394.h>
42
43 static int s1394_fcp_register_common(s1394_target_t *target,
44 t1394_fcp_evts_t *evts, s1394_fa_type_t type, s1394_fa_descr_t *descr);
45 static int s1394_fcp_unregister_common(s1394_target_t *target,
46 s1394_fa_type_t type);
47 static void s1394_fcp_resp_recv_write_request(cmd1394_cmd_t *req);
48 static void s1394_fcp_cmd_recv_write_request(cmd1394_cmd_t *req);
49 static void s1394_fcp_recv_write_request(cmd1394_cmd_t *req,
50 s1394_fa_type_t type);
51 static void s1394_fcp_recv_write_unclaimed(s1394_hal_t *hal,
52 cmd1394_cmd_t *req);
53
54
55 /*
56 * number of retries to notify registered targets in case target list
57 * changes while the list rwlock is dropped for the time of callback
58 */
59 uint_t s1394_fcp_notify_retry_cnt = 3;
60
61 s1394_fa_descr_t s1394_fcp_ctl_descr = {
62 IEC61883_FCP_RESP_ADDR,
63 IEC61883_FCP_RESP_SIZE,
64 T1394_ADDR_WRENBL,
65 { NULL, s1394_fcp_resp_recv_write_request, NULL },
66 IEC61883_FCP_CMD_ADDR
67 };
68
69 s1394_fa_descr_t s1394_fcp_tgt_descr = {
70 IEC61883_FCP_CMD_ADDR,
71 IEC61883_FCP_CMD_SIZE,
72 T1394_ADDR_WRENBL,
73 { NULL, s1394_fcp_cmd_recv_write_request, NULL },
74 IEC61883_FCP_RESP_ADDR
75 };
76
77
78 int
s1394_fcp_hal_init(s1394_hal_t * hal)79 s1394_fcp_hal_init(s1394_hal_t *hal)
80 {
81 int ret = DDI_SUCCESS;
82
83 if ((ddi_prop_exists(DDI_DEV_T_ANY, hal->halinfo.dip, DDI_PROP_DONTPASS,
84 "h1394-fcp-claim-on-demand")) == 0) {
85 /* if not on-demand, claim addresses now */
86 ret = s1394_fa_claim_addr(hal, S1394_FA_TYPE_FCP_CTL,
87 &s1394_fcp_ctl_descr);
88 if (ret == DDI_SUCCESS) {
89 ret = s1394_fa_claim_addr(hal, S1394_FA_TYPE_FCP_TGT,
90 &s1394_fcp_tgt_descr);
91 if (ret != DDI_SUCCESS) {
92 s1394_fa_free_addr(hal, S1394_FA_TYPE_FCP_CTL);
93 }
94 }
95 }
96
97 return (ret);
98 }
99
100 int
s1394_fcp_register_ctl(s1394_target_t * target,t1394_fcp_evts_t * evts)101 s1394_fcp_register_ctl(s1394_target_t *target, t1394_fcp_evts_t *evts)
102 {
103 return (s1394_fcp_register_common(target, evts, S1394_FA_TYPE_FCP_CTL,
104 &s1394_fcp_ctl_descr));
105 }
106
107 int
s1394_fcp_register_tgt(s1394_target_t * target,t1394_fcp_evts_t * evts)108 s1394_fcp_register_tgt(s1394_target_t *target, t1394_fcp_evts_t *evts)
109 {
110 return (s1394_fcp_register_common(target, evts, S1394_FA_TYPE_FCP_TGT,
111 &s1394_fcp_tgt_descr));
112 }
113
114 int
s1394_fcp_unregister_ctl(s1394_target_t * target)115 s1394_fcp_unregister_ctl(s1394_target_t *target)
116 {
117 return (s1394_fcp_unregister_common(target, S1394_FA_TYPE_FCP_CTL));
118 }
119
120 int
s1394_fcp_unregister_tgt(s1394_target_t * target)121 s1394_fcp_unregister_tgt(s1394_target_t *target)
122 {
123 return (s1394_fcp_unregister_common(target, S1394_FA_TYPE_FCP_TGT));
124 }
125
126
127 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)128 s1394_fcp_register_common(s1394_target_t *target, t1394_fcp_evts_t *evts,
129 s1394_fa_type_t type, s1394_fa_descr_t *descr)
130 {
131 s1394_hal_t *hal = target->on_hal;
132 s1394_fcp_target_t *fcp;
133
134 rw_enter(&hal->target_list_rwlock, RW_WRITER);
135
136 if (s1394_fa_list_is_empty(hal, type)) {
137 if (s1394_fa_claim_addr(hal, type, descr) != DDI_SUCCESS) {
138 rw_exit(&hal->target_list_rwlock);
139 return (DDI_FAILURE);
140 }
141 }
142
143 /* Add on the target list */
144 s1394_fa_list_add(hal, target, type);
145
146 fcp = &target->target_fa[type].fat_u.fcp;
147 fcp->fc_evts = *evts;
148
149 rw_exit(&hal->target_list_rwlock);
150
151 return (DDI_SUCCESS);
152 }
153
154 static int
s1394_fcp_unregister_common(s1394_target_t * target,s1394_fa_type_t type)155 s1394_fcp_unregister_common(s1394_target_t *target, s1394_fa_type_t type)
156 {
157 s1394_hal_t *hal = target->on_hal;
158 int result;
159
160 rw_enter(&hal->target_list_rwlock, RW_WRITER);
161
162 result = s1394_fa_list_remove(hal, target, type);
163 if (result == DDI_SUCCESS) {
164 if (s1394_fa_list_is_empty(hal, type)) {
165 s1394_fa_free_addr(hal, type);
166 }
167 }
168
169 rw_exit(&hal->target_list_rwlock);
170
171 return (result);
172 }
173
174 /*
175 * s1394_fcp_write_check_cmd()
176 * Check if an FCP command is formed correctly;
177 * set cmd_result and return DDI_FAILURE if not.
178 */
179 int
s1394_fcp_write_check_cmd(cmd1394_cmd_t * cmd)180 s1394_fcp_write_check_cmd(cmd1394_cmd_t *cmd)
181 {
182 int len;
183
184 /* 4-byte writes must be quadlet writes */
185 if (cmd->cmd_type == CMD1394_ASYNCH_WR_BLOCK) {
186 len = cmd->cmd_u.b.blk_length;
187 if (len == 4) {
188 cmd->cmd_result = CMD1394_ETYPE_ERROR;
189 return (DDI_FAILURE);
190 }
191 } else {
192 len = 4;
193 }
194
195 /*
196 * request must be within FCP range. we avoid extra checks by
197 * using the fact that command and response are of the same size
198 */
199 if ((cmd->cmd_addr & IEEE1394_ADDR_OFFSET_MASK) + len >
200 IEC61883_FCP_CMD_SIZE) {
201 cmd->cmd_result = CMD1394_EADDRESS_ERROR;
202 return (DDI_FAILURE);
203 }
204
205 /* some options don't make sense for FCP commands */
206 if (cmd->cmd_options & CMD1394_OVERRIDE_ADDR) {
207 cmd->cmd_result = CMD1394_EINVALID_COMMAND;
208 return (DDI_FAILURE);
209 }
210
211 return (DDI_SUCCESS);
212 }
213
214 static void
s1394_fcp_resp_recv_write_request(cmd1394_cmd_t * req)215 s1394_fcp_resp_recv_write_request(cmd1394_cmd_t *req)
216 {
217 s1394_fcp_recv_write_request(req, S1394_FA_TYPE_FCP_CTL);
218 }
219
220 static void
s1394_fcp_cmd_recv_write_request(cmd1394_cmd_t * req)221 s1394_fcp_cmd_recv_write_request(cmd1394_cmd_t *req)
222 {
223 s1394_fcp_recv_write_request(req, S1394_FA_TYPE_FCP_TGT);
224 }
225
226 /*
227 * s1394_fcp_recv_write_request()
228 * Common write request handler
229 */
230 static void
s1394_fcp_recv_write_request(cmd1394_cmd_t * req,s1394_fa_type_t type)231 s1394_fcp_recv_write_request(cmd1394_cmd_t *req, s1394_fa_type_t type)
232 {
233 s1394_hal_t *hal = (s1394_hal_t *)req->cmd_callback_arg;
234 s1394_target_t *target;
235 s1394_fa_target_t *fat;
236 uint_t saved_gen;
237 int num_retries = 0;
238 int (*cb)(cmd1394_cmd_t *req);
239 boolean_t restored = B_FALSE;
240 int ret = T1394_REQ_UNCLAIMED;
241
242 rw_enter(&hal->target_list_rwlock, RW_READER);
243
244 start:
245 target = hal->hal_fa[type].fal_head;
246
247 if (target) {
248 s1394_fa_restore_cmd(hal, req);
249 restored = B_TRUE;
250
251 /* Find a target that claims the request */
252 do {
253 fat = &target->target_fa[type];
254
255 cb = fat->fat_u.fcp.fc_evts.fcp_write_request;
256 if (cb == NULL) {
257 continue;
258 }
259 req->cmd_callback_arg = fat->fat_u.fcp.fc_evts.fcp_arg;
260
261 saved_gen = s1394_fa_list_gen(hal, type);
262
263 rw_exit(&hal->target_list_rwlock);
264 ret = cb(req);
265 rw_enter(&hal->target_list_rwlock, RW_READER);
266
267 if (ret == T1394_REQ_CLAIMED) {
268 break;
269 }
270
271 /*
272 * List could change while we dropped the lock. In such
273 * case, start all over again, because missing a write
274 * request can have more serious consequences for a
275 * target than receiving same request more than once
276 */
277 if (saved_gen != s1394_fa_list_gen(hal, type)) {
278 num_retries++;
279 if (num_retries <= s1394_fcp_notify_retry_cnt) {
280 goto start;
281 } else {
282 break;
283 }
284 }
285
286 target = fat->fat_next;
287 } while (target != NULL);
288 }
289
290 rw_exit(&hal->target_list_rwlock);
291
292 if (ret != T1394_REQ_CLAIMED) {
293 if (restored) {
294 s1394_fa_convert_cmd(hal, req);
295 }
296 s1394_fcp_recv_write_unclaimed(hal, req);
297 }
298 }
299
300 /*
301 * none of the targets claimed the request - send an appropriate response
302 */
303 static void
s1394_fcp_recv_write_unclaimed(s1394_hal_t * hal,cmd1394_cmd_t * req)304 s1394_fcp_recv_write_unclaimed(s1394_hal_t *hal, cmd1394_cmd_t *req)
305 {
306 req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR;
307 (void) s1394_send_response(hal, req);
308 }
309