File: /usr/src/linux/arch/ia64/sn/io/hcl.c
1 /* $Id$
2 *
3 * This file is subject to the terms and conditions of the GNU General Public
4 * License. See the file "COPYING" in the main directory of this archive
5 * for more details.
6 *
7 * hcl - SGI's Hardware Graph compatibility layer.
8 *
9 * Copyright (C) 1992 - 1997, 2000 Silicon Graphics, Inc.
10 * Copyright (C) 2000 by Colin Ngam
11 */
12
13 #include <linux/types.h>
14 #include <linux/config.h>
15 #include <linux/slab.h>
16 #include <linux/ctype.h>
17 #include <linux/module.h>
18 #include <linux/init.h>
19 #include <asm/sn/sgi.h>
20 #include <linux/devfs_fs.h>
21 #include <linux/devfs_fs_kernel.h>
22 #include <asm/io.h>
23 #include <asm/sn/iograph.h>
24 #include <asm/sn/invent.h>
25 #include <asm/sn/hcl.h>
26 #include <asm/sn/labelcl.h>
27
28 #define HCL_NAME "SGI-HWGRAPH COMPATIBILITY DRIVER"
29 #define HCL_TEMP_NAME "HCL_TEMP_NAME_USED_FOR_HWGRAPH_VERTEX_CREATE"
30 #define HCL_TEMP_NAME_LEN 44
31 #define HCL_VERSION "1.0"
32 devfs_handle_t hwgraph_root = NULL;
33 devfs_handle_t linux_busnum = NULL;
34
35 /*
36 * Debug flag definition.
37 */
38 #define OPTION_NONE 0x00
39 #define HCL_DEBUG_NONE 0x00000
40 #define HCL_DEBUG_ALL 0x0ffff
41 #if defined(CONFIG_HCL_DEBUG)
42 static unsigned int hcl_debug_init __initdata = HCL_DEBUG_NONE;
43 #endif
44 static unsigned int hcl_debug = HCL_DEBUG_NONE;
45 #if defined(CONFIG_HCL_DEBUG) && !defined(MODULE)
46 static unsigned int boot_options = OPTION_NONE;
47 #endif
48
49 /*
50 * Some Global definitions.
51 */
52 spinlock_t hcl_spinlock;
53 devfs_handle_t hcl_handle = NULL;
54
55 invplace_t invplace_none = {
56 GRAPH_VERTEX_NONE,
57 GRAPH_VERTEX_PLACE_NONE,
58 NULL
59 };
60
61 /*
62 * HCL device driver.
63 * The purpose of this device driver is to provide a facility
64 * for User Level Apps e.g. hinv, ioconfig etc. an ioctl path
65 * to manipulate label entries without having to implement
66 * system call interfaces. This methodology will enable us to
67 * make this feature module loadable.
68 */
69 static int hcl_open(struct inode * inode, struct file * filp)
70 {
71 if (hcl_debug) {
72 printk("HCL: hcl_open called.\n");
73 }
74
75 return(0);
76
77 }
78
79 static int hcl_close(struct inode * inode, struct file * filp)
80 {
81
82 if (hcl_debug) {
83 printk("HCL: hcl_close called.\n");
84 }
85
86 return(0);
87
88 }
89
90 static int hcl_ioctl(struct inode * inode, struct file * file,
91 unsigned int cmd, unsigned long arg)
92 {
93
94 if (hcl_debug) {
95 printk("HCL: hcl_ioctl called.\n");
96 }
97
98 switch (cmd) {
99 default:
100 if (hcl_debug) {
101 printk("HCL: hcl_ioctl cmd = 0x%x\n", cmd);
102 }
103 }
104
105 return(0);
106
107 }
108
109 struct file_operations hcl_fops = {
110 (struct module *)0,
111 NULL, /* lseek - default */
112 NULL, /* read - general block-dev read */
113 NULL, /* write - general block-dev write */
114 NULL, /* readdir - bad */
115 NULL, /* poll */
116 hcl_ioctl, /* ioctl */
117 NULL, /* mmap */
118 hcl_open, /* open */
119 NULL, /* flush */
120 hcl_close, /* release */
121 NULL, /* fsync */
122 NULL, /* fasync */
123 NULL, /* lock */
124 NULL, /* readv */
125 NULL, /* writev */
126 };
127
128
129 /*
130 * init_hcl() - Boot time initialization. Ensure that it is called
131 * after devfs has been initialized.
132 *
133 * For now this routine is being called out of devfs/base.c. Actually
134 * Not a bad place to be ..
135 *
136 */
137 #ifdef MODULE
138 int init_module (void)
139 #else
140 int __init init_hcl(void)
141 #endif
142 {
143 extern void string_table_init(struct string_table *);
144 extern struct string_table label_string_table;
145 int rv = 0;
146
147 #if defined(CONFIG_HCL_DEBUG) && !defined(MODULE)
148 printk ("\n%s: v%s Colin Ngam (cngam@sgi.com)\n",
149 HCL_NAME, HCL_VERSION);
150
151 hcl_debug = hcl_debug_init;
152 printk ("%s: hcl_debug: 0x%0x\n", HCL_NAME, hcl_debug);
153 printk ("\n%s: boot_options: 0x%0x\n", HCL_NAME, boot_options);
154 #endif
155
156 spin_lock_init(&hcl_spinlock);
157
158 /*
159 * Create the hwgraph_root on devfs.
160 */
161 rv = hwgraph_path_add(NULL, "hw", &hwgraph_root);
162 if (rv)
163 printk ("WARNING: init_hcl: Failed to create hwgraph_root. Error = %d.\n", rv);
164
165 /*
166 * Create the hcl driver to support inventory entry manipulations.
167 * By default, it is expected that devfs is mounted on /dev.
168 *
169 */
170 hcl_handle = hwgraph_register(hwgraph_root, ".hcl",
171 0, DEVFS_FL_AUTO_DEVNUM,
172 0, 0,
173 S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0,
174 &hcl_fops, NULL);
175
176 if (hcl_handle == NULL) {
177 panic("HCL: Unable to create HCL Driver in init_hcl().\n");
178 return(0);
179 }
180
181 /*
182 * Initialize the HCL string table.
183 */
184 string_table_init(&label_string_table);
185
186 /*
187 * Create the directory that links Linux bus numbers to our Xwidget.
188 */
189 rv = hwgraph_path_add(hwgraph_root, "linux/busnum", &linux_busnum);
190 if (linux_busnum == NULL) {
191 panic("HCL: Unable to create hw/linux/busnum\n");
192 return(0);
193 }
194
195 return(0);
196
197 }
198
199
200 /*
201 * hcl_setup() - Process boot time parameters if given.
202 * "hcl="
203 * This routine gets called only if "hcl=" is given in the
204 * boot line and before init_hcl().
205 *
206 * We currently do not have any boot options .. when we do,
207 * functionalities can be added here.
208 *
209 */
210 static int __init hcl_setup(char *str)
211 {
212 while ( (*str != '\0') && !isspace (*str) )
213 {
214 #ifdef CONFIG_HCL_DEBUG
215 if (strncmp (str, "all", 3) == 0) {
216 hcl_debug_init |= HCL_DEBUG_ALL;
217 str += 3;
218 } else
219 return 0;
220 #endif
221 if (*str != ',') return 0;
222 ++str;
223 }
224
225 return 1;
226
227 }
228
229 __setup("hcl=", hcl_setup);
230
231
232 /*
233 * Set device specific "fast information".
234 *
235 */
236 void
237 hwgraph_fastinfo_set(devfs_handle_t de, arbitrary_info_t fastinfo)
238 {
239
240 if (hcl_debug) {
241 printk("HCL: hwgraph_fastinfo_set handle 0x%p fastinfo %ld\n",
242 de, fastinfo);
243 }
244
245 labelcl_info_replace_IDX(de, HWGRAPH_FASTINFO, fastinfo, NULL);
246
247 }
248
249
250 /*
251 * Get device specific "fast information".
252 *
253 */
254 arbitrary_info_t
255 hwgraph_fastinfo_get(devfs_handle_t de)
256 {
257 arbitrary_info_t fastinfo;
258 int rv;
259
260 if (!de) {
261 printk(KERN_WARNING "HCL: hwgraph_fastinfo_get handle given is NULL.\n");
262 return(-1);
263 }
264
265 rv = labelcl_info_get_IDX(de, HWGRAPH_FASTINFO, &fastinfo);
266 if (rv == 0)
267 return(fastinfo);
268
269 return(0);
270 }
271
272
273 /*
274 * hwgraph_connectpt_set - Sets the connect point handle in de to the
275 * given connect_de handle. By default, the connect point of the
276 * devfs node is the parent. This effectively changes this assumption.
277 */
278 int
279 hwgraph_connectpt_set(devfs_handle_t de, devfs_handle_t connect_de)
280 {
281 int rv;
282
283 if (!de)
284 return(-1);
285
286 rv = labelcl_info_connectpt_set(de, connect_de);
287
288 return(rv);
289 }
290
291
292 /*
293 * hwgraph_connectpt_get: Returns the entry's connect point in the devfs
294 * tree.
295 */
296 devfs_handle_t
297 hwgraph_connectpt_get(devfs_handle_t de)
298 {
299 int rv;
300 arbitrary_info_t info;
301 devfs_handle_t connect;
302
303 rv = labelcl_info_get_IDX(de, HWGRAPH_CONNECTPT, &info);
304 if (rv != 0) {
305 return(NULL);
306 }
307
308 connect = (devfs_handle_t)info;
309 return(connect);
310
311 }
312
313
314 /*
315 * hwgraph_mk_dir - Creates a directory entry with devfs.
316 * Note that a directory entry in devfs can have children
317 * but it cannot be a char|block special file.
318 */
319 devfs_handle_t
320 hwgraph_mk_dir(devfs_handle_t de, const char *name,
321 unsigned int namelen, void *info)
322 {
323
324 int rv;
325 labelcl_info_t *labelcl_info = NULL;
326 devfs_handle_t new_devfs_handle = NULL;
327 devfs_handle_t parent = NULL;
328
329 /*
330 * Create the device info structure for hwgraph compatiblity support.
331 */
332 labelcl_info = labelcl_info_create();
333 if (!labelcl_info)
334 return(NULL);
335
336 /*
337 * Create a devfs entry.
338 */
339 new_devfs_handle = devfs_mk_dir(de, name, (void *)labelcl_info);
340 if (!new_devfs_handle) {
341 labelcl_info_destroy(labelcl_info);
342 return(NULL);
343 }
344
345 /*
346 * Get the parent handle.
347 */
348 parent = devfs_get_parent (new_devfs_handle);
349
350 /*
351 * To provide the same semantics as the hwgraph, set the connect point.
352 */
353 rv = hwgraph_connectpt_set(new_devfs_handle, parent);
354 if (!rv) {
355 /*
356 * We need to clean up!
357 */
358 }
359
360 /*
361 * If the caller provides a private data pointer, save it in the
362 * labelcl info structure(fastinfo). This can be retrieved via
363 * hwgraph_fastinfo_get()
364 */
365 if (info)
366 hwgraph_fastinfo_set(new_devfs_handle, (arbitrary_info_t)info);
367
368 return(new_devfs_handle);
369
370 }
371
372 /*
373 * hwgraph_vertex_create - Create a vertex by giving it a temp name.
374 */
375
376 /*
377 * hwgraph_path_add - Create a directory node with the given path starting
378 * from the given devfs_handle_t.
379 */
380 extern char * dev_to_name(devfs_handle_t, char *, uint);
381 int
382 hwgraph_path_add(devfs_handle_t fromv,
383 char *path,
384 devfs_handle_t *new_de)
385 {
386
387 unsigned int namelen = strlen(path);
388 int rv;
389
390 /*
391 * We need to handle the case when fromv is NULL ..
392 * in this case we need to create the path from the
393 * hwgraph root!
394 */
395 if (fromv == NULL)
396 fromv = hwgraph_root;
397
398 /*
399 * check the entry doesn't already exist, if it does
400 * then we simply want new_de to point to it (otherwise
401 * we'll overwrite the existing labelcl_info struct)
402 */
403 rv = hwgraph_edge_get(fromv, path, new_de);
404 if (rv) { /* couldn't find entry so we create it */
405 *new_de = hwgraph_mk_dir(fromv, path, namelen, NULL);
406 if (new_de == NULL)
407 return(-1);
408 else
409 return(0);
410 }
411 else
412 return(0);
413
414 }
415
416 /*
417 * hwgraph_register - Creates a file entry with devfs.
418 * Note that a file entry cannot have children .. it is like a
419 * char|block special vertex in hwgraph.
420 */
421 devfs_handle_t
422 hwgraph_register(devfs_handle_t de, const char *name,
423 unsigned int namelen, unsigned int flags,
424 unsigned int major, unsigned int minor,
425 umode_t mode, uid_t uid, gid_t gid,
426 struct file_operations *fops,
427 void *info)
428 {
429
430 int rv;
431 void *labelcl_info = NULL;
432 devfs_handle_t new_devfs_handle = NULL;
433 devfs_handle_t parent = NULL;
434
435 /*
436 * Create the labelcl info structure for hwgraph compatiblity support.
437 */
438 labelcl_info = labelcl_info_create();
439 if (!labelcl_info)
440 return(NULL);
441
442 /*
443 * Create a devfs entry.
444 */
445 new_devfs_handle = devfs_register(de, name, flags, major,
446 minor, mode, fops, labelcl_info);
447 if (!new_devfs_handle) {
448 labelcl_info_destroy((labelcl_info_t *)labelcl_info);
449 return(NULL);
450 }
451
452 /*
453 * Get the parent handle.
454 */
455 if (de == NULL)
456 parent = devfs_get_parent (new_devfs_handle);
457 else
458 parent = de;
459
460 /*
461 * To provide the same semantics as the hwgraph, set the connect point.
462 */
463 rv = hwgraph_connectpt_set(new_devfs_handle, parent);
464 if (rv) {
465 /*
466 * We need to clean up!
467 */
468 printk(KERN_WARNING "HCL: Unable to set the connect point to it's parent 0x%p\n",
469 new_devfs_handle);
470 }
471
472 /*
473 * If the caller provides a private data pointer, save it in the
474 * labelcl info structure(fastinfo). This can be retrieved via
475 * hwgraph_fastinfo_get()
476 */
477 if (info)
478 hwgraph_fastinfo_set(new_devfs_handle, (arbitrary_info_t)info);
479
480 return(new_devfs_handle);
481
482 }
483
484
485 /*
486 * hwgraph_mk_symlink - Create a symbolic link.
487 */
488 int
489 hwgraph_mk_symlink(devfs_handle_t de, const char *name, unsigned int namelen,
490 unsigned int flags, const char *link, unsigned int linklen,
491 devfs_handle_t *handle, void *info)
492 {
493
494 void *labelcl_info = NULL;
495 int status = 0;
496 devfs_handle_t new_devfs_handle = NULL;
497
498 /*
499 * Create the labelcl info structure for hwgraph compatiblity support.
500 */
501 labelcl_info = labelcl_info_create();
502 if (!labelcl_info)
503 return(-1);
504
505 /*
506 * Create a symbolic link devfs entry.
507 */
508 status = devfs_mk_symlink(de, name, flags, link,
509 &new_devfs_handle, labelcl_info);
510 if ( (!new_devfs_handle) || (!status) ){
511 labelcl_info_destroy((labelcl_info_t *)labelcl_info);
512 return(-1);
513 }
514
515 /*
516 * If the caller provides a private data pointer, save it in the
517 * labelcl info structure(fastinfo). This can be retrieved via
518 * hwgraph_fastinfo_get()
519 */
520 if (info)
521 hwgraph_fastinfo_set(new_devfs_handle, (arbitrary_info_t)info);
522
523 *handle = new_devfs_handle;
524 return(0);
525
526 }
527
528 /*
529 * hwgraph_vertex_get_next - this routine returns the next sibbling for the
530 * device entry given in de. If there are no more sibbling, NULL
531 * is returned in next_sibbling.
532 *
533 * Currently we do not have any protection against de being deleted
534 * while it's handle is being held.
535 */
536 int
537 hwgraph_vertex_get_next(devfs_handle_t *next_sibbling, devfs_handle_t *de)
538 {
539 *next_sibbling = devfs_get_next_sibling (*de);
540
541 if (*next_sibbling != NULL)
542 *de = *next_sibbling;
543 return (0);
544 }
545
546
547 /*
548 * hwgraph_vertex_destroy - Destroy the devfs entry
549 */
550 int
551 hwgraph_vertex_destroy(devfs_handle_t de)
552 {
553
554 void *labelcl_info = NULL;
555
556 labelcl_info = devfs_get_info(de);
557 devfs_unregister(de);
558
559 if (labelcl_info)
560 labelcl_info_destroy((labelcl_info_t *)labelcl_info);
561
562 return(0);
563 }
564
565 /*
566 ** See if a vertex has an outgoing edge with a specified name.
567 ** Vertices in the hwgraph *implicitly* contain these edges:
568 ** "." refers to "current vertex"
569 ** ".." refers to "connect point vertex"
570 ** "char" refers to current vertex (character device access)
571 ** "block" refers to current vertex (block device access)
572 */
573
574 /*
575 * hwgraph_edge_add - This routines has changed from the original conext.
576 * All it does now is to create a symbolic link from "from" to "to".
577 */
578 /* ARGSUSED */
579 int
580 hwgraph_edge_add(devfs_handle_t from, devfs_handle_t to, char *name)
581 {
582
583 char *path;
584 char *s1;
585 char *index;
586 int name_start;
587 devfs_handle_t handle = NULL;
588 int rv;
589 int i, count;
590
591 path = kmalloc(1024, GFP_KERNEL);
592 memset(path, 0x0, 1024);
593 name_start = devfs_generate_path (from, path, 1024);
594 s1 = &path[name_start];
595 count = 0;
596 while (1) {
597 index = strstr (s1, "/");
598 if (index) {
599 count++;
600 s1 = ++index;
601 } else {
602 count++;
603 break;
604 }
605 }
606
607 memset(path, 0x0, 1024);
608 name_start = devfs_generate_path (to, path, 1024);
609
610 for (i = 0; i < count; i++) {
611 strcat(path,"../");
612 }
613
614 strcat(path, &path[name_start]);
615
616 /*
617 * Otherwise, just create a symlink to the vertex.
618 * In this case the vertex was previous created with a REAL pathname.
619 */
620 rv = devfs_mk_symlink (from, (const char *)name,
621 DEVFS_FL_DEFAULT, path,
622 &handle, NULL);
623
624 name_start = devfs_generate_path (handle, path, 1024);
625 return(rv);
626
627
628 }
629 /* ARGSUSED */
630 int
631 hwgraph_edge_get(devfs_handle_t from, char *name, devfs_handle_t *toptr)
632 {
633
634 int namelen = 0;
635 devfs_handle_t target_handle = NULL;
636
637 if (name == NULL)
638 return(-1);
639
640 if (toptr == NULL)
641 return(-1);
642
643 /*
644 * If the name is "." just return the current devfs entry handle.
645 */
646 if (!strcmp(name, HWGRAPH_EDGELBL_DOT)) {
647 if (toptr) {
648 *toptr = from;
649 }
650 } else if (!strcmp(name, HWGRAPH_EDGELBL_DOTDOT)) {
651 /*
652 * Hmmm .. should we return the connect point or parent ..
653 * see in hwgraph, the concept of parent is the connectpt!
654 *
655 * Maybe we should see whether the connectpt is set .. if
656 * not just return the parent!
657 */
658 target_handle = hwgraph_connectpt_get(from);
659 if (target_handle) {
660 /*
661 * Just return the connect point.
662 */
663 *toptr = target_handle;
664 return(0);
665 }
666 target_handle = devfs_get_parent(from);
667 *toptr = target_handle;
668
669 } else {
670 /*
671 * Call devfs to get the devfs entry.
672 */
673 namelen = (int) strlen(name);
674 target_handle = devfs_find_handle (from, name, 0, 0,
675 0, 1); /* Yes traverse symbolic links */
676 if (target_handle == NULL)
677 return(-1);
678 else
679 *toptr = target_handle;
680 }
681
682 return(0);
683 }
684
685
686 /*
687 * hwgraph_edge_get_next - Retrieves the next sibbling given the current
688 * entry number "placeptr".
689 *
690 * Allow the caller to retrieve walk through the sibblings of "source"
691 * devfs_handle_t. The implicit edges "." and ".." is returned first
692 * followed by each of the real children.
693 *
694 * We may end up returning garbage if another thread perform any deletion
695 * in this directory before "placeptr".
696 *
697 */
698 /* ARGSUSED */
699 int
700 hwgraph_edge_get_next(devfs_handle_t source, char *name, devfs_handle_t *target,
701 uint *placeptr)
702
703 {
704
705 uint which_place;
706 unsigned int namelen = 0;
707 const char *tempname = NULL;
708
709 if (placeptr == NULL)
710 return(-1);
711
712 which_place = *placeptr;
713
714 again:
715 if (which_place <= HWGRAPH_RESERVED_PLACES) {
716 if (which_place == EDGE_PLACE_WANT_CURRENT) {
717 /*
718 * Looking for "."
719 * Return the current devfs handle.
720 */
721 if (name != NULL)
722 strcpy(name, HWGRAPH_EDGELBL_DOT);
723
724 if (target != NULL) {
725 *target = source;
726 /* XXX should incr "source" ref count here if we
727 * ever implement ref counts */
728 }
729
730 } else if (which_place == EDGE_PLACE_WANT_CONNECTPT) {
731 /*
732 * Looking for the connect point or parent.
733 * If the connect point is set .. it returns the connect point.
734 * Otherwise, it returns the parent .. will we support
735 * connect point?
736 */
737 devfs_handle_t connect_point = hwgraph_connectpt_get(source);
738
739 if (connect_point == NULL) {
740 /*
741 * No connectpoint set .. either the User
742 * explicitly NULL it or this node was not
743 * created via hcl.
744 */
745 which_place++;
746 goto again;
747 }
748
749 if (name != NULL)
750 strcpy(name, HWGRAPH_EDGELBL_DOTDOT);
751
752 if (target != NULL)
753 *target = connect_point;
754
755 } else if (which_place == EDGE_PLACE_WANT_REAL_EDGES) {
756 /*
757 * return first "real" entry in directory, and increment
758 * placeptr. Next time around we should have
759 * which_place > HWGRAPH_RESERVED_EDGES so we'll fall through
760 * this nested if block.
761 */
762 *target = devfs_get_first_child(source);
763 if (*target && name) {
764 tempname = devfs_get_name(*target, &namelen);
765 if (tempname && namelen)
766 strcpy(name, tempname);
767 }
768
769 *placeptr = which_place + 1;
770 return (0);
771 }
772
773 *placeptr = which_place+1;
774 return(0);
775 }
776
777 /*
778 * walk linked list, (which_place - HWGRAPH_RESERVED_PLACES) times
779 */
780 {
781 devfs_handle_t curr;
782 int i = 0;
783
784 for (curr=devfs_get_first_child(source), i= i+HWGRAPH_RESERVED_PLACES;
785 curr!=NULL && i<which_place;
786 curr=devfs_get_next_sibling(curr), i++)
787 ;
788 *target = curr;
789 *placeptr = which_place + 1;
790 if (curr && name) {
791 tempname = devfs_get_name(*target, &namelen);
792 if (tempname && namelen)
793 strcpy(name, tempname);
794 }
795 }
796 if (target == NULL)
797 return(-1);
798 else
799 return(0);
800 }
801
802 /*
803 * hwgraph_info_add_LBL - Adds a new label for the device. Mark the info_desc
804 * of the label as INFO_DESC_PRIVATE and store the info in the label.
805 */
806 /* ARGSUSED */
807 int
808 hwgraph_info_add_LBL( devfs_handle_t de,
809 char *name,
810 arbitrary_info_t info)
811 {
812 return(labelcl_info_add_LBL(de, name, INFO_DESC_PRIVATE, info));
813 }
814
815 /*
816 * hwgraph_info_remove_LBL - Remove the label entry for the device.
817 */
818 /* ARGSUSED */
819 int
820 hwgraph_info_remove_LBL( devfs_handle_t de,
821 char *name,
822 arbitrary_info_t *old_info)
823 {
824 return(labelcl_info_remove_LBL(de, name, NULL, old_info));
825 }
826
827 /*
828 * hwgraph_info_replace_LBL - replaces an existing label with
829 * a new label info value.
830 */
831 /* ARGSUSED */
832 int
833 hwgraph_info_replace_LBL( devfs_handle_t de,
834 char *name,
835 arbitrary_info_t info,
836 arbitrary_info_t *old_info)
837 {
838 return(labelcl_info_replace_LBL(de, name,
839 INFO_DESC_PRIVATE, info,
840 NULL, old_info));
841 }
842 /*
843 * hwgraph_info_get_LBL - Get and return the info value in the label of the
844 * device.
845 */
846 /* ARGSUSED */
847 int
848 hwgraph_info_get_LBL( devfs_handle_t de,
849 char *name,
850 arbitrary_info_t *infop)
851 {
852 return(labelcl_info_get_LBL(de, name, NULL, infop));
853 }
854
855 /*
856 * hwgraph_info_get_exported_LBL - Retrieve the info_desc and info pointer
857 * of the given label for the device. The weird thing is that the label
858 * that matches the name is return irrespective of the info_desc value!
859 * Do not understand why the word "exported" is used!
860 */
861 /* ARGSUSED */
862 int
863 hwgraph_info_get_exported_LBL( devfs_handle_t de,
864 char *name,
865 int *export_info,
866 arbitrary_info_t *infop)
867 {
868 int rc;
869 arb_info_desc_t info_desc;
870
871 rc = labelcl_info_get_LBL(de, name, &info_desc, infop);
872 if (rc == 0)
873 *export_info = (int)info_desc;
874
875 return(rc);
876 }
877
878 /*
879 * hwgraph_info_get_next_LBL - Returns the next label info given the
880 * current label entry in place.
881 *
882 * Once again this has no locking or reference count for protection.
883 *
884 */
885 /* ARGSUSED */
886 int
887 hwgraph_info_get_next_LBL( devfs_handle_t de,
888 char *buf,
889 arbitrary_info_t *infop,
890 labelcl_info_place_t *place)
891 {
892 return(labelcl_info_get_next_LBL(de, buf, NULL, infop, place));
893 }
894
895 /*
896 * hwgraph_info_export_LBL - Retrieve the specified label entry and modify
897 * the info_desc field with the given value in nbytes.
898 */
899 /* ARGSUSED */
900 int
901 hwgraph_info_export_LBL(devfs_handle_t de, char *name, int nbytes)
902 {
903 arbitrary_info_t info;
904 int rc;
905
906 if (nbytes == 0)
907 nbytes = INFO_DESC_EXPORT;
908
909 if (nbytes < 0)
910 return(-1);
911
912 rc = labelcl_info_get_LBL(de, name, NULL, &info);
913 if (rc != 0)
914 return(rc);
915
916 rc = labelcl_info_replace_LBL(de, name,
917 nbytes, info, NULL, NULL);
918
919 return(rc);
920 }
921
922 /*
923 * hwgraph_info_unexport_LBL - Retrieve the given label entry and change the
924 * label info_descr filed to INFO_DESC_PRIVATE.
925 */
926 /* ARGSUSED */
927 int
928 hwgraph_info_unexport_LBL(devfs_handle_t de, char *name)
929 {
930 arbitrary_info_t info;
931 int rc;
932
933 rc = labelcl_info_get_LBL(de, name, NULL, &info);
934 if (rc != 0)
935 return(rc);
936
937 rc = labelcl_info_replace_LBL(de, name,
938 INFO_DESC_PRIVATE, info, NULL, NULL);
939
940 return(rc);
941 }
942
943 /*
944 * hwgraph_path_lookup - return the handle for the given path.
945 *
946 */
947 int
948 hwgraph_path_lookup( devfs_handle_t start_vertex_handle,
949 char *lookup_path,
950 devfs_handle_t *vertex_handle_ptr,
951 char **remainder)
952 {
953 *vertex_handle_ptr = devfs_find_handle(start_vertex_handle, /* start dir */
954 lookup_path, /* path */
955 0, /* major */
956 0, /* minor */
957 0, /* char | block */
958 1); /* traverse symlinks */
959 if (*vertex_handle_ptr == NULL)
960 return(-1);
961 else
962 return(0);
963 }
964
965 /*
966 * hwgraph_traverse - Find and return the devfs handle starting from de.
967 *
968 */
969 graph_error_t
970 hwgraph_traverse(devfs_handle_t de, char *path, devfs_handle_t *found)
971 {
972 /*
973 * get the directory entry (path should end in a directory)
974 */
975
976 *found = devfs_find_handle(de, /* start dir */
977 path, /* path */
978 0, /* major */
979 0, /* minor */
980 0, /* char | block */
981 1); /* traverse symlinks */
982 if (*found == NULL)
983 return(GRAPH_NOT_FOUND);
984 else
985 return(GRAPH_SUCCESS);
986 }
987
988 /*
989 * hwgraph_path_to_vertex - Return the devfs entry handle for the given
990 * pathname .. assume traverse symlinks too!.
991 */
992 devfs_handle_t
993 hwgraph_path_to_vertex(char *path)
994 {
995 return(devfs_find_handle(NULL, /* start dir */
996 path, /* path */
997 0, /* major */
998 0, /* minor */
999 0, /* char | block */
1000 1)); /* traverse symlinks */
1001 }
1002
1003 /*
1004 * hwgraph_path_to_dev - Returns the devfs_handle_t of the given path ..
1005 * We only deal with devfs handle and not devfs_handle_t.
1006 */
1007 devfs_handle_t
1008 hwgraph_path_to_dev(char *path)
1009 {
1010 devfs_handle_t de;
1011
1012 de = hwgraph_path_to_vertex(path);
1013 return(de);
1014 }
1015
1016 /*
1017 * hwgraph_block_device_get - return the handle of the block device file.
1018 * The assumption here is that de is a directory.
1019 */
1020 devfs_handle_t
1021 hwgraph_block_device_get(devfs_handle_t de)
1022 {
1023 return(devfs_find_handle(de, /* start dir */
1024 "block", /* path */
1025 0, /* major */
1026 0, /* minor */
1027 DEVFS_SPECIAL_BLK, /* char | block */
1028 1)); /* traverse symlinks */
1029 }
1030
1031 /*
1032 * hwgraph_char_device_get - return the handle of the char device file.
1033 * The assumption here is that de is a directory.
1034 */
1035 devfs_handle_t
1036 hwgraph_char_device_get(devfs_handle_t de)
1037 {
1038 return(devfs_find_handle(de, /* start dir */
1039 "char", /* path */
1040 0, /* major */
1041 0, /* minor */
1042 DEVFS_SPECIAL_CHR, /* char | block */
1043 1)); /* traverse symlinks */
1044 }
1045
1046 /*
1047 * hwgraph_cdevsw_get - returns the fops of the given devfs entry.
1048 */
1049 struct file_operations *
1050 hwgraph_cdevsw_get(devfs_handle_t de)
1051 {
1052 return(devfs_get_ops(de));
1053 }
1054
1055 /*
1056 * hwgraph_bdevsw_get - returns the fops of the given devfs entry.
1057 */
1058 struct file_operations *
1059 hwgraph_bdevsw_get(devfs_handle_t de)
1060 {
1061 return(devfs_get_ops(de));
1062 }
1063
1064 /*
1065 ** Inventory is now associated with a vertex in the graph. For items that
1066 ** belong in the inventory but have no vertex
1067 ** (e.g. old non-graph-aware drivers), we create a bogus vertex under the
1068 ** INFO_LBL_INVENT name.
1069 **
1070 ** For historical reasons, we prevent exact duplicate entries from being added
1071 ** to a single vertex.
1072 */
1073
1074 /*
1075 * hwgraph_inventory_add - Adds an inventory entry into de.
1076 */
1077 int
1078 hwgraph_inventory_add( devfs_handle_t de,
1079 int class,
1080 int type,
1081 major_t controller,
1082 minor_t unit,
1083 int state)
1084 {
1085 inventory_t *pinv = NULL, *old_pinv = NULL, *last_pinv = NULL;
1086 int rv;
1087
1088 /*
1089 * Add our inventory data to the list of inventory data
1090 * associated with this vertex.
1091 */
1092 again:
1093 /* GRAPH_LOCK_UPDATE(&invent_lock); */
1094 rv = labelcl_info_get_LBL(de,
1095 INFO_LBL_INVENT,
1096 NULL, (arbitrary_info_t *)&old_pinv);
1097 if ((rv != LABELCL_SUCCESS) && (rv != LABELCL_NOT_FOUND))
1098 goto failure;
1099
1100 /*
1101 * Seek to end of inventory items associated with this
1102 * vertex. Along the way, make sure we're not duplicating
1103 * an inventory item (for compatibility with old add_to_inventory)
1104 */
1105 for (;old_pinv; last_pinv = old_pinv, old_pinv = old_pinv->inv_next) {
1106 if ((int)class != -1 && old_pinv->inv_class != class)
1107 continue;
1108 if ((int)type != -1 && old_pinv->inv_type != type)
1109 continue;
1110 if ((int)state != -1 && old_pinv->inv_state != state)
1111 continue;
1112 if ((int)controller != -1
1113 && old_pinv->inv_controller != controller)
1114 continue;
1115 if ((int)unit != -1 && old_pinv->inv_unit != unit)
1116 continue;
1117
1118 /* exact duplicate of previously-added inventory item */
1119 rv = LABELCL_DUP;
1120 goto failure;
1121 }
1122
1123 /* Not a duplicate, so we know that we need to add something. */
1124 if (pinv == NULL) {
1125 /* Release lock while we wait for memory. */
1126 /* GRAPH_LOCK_DONE_UPDATE(&invent_lock); */
1127 pinv = (inventory_t *)kmalloc(sizeof(inventory_t), GFP_KERNEL);
1128 replace_in_inventory(pinv, class, type, controller, unit, state);
1129 goto again;
1130 }
1131
1132 pinv->inv_next = NULL;
1133 if (last_pinv) {
1134 last_pinv->inv_next = pinv;
1135 } else {
1136 rv = labelcl_info_add_LBL(de, INFO_LBL_INVENT,
1137 sizeof(inventory_t), (arbitrary_info_t)pinv);
1138
1139 if (!rv)
1140 goto failure;
1141 }
1142
1143 /* GRAPH_LOCK_DONE_UPDATE(&invent_lock); */
1144 return(0);
1145
1146 failure:
1147 /* GRAPH_LOCK_DONE_UPDATE(&invent_lock); */
1148 if (pinv)
1149 kfree(pinv);
1150 return(rv);
1151 }
1152
1153
1154 /*
1155 * hwgraph_inventory_remove - Removes an inventory entry.
1156 *
1157 * Remove an inventory item associated with a vertex. It is the caller's
1158 * responsibility to make sure that there are no races between removing
1159 * inventory from a vertex and simultaneously removing that vertex.
1160 */
1161 int
1162 hwgraph_inventory_remove( devfs_handle_t de,
1163 int class,
1164 int type,
1165 major_t controller,
1166 minor_t unit,
1167 int state)
1168 {
1169 inventory_t *pinv = NULL, *last_pinv = NULL, *next_pinv = NULL;
1170 labelcl_error_t rv;
1171
1172 /*
1173 * We never remove stuff from ".invent" ..
1174 */
1175 if (!de)
1176 return (-1);
1177
1178 /*
1179 * Remove our inventory data to the list of inventory data
1180 * associated with this vertex.
1181 */
1182 /* GRAPH_LOCK_UPDATE(&invent_lock); */
1183 rv = labelcl_info_get_LBL(de,
1184 INFO_LBL_INVENT,
1185 NULL, (arbitrary_info_t *)&pinv);
1186 if (rv != LABELCL_SUCCESS)
1187 goto failure;
1188
1189 /*
1190 * Search through inventory items associated with this
1191 * vertex, looking for a match.
1192 */
1193 for (;pinv; pinv = next_pinv) {
1194 next_pinv = pinv->inv_next;
1195
1196 if(((int)class == -1 || pinv->inv_class == class) &&
1197 ((int)type == -1 || pinv->inv_type == type) &&
1198 ((int)state == -1 || pinv->inv_state == state) &&
1199 ((int)controller == -1 || pinv->inv_controller == controller) &&
1200 ((int)unit == -1 || pinv->inv_unit == unit)) {
1201
1202 /* Found a matching inventory item. Remove it. */
1203 if (last_pinv) {
1204 last_pinv->inv_next = pinv->inv_next;
1205 } else {
1206 rv = hwgraph_info_replace_LBL(de, INFO_LBL_INVENT, (arbitrary_info_t)pinv->inv_next, NULL);
1207 if (rv != LABELCL_SUCCESS)
1208 goto failure;
1209 }
1210
1211 pinv->inv_next = NULL; /* sanity */
1212 kfree(pinv);
1213 } else
1214 last_pinv = pinv;
1215 }
1216
1217 if (last_pinv == NULL) {
1218 rv = hwgraph_info_remove_LBL(de, INFO_LBL_INVENT, NULL);
1219 if (rv != LABELCL_SUCCESS)
1220 goto failure;
1221 }
1222
1223 rv = LABELCL_SUCCESS;
1224
1225 failure:
1226 /* GRAPH_LOCK_DONE_UPDATE(&invent_lock); */
1227 return(rv);
1228 }
1229
1230 /*
1231 * hwgraph_inventory_get_next - Get next inventory item associated with the
1232 * specified vertex.
1233 *
1234 * No locking is really needed. We don't yet have the ability
1235 * to remove inventory items, and new items are always added to
1236 * the end of a vertex' inventory list.
1237 *
1238 * However, a devfs entry can be removed!
1239 */
1240 int
1241 hwgraph_inventory_get_next(devfs_handle_t de, invplace_t *place, inventory_t **ppinv)
1242 {
1243 inventory_t *pinv;
1244 labelcl_error_t rv;
1245
1246 if (de == NULL)
1247 return(LABELCL_BAD_PARAM);
1248
1249 if (place->invplace_vhdl == NULL) {
1250 place->invplace_vhdl = de;
1251 place->invplace_inv = NULL;
1252 }
1253
1254 if (de != place->invplace_vhdl)
1255 return(LABELCL_BAD_PARAM);
1256
1257 if (place->invplace_inv == NULL) {
1258 /* Just starting on this vertex */
1259 rv = labelcl_info_get_LBL(de, INFO_LBL_INVENT,
1260 NULL, (arbitrary_info_t *)&pinv);
1261 if (rv != LABELCL_SUCCESS)
1262 return(LABELCL_NOT_FOUND);
1263
1264 } else {
1265 /* Advance to next item on this vertex */
1266 pinv = place->invplace_inv->inv_next;
1267 }
1268 place->invplace_inv = pinv;
1269 *ppinv = pinv;
1270
1271 return(LABELCL_SUCCESS);
1272 }
1273
1274 /*
1275 * hwgraph_controller_num_get - Returns the controller number in the inventory
1276 * entry.
1277 */
1278 int
1279 hwgraph_controller_num_get(devfs_handle_t device)
1280 {
1281 inventory_t *pinv;
1282 invplace_t invplace = { NULL, NULL, NULL };
1283 int val = -1;
1284 if ((pinv = device_inventory_get_next(device, &invplace)) != NULL) {
1285 val = (pinv->inv_class == INV_NETWORK)? pinv->inv_unit: pinv->inv_controller;
1286 }
1287 #ifdef DEBUG
1288 /*
1289 * It does not make any sense to call this on vertexes with multiple
1290 * inventory structs chained together
1291 */
1292 if ( device_inventory_get_next(device, &invplace) != NULL ) {
1293 printk("Should panic here ... !\n");
1294 #endif
1295 return (val);
1296 }
1297
1298 /*
1299 * hwgraph_controller_num_set - Sets the controller number in the inventory
1300 * entry.
1301 */
1302 void
1303 hwgraph_controller_num_set(devfs_handle_t device, int contr_num)
1304 {
1305 inventory_t *pinv;
1306 invplace_t invplace = { NULL, NULL, NULL };
1307 if ((pinv = device_inventory_get_next(device, &invplace)) != NULL) {
1308 if (pinv->inv_class == INV_NETWORK)
1309 pinv->inv_unit = contr_num;
1310 else {
1311 if (pinv->inv_class == INV_FCNODE)
1312 pinv = device_inventory_get_next(device, &invplace);
1313 if (pinv != NULL)
1314 pinv->inv_controller = contr_num;
1315 }
1316 }
1317 #ifdef DEBUG
1318 /*
1319 * It does not make any sense to call this on vertexes with multiple
1320 * inventory structs chained together
1321 */
1322 if(pinv != NULL)
1323 ASSERT(device_inventory_get_next(device, &invplace) == NULL);
1324 #endif
1325 }
1326
1327 /*
1328 * Find the canonical name for a given vertex by walking back through
1329 * connectpt's until we hit the hwgraph root vertex (or until we run
1330 * out of buffer space or until something goes wrong).
1331 *
1332 * COMPATIBILITY FUNCTIONALITY
1333 * Walks back through 'parents', not necessarily the same as connectpts.
1334 *
1335 * Need to resolve the fact that devfs does not return the path from
1336 * "/" but rather it just stops right before /dev ..
1337 */
1338 int
1339 hwgraph_vertex_name_get(devfs_handle_t vhdl, char *buf, uint buflen)
1340 {
1341 char *locbuf;
1342 int pos;
1343
1344 if (buflen < 1)
1345 return(-1); /* XXX should be GRAPH_BAD_PARAM ? */
1346
1347 locbuf = kmalloc(buflen, GFP_KERNEL);
1348
1349 pos = devfs_generate_path(vhdl, locbuf, buflen);
1350 if (pos < 0) {
1351 kfree(locbuf);
1352 return pos;
1353 }
1354
1355 strcpy(buf, &locbuf[pos]);
1356 kfree(locbuf);
1357 return 0;
1358 }
1359
1360 /*
1361 ** vertex_to_name converts a vertex into a canonical name by walking
1362 ** back through connect points until we hit the hwgraph root (or until
1363 ** we run out of buffer space).
1364 **
1365 ** Usually returns a pointer to the original buffer, filled in as
1366 ** appropriate. If the buffer is too small to hold the entire name,
1367 ** or if anything goes wrong while determining the name, vertex_to_name
1368 ** returns "UnknownDevice".
1369 */
1370
1371 #define DEVNAME_UNKNOWN "UnknownDevice"
1372
1373 char *
1374 vertex_to_name(devfs_handle_t vhdl, char *buf, uint buflen)
1375 {
1376 if (hwgraph_vertex_name_get(vhdl, buf, buflen) == GRAPH_SUCCESS)
1377 return(buf);
1378 else
1379 return(DEVNAME_UNKNOWN);
1380 }
1381
1382 #ifdef LATER
1383 /*
1384 ** Return the compact node id of the node that ultimately "owns" the specified
1385 ** vertex. In order to do this, we walk back through masters and connect points
1386 ** until we reach a vertex that represents a node.
1387 */
1388 cnodeid_t
1389 master_node_get(devfs_handle_t vhdl)
1390 {
1391 cnodeid_t cnodeid;
1392 devfs_handle_t master;
1393
1394 for (;;) {
1395 cnodeid = nodevertex_to_cnodeid(vhdl);
1396 if (cnodeid != CNODEID_NONE)
1397 return(cnodeid);
1398
1399 master = device_master_get(vhdl);
1400
1401 /* Check for exceptional cases */
1402 if (master == vhdl) {
1403 /* Since we got a reference to the "master" thru
1404 * device_master_get() we should decrement
1405 * its reference count by 1
1406 */
1407 hwgraph_vertex_unref(master);
1408 return(CNODEID_NONE);
1409 }
1410
1411 if (master == GRAPH_VERTEX_NONE) {
1412 master = hwgraph_connectpt_get(vhdl);
1413 if ((master == GRAPH_VERTEX_NONE) ||
1414 (master == vhdl)) {
1415 if (master == vhdl)
1416 /* Since we got a reference to the
1417 * "master" thru
1418 * hwgraph_connectpt_get() we should
1419 * decrement its reference count by 1
1420 */
1421 hwgraph_vertex_unref(master);
1422 return(CNODEID_NONE);
1423 }
1424 }
1425
1426 vhdl = master;
1427 /* Decrement the reference to "master" which was got
1428 * either thru device_master_get() or hwgraph_connectpt_get()
1429 * above.
1430 */
1431 hwgraph_vertex_unref(master);
1432 }
1433 }
1434
1435 /*
1436 * Using the canonical path name to get hold of the desired vertex handle will
1437 * not work on multi-hub sn0 nodes. Hence, we use the following (slightly
1438 * convoluted) algorithm.
1439 *
1440 * - Start at the vertex corresponding to the driver (provided as input parameter)
1441 * - Loop till you reach a vertex which has EDGE_LBL_MEMORY
1442 * - If EDGE_LBL_CONN exists, follow that up.
1443 * else if EDGE_LBL_MASTER exists, follow that up.
1444 * else follow EDGE_LBL_DOTDOT up.
1445 *
1446 * * We should be at desired hub/heart vertex now *
1447 * - Follow EDGE_LBL_CONN to the widget vertex.
1448 *
1449 * - return vertex handle of this widget.
1450 */
1451 devfs_handle_t
1452 mem_vhdl_get(devfs_handle_t drv_vhdl)
1453 {
1454 devfs_handle_t cur_vhdl, cur_upper_vhdl;
1455 devfs_handle_t tmp_mem_vhdl, mem_vhdl;
1456 graph_error_t loop_rv;
1457
1458 /* Initializations */
1459 cur_vhdl = drv_vhdl;
1460 loop_rv = ~GRAPH_SUCCESS;
1461
1462 /* Loop till current vertex has EDGE_LBL_MEMORY */
1463 while (loop_rv != GRAPH_SUCCESS) {
1464
1465 if ((hwgraph_edge_get(cur_vhdl, EDGE_LBL_CONN, &cur_upper_vhdl)) == GRAPH_SUCCESS) {
1466
1467 } else if ((hwgraph_edge_get(cur_vhdl, EDGE_LBL_MASTER, &cur_upper_vhdl)) == GRAPH_SUCCESS) {
1468 } else { /* Follow HWGRAPH_EDGELBL_DOTDOT up */
1469 (void) hwgraph_edge_get(cur_vhdl, HWGRAPH_EDGELBL_DOTDOT, &cur_upper_vhdl);
1470 }
1471
1472 cur_vhdl = cur_upper_vhdl;
1473
1474 #if DEBUG && HWG_DEBUG
1475 printf("Current vhdl %d \n", cur_vhdl);
1476 #endif /* DEBUG */
1477
1478 loop_rv = hwgraph_edge_get(cur_vhdl, EDGE_LBL_MEMORY, &tmp_mem_vhdl);
1479 }
1480
1481 /* We should be at desired hub/heart vertex now */
1482 if ((hwgraph_edge_get(cur_vhdl, EDGE_LBL_CONN, &mem_vhdl)) != GRAPH_SUCCESS)
1483 return (GRAPH_VERTEX_NONE);
1484
1485 return (mem_vhdl);
1486 }
1487 #endif /* LATER */
1488
1489
1490 /*
1491 ** Add a char device -- if the driver supports it -- at a specified vertex.
1492 */
1493 graph_error_t
1494 hwgraph_char_device_add( devfs_handle_t from,
1495 char *path,
1496 char *prefix,
1497 devfs_handle_t *devhdl)
1498 {
1499 devfs_handle_t xx = NULL;
1500
1501 printk("WARNING: hwgraph_char_device_add() not supported .. use hwgraph_register.\n");
1502 *devhdl = xx; // Must set devhdl
1503 return(GRAPH_SUCCESS);
1504 }
1505
1506 graph_error_t
1507 hwgraph_edge_remove(devfs_handle_t from, char *name, devfs_handle_t *toptr)
1508 {
1509 printk("WARNING: hwgraph_edge_remove NOT supported.\n");
1510 return(GRAPH_ILLEGAL_REQUEST);
1511 }
1512
1513 graph_error_t
1514 hwgraph_vertex_unref(devfs_handle_t vhdl)
1515 {
1516 return(GRAPH_ILLEGAL_REQUEST);
1517 }
1518
1519
1520 EXPORT_SYMBOL(hwgraph_mk_dir);
1521 EXPORT_SYMBOL(hwgraph_path_add);
1522 EXPORT_SYMBOL(hwgraph_char_device_add);
1523 EXPORT_SYMBOL(hwgraph_register);
1524 EXPORT_SYMBOL(hwgraph_vertex_destroy);
1525
1526 EXPORT_SYMBOL(hwgraph_fastinfo_get);
1527 EXPORT_SYMBOL(hwgraph_edge_get);
1528
1529 EXPORT_SYMBOL(hwgraph_fastinfo_set);
1530 EXPORT_SYMBOL(hwgraph_connectpt_set);
1531 EXPORT_SYMBOL(hwgraph_connectpt_get);
1532 EXPORT_SYMBOL(hwgraph_edge_get_next);
1533 EXPORT_SYMBOL(hwgraph_info_add_LBL);
1534 EXPORT_SYMBOL(hwgraph_info_remove_LBL);
1535 EXPORT_SYMBOL(hwgraph_info_replace_LBL);
1536 EXPORT_SYMBOL(hwgraph_info_get_LBL);
1537 EXPORT_SYMBOL(hwgraph_info_get_exported_LBL);
1538 EXPORT_SYMBOL(hwgraph_info_get_next_LBL);
1539 EXPORT_SYMBOL(hwgraph_info_export_LBL);
1540 EXPORT_SYMBOL(hwgraph_info_unexport_LBL);
1541 EXPORT_SYMBOL(hwgraph_path_lookup);
1542 EXPORT_SYMBOL(hwgraph_traverse);
1543 EXPORT_SYMBOL(hwgraph_path_to_vertex);
1544 EXPORT_SYMBOL(hwgraph_path_to_dev);
1545 EXPORT_SYMBOL(hwgraph_block_device_get);
1546 EXPORT_SYMBOL(hwgraph_char_device_get);
1547 EXPORT_SYMBOL(hwgraph_cdevsw_get);
1548 EXPORT_SYMBOL(hwgraph_bdevsw_get);
1549 EXPORT_SYMBOL(hwgraph_vertex_name_get);
1550