xref: /illumos-gate/usr/src/uts/common/io/1394/s1394_fcp.c (revision dd72704bd9e794056c558153663c739e2012d721)
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
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
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
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
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
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
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
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
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
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
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
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
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