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