/* labelcl - SGI's Hwgraph Compatibility Layer. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Colin Ngam may be reached by email at cngam@sgi.com */ #include #include #include #include #include #include #include #include /* ** Very simple and dumb string table that supports only find/insert. ** In practice, if this table gets too large, we may need a more ** efficient data structure. Also note that currently there is no ** way to delete an item once it's added. Therefore, name collision ** will return an error. */ struct string_table label_string_table; /* * string_table_init - Initialize the given string table. */ void string_table_init(struct string_table *string_table) { string_table->string_table_head = NULL; string_table->string_table_generation = 0; /* * We nedd to initialize locks here! */ return; } /* * string_table_destroy - Destroy the given string table. */ void string_table_destroy(struct string_table *string_table) { struct string_table_item *item, *next_item; item = string_table->string_table_head; while (item) { next_item = item->next; STRTBL_FREE(item); item = next_item; } /* * We need to destroy whatever lock we have here */ return; } /* * string_table_insert - Insert an entry in the string table .. duplicate * names are not allowed. */ char * string_table_insert(struct string_table *string_table, char *name) { struct string_table_item *item, *new_item = NULL, *last_item = NULL; again: /* * Need to lock the table .. */ item = string_table->string_table_head; last_item = NULL; while (item) { if (!strcmp(item->string, name)) { /* * If we allocated space for the string and the found that * someone else already entered it into the string table, * free the space we just allocated. */ if (new_item) STRTBL_FREE(new_item); /* * Search optimization: move the found item to the head * of the list. */ if (last_item != NULL) { last_item->next = item->next; item->next = string_table->string_table_head; string_table->string_table_head = item; } goto out; } last_item = item; item=item->next; } /* * name was not found, so add it to the string table. */ if (new_item == NULL) { long old_generation = string_table->string_table_generation; new_item = STRTBL_ALLOC(strlen(name)); strcpy(new_item->string, name); /* * While we allocated memory for the new string, someone else * changed the string table. */ if (old_generation != string_table->string_table_generation) { goto again; } } else { /* At this we only have the string table lock in access mode. * Promote the access lock to an update lock for the string * table insertion below. */ long old_generation = string_table->string_table_generation; /* * After we did the unlock and wer waiting for update * lock someone could have potentially updated * the string table. Check the generation number * for this case. If it is the case we have to * try all over again. */ if (old_generation != string_table->string_table_generation) { goto again; } } /* * At this point, we're committed to adding new_item to the string table. */ new_item->next = string_table->string_table_head; item = string_table->string_table_head = new_item; string_table->string_table_generation++; out: /* * Need to unlock here. */ return(item->string); } /* * labelcl_info_create - Creates the data structure that will hold the * device private information asscoiated with a devfs entry. * The pointer to this structure is what gets stored in the devfs * (void * info). */ labelcl_info_t * labelcl_info_create() { labelcl_info_t *new = NULL; /* Initial allocation does not include any area for labels */ if ( ( new = (labelcl_info_t *)kmalloc (sizeof(labelcl_info_t), GFP_KERNEL) ) == NULL ) return NULL; memset (new, 0, sizeof(labelcl_info_t)); new->hwcl_magic = LABELCL_MAGIC; return( new); } /* * labelcl_info_destroy - Frees the data structure that holds the * device private information asscoiated with a devfs entry. This * data structure was created by device_info_create(). * * The caller is responsible for nulling the (void *info) in the * corresponding devfs entry. */ int labelcl_info_destroy(labelcl_info_t *labelcl_info) { if (labelcl_info == NULL) return(0); /* Free the label list */ if (labelcl_info->label_list) kfree(labelcl_info->label_list); /* Now free the label info area */ labelcl_info->hwcl_magic = 0; kfree(labelcl_info); return(0); } /* * labelcl_info_add_LBL - Adds a new label entry in the labelcl info * structure. * * Error is returned if we find another label with the same name. */ int labelcl_info_add_LBL(devfs_handle_t de, char *info_name, arb_info_desc_t info_desc, arbitrary_info_t info) { labelcl_info_t *labelcl_info = NULL; int num_labels; int new_label_list_size; label_info_t *old_label_list, *new_label_list = NULL; char *name; int i; if (de == NULL) return(-1); labelcl_info = devfs_get_info(de); if (labelcl_info == NULL) return(-1); if (labelcl_info->hwcl_magic != LABELCL_MAGIC) return(-1); if (info_name == NULL) return(-1); if (strlen(info_name) >= LABEL_LENGTH_MAX) return(-1); name = string_table_insert(&label_string_table, info_name); num_labels = labelcl_info->num_labels; new_label_list_size = sizeof(label_info_t) * (num_labels+1); /* * Create a new label info area. */ if (new_label_list_size != 0) { new_label_list = (label_info_t *) kmalloc(new_label_list_size, GFP_KERNEL); if (new_label_list == NULL) return(-1); } /* * At this point, we are committed to adding the labelled info, * if there isn't already information there with the same name. */ old_label_list = labelcl_info->label_list; /* * Look for matching info name. */ for (i=0; inum_labels = num_labels+1; labelcl_info->label_list = new_label_list; if (old_label_list != NULL) kfree(old_label_list); return(0); } /* * labelcl_info_remove_LBL - Remove a label entry. */ int labelcl_info_remove_LBL(devfs_handle_t de, char *info_name, arb_info_desc_t *info_desc, arbitrary_info_t *info) { labelcl_info_t *labelcl_info = NULL; int num_labels; int new_label_list_size; label_info_t *old_label_list, *new_label_list = NULL; arb_info_desc_t label_desc_found; arbitrary_info_t label_info_found; int i; if (de == NULL) return(-1); labelcl_info = devfs_get_info(de); if (labelcl_info == NULL) return(-1); if (labelcl_info->hwcl_magic != LABELCL_MAGIC) return(-1); num_labels = labelcl_info->num_labels; if (num_labels == 0) { return(-1); } /* * Create a new info area. */ new_label_list_size = sizeof(label_info_t) * (num_labels-1); if (new_label_list_size) { new_label_list = (label_info_t *) kmalloc(new_label_list_size, GFP_KERNEL); if (new_label_list == NULL) return(-1); } /* * At this point, we are committed to removing the labelled info, * if it still exists. */ old_label_list = labelcl_info->label_list; /* * Find matching info name. */ for (i=0; inum_labels = num_labels+1; labelcl_info->label_list = new_label_list; kfree(old_label_list); if (info != NULL) *info = label_info_found; if (info_desc != NULL) *info_desc = label_desc_found; return(0); } /* * labelcl_info_replace_LBL - Replace an existing label entry with the * given new information. * * Label entry must exist. */ int labelcl_info_replace_LBL(devfs_handle_t de, char *info_name, arb_info_desc_t info_desc, arbitrary_info_t info, arb_info_desc_t *old_info_desc, arbitrary_info_t *old_info) { labelcl_info_t *labelcl_info = NULL; int num_labels; label_info_t *label_list; int i; if (de == NULL) return(-1); labelcl_info = devfs_get_info(de); if (labelcl_info == NULL) return(-1); if (labelcl_info->hwcl_magic != LABELCL_MAGIC) return(-1); num_labels = labelcl_info->num_labels; if (num_labels == 0) { return(-1); } if (info_name == NULL) return(-1); label_list = labelcl_info->label_list; /* * Verify that information under info_name already exists. */ for (i=0; ihwcl_magic != LABELCL_MAGIC) return(-1); num_labels = labelcl_info->num_labels; if (num_labels == 0) { return(-1); } label_list = labelcl_info->label_list; /* * Find information under info_name. */ for (i=0; ihwcl_magic != LABELCL_MAGIC) return(-1); which_info = *placeptr; if (which_info >= labelcl_info->num_labels) { return(-1); } label_list = (label_info_t *) labelcl_info->label_list; if (buffer != NULL) strcpy(buffer, label_list[which_info].name); if (infop) *infop = label_list[which_info].info; if (info_descp) *info_descp = label_list[which_info].desc; *placeptr = which_info + 1; return(0); } int labelcl_info_replace_IDX(devfs_handle_t de, int index, arbitrary_info_t info, arbitrary_info_t *old_info) { arbitrary_info_t *info_list_IDX; labelcl_info_t *labelcl_info = NULL; if (de == NULL) { printk(KERN_ALERT "labelcl: NULL devfs handle given.\n"); return(-1); } labelcl_info = devfs_get_info(de); if (labelcl_info == NULL) { printk(KERN_ALERT "labelcl: Entry does not have info pointer.\n"); return(-1); } if (labelcl_info->hwcl_magic != LABELCL_MAGIC) return(-1); if ( (index < 0) || (index >= HWGRAPH_NUM_INDEX_INFO) ) return(-1); /* * Replace information at the appropriate index in this vertex with * the new info. */ info_list_IDX = labelcl_info->IDX_list; if (old_info != NULL) *old_info = info_list_IDX[index]; info_list_IDX[index] = info; return(0); } /* * labelcl_info_connectpt_set - Sets the connectpt. */ int labelcl_info_connectpt_set(struct devfs_entry *de, struct devfs_entry *connect_de) { arbitrary_info_t old_info; int rv; rv = labelcl_info_replace_IDX(de, HWGRAPH_CONNECTPT, (arbitrary_info_t) connect_de, &old_info); if (rv) { return(rv); } return(0); } /* * labelcl_info_get_IDX - Returns the information pointed at by index. * */ int labelcl_info_get_IDX(devfs_handle_t de, int index, arbitrary_info_t *info) { arbitrary_info_t *info_list_IDX; labelcl_info_t *labelcl_info = NULL; if (de == NULL) return(-1); labelcl_info = devfs_get_info(de); if (labelcl_info == NULL) return(-1); if (labelcl_info->hwcl_magic != LABELCL_MAGIC) return(-1); if ( (index < 0) || (index >= HWGRAPH_NUM_INDEX_INFO) ) return(-1); /* * Return information at the appropriate index in this vertex. */ info_list_IDX = labelcl_info->IDX_list; if (info != NULL) *info = info_list_IDX[index]; return(0); } /* * labelcl_info_connectpt_get - Retrieve the connect point for a device entry. */ struct devfs_entry * labelcl_info_connectpt_get(struct devfs_entry *de) { int rv; arbitrary_info_t info; rv = labelcl_info_get_IDX(de, HWGRAPH_CONNECTPT, &info); if (rv) return(NULL); return((struct devfs_entry *)info); }