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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27 /*
28 * av1394 configuration ROM
29 */
30 #include <sys/file.h>
31 #include <sys/stream.h>
32 #include <sys/strsun.h>
33 #include <sys/1394/targets/av1394/av1394_impl.h>
34
35 /* configROM parsing */
36 static int av1394_cfgrom_parse_rom(av1394_inst_t *);
37 static void av1394_cfgrom_unparse_rom(av1394_inst_t *);
38 static int av1394_cfgrom_parse_dir(av1394_inst_t *, cmd1394_cmd_t *,
39 av1394_cfgrom_parse_arg_t *);
40 static void av1394_cfgrom_add_text_leaf(av1394_inst_t *,
41 av1394_cfgrom_parsed_dir_t *, uint64_t, uint32_t);
42 static int av1394_cfgrom_read_leaf(av1394_inst_t *, uint64_t, mblk_t **);
43 static void av1394_cfgrom_grow_parsed_dir(av1394_cfgrom_parsed_dir_t *,
44 int);
45
46 /* routines involving bus transactions */
47 static int av1394_cfgrom_rq(av1394_inst_t *, cmd1394_cmd_t *,
48 uint64_t, uint32_t *);
49
50 /* the following macros emulate throwing an exception when read fails */
51 #define AV1394_CFGROM_RQ(avp, cmd, addr, valp) \
52 if ((ret = av1394_cfgrom_rq(avp, cmd, addr, valp)) != 0) { \
53 goto catch; \
54 }
55
56 #define AV1394_TNF_ENTER(func) \
57 TNF_PROBE_0_DEBUG(func##_enter, AV1394_TNF_ASYNC_STACK, "");
58
59 #define AV1394_TNF_EXIT(func) \
60 TNF_PROBE_0_DEBUG(func##_exit, AV1394_TNF_ASYNC_STACK, "");
61
62 int
av1394_cfgrom_init(av1394_inst_t * avp)63 av1394_cfgrom_init(av1394_inst_t *avp)
64 {
65 av1394_cfgrom_t *crp = &avp->av_a.a_cfgrom;
66 ddi_iblock_cookie_t ibc = avp->av_attachinfo.iblock_cookie;
67
68 AV1394_TNF_ENTER(av1394_cfgrom_init);
69
70 rw_init(&crp->cr_rwlock, NULL, RW_DRIVER, ibc);
71
72 AV1394_TNF_EXIT(av1394_cfgrom_init);
73 return (DDI_SUCCESS);
74 }
75
76 void
av1394_cfgrom_fini(av1394_inst_t * avp)77 av1394_cfgrom_fini(av1394_inst_t *avp)
78 {
79 av1394_cfgrom_t *crp = &avp->av_a.a_cfgrom;
80
81 AV1394_TNF_ENTER(av1394_cfgrom_fini);
82
83 rw_destroy(&crp->cr_rwlock);
84
85 AV1394_TNF_EXIT(av1394_cfgrom_fini);
86 }
87
88 void
av1394_cfgrom_close(av1394_inst_t * avp)89 av1394_cfgrom_close(av1394_inst_t *avp)
90 {
91 av1394_cfgrom_t *crp = &avp->av_a.a_cfgrom;
92
93 AV1394_TNF_ENTER(av1394_cfgrom_close);
94
95 rw_enter(&crp->cr_rwlock, RW_WRITER);
96 if (crp->cr_parsed) {
97 av1394_cfgrom_unparse_rom(avp);
98 }
99 rw_exit(&crp->cr_rwlock);
100
101 AV1394_TNF_EXIT(av1394_cfgrom_close);
102 }
103
104 int
av1394_ioctl_node_get_bus_name(av1394_inst_t * avp,void * arg,int mode)105 av1394_ioctl_node_get_bus_name(av1394_inst_t *avp, void *arg, int mode)
106 {
107 cmd1394_cmd_t *cmd;
108 uint32_t val;
109 int err;
110 int ret = 0;
111
112 AV1394_TNF_ENTER(av1394_ioctl_node_get_bus_name);
113
114 err = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd);
115 if (err != DDI_SUCCESS) {
116 AV1394_TNF_EXIT(av1394_ioctl_node_get_bus_name);
117 return (ENOMEM);
118 }
119
120 ret = av1394_cfgrom_rq(avp, cmd, AV1394_CFGROM_BUS_NAME_ADDR, &val);
121 if (ret == 0) {
122 if (ddi_copyout(&val, arg, sizeof (uint32_t), mode) != 0) {
123 ret = EFAULT;
124 }
125 }
126
127 err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd);
128 ASSERT(err == DDI_SUCCESS);
129
130 AV1394_TNF_EXIT(av1394_ioctl_node_get_bus_name);
131 return (ret);
132 }
133
134 int
av1394_ioctl_node_get_uid(av1394_inst_t * avp,void * arg,int mode)135 av1394_ioctl_node_get_uid(av1394_inst_t *avp, void *arg, int mode)
136 {
137 cmd1394_cmd_t *cmd;
138 uint64_t eui64;
139 uint32_t hi, lo;
140 int err;
141 int ret = 0;
142
143 AV1394_TNF_ENTER(av1394_ioctl_node_get_uid);
144
145 err = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd);
146 if (err != DDI_SUCCESS) {
147 AV1394_TNF_EXIT(av1394_ioctl_node_get_uid);
148 return (ENOMEM);
149 }
150
151 AV1394_CFGROM_RQ(avp, cmd, AV1394_CFGROM_EUI64_HI_ADDR, &hi);
152 AV1394_CFGROM_RQ(avp, cmd, AV1394_CFGROM_EUI64_LO_ADDR, &lo);
153
154 eui64 = ((uint64_t)hi << 32) | lo;
155 if (ddi_copyout(&eui64, arg, sizeof (uint64_t), mode) != 0) {
156 ret = EFAULT;
157 }
158
159 catch:
160 err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd);
161 ASSERT(err == DDI_SUCCESS);
162
163 AV1394_TNF_EXIT(av1394_ioctl_node_get_uid);
164 return (ret);
165 }
166
167 int
av1394_ioctl_node_get_text_leaf(av1394_inst_t * avp,void * arg,int mode)168 av1394_ioctl_node_get_text_leaf(av1394_inst_t *avp, void *arg, int mode)
169 {
170 av1394_cfgrom_t *crp = &avp->av_a.a_cfgrom;
171 iec61883_node_text_leaf_t tl;
172 #ifdef _MULTI_DATAMODEL
173 iec61883_node_text_leaf32_t tl32;
174 #endif
175 int n; /* text leaf number requested */
176 int parent; /* leaf parent */
177 mblk_t *bp = NULL;
178 av1394_cfgrom_parsed_dir_t *pd;
179 int leaf_len;
180 uint32_t spec, lang_id, desc_entry;
181 int ret = 0;
182
183 AV1394_TNF_ENTER(av1394_ioctl_node_get_text_leaf);
184
185 /* copyin arguments */
186 #ifdef _MULTI_DATAMODEL
187 if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
188 if (ddi_copyin(arg, &tl32, sizeof (tl32), mode) != 0) {
189 AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf);
190 return (EFAULT);
191 }
192 n = tl32.tl_num;
193 parent = tl32.tl_parent;
194 } else {
195 #endif
196 if (ddi_copyin(arg, &tl, sizeof (tl), mode) != 0) {
197 AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf);
198 return (EFAULT);
199 }
200 n = tl.tl_num;
201 parent = tl.tl_parent;
202 #ifdef _MULTI_DATAMODEL
203 }
204 #endif
205 /* verify arguments */
206 if (((parent != IEC61883_ROM_ROOT) && (parent != IEC61883_ROM_UNIT)) ||
207 (n < 0)) {
208 return (EINVAL);
209 }
210
211 /* parse ConfigROM if not already */
212 rw_enter(&crp->cr_rwlock, RW_WRITER);
213 if (!crp->cr_parsed) {
214 ret = av1394_cfgrom_parse_rom(avp);
215 if (ret != 0) {
216 rw_exit(&crp->cr_rwlock);
217 AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf);
218 return (ret);
219 }
220 }
221 rw_downgrade(&crp->cr_rwlock);
222
223 /* get parsed leaf info */
224 if (parent == IEC61883_ROM_ROOT) {
225 pd = &crp->cr_root_dir;
226 } else {
227 pd = &crp->cr_unit_dir;
228 }
229
230 if (n < pd->pd_tl_next) {
231 /* read the leaf */
232 ret = av1394_cfgrom_read_leaf(avp, pd->pd_tl[n].tl_addr, &bp);
233 if (ret != 0) {
234 rw_exit(&crp->cr_rwlock);
235 AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf);
236 return (ret);
237 }
238 leaf_len = MBLKL(bp) / 4 - 2;
239 ASSERT(leaf_len > 0);
240 spec = *(uint32_t *)bp->b_rptr;
241 bp->b_rptr += 4;
242 lang_id = *(uint32_t *)bp->b_rptr;
243 bp->b_rptr += 4;
244 desc_entry = pd->pd_tl[n].tl_desc_entry;
245 } else {
246 /* return success anyway, but with tl_cnt < tl_num */
247 spec = lang_id = desc_entry = 0;
248 leaf_len = 0;
249 }
250
251 /* copyout the results */
252 #ifdef _MULTI_DATAMODEL
253 if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
254 tl32.tl_cnt = pd->pd_tl_next;
255 tl32.tl_desc_entry = desc_entry;
256 tl32.tl_rlen = leaf_len;
257 tl32.tl_spec = spec;
258 tl32.tl_lang_id = lang_id;
259 if (ddi_copyout(&tl32, arg, sizeof (tl32), mode) != 0) {
260 ret = EFAULT;
261 } else if (bp && ddi_copyout(bp->b_rptr,
262 (void *)(uintptr_t)tl32.tl_data,
263 4 * min(tl32.tl_len, tl32.tl_rlen), mode) != 0) {
264 ret = EFAULT;
265 }
266 } else {
267 #endif
268 tl.tl_cnt = pd->pd_tl_next;
269 tl.tl_desc_entry = desc_entry;
270 tl.tl_rlen = leaf_len;
271 tl.tl_spec = spec;
272 tl.tl_lang_id = lang_id;
273 if (ddi_copyout(&tl, arg, sizeof (tl), mode) != 0) {
274 ret = EFAULT;
275 } else if (bp && ddi_copyout(bp->b_rptr, tl.tl_data,
276 4 * min(tl.tl_len, tl.tl_rlen), mode) != 0) {
277 ret = EFAULT;
278 }
279 #ifdef _MULTI_DATAMODEL
280 }
281 #endif
282 rw_exit(&crp->cr_rwlock);
283
284 freemsg(bp);
285
286 AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf);
287 return (ret);
288 }
289
290
291 /*
292 *
293 * --- configROM parsing
294 *
295 * Parse entire configROM. Only extract information that interests us.
296 * ConfigROM integrity checks are only made to ensure correct parsing.
297 */
298 static int
av1394_cfgrom_parse_rom(av1394_inst_t * avp)299 av1394_cfgrom_parse_rom(av1394_inst_t *avp)
300 {
301 av1394_cfgrom_t *crp = &avp->av_a.a_cfgrom;
302 cmd1394_cmd_t *cmd;
303 uint32_t val;
304 uint64_t root_addr; /* root dir address */
305 uint16_t root_len; /* root dir length */
306 av1394_cfgrom_parse_arg_t pa;
307 int err;
308 int ret;
309
310 ASSERT(crp->cr_parsed == B_FALSE);
311
312 err = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd);
313 if (err != DDI_SUCCESS) {
314 return (ENOMEM);
315 }
316
317 /* skip info_len quadlets to get root dir address and length */
318 AV1394_CFGROM_RQ(avp, cmd, AV1394_CFGROM_INFO_LEN_ADDR, &val);
319 val = AV_SWAP32(val);
320 root_addr = IEEE1394_CONFIG_ROM_ADDR + 4 + (val >> 24) * 4;
321 AV1394_CFGROM_RQ(avp, cmd, root_addr, &val);
322 val = AV_SWAP32(val);
323 root_len = IEEE1212_DIR_LEN(val);
324
325 /* parse root dir and everything underneath */
326 pa.pa_depth = 0;
327 pa.pa_desc_entry = 0;
328 pa.pa_parent_k = 0;
329 pa.pa_addr = root_addr + 4;
330 pa.pa_len = root_len;
331 pa.pa_dir = &crp->cr_root_dir;
332
333 ret = av1394_cfgrom_parse_dir(avp, cmd, &pa);
334
335 catch:
336 if (ret == 0) {
337 crp->cr_parsed = B_TRUE;
338 } else {
339 av1394_cfgrom_unparse_rom(avp);
340 }
341 err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd);
342 ASSERT(err == DDI_SUCCESS);
343
344 return (ret);
345 }
346
347 /*
348 * parse a directory
349 */
350 static int
av1394_cfgrom_parse_dir(av1394_inst_t * avp,cmd1394_cmd_t * cmd,av1394_cfgrom_parse_arg_t * pa)351 av1394_cfgrom_parse_dir(av1394_inst_t *avp, cmd1394_cmd_t *cmd,
352 av1394_cfgrom_parse_arg_t *pa)
353 {
354 av1394_cfgrom_t *crp = &avp->av_a.a_cfgrom;
355 int i;
356 uint64_t entry_addr;
357 uint32_t entry;
358 uint64_t leaf_addr;
359 uint64_t dir_addr;
360 uint16_t dir_len;
361 uint8_t t, k;
362 uint16_t v;
363 uint32_t val;
364 av1394_cfgrom_parse_arg_t this_pa;
365 int ret = 0;
366
367 /* safeguard against deep recursion */
368 if (pa->pa_depth > AV1394_CFGROM_PARSE_MAX_DEPTH) {
369 return (ENOMEM);
370 }
371
372 /* initialize parse arguments */
373 this_pa.pa_depth = pa->pa_depth + 1;
374 this_pa.pa_desc_entry = pa->pa_desc_entry;
375
376 /* walk dir entries */
377 entry_addr = pa->pa_addr;
378 for (i = 0; i < pa->pa_len; i++) {
379 AV1394_CFGROM_RQ(avp, cmd, entry_addr, &entry);
380 entry = AV_SWAP32(entry);
381
382 CFGROM_TYPE_KEY_VALUE(entry, t, k, v);
383 if ((t == IEEE1212_LEAF_TYPE) &&
384 (k == IEEE1212_TEXTUAL_DESCRIPTOR)) {
385 /* save this leaf */
386 leaf_addr = entry_addr + 4 * v;
387 av1394_cfgrom_add_text_leaf(avp, pa->pa_dir,
388 leaf_addr, this_pa.pa_desc_entry);
389 } else if (t == IEEE1212_DIRECTORY_TYPE) {
390 dir_addr = entry_addr + 4 * v;
391 AV1394_CFGROM_RQ(avp, cmd, dir_addr, &val);
392 val = AV_SWAP32(val);
393 dir_len = IEEE1212_DIR_LEN(val);
394
395 /* parse this dir */
396 this_pa.pa_parent_k = k;
397 this_pa.pa_addr = dir_addr + 4;
398 this_pa.pa_len = dir_len;
399 /* leaves will be added to either root or unit array */
400 if (k == IEEE1212_UNIT_DIRECTORY) {
401 this_pa.pa_dir = &crp->cr_unit_dir;
402 } else {
403 this_pa.pa_dir = pa->pa_dir;
404 }
405
406 ret = av1394_cfgrom_parse_dir(avp, cmd, &this_pa);
407 if (ret != 0) {
408 goto catch;
409 }
410 }
411
412 /*
413 * if we're walking Textual_Descriptor directory,
414 * the described entry is the one preceding directory's entry,
415 * so we need to preserve what was passed in pa->pa_desc_entry
416 */
417 if (pa->pa_parent_k != IEEE1212_TEXTUAL_DESCRIPTOR) {
418 this_pa.pa_desc_entry = entry;
419 }
420 entry_addr += 4;
421 }
422
423 catch:
424 return (ret);
425 }
426
427 /*ARGSUSED*/
428 static void
av1394_cfgrom_add_text_leaf(av1394_inst_t * avp,av1394_cfgrom_parsed_dir_t * pd,uint64_t addr,uint32_t desc_entry)429 av1394_cfgrom_add_text_leaf(av1394_inst_t *avp, av1394_cfgrom_parsed_dir_t *pd,
430 uint64_t addr, uint32_t desc_entry)
431 {
432 /* grow array of needed */
433 if (pd->pd_tl_next >= pd->pd_tl_size) {
434 av1394_cfgrom_grow_parsed_dir(pd, 2);
435 }
436 pd->pd_tl[pd->pd_tl_next].tl_addr = addr;
437 pd->pd_tl[pd->pd_tl_next].tl_desc_entry = desc_entry;
438 pd->pd_tl_next++;
439 }
440
441 /*
442 * this routine cleans up after av1394_cfgrom_parse()
443 */
444 static void
av1394_cfgrom_unparse_rom(av1394_inst_t * avp)445 av1394_cfgrom_unparse_rom(av1394_inst_t *avp)
446 {
447 av1394_cfgrom_t *crp = &avp->av_a.a_cfgrom;
448 av1394_cfgrom_parsed_dir_t *pd;
449
450 pd = &crp->cr_root_dir;
451 if (pd->pd_tl) {
452 kmem_free(pd->pd_tl, pd->pd_tl_size * sizeof (*pd->pd_tl));
453 bzero(pd, sizeof (*pd));
454 }
455 pd = &crp->cr_unit_dir;
456 if (pd->pd_tl) {
457 kmem_free(pd->pd_tl, pd->pd_tl_size * sizeof (*pd->pd_tl));
458 bzero(pd, sizeof (*pd));
459 }
460 crp->cr_parsed = B_FALSE;
461 }
462
463 /*
464 * grow parsed dir leaf array by 'cnt' entries
465 */
466 static void
av1394_cfgrom_grow_parsed_dir(av1394_cfgrom_parsed_dir_t * pd,int cnt)467 av1394_cfgrom_grow_parsed_dir(av1394_cfgrom_parsed_dir_t *pd, int cnt)
468 {
469 int new_size;
470 void *new_tl;
471
472 ASSERT(cnt > 0);
473
474 new_size = (pd->pd_tl_size + cnt) * sizeof (av1394_cfgrom_text_leaf_t);
475 new_tl = kmem_zalloc(new_size, KM_SLEEP);
476 if (pd->pd_tl_size > 0) {
477 bcopy(pd->pd_tl, new_tl, pd->pd_tl_size * sizeof (*pd->pd_tl));
478 kmem_free(pd->pd_tl, pd->pd_tl_size * sizeof (*pd->pd_tl));
479 }
480 pd->pd_tl = new_tl;
481 pd->pd_tl_size += cnt;
482 }
483
484 static int
av1394_cfgrom_read_leaf(av1394_inst_t * avp,uint64_t leaf_addr,mblk_t ** bpp)485 av1394_cfgrom_read_leaf(av1394_inst_t *avp, uint64_t leaf_addr, mblk_t **bpp)
486 {
487 cmd1394_cmd_t *cmd;
488 uint64_t addr;
489 uint32_t val;
490 int leaf_len; /* leaf length in quadlets */
491 mblk_t *bp = NULL;
492 int i;
493 int err;
494 int ret = 0;
495
496 err = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd);
497 if (err != DDI_SUCCESS) {
498 AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf);
499 return (ENOMEM);
500 }
501
502 /* read leaf length */
503 AV1394_CFGROM_RQ(avp, cmd, leaf_addr, &val);
504 val = AV_SWAP32(val);
505 leaf_len = IEEE1212_DIR_LEN(val);
506
507 if (leaf_len < 3) {
508 ret = EIO;
509 goto catch;
510 }
511
512 if ((bp = allocb(leaf_len * 4, BPRI_HI)) == NULL) {
513 TNF_PROBE_0(aav1394_cfgrom_read_leaf_error_allocb,
514 AV1394_TNF_ASYNC_ERROR, "");
515 return (ENOMEM);
516 }
517
518 /* read leaf value */
519 addr = leaf_addr + 4;
520 for (i = 0; i < leaf_len; i++) {
521 AV1394_CFGROM_RQ(avp, cmd, addr, (uint32_t *)bp->b_wptr);
522 bp->b_wptr += 4;
523 addr += 4;
524 }
525
526 catch:
527 if (ret == 0) {
528 *bpp = bp;
529 } else {
530 freemsg(bp);
531 }
532 err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd);
533 ASSERT(err == DDI_SUCCESS);
534
535 return (ret);
536 }
537
538 /*
539 *
540 * --- routines involving bus transactions
541 *
542 */
543 static int
av1394_cfgrom_rq(av1394_inst_t * avp,cmd1394_cmd_t * cmd,uint64_t addr,uint32_t * rval)544 av1394_cfgrom_rq(av1394_inst_t *avp, cmd1394_cmd_t *cmd, uint64_t addr,
545 uint32_t *rval)
546 {
547 int err;
548
549 cmd->cmd_type = CMD1394_ASYNCH_RD_QUAD;
550 cmd->cmd_options = CMD1394_BLOCKING;
551 cmd->cmd_addr = addr;
552
553 err = t1394_read(avp->av_t1394_hdl, cmd);
554 if ((err == DDI_SUCCESS) && (cmd->cmd_result == CMD1394_CMDSUCCESS)) {
555 *rval = cmd->cmd_u.q.quadlet_data;
556 return (0);
557 } else {
558 TNF_PROBE_2(av1394_cfgrom_rq_error,
559 AV1394_TNF_ASYNC_ERROR, "", tnf_int, err, err,
560 tnf_int, result, cmd->cmd_result);
561 return (EIO);
562 }
563 }
564