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 (c) 1999-2000 by Sun Microsystems, Inc.
24 * All rights reserved.
25 */
26
27 /*
28 * hci1394_tlabel.h
29 * These routines track the tlabel usage for a 1394 adapter.
30 */
31
32 #include <sys/kmem.h>
33 #include <sys/types.h>
34 #include <sys/conf.h>
35 #include <sys/ddi.h>
36 #include <sys/sunddi.h>
37
38 #include <sys/1394/ieee1394.h>
39 #include <sys/1394/adapters/hci1394.h>
40
41
42 /*
43 * hci1394_tlabel_init()
44 * Initialize the tlabel structures. These structures will be protected
45 * by a mutex at the iblock_cookie passed in. Bad tlabels will be usable
46 * when > reclaim_time_nS has gone by. init() returns a handle to be used
47 * for the rest of the tlabel functions.
48 */
49 void
hci1394_tlabel_init(hci1394_drvinfo_t * drvinfo,hrtime_t reclaim_time_nS,hci1394_tlabel_handle_t * tlabel_handle)50 hci1394_tlabel_init(hci1394_drvinfo_t *drvinfo, hrtime_t reclaim_time_nS,
51 hci1394_tlabel_handle_t *tlabel_handle)
52 {
53 hci1394_tlabel_t *tstruct;
54
55
56 ASSERT(tlabel_handle != NULL);
57
58 /* alloc space for tlabel data */
59 tstruct = kmem_alloc(sizeof (hci1394_tlabel_t), KM_SLEEP);
60
61 /* setup handle which is returned from this function */
62 *tlabel_handle = tstruct;
63
64 /*
65 * Initialize tlabel structure. We start with max node set to the
66 * maxiumum node we could have so that we make sure the arrays are
67 * initialized correctly in hci1394_tlabel_reset().
68 */
69 tstruct->tb_drvinfo = drvinfo;
70 tstruct->tb_reclaim_time = reclaim_time_nS;
71 tstruct->tb_max_node = TLABEL_RANGE - 1;
72 tstruct->tb_bcast_sent = B_FALSE;
73
74 mutex_init(&tstruct->tb_mutex, NULL, MUTEX_DRIVER,
75 drvinfo->di_iblock_cookie);
76
77 /*
78 * The mutex must be initialized before tlabel_reset()
79 * is called. This is because tlabel_reset is also
80 * used in normal tlabel processing (i.e. not just during
81 * initialization)
82 */
83 hci1394_tlabel_reset(tstruct);
84 }
85
86
87 /*
88 * hci1394_tlabel_fini()
89 * Frees up the space allocated in init(). Notice that a pointer to the
90 * handle is used for the parameter. fini() will set your handle to NULL
91 * before returning.
92 */
93 void
hci1394_tlabel_fini(hci1394_tlabel_handle_t * tlabel_handle)94 hci1394_tlabel_fini(hci1394_tlabel_handle_t *tlabel_handle)
95 {
96 hci1394_tlabel_t *tstruct;
97
98
99 ASSERT(tlabel_handle != NULL);
100
101 tstruct = (hci1394_tlabel_t *)*tlabel_handle;
102
103 mutex_destroy(&tstruct->tb_mutex);
104 kmem_free(tstruct, sizeof (hci1394_tlabel_t));
105
106 /* set handle to null. This helps catch bugs. */
107 *tlabel_handle = NULL;
108 }
109
110
111 /*
112 * hci1394_tlabel_alloc()
113 * alloc a tlabel based on the node id. If alloc fails, we are out of
114 * tlabels for that node. See comments before set_reclaim_time() on when
115 * bad tlabel's are free to be used again.
116 */
117 int
hci1394_tlabel_alloc(hci1394_tlabel_handle_t tlabel_handle,uint_t destination,hci1394_tlabel_info_t * tlabel_info)118 hci1394_tlabel_alloc(hci1394_tlabel_handle_t tlabel_handle, uint_t destination,
119 hci1394_tlabel_info_t *tlabel_info)
120 {
121 uint_t node_number;
122 uint_t index;
123 uint64_t bad;
124 uint64_t free;
125 hrtime_t time;
126 uint8_t last;
127
128
129 ASSERT(tlabel_handle != NULL);
130 ASSERT(tlabel_info != NULL);
131
132 /* copy destination into tlabel_info */
133 tlabel_info->tbi_destination = destination;
134
135 /* figure out what node we are going to */
136 node_number = IEEE1394_NODE_NUM(destination);
137
138 mutex_enter(&tlabel_handle->tb_mutex);
139
140 /*
141 * Keep track of if we have sent out a broadcast request and what the
142 * maximum # node we have sent to for reset processing optimization
143 */
144 if (node_number == IEEE1394_BROADCAST_NODEID) {
145 tlabel_handle->tb_bcast_sent = B_TRUE;
146 } else if (node_number > tlabel_handle->tb_max_node) {
147 tlabel_handle->tb_max_node = node_number;
148 }
149
150 /* setup copies so we don't take up so much space :-) */
151 bad = tlabel_handle->tb_bad[node_number];
152 free = tlabel_handle->tb_free[node_number];
153 time = tlabel_handle->tb_bad_timestamp[node_number];
154 last = tlabel_handle->tb_last[node_number];
155
156 /*
157 * If there are any bad tlabels, see if the last bad tlabel recorded for
158 * this nodeid is now good to use. If so, add all bad tlabels for that
159 * node id back into the free list
160 *
161 * NOTE: This assumes that bad tlabels are infrequent.
162 */
163 if (bad != 0) {
164 if (gethrtime() > time) {
165
166 /* add the bad tlabels back into the free list */
167 free |= bad;
168
169 /* clear the bad list */
170 bad = 0;
171 }
172 }
173
174 /*
175 * Find a free tlabel. This will break out of the loop once it finds a
176 * tlabel. There are a total of TLABEL_RANGE tlabels. The alloc
177 * rotates the check so that we don't always use the same tlabel. It
178 * stores the last tlabel used in last.
179 */
180 for (index = 0; index < TLABEL_RANGE; index++) {
181
182 /* if the next tlabel to check is free */
183 if ((free & ((uint64_t)1 << last)) != 0) {
184 /* we are using this tlabel */
185 tlabel_info->tbi_tlabel = last;
186
187 /* take it out of the free list */
188 free = free & ~((uint64_t)1 << last);
189
190 /*
191 * increment the last count so we start checking on the
192 * next tlabel next alloc(). Note the rollover at
193 * TLABEL_RANGE since we only have TLABEL_RANGE tlabels.
194 */
195 (last)++;
196 if (last >= TLABEL_RANGE) {
197 last = 0;
198 }
199
200 /* Copy the copies back */
201 tlabel_handle->tb_bad[node_number] = bad;
202 tlabel_handle->tb_free[node_number] = free;
203 tlabel_handle->tb_bad_timestamp[node_number] = time;
204 tlabel_handle->tb_last[node_number] = last;
205
206 /* unlock the tlabel structure */
207 mutex_exit(&tlabel_handle->tb_mutex);
208 return (DDI_SUCCESS);
209 }
210
211 /*
212 * This tlabel is not free, lets go to the next one. Note the
213 * rollover at TLABEL_RANGE since we only have TLABEL_RANGE
214 * tlabels.
215 */
216 (last)++;
217 if (last >= TLABEL_RANGE) {
218 last = 0;
219 }
220 }
221
222 /* Copy the copies back */
223 tlabel_handle->tb_bad[node_number] = bad;
224 tlabel_handle->tb_free[node_number] = free;
225 tlabel_handle->tb_bad_timestamp[node_number] = time;
226 tlabel_handle->tb_last[node_number] = last;
227
228 mutex_exit(&tlabel_handle->tb_mutex);
229
230 return (DDI_FAILURE);
231 }
232
233
234 /*
235 * hci1394_tlabel_free()
236 * free the previously alloc()'d tlabel. Once a tlabel has been free'd, it
237 * can be used again when alloc() is called.
238 */
239 void
hci1394_tlabel_free(hci1394_tlabel_handle_t tlabel_handle,hci1394_tlabel_info_t * tlabel_info)240 hci1394_tlabel_free(hci1394_tlabel_handle_t tlabel_handle,
241 hci1394_tlabel_info_t *tlabel_info)
242 {
243 uint_t node_number;
244 uint_t tlabel;
245
246
247 ASSERT(tlabel_handle != NULL);
248 ASSERT(tlabel_info != NULL);
249 ASSERT(tlabel_info->tbi_tlabel <= TLABEL_MASK);
250
251 /* figure out what node and tlabel we are using */
252 node_number = IEEE1394_NODE_NUM(tlabel_info->tbi_destination);
253 tlabel = tlabel_info->tbi_tlabel;
254
255 mutex_enter(&tlabel_handle->tb_mutex);
256
257 /*
258 * Put the tlabel back in the free list and NULL out the (void *) in the
259 * lookup structure. You wouldn't expect to have to null out the lookup
260 * structure, but we know first hand that bad HW will send invalid
261 * tlabels which could really mess things up if you didn't :-)
262 */
263 tlabel_handle->tb_lookup[node_number][tlabel] = NULL;
264 tlabel_handle->tb_free[node_number] |= ((uint64_t)1 << tlabel);
265
266 mutex_exit(&tlabel_handle->tb_mutex);
267 }
268
269
270 /*
271 * hci1394_tlabel_register()
272 * Register an opaque command with an alloc()'d tlabel. Each nodeID has it's
273 * own tlabel list.
274 */
275 void
hci1394_tlabel_register(hci1394_tlabel_handle_t tlabel_handle,hci1394_tlabel_info_t * tlabel_info,void * cmd)276 hci1394_tlabel_register(hci1394_tlabel_handle_t tlabel_handle,
277 hci1394_tlabel_info_t *tlabel_info, void *cmd)
278 {
279 uint_t node_number;
280 uint_t tlabel;
281
282
283 ASSERT(tlabel_handle != NULL);
284 ASSERT(tlabel_info != NULL);
285 ASSERT(tlabel_info->tbi_tlabel <= TLABEL_MASK);
286
287 /* figure out what node and tlabel we are using */
288 node_number = IEEE1394_NODE_NUM(tlabel_info->tbi_destination);
289 tlabel = tlabel_info->tbi_tlabel;
290
291 mutex_enter(&tlabel_handle->tb_mutex);
292
293 /* enter the (void *) into the lookup table */
294 tlabel_handle->tb_lookup[node_number][tlabel] = cmd;
295
296 mutex_exit(&tlabel_handle->tb_mutex);
297 }
298
299
300 /*
301 * hci1394_tlabel_lookup()
302 * returns (in cmd) the opaque command which was registered with the
303 * specified tlabel from alloc(). If a tlabel was not registered, cmd ='s
304 * NULL.
305 */
306 void
hci1394_tlabel_lookup(hci1394_tlabel_handle_t tlabel_handle,hci1394_tlabel_info_t * tlabel_info,void ** cmd)307 hci1394_tlabel_lookup(hci1394_tlabel_handle_t tlabel_handle,
308 hci1394_tlabel_info_t *tlabel_info, void **cmd)
309 {
310 uint_t node_number;
311 uint_t tlabel;
312
313
314 ASSERT(tlabel_handle != NULL);
315 ASSERT(tlabel_info != NULL);
316 ASSERT(cmd != NULL);
317 ASSERT(tlabel_info->tbi_tlabel <= TLABEL_MASK);
318
319 /* figure out what node and tlabel we are using */
320 node_number = IEEE1394_NODE_NUM(tlabel_info->tbi_destination);
321 tlabel = tlabel_info->tbi_tlabel;
322
323 mutex_enter(&tlabel_handle->tb_mutex);
324
325 /*
326 * fetch the (void *) from the lookup table. The case where the pointer
327 * equals NULL will be handled by the layer above.
328 */
329 *cmd = tlabel_handle->tb_lookup[node_number][tlabel];
330
331 mutex_exit(&tlabel_handle->tb_mutex);
332 }
333
334
335 /*
336 * hci1394_tlabel_bad()
337 * Register the specified tlabel as bad. tlabel_lookup() will no longer
338 * return a registered opaque command and this tlabel will not be returned
339 * from alloc() until > reclaim_time has passed. See set_reclaim_time() for
340 * more info.
341 */
342 void
hci1394_tlabel_bad(hci1394_tlabel_handle_t tlabel_handle,hci1394_tlabel_info_t * tlabel_info)343 hci1394_tlabel_bad(hci1394_tlabel_handle_t tlabel_handle,
344 hci1394_tlabel_info_t *tlabel_info)
345 {
346 uint_t node_number;
347 uint_t tlabel;
348
349
350 ASSERT(tlabel_handle != NULL);
351 ASSERT(tlabel_info != NULL);
352
353 /* figure out what node and tlabel we are using */
354 node_number = IEEE1394_NODE_NUM(tlabel_info->tbi_destination);
355 tlabel = tlabel_info->tbi_tlabel & TLABEL_MASK;
356
357 mutex_enter(&tlabel_handle->tb_mutex);
358
359 /*
360 * Put the tlabel in the bad list and NULL out the (void *) in the
361 * lookup structure. We may see this tlabel shortly if the device is
362 * late in responding. We want to make sure to drop the message if we
363 * do. Set the bad timestamp to the current time plus the reclaim time.
364 * This is the "new" time when all of the bad tlabels for this node will
365 * be free'd.
366 */
367 tlabel_handle->tb_bad_timestamp[node_number] = gethrtime() +
368 tlabel_handle->tb_reclaim_time;
369 tlabel_handle->tb_bad[node_number] |= ((uint64_t)1 << tlabel);
370 tlabel_handle->tb_lookup[node_number][tlabel] = NULL;
371
372 mutex_exit(&tlabel_handle->tb_mutex);
373 }
374
375
376 /*
377 * hci1394_tlabel_reset()
378 * resets the tlabel tracking structures to an initial state where no
379 * tlabels are outstanding and all tlabels are registered as good. This
380 * routine should be called every bus reset.
381 */
382 void
hci1394_tlabel_reset(hci1394_tlabel_handle_t tlabel_handle)383 hci1394_tlabel_reset(hci1394_tlabel_handle_t tlabel_handle)
384 {
385 int index;
386 int index2;
387
388
389 ASSERT(tlabel_handle != NULL);
390
391 mutex_enter(&tlabel_handle->tb_mutex);
392
393 /* Bus reset optimization. handle broadcast writes separately */
394 if (tlabel_handle->tb_bcast_sent == B_TRUE) {
395 tlabel_handle->tb_free[IEEE1394_BROADCAST_NODEID] =
396 (uint64_t)0xFFFFFFFFFFFFFFFF;
397 tlabel_handle->tb_bad[IEEE1394_BROADCAST_NODEID] =
398 (uint64_t)0;
399 tlabel_handle->tb_bad_timestamp[IEEE1394_BROADCAST_NODEID] =
400 (hrtime_t)0;
401 tlabel_handle->tb_last[IEEE1394_BROADCAST_NODEID] = 0;
402 for (index2 = 0; index2 < TLABEL_RANGE; index2++) {
403 tlabel_handle->tb_lookup[IEEE1394_BROADCAST_NODEID
404 ][index2] = NULL;
405 }
406 }
407
408 /*
409 * Mark all tlabels as free. No bad tlabels. Start the first tlabel
410 * alloc at 0. Cleanout the lookup table. An optimization to only do
411 * this up to the max node we have seen on the bus has been added.
412 */
413 for (index = 0; index <= tlabel_handle->tb_max_node; index++) {
414 tlabel_handle->tb_free[index] = (uint64_t)0xFFFFFFFFFFFFFFFF;
415 tlabel_handle->tb_bad[index] = (uint64_t)0;
416 tlabel_handle->tb_bad_timestamp[index] = (hrtime_t)0;
417 tlabel_handle->tb_last[index] = 0;
418 for (index2 = 0; index2 < TLABEL_RANGE; index2++) {
419 tlabel_handle->tb_lookup[index][index2] = NULL;
420 }
421 }
422
423 tlabel_handle->tb_max_node = 0;
424 tlabel_handle->tb_bcast_sent = B_FALSE;
425
426 mutex_exit(&tlabel_handle->tb_mutex);
427 }
428
429
430 /*
431 * hci1394_tlabel_set_reclaim_time()
432 * This function should be called if a change to the reclaim_time is
433 * required after the initial call to init(). It is not necessary to call
434 * this function if the reclaim time never changes.
435 *
436 * Currently, bad tlabels are reclaimed in tlabel_alloc().
437 * It looks like the following for a given node:
438 *
439 * if bad tlabels exist
440 * if ((current time + reclaim time) >= last bad tlabel time)
441 * free all bad tlabels.
442 */
443 void
hci1394_tlabel_set_reclaim_time(hci1394_tlabel_handle_t tlabel_handle,hrtime_t reclaim_time_nS)444 hci1394_tlabel_set_reclaim_time(hci1394_tlabel_handle_t tlabel_handle,
445 hrtime_t reclaim_time_nS)
446 {
447 ASSERT(tlabel_handle != NULL);
448
449 /*
450 * We do not need to lock the tlabel structure in this because we are
451 * doing a single write to reclaim_time. If this changes in the future,
452 * we may need to add calls to lock() and unlock().
453 */
454 tlabel_handle->tb_reclaim_time = reclaim_time_nS;
455 }
456