/* $Id$ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * hcl - SGI's Hardware Graph compatibility layer. * * Copyright (C) 1992 - 1997, 2000 Silicon Graphics, Inc. * Copyright (C) 2000 by Colin Ngam */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define HCL_NAME "SGI-HWGRAPH COMPATIBILITY DRIVER" #define HCL_TEMP_NAME "HCL_TEMP_NAME_USED_FOR_HWGRAPH_VERTEX_CREATE" #define HCL_TEMP_NAME_LEN 44 #define HCL_VERSION "1.0" devfs_handle_t hwgraph_root = NULL; devfs_handle_t linux_busnum = NULL; /* * Debug flag definition. */ #define OPTION_NONE 0x00 #define HCL_DEBUG_NONE 0x00000 #define HCL_DEBUG_ALL 0x0ffff #if defined(CONFIG_HCL_DEBUG) static unsigned int hcl_debug_init __initdata = HCL_DEBUG_NONE; #endif static unsigned int hcl_debug = HCL_DEBUG_NONE; #if defined(CONFIG_HCL_DEBUG) && !defined(MODULE) static unsigned int boot_options = OPTION_NONE; #endif /* * Some Global definitions. */ spinlock_t hcl_spinlock; devfs_handle_t hcl_handle = NULL; invplace_t invplace_none = { GRAPH_VERTEX_NONE, GRAPH_VERTEX_PLACE_NONE, NULL }; /* * HCL device driver. * The purpose of this device driver is to provide a facility * for User Level Apps e.g. hinv, ioconfig etc. an ioctl path * to manipulate label entries without having to implement * system call interfaces. This methodology will enable us to * make this feature module loadable. */ static int hcl_open(struct inode * inode, struct file * filp) { if (hcl_debug) { printk("HCL: hcl_open called.\n"); } return(0); } static int hcl_close(struct inode * inode, struct file * filp) { if (hcl_debug) { printk("HCL: hcl_close called.\n"); } return(0); } static int hcl_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg) { if (hcl_debug) { printk("HCL: hcl_ioctl called.\n"); } switch (cmd) { default: if (hcl_debug) { printk("HCL: hcl_ioctl cmd = 0x%x\n", cmd); } } return(0); } struct file_operations hcl_fops = { (struct module *)0, NULL, /* lseek - default */ NULL, /* read - general block-dev read */ NULL, /* write - general block-dev write */ NULL, /* readdir - bad */ NULL, /* poll */ hcl_ioctl, /* ioctl */ NULL, /* mmap */ hcl_open, /* open */ NULL, /* flush */ hcl_close, /* release */ NULL, /* fsync */ NULL, /* fasync */ NULL, /* lock */ NULL, /* readv */ NULL, /* writev */ }; /* * init_hcl() - Boot time initialization. Ensure that it is called * after devfs has been initialized. * * For now this routine is being called out of devfs/base.c. Actually * Not a bad place to be .. * */ #ifdef MODULE int init_module (void) #else int __init init_hcl(void) #endif { extern void string_table_init(struct string_table *); extern struct string_table label_string_table; int rv = 0; #if defined(CONFIG_HCL_DEBUG) && !defined(MODULE) printk ("\n%s: v%s Colin Ngam (cngam@sgi.com)\n", HCL_NAME, HCL_VERSION); hcl_debug = hcl_debug_init; printk ("%s: hcl_debug: 0x%0x\n", HCL_NAME, hcl_debug); printk ("\n%s: boot_options: 0x%0x\n", HCL_NAME, boot_options); #endif spin_lock_init(&hcl_spinlock); /* * Create the hwgraph_root on devfs. */ rv = hwgraph_path_add(NULL, "hw", &hwgraph_root); if (rv) printk ("WARNING: init_hcl: Failed to create hwgraph_root. Error = %d.\n", rv); /* * Create the hcl driver to support inventory entry manipulations. * By default, it is expected that devfs is mounted on /dev. * */ hcl_handle = hwgraph_register(hwgraph_root, ".hcl", 0, DEVFS_FL_AUTO_DEVNUM, 0, 0, S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0, &hcl_fops, NULL); if (hcl_handle == NULL) { panic("HCL: Unable to create HCL Driver in init_hcl().\n"); return(0); } /* * Initialize the HCL string table. */ string_table_init(&label_string_table); /* * Create the directory that links Linux bus numbers to our Xwidget. */ rv = hwgraph_path_add(hwgraph_root, "linux/busnum", &linux_busnum); if (linux_busnum == NULL) { panic("HCL: Unable to create hw/linux/busnum\n"); return(0); } return(0); } /* * hcl_setup() - Process boot time parameters if given. * "hcl=" * This routine gets called only if "hcl=" is given in the * boot line and before init_hcl(). * * We currently do not have any boot options .. when we do, * functionalities can be added here. * */ static int __init hcl_setup(char *str) { while ( (*str != '\0') && !isspace (*str) ) { #ifdef CONFIG_HCL_DEBUG if (strncmp (str, "all", 3) == 0) { hcl_debug_init |= HCL_DEBUG_ALL; str += 3; } else return 0; #endif if (*str != ',') return 0; ++str; } return 1; } __setup("hcl=", hcl_setup); /* * Set device specific "fast information". * */ void hwgraph_fastinfo_set(devfs_handle_t de, arbitrary_info_t fastinfo) { if (hcl_debug) { printk("HCL: hwgraph_fastinfo_set handle 0x%p fastinfo %ld\n", de, fastinfo); } labelcl_info_replace_IDX(de, HWGRAPH_FASTINFO, fastinfo, NULL); } /* * Get device specific "fast information". * */ arbitrary_info_t hwgraph_fastinfo_get(devfs_handle_t de) { arbitrary_info_t fastinfo; int rv; if (!de) { printk(KERN_WARNING "HCL: hwgraph_fastinfo_get handle given is NULL.\n"); return(-1); } rv = labelcl_info_get_IDX(de, HWGRAPH_FASTINFO, &fastinfo); if (rv == 0) return(fastinfo); return(0); } /* * hwgraph_connectpt_set - Sets the connect point handle in de to the * given connect_de handle. By default, the connect point of the * devfs node is the parent. This effectively changes this assumption. */ int hwgraph_connectpt_set(devfs_handle_t de, devfs_handle_t connect_de) { int rv; if (!de) return(-1); rv = labelcl_info_connectpt_set(de, connect_de); return(rv); } /* * hwgraph_connectpt_get: Returns the entry's connect point in the devfs * tree. */ devfs_handle_t hwgraph_connectpt_get(devfs_handle_t de) { int rv; arbitrary_info_t info; devfs_handle_t connect; rv = labelcl_info_get_IDX(de, HWGRAPH_CONNECTPT, &info); if (rv != 0) { return(NULL); } connect = (devfs_handle_t)info; return(connect); } /* * hwgraph_mk_dir - Creates a directory entry with devfs. * Note that a directory entry in devfs can have children * but it cannot be a char|block special file. */ devfs_handle_t hwgraph_mk_dir(devfs_handle_t de, const char *name, unsigned int namelen, void *info) { int rv; labelcl_info_t *labelcl_info = NULL; devfs_handle_t new_devfs_handle = NULL; devfs_handle_t parent = NULL; /* * Create the device info structure for hwgraph compatiblity support. */ labelcl_info = labelcl_info_create(); if (!labelcl_info) return(NULL); /* * Create a devfs entry. */ new_devfs_handle = devfs_mk_dir(de, name, (void *)labelcl_info); if (!new_devfs_handle) { labelcl_info_destroy(labelcl_info); return(NULL); } /* * Get the parent handle. */ parent = devfs_get_parent (new_devfs_handle); /* * To provide the same semantics as the hwgraph, set the connect point. */ rv = hwgraph_connectpt_set(new_devfs_handle, parent); if (!rv) { /* * We need to clean up! */ } /* * If the caller provides a private data pointer, save it in the * labelcl info structure(fastinfo). This can be retrieved via * hwgraph_fastinfo_get() */ if (info) hwgraph_fastinfo_set(new_devfs_handle, (arbitrary_info_t)info); return(new_devfs_handle); } /* * hwgraph_vertex_create - Create a vertex by giving it a temp name. */ /* * hwgraph_path_add - Create a directory node with the given path starting * from the given devfs_handle_t. */ extern char * dev_to_name(devfs_handle_t, char *, uint); int hwgraph_path_add(devfs_handle_t fromv, char *path, devfs_handle_t *new_de) { unsigned int namelen = strlen(path); int rv; /* * We need to handle the case when fromv is NULL .. * in this case we need to create the path from the * hwgraph root! */ if (fromv == NULL) fromv = hwgraph_root; /* * check the entry doesn't already exist, if it does * then we simply want new_de to point to it (otherwise * we'll overwrite the existing labelcl_info struct) */ rv = hwgraph_edge_get(fromv, path, new_de); if (rv) { /* couldn't find entry so we create it */ *new_de = hwgraph_mk_dir(fromv, path, namelen, NULL); if (new_de == NULL) return(-1); else return(0); } else return(0); } /* * hwgraph_register - Creates a file entry with devfs. * Note that a file entry cannot have children .. it is like a * char|block special vertex in hwgraph. */ devfs_handle_t hwgraph_register(devfs_handle_t de, const char *name, unsigned int namelen, unsigned int flags, unsigned int major, unsigned int minor, umode_t mode, uid_t uid, gid_t gid, struct file_operations *fops, void *info) { int rv; void *labelcl_info = NULL; devfs_handle_t new_devfs_handle = NULL; devfs_handle_t parent = NULL; /* * Create the labelcl info structure for hwgraph compatiblity support. */ labelcl_info = labelcl_info_create(); if (!labelcl_info) return(NULL); /* * Create a devfs entry. */ new_devfs_handle = devfs_register(de, name, flags, major, minor, mode, fops, labelcl_info); if (!new_devfs_handle) { labelcl_info_destroy((labelcl_info_t *)labelcl_info); return(NULL); } /* * Get the parent handle. */ if (de == NULL) parent = devfs_get_parent (new_devfs_handle); else parent = de; /* * To provide the same semantics as the hwgraph, set the connect point. */ rv = hwgraph_connectpt_set(new_devfs_handle, parent); if (rv) { /* * We need to clean up! */ printk(KERN_WARNING "HCL: Unable to set the connect point to it's parent 0x%p\n", new_devfs_handle); } /* * If the caller provides a private data pointer, save it in the * labelcl info structure(fastinfo). This can be retrieved via * hwgraph_fastinfo_get() */ if (info) hwgraph_fastinfo_set(new_devfs_handle, (arbitrary_info_t)info); return(new_devfs_handle); } /* * hwgraph_mk_symlink - Create a symbolic link. */ int hwgraph_mk_symlink(devfs_handle_t de, const char *name, unsigned int namelen, unsigned int flags, const char *link, unsigned int linklen, devfs_handle_t *handle, void *info) { void *labelcl_info = NULL; int status = 0; devfs_handle_t new_devfs_handle = NULL; /* * Create the labelcl info structure for hwgraph compatiblity support. */ labelcl_info = labelcl_info_create(); if (!labelcl_info) return(-1); /* * Create a symbolic link devfs entry. */ status = devfs_mk_symlink(de, name, flags, link, &new_devfs_handle, labelcl_info); if ( (!new_devfs_handle) || (!status) ){ labelcl_info_destroy((labelcl_info_t *)labelcl_info); return(-1); } /* * If the caller provides a private data pointer, save it in the * labelcl info structure(fastinfo). This can be retrieved via * hwgraph_fastinfo_get() */ if (info) hwgraph_fastinfo_set(new_devfs_handle, (arbitrary_info_t)info); *handle = new_devfs_handle; return(0); } /* * hwgraph_vertex_get_next - this routine returns the next sibbling for the * device entry given in de. If there are no more sibbling, NULL * is returned in next_sibbling. * * Currently we do not have any protection against de being deleted * while it's handle is being held. */ int hwgraph_vertex_get_next(devfs_handle_t *next_sibbling, devfs_handle_t *de) { *next_sibbling = devfs_get_next_sibling (*de); if (*next_sibbling != NULL) *de = *next_sibbling; return (0); } /* * hwgraph_vertex_destroy - Destroy the devfs entry */ int hwgraph_vertex_destroy(devfs_handle_t de) { void *labelcl_info = NULL; labelcl_info = devfs_get_info(de); devfs_unregister(de); if (labelcl_info) labelcl_info_destroy((labelcl_info_t *)labelcl_info); return(0); } /* ** See if a vertex has an outgoing edge with a specified name. ** Vertices in the hwgraph *implicitly* contain these edges: ** "." refers to "current vertex" ** ".." refers to "connect point vertex" ** "char" refers to current vertex (character device access) ** "block" refers to current vertex (block device access) */ /* * hwgraph_edge_add - This routines has changed from the original conext. * All it does now is to create a symbolic link from "from" to "to". */ /* ARGSUSED */ int hwgraph_edge_add(devfs_handle_t from, devfs_handle_t to, char *name) { char *path; char *s1; char *index; int name_start; devfs_handle_t handle = NULL; int rv; int i, count; path = kmalloc(1024, GFP_KERNEL); memset(path, 0x0, 1024); name_start = devfs_generate_path (from, path, 1024); s1 = &path[name_start]; count = 0; while (1) { index = strstr (s1, "/"); if (index) { count++; s1 = ++index; } else { count++; break; } } memset(path, 0x0, 1024); name_start = devfs_generate_path (to, path, 1024); for (i = 0; i < count; i++) { strcat(path,"../"); } strcat(path, &path[name_start]); /* * Otherwise, just create a symlink to the vertex. * In this case the vertex was previous created with a REAL pathname. */ rv = devfs_mk_symlink (from, (const char *)name, DEVFS_FL_DEFAULT, path, &handle, NULL); name_start = devfs_generate_path (handle, path, 1024); return(rv); } /* ARGSUSED */ int hwgraph_edge_get(devfs_handle_t from, char *name, devfs_handle_t *toptr) { int namelen = 0; devfs_handle_t target_handle = NULL; if (name == NULL) return(-1); if (toptr == NULL) return(-1); /* * If the name is "." just return the current devfs entry handle. */ if (!strcmp(name, HWGRAPH_EDGELBL_DOT)) { if (toptr) { *toptr = from; } } else if (!strcmp(name, HWGRAPH_EDGELBL_DOTDOT)) { /* * Hmmm .. should we return the connect point or parent .. * see in hwgraph, the concept of parent is the connectpt! * * Maybe we should see whether the connectpt is set .. if * not just return the parent! */ target_handle = hwgraph_connectpt_get(from); if (target_handle) { /* * Just return the connect point. */ *toptr = target_handle; return(0); } target_handle = devfs_get_parent(from); *toptr = target_handle; } else { /* * Call devfs to get the devfs entry. */ namelen = (int) strlen(name); target_handle = devfs_find_handle (from, name, 0, 0, 0, 1); /* Yes traverse symbolic links */ if (target_handle == NULL) return(-1); else *toptr = target_handle; } return(0); } /* * hwgraph_edge_get_next - Retrieves the next sibbling given the current * entry number "placeptr". * * Allow the caller to retrieve walk through the sibblings of "source" * devfs_handle_t. The implicit edges "." and ".." is returned first * followed by each of the real children. * * We may end up returning garbage if another thread perform any deletion * in this directory before "placeptr". * */ /* ARGSUSED */ int hwgraph_edge_get_next(devfs_handle_t source, char *name, devfs_handle_t *target, uint *placeptr) { uint which_place; unsigned int namelen = 0; const char *tempname = NULL; if (placeptr == NULL) return(-1); which_place = *placeptr; again: if (which_place <= HWGRAPH_RESERVED_PLACES) { if (which_place == EDGE_PLACE_WANT_CURRENT) { /* * Looking for "." * Return the current devfs handle. */ if (name != NULL) strcpy(name, HWGRAPH_EDGELBL_DOT); if (target != NULL) { *target = source; /* XXX should incr "source" ref count here if we * ever implement ref counts */ } } else if (which_place == EDGE_PLACE_WANT_CONNECTPT) { /* * Looking for the connect point or parent. * If the connect point is set .. it returns the connect point. * Otherwise, it returns the parent .. will we support * connect point? */ devfs_handle_t connect_point = hwgraph_connectpt_get(source); if (connect_point == NULL) { /* * No connectpoint set .. either the User * explicitly NULL it or this node was not * created via hcl. */ which_place++; goto again; } if (name != NULL) strcpy(name, HWGRAPH_EDGELBL_DOTDOT); if (target != NULL) *target = connect_point; } else if (which_place == EDGE_PLACE_WANT_REAL_EDGES) { /* * return first "real" entry in directory, and increment * placeptr. Next time around we should have * which_place > HWGRAPH_RESERVED_EDGES so we'll fall through * this nested if block. */ *target = devfs_get_first_child(source); if (*target && name) { tempname = devfs_get_name(*target, &namelen); if (tempname && namelen) strcpy(name, tempname); } *placeptr = which_place + 1; return (0); } *placeptr = which_place+1; return(0); } /* * walk linked list, (which_place - HWGRAPH_RESERVED_PLACES) times */ { devfs_handle_t curr; int i = 0; for (curr=devfs_get_first_child(source), i= i+HWGRAPH_RESERVED_PLACES; curr!=NULL && iinv_next) { if ((int)class != -1 && old_pinv->inv_class != class) continue; if ((int)type != -1 && old_pinv->inv_type != type) continue; if ((int)state != -1 && old_pinv->inv_state != state) continue; if ((int)controller != -1 && old_pinv->inv_controller != controller) continue; if ((int)unit != -1 && old_pinv->inv_unit != unit) continue; /* exact duplicate of previously-added inventory item */ rv = LABELCL_DUP; goto failure; } /* Not a duplicate, so we know that we need to add something. */ if (pinv == NULL) { /* Release lock while we wait for memory. */ /* GRAPH_LOCK_DONE_UPDATE(&invent_lock); */ pinv = (inventory_t *)kmalloc(sizeof(inventory_t), GFP_KERNEL); replace_in_inventory(pinv, class, type, controller, unit, state); goto again; } pinv->inv_next = NULL; if (last_pinv) { last_pinv->inv_next = pinv; } else { rv = labelcl_info_add_LBL(de, INFO_LBL_INVENT, sizeof(inventory_t), (arbitrary_info_t)pinv); if (!rv) goto failure; } /* GRAPH_LOCK_DONE_UPDATE(&invent_lock); */ return(0); failure: /* GRAPH_LOCK_DONE_UPDATE(&invent_lock); */ if (pinv) kfree(pinv); return(rv); } /* * hwgraph_inventory_remove - Removes an inventory entry. * * Remove an inventory item associated with a vertex. It is the caller's * responsibility to make sure that there are no races between removing * inventory from a vertex and simultaneously removing that vertex. */ int hwgraph_inventory_remove( devfs_handle_t de, int class, int type, major_t controller, minor_t unit, int state) { inventory_t *pinv = NULL, *last_pinv = NULL, *next_pinv = NULL; labelcl_error_t rv; /* * We never remove stuff from ".invent" .. */ if (!de) return (-1); /* * Remove our inventory data to the list of inventory data * associated with this vertex. */ /* GRAPH_LOCK_UPDATE(&invent_lock); */ rv = labelcl_info_get_LBL(de, INFO_LBL_INVENT, NULL, (arbitrary_info_t *)&pinv); if (rv != LABELCL_SUCCESS) goto failure; /* * Search through inventory items associated with this * vertex, looking for a match. */ for (;pinv; pinv = next_pinv) { next_pinv = pinv->inv_next; if(((int)class == -1 || pinv->inv_class == class) && ((int)type == -1 || pinv->inv_type == type) && ((int)state == -1 || pinv->inv_state == state) && ((int)controller == -1 || pinv->inv_controller == controller) && ((int)unit == -1 || pinv->inv_unit == unit)) { /* Found a matching inventory item. Remove it. */ if (last_pinv) { last_pinv->inv_next = pinv->inv_next; } else { rv = hwgraph_info_replace_LBL(de, INFO_LBL_INVENT, (arbitrary_info_t)pinv->inv_next, NULL); if (rv != LABELCL_SUCCESS) goto failure; } pinv->inv_next = NULL; /* sanity */ kfree(pinv); } else last_pinv = pinv; } if (last_pinv == NULL) { rv = hwgraph_info_remove_LBL(de, INFO_LBL_INVENT, NULL); if (rv != LABELCL_SUCCESS) goto failure; } rv = LABELCL_SUCCESS; failure: /* GRAPH_LOCK_DONE_UPDATE(&invent_lock); */ return(rv); } /* * hwgraph_inventory_get_next - Get next inventory item associated with the * specified vertex. * * No locking is really needed. We don't yet have the ability * to remove inventory items, and new items are always added to * the end of a vertex' inventory list. * * However, a devfs entry can be removed! */ int hwgraph_inventory_get_next(devfs_handle_t de, invplace_t *place, inventory_t **ppinv) { inventory_t *pinv; labelcl_error_t rv; if (de == NULL) return(LABELCL_BAD_PARAM); if (place->invplace_vhdl == NULL) { place->invplace_vhdl = de; place->invplace_inv = NULL; } if (de != place->invplace_vhdl) return(LABELCL_BAD_PARAM); if (place->invplace_inv == NULL) { /* Just starting on this vertex */ rv = labelcl_info_get_LBL(de, INFO_LBL_INVENT, NULL, (arbitrary_info_t *)&pinv); if (rv != LABELCL_SUCCESS) return(LABELCL_NOT_FOUND); } else { /* Advance to next item on this vertex */ pinv = place->invplace_inv->inv_next; } place->invplace_inv = pinv; *ppinv = pinv; return(LABELCL_SUCCESS); } /* * hwgraph_controller_num_get - Returns the controller number in the inventory * entry. */ int hwgraph_controller_num_get(devfs_handle_t device) { inventory_t *pinv; invplace_t invplace = { NULL, NULL, NULL }; int val = -1; if ((pinv = device_inventory_get_next(device, &invplace)) != NULL) { val = (pinv->inv_class == INV_NETWORK)? pinv->inv_unit: pinv->inv_controller; } #ifdef DEBUG /* * It does not make any sense to call this on vertexes with multiple * inventory structs chained together */ if ( device_inventory_get_next(device, &invplace) != NULL ) { printk("Should panic here ... !\n"); #endif return (val); } /* * hwgraph_controller_num_set - Sets the controller number in the inventory * entry. */ void hwgraph_controller_num_set(devfs_handle_t device, int contr_num) { inventory_t *pinv; invplace_t invplace = { NULL, NULL, NULL }; if ((pinv = device_inventory_get_next(device, &invplace)) != NULL) { if (pinv->inv_class == INV_NETWORK) pinv->inv_unit = contr_num; else { if (pinv->inv_class == INV_FCNODE) pinv = device_inventory_get_next(device, &invplace); if (pinv != NULL) pinv->inv_controller = contr_num; } } #ifdef DEBUG /* * It does not make any sense to call this on vertexes with multiple * inventory structs chained together */ if(pinv != NULL) ASSERT(device_inventory_get_next(device, &invplace) == NULL); #endif } /* * Find the canonical name for a given vertex by walking back through * connectpt's until we hit the hwgraph root vertex (or until we run * out of buffer space or until something goes wrong). * * COMPATIBILITY FUNCTIONALITY * Walks back through 'parents', not necessarily the same as connectpts. * * Need to resolve the fact that devfs does not return the path from * "/" but rather it just stops right before /dev .. */ int hwgraph_vertex_name_get(devfs_handle_t vhdl, char *buf, uint buflen) { char *locbuf; int pos; if (buflen < 1) return(-1); /* XXX should be GRAPH_BAD_PARAM ? */ locbuf = kmalloc(buflen, GFP_KERNEL); pos = devfs_generate_path(vhdl, locbuf, buflen); if (pos < 0) { kfree(locbuf); return pos; } strcpy(buf, &locbuf[pos]); kfree(locbuf); return 0; } /* ** vertex_to_name converts a vertex into a canonical name by walking ** back through connect points until we hit the hwgraph root (or until ** we run out of buffer space). ** ** Usually returns a pointer to the original buffer, filled in as ** appropriate. If the buffer is too small to hold the entire name, ** or if anything goes wrong while determining the name, vertex_to_name ** returns "UnknownDevice". */ #define DEVNAME_UNKNOWN "UnknownDevice" char * vertex_to_name(devfs_handle_t vhdl, char *buf, uint buflen) { if (hwgraph_vertex_name_get(vhdl, buf, buflen) == GRAPH_SUCCESS) return(buf); else return(DEVNAME_UNKNOWN); } #ifdef LATER /* ** Return the compact node id of the node that ultimately "owns" the specified ** vertex. In order to do this, we walk back through masters and connect points ** until we reach a vertex that represents a node. */ cnodeid_t master_node_get(devfs_handle_t vhdl) { cnodeid_t cnodeid; devfs_handle_t master; for (;;) { cnodeid = nodevertex_to_cnodeid(vhdl); if (cnodeid != CNODEID_NONE) return(cnodeid); master = device_master_get(vhdl); /* Check for exceptional cases */ if (master == vhdl) { /* Since we got a reference to the "master" thru * device_master_get() we should decrement * its reference count by 1 */ hwgraph_vertex_unref(master); return(CNODEID_NONE); } if (master == GRAPH_VERTEX_NONE) { master = hwgraph_connectpt_get(vhdl); if ((master == GRAPH_VERTEX_NONE) || (master == vhdl)) { if (master == vhdl) /* Since we got a reference to the * "master" thru * hwgraph_connectpt_get() we should * decrement its reference count by 1 */ hwgraph_vertex_unref(master); return(CNODEID_NONE); } } vhdl = master; /* Decrement the reference to "master" which was got * either thru device_master_get() or hwgraph_connectpt_get() * above. */ hwgraph_vertex_unref(master); } } /* * Using the canonical path name to get hold of the desired vertex handle will * not work on multi-hub sn0 nodes. Hence, we use the following (slightly * convoluted) algorithm. * * - Start at the vertex corresponding to the driver (provided as input parameter) * - Loop till you reach a vertex which has EDGE_LBL_MEMORY * - If EDGE_LBL_CONN exists, follow that up. * else if EDGE_LBL_MASTER exists, follow that up. * else follow EDGE_LBL_DOTDOT up. * * * We should be at desired hub/heart vertex now * * - Follow EDGE_LBL_CONN to the widget vertex. * * - return vertex handle of this widget. */ devfs_handle_t mem_vhdl_get(devfs_handle_t drv_vhdl) { devfs_handle_t cur_vhdl, cur_upper_vhdl; devfs_handle_t tmp_mem_vhdl, mem_vhdl; graph_error_t loop_rv; /* Initializations */ cur_vhdl = drv_vhdl; loop_rv = ~GRAPH_SUCCESS; /* Loop till current vertex has EDGE_LBL_MEMORY */ while (loop_rv != GRAPH_SUCCESS) { if ((hwgraph_edge_get(cur_vhdl, EDGE_LBL_CONN, &cur_upper_vhdl)) == GRAPH_SUCCESS) { } else if ((hwgraph_edge_get(cur_vhdl, EDGE_LBL_MASTER, &cur_upper_vhdl)) == GRAPH_SUCCESS) { } else { /* Follow HWGRAPH_EDGELBL_DOTDOT up */ (void) hwgraph_edge_get(cur_vhdl, HWGRAPH_EDGELBL_DOTDOT, &cur_upper_vhdl); } cur_vhdl = cur_upper_vhdl; #if DEBUG && HWG_DEBUG printf("Current vhdl %d \n", cur_vhdl); #endif /* DEBUG */ loop_rv = hwgraph_edge_get(cur_vhdl, EDGE_LBL_MEMORY, &tmp_mem_vhdl); } /* We should be at desired hub/heart vertex now */ if ((hwgraph_edge_get(cur_vhdl, EDGE_LBL_CONN, &mem_vhdl)) != GRAPH_SUCCESS) return (GRAPH_VERTEX_NONE); return (mem_vhdl); } #endif /* LATER */ /* ** Add a char device -- if the driver supports it -- at a specified vertex. */ graph_error_t hwgraph_char_device_add( devfs_handle_t from, char *path, char *prefix, devfs_handle_t *devhdl) { devfs_handle_t xx = NULL; printk("WARNING: hwgraph_char_device_add() not supported .. use hwgraph_register.\n"); *devhdl = xx; // Must set devhdl return(GRAPH_SUCCESS); } graph_error_t hwgraph_edge_remove(devfs_handle_t from, char *name, devfs_handle_t *toptr) { printk("WARNING: hwgraph_edge_remove NOT supported.\n"); return(GRAPH_ILLEGAL_REQUEST); } graph_error_t hwgraph_vertex_unref(devfs_handle_t vhdl) { return(GRAPH_ILLEGAL_REQUEST); } EXPORT_SYMBOL(hwgraph_mk_dir); EXPORT_SYMBOL(hwgraph_path_add); EXPORT_SYMBOL(hwgraph_char_device_add); EXPORT_SYMBOL(hwgraph_register); EXPORT_SYMBOL(hwgraph_vertex_destroy); EXPORT_SYMBOL(hwgraph_fastinfo_get); EXPORT_SYMBOL(hwgraph_edge_get); EXPORT_SYMBOL(hwgraph_fastinfo_set); EXPORT_SYMBOL(hwgraph_connectpt_set); EXPORT_SYMBOL(hwgraph_connectpt_get); EXPORT_SYMBOL(hwgraph_edge_get_next); EXPORT_SYMBOL(hwgraph_info_add_LBL); EXPORT_SYMBOL(hwgraph_info_remove_LBL); EXPORT_SYMBOL(hwgraph_info_replace_LBL); EXPORT_SYMBOL(hwgraph_info_get_LBL); EXPORT_SYMBOL(hwgraph_info_get_exported_LBL); EXPORT_SYMBOL(hwgraph_info_get_next_LBL); EXPORT_SYMBOL(hwgraph_info_export_LBL); EXPORT_SYMBOL(hwgraph_info_unexport_LBL); EXPORT_SYMBOL(hwgraph_path_lookup); EXPORT_SYMBOL(hwgraph_traverse); EXPORT_SYMBOL(hwgraph_path_to_vertex); EXPORT_SYMBOL(hwgraph_path_to_dev); EXPORT_SYMBOL(hwgraph_block_device_get); EXPORT_SYMBOL(hwgraph_char_device_get); EXPORT_SYMBOL(hwgraph_cdevsw_get); EXPORT_SYMBOL(hwgraph_bdevsw_get); EXPORT_SYMBOL(hwgraph_vertex_name_get);