ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / ia64 / sn / io / hwgfs / hcl.c
1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  *  hcl - SGI's Hardware Graph compatibility layer.
7  *
8  * Copyright (C) 1992-1997,2000-2003 Silicon Graphics, Inc. All rights reserved.
9  */
10
11 #include <linux/types.h>
12 #include <linux/config.h>
13 #include <linux/slab.h>
14 #include <linux/ctype.h>
15 #include <linux/module.h>
16 #include <linux/init.h>
17 #include <linux/kernel.h>
18 #include <linux/fs.h>
19 #include <linux/string.h>
20 #include <linux/sched.h>                /* needed for smp_lock.h :( */
21 #include <linux/smp_lock.h>
22 #include <asm/sn/sgi.h>
23 #include <asm/io.h>
24 #include <asm/sn/iograph.h>
25 #include <asm/sn/hwgfs.h>
26 #include <asm/sn/hcl.h>
27 #include <asm/sn/labelcl.h>
28 #include <asm/sn/simulator.h>
29
30 #define vertex_hdl_t hwgfs_handle_t
31
32 vertex_hdl_t hwgraph_root;
33 vertex_hdl_t linux_busnum;
34 extern int pci_bus_cvlink_init(void);
35 unsigned long hwgraph_debug_mask;
36
37 /*
38  * init_hcl() - Boot time initialization.
39  *
40  */
41 int __init init_hcl(void)
42 {
43         extern void string_table_init(struct string_table *);
44         extern struct string_table label_string_table;
45         extern int init_ioconfig_bus(void);
46         extern int init_hwgfs_fs(void);
47         int rv = 0;
48
49         init_hwgfs_fs();
50
51         /*
52          * Create the hwgraph_root.
53          */
54         rv = hwgraph_path_add(NULL, EDGE_LBL_HW, &hwgraph_root);
55         if (rv) {
56                 printk("init_hcl: Failed to create hwgraph_root.\n");
57                 return -1;
58         }
59
60         /*
61          * Initialize the HCL string table.
62          */
63
64         string_table_init(&label_string_table);
65
66         /*
67          * Create the directory that links Linux bus numbers to our Xwidget.
68          */
69         rv = hwgraph_path_add(hwgraph_root, EDGE_LBL_LINUX_BUS, &linux_busnum);
70         if (linux_busnum == NULL) {
71                 printk("HCL: Unable to create %s\n", EDGE_LBL_LINUX_BUS);
72                 return -1;
73         }
74
75         if (pci_bus_cvlink_init() < 0 ) {
76                 printk("init_hcl: Failed to create pcibus cvlink.\n");
77                 return -1;
78         }
79
80         /*
81          * Persistent Naming.
82          */
83         init_ioconfig_bus();
84
85         return 0;
86 }
87
88 /*
89  * Get device specific "fast information".
90  *
91  */
92 arbitrary_info_t
93 hwgraph_fastinfo_get(vertex_hdl_t de)
94 {
95         arbitrary_info_t fastinfo;
96         int rv;
97
98         if (!de) {
99                 printk(KERN_WARNING "HCL: hwgraph_fastinfo_get handle given is NULL.\n");
100                 dump_stack();
101                 return(-1);
102         }
103
104         rv = labelcl_info_get_IDX(de, HWGRAPH_FASTINFO, &fastinfo);
105         if (rv == 0)
106                 return(fastinfo);
107
108         return(0);
109 }
110
111
112 /*
113  * hwgraph_connectpt_get: Returns the entry's connect point.
114  *
115  */
116 vertex_hdl_t
117 hwgraph_connectpt_get(vertex_hdl_t de)
118 {
119         int rv;
120         arbitrary_info_t info;
121         vertex_hdl_t connect;
122
123         rv = labelcl_info_get_IDX(de, HWGRAPH_CONNECTPT, &info);
124         if (rv != 0) {
125                 return(NULL);
126         }
127
128         connect = (vertex_hdl_t)info;
129         return(connect);
130
131 }
132
133
134 /*
135  * hwgraph_mk_dir - Creates a directory entry.
136  */
137 vertex_hdl_t
138 hwgraph_mk_dir(vertex_hdl_t de, const char *name,
139                 unsigned int namelen, void *info)
140 {
141
142         int rv;
143         labelcl_info_t *labelcl_info = NULL;
144         vertex_hdl_t new_handle = NULL;
145         vertex_hdl_t parent = NULL;
146
147         /*
148          * Create the device info structure for hwgraph compatiblity support.
149          */
150         labelcl_info = labelcl_info_create();
151         if (!labelcl_info)
152                 return(NULL);
153
154         /*
155          * Create an entry.
156          */
157         new_handle = hwgfs_mk_dir(de, name, (void *)labelcl_info);
158         if (!new_handle) {
159                 labelcl_info_destroy(labelcl_info);
160                 return(NULL);
161         }
162
163         /*
164          * Get the parent handle.
165          */
166         parent = hwgfs_get_parent (new_handle);
167
168         /*
169          * To provide the same semantics as the hwgraph, set the connect point.
170          */
171         rv = hwgraph_connectpt_set(new_handle, parent);
172         if (!rv) {
173                 /*
174                  * We need to clean up!
175                  */
176         }
177
178         /*
179          * If the caller provides a private data pointer, save it in the 
180          * labelcl info structure(fastinfo).  This can be retrieved via
181          * hwgraph_fastinfo_get()
182          */
183         if (info)
184                 hwgraph_fastinfo_set(new_handle, (arbitrary_info_t)info);
185                 
186         return(new_handle);
187
188 }
189
190 /*
191  * hwgraph_path_add - Create a directory node with the given path starting 
192  * from the given fromv.
193  */
194 int
195 hwgraph_path_add(vertex_hdl_t  fromv,
196                  char *path,
197                  vertex_hdl_t *new_de)
198 {
199
200         unsigned int    namelen = strlen(path);
201         int             rv;
202
203         /*
204          * We need to handle the case when fromv is NULL ..
205          * in this case we need to create the path from the 
206          * hwgraph root!
207          */
208         if (fromv == NULL)
209                 fromv = hwgraph_root;
210
211         /*
212          * check the entry doesn't already exist, if it does
213          * then we simply want new_de to point to it (otherwise
214          * we'll overwrite the existing labelcl_info struct)
215          */
216         rv = hwgraph_edge_get(fromv, path, new_de);
217         if (rv) {       /* couldn't find entry so we create it */
218                 *new_de = hwgraph_mk_dir(fromv, path, namelen, NULL);
219                 if (new_de == NULL)
220                         return(-1);
221                 else
222                         return(0);
223         }
224         else 
225                 return(0);
226
227 }
228
229 /*
230  * hwgraph_register  - Creates a special device file.
231  *
232  */
233 vertex_hdl_t
234 hwgraph_register(vertex_hdl_t de, const char *name,
235                 unsigned int namelen, unsigned int flags, 
236                 unsigned int major, unsigned int minor,
237                 umode_t mode, uid_t uid, gid_t gid, 
238                 struct file_operations *fops,
239                 void *info)
240 {
241
242         vertex_hdl_t new_handle = NULL;
243
244         /*
245          * Create an entry.
246          */
247         new_handle = hwgfs_register(de, name, flags, major,
248                                 minor, mode, fops, info);
249
250         return(new_handle);
251
252 }
253
254
255 /*
256  * hwgraph_mk_symlink - Create a symbolic link.
257  */
258 int
259 hwgraph_mk_symlink(vertex_hdl_t de, const char *name, unsigned int namelen,
260                 unsigned int flags, const char *link, unsigned int linklen, 
261                 vertex_hdl_t *handle, void *info)
262 {
263
264         void *labelcl_info = NULL;
265         int status = 0;
266         vertex_hdl_t new_handle = NULL;
267
268         /*
269          * Create the labelcl info structure for hwgraph compatiblity support.
270          */
271         labelcl_info = labelcl_info_create();
272         if (!labelcl_info)
273                 return(-1);
274
275         /*
276          * Create a symbolic link.
277          */
278         status = hwgfs_mk_symlink(de, name, flags, link,
279                                 &new_handle, labelcl_info);
280         if ( (!new_handle) || (!status) ){
281                 labelcl_info_destroy((labelcl_info_t *)labelcl_info);
282                 return(-1);
283         }
284
285         /*
286          * If the caller provides a private data pointer, save it in the 
287          * labelcl info structure(fastinfo).  This can be retrieved via
288          * hwgraph_fastinfo_get()
289          */
290         if (info)
291                 hwgraph_fastinfo_set(new_handle, (arbitrary_info_t)info);
292
293         *handle = new_handle;
294         return(0);
295
296 }
297
298 /*
299  * hwgraph_vertex_destroy - Destroy the entry
300  */
301 int
302 hwgraph_vertex_destroy(vertex_hdl_t de)
303 {
304
305         void *labelcl_info = NULL;
306
307         labelcl_info = hwgfs_get_info(de);
308         hwgfs_unregister(de);
309
310         if (labelcl_info)
311                 labelcl_info_destroy((labelcl_info_t *)labelcl_info);
312
313         return(0);
314 }
315
316 int
317 hwgraph_edge_add(vertex_hdl_t from, vertex_hdl_t to, char *name)
318 {
319
320         char *path, *link;
321         char *s1;
322         char *index;
323         vertex_hdl_t handle = NULL;
324         int rv;
325         int i, count;
326
327         path = kmalloc(1024, GFP_KERNEL);
328         if (!path)
329                 return -ENOMEM;
330         memset((char *)path, 0x0, 1024);
331         link = kmalloc(1024, GFP_KERNEL);
332         if (!link) {
333                 kfree(path);
334                 return -ENOMEM;
335         }
336         memset((char *)link, 0x0, 1024);
337
338         i = hwgfs_generate_path (from, path, 1024);
339         s1 = (char *)path;
340         count = 0;
341         while (1) {
342                 index = strstr (s1, "/");
343                 if (index) {
344                         count++;
345                         s1 = ++index;
346                 } else {
347                         count++;
348                         break;
349                 }
350         }
351
352         for (i = 0; i < count; i++) {
353                 strcat((char *)link,"../");
354         }
355
356         memset(path, 0x0, 1024);
357         i = hwgfs_generate_path (to, path, 1024);
358         strcat((char *)link, (char *)path);
359
360         /*
361          * Otherwise, just create a symlink to the vertex.
362          * In this case the vertex was previous created with a REAL pathname.
363          */
364         rv = hwgfs_mk_symlink (from, (const char *)name,
365                                0, link,
366                                &handle, NULL);
367         kfree(path);
368         kfree(link);
369
370         return(rv);
371
372
373 }
374
375 /* ARGSUSED */
376 int
377 hwgraph_edge_get(vertex_hdl_t from, char *name, vertex_hdl_t *toptr)
378 {
379
380         vertex_hdl_t target_handle = NULL;
381
382         if (name == NULL)
383                 return(-1);
384
385         if (toptr == NULL)
386                 return(-1);
387
388         /*
389          * If the name is "." just return the current entry handle.
390          */
391         if (!strcmp(name, HWGRAPH_EDGELBL_DOT)) {
392                 if (toptr) {
393                         *toptr = from;
394                 }
395         } else if (!strcmp(name, HWGRAPH_EDGELBL_DOTDOT)) {
396                 /*
397                  * Hmmm .. should we return the connect point or parent ..
398                  * see in hwgraph, the concept of parent is the connectpt!
399                  *
400                  * Maybe we should see whether the connectpt is set .. if 
401                  * not just return the parent!
402                  */
403                 target_handle = hwgraph_connectpt_get(from);
404                 if (target_handle) {
405                         /*
406                          * Just return the connect point.
407                          */
408                         *toptr = target_handle;
409                         return(0);
410                 }
411                 target_handle = hwgfs_get_parent(from);
412                 *toptr = target_handle;
413
414         } else {
415                 target_handle = hwgfs_find_handle (from, name, 0, 0,
416                                         0, 1); /* Yes traverse symbolic links */
417         }
418
419         if (target_handle == NULL)
420                 return(-1);
421         else
422          *toptr = target_handle;
423
424         return(0);
425 }
426
427 /*
428  * hwgraph_info_add_LBL - Adds a new label for the device.  Mark the info_desc
429  *      of the label as INFO_DESC_PRIVATE and store the info in the label.
430  */
431 /* ARGSUSED */
432 int
433 hwgraph_info_add_LBL( vertex_hdl_t de,
434                         char *name,
435                         arbitrary_info_t info)
436 {
437         return(labelcl_info_add_LBL(de, name, INFO_DESC_PRIVATE, info));
438 }
439
440 /*
441  * hwgraph_info_remove_LBL - Remove the label entry for the device.
442  */
443 /* ARGSUSED */
444 int
445 hwgraph_info_remove_LBL( vertex_hdl_t de,
446                                 char *name,
447                                 arbitrary_info_t *old_info)
448 {
449         return(labelcl_info_remove_LBL(de, name, NULL, old_info));
450 }
451
452 /*
453  * hwgraph_info_replace_LBL - replaces an existing label with 
454  *      a new label info value.
455  */
456 /* ARGSUSED */
457 int
458 hwgraph_info_replace_LBL( vertex_hdl_t de,
459                                 char *name,
460                                 arbitrary_info_t info,
461                                 arbitrary_info_t *old_info)
462 {
463         return(labelcl_info_replace_LBL(de, name,
464                         INFO_DESC_PRIVATE, info,
465                         NULL, old_info));
466 }
467 /*
468  * hwgraph_info_get_LBL - Get and return the info value in the label of the 
469  *      device.
470  */
471 /* ARGSUSED */
472 int
473 hwgraph_info_get_LBL(vertex_hdl_t de,
474                         char *name,
475                         arbitrary_info_t *infop)
476 {
477         return(labelcl_info_get_LBL(de, name, NULL, infop));
478 }
479
480 /*
481  * hwgraph_info_get_exported_LBL - Retrieve the info_desc and info pointer 
482  *      of the given label for the device.  The weird thing is that the label 
483  *      that matches the name is return irrespective of the info_desc value!
484  *      Do not understand why the word "exported" is used!
485  */
486 /* ARGSUSED */
487 int
488 hwgraph_info_get_exported_LBL(vertex_hdl_t de,
489                                 char *name,
490                                 int *export_info,
491                                 arbitrary_info_t *infop)
492 {
493         int rc;
494         arb_info_desc_t info_desc;
495
496         rc = labelcl_info_get_LBL(de, name, &info_desc, infop);
497         if (rc == 0)
498                 *export_info = (int)info_desc;
499
500         return(rc);
501 }
502
503 /*
504  * hwgraph_info_get_next_LBL - Returns the next label info given the 
505  *      current label entry in place.
506  *
507  *      Once again this has no locking or reference count for protection.
508  *
509  */
510 /* ARGSUSED */
511 int
512 hwgraph_info_get_next_LBL(vertex_hdl_t de,
513                                 char *buf,
514                                 arbitrary_info_t *infop,
515                                 labelcl_info_place_t *place)
516 {
517         return(labelcl_info_get_next_LBL(de, buf, NULL, infop, place));
518 }
519
520 /*
521  * hwgraph_info_export_LBL - Retrieve the specified label entry and modify 
522  *      the info_desc field with the given value in nbytes.
523  */
524 /* ARGSUSED */
525 int
526 hwgraph_info_export_LBL(vertex_hdl_t de, char *name, int nbytes)
527 {
528         arbitrary_info_t info;
529         int rc;
530
531         if (nbytes == 0)
532                 nbytes = INFO_DESC_EXPORT;
533
534         if (nbytes < 0)
535                 return(-1);
536
537         rc = labelcl_info_get_LBL(de, name, NULL, &info);
538         if (rc != 0)
539                 return(rc);
540
541         rc = labelcl_info_replace_LBL(de, name,
542                                 nbytes, info, NULL, NULL);
543
544         return(rc);
545 }
546
547 /*
548  * hwgraph_info_unexport_LBL - Retrieve the given label entry and change the 
549  * label info_descr filed to INFO_DESC_PRIVATE.
550  */
551 /* ARGSUSED */
552 int
553 hwgraph_info_unexport_LBL(vertex_hdl_t de, char *name)
554 {
555         arbitrary_info_t info;
556         int rc;
557
558         rc = labelcl_info_get_LBL(de, name, NULL, &info);
559         if (rc != 0)
560                 return(rc);
561
562         rc = labelcl_info_replace_LBL(de, name,
563                                 INFO_DESC_PRIVATE, info, NULL, NULL);
564
565         return(rc);
566 }
567
568 /*
569  * hwgraph_traverse - Find and return the handle starting from de.
570  *
571  */
572 graph_error_t
573 hwgraph_traverse(vertex_hdl_t de, char *path, vertex_hdl_t *found)
574 {
575         /* 
576          * get the directory entry (path should end in a directory)
577          */
578
579         *found = hwgfs_find_handle(de,  /* start dir */
580                             path,       /* path */
581                             0,          /* major */
582                             0,          /* minor */
583                             0,          /* char | block */
584                             1);         /* traverse symlinks */
585         if (*found == NULL)
586                 return(GRAPH_NOT_FOUND);
587         else
588                 return(GRAPH_SUCCESS);
589 }
590
591 /*
592  * Find the canonical name for a given vertex by walking back through
593  * connectpt's until we hit the hwgraph root vertex (or until we run
594  * out of buffer space or until something goes wrong).
595  *
596  *      COMPATIBILITY FUNCTIONALITY
597  * Walks back through 'parents', not necessarily the same as connectpts.
598  *
599  * Need to resolve the fact that does not return the path from 
600  * "/" but rather it just stops right before /dev ..
601  */
602 int
603 hwgraph_vertex_name_get(vertex_hdl_t vhdl, char *buf, unsigned int buflen)
604 {
605         char *locbuf;
606         int   pos;
607
608         if (buflen < 1)
609                 return(-1);     /* XXX should be GRAPH_BAD_PARAM ? */
610
611         locbuf = kmalloc(buflen, GFP_KERNEL);
612
613         pos = hwgfs_generate_path(vhdl, locbuf, buflen);
614         if (pos < 0) {
615                 kfree(locbuf);
616                 return pos;
617         }
618
619         strcpy(buf, &locbuf[pos]);
620         kfree(locbuf);
621         return 0;
622 }
623
624 /*
625 ** vertex_to_name converts a vertex into a canonical name by walking
626 ** back through connect points until we hit the hwgraph root (or until
627 ** we run out of buffer space).
628 **
629 ** Usually returns a pointer to the original buffer, filled in as
630 ** appropriate.  If the buffer is too small to hold the entire name,
631 ** or if anything goes wrong while determining the name, vertex_to_name
632 ** returns "UnknownDevice".
633 */
634
635 #define DEVNAME_UNKNOWN "UnknownDevice"
636
637 char *
638 vertex_to_name(vertex_hdl_t vhdl, char *buf, unsigned int buflen)
639 {
640         if (hwgraph_vertex_name_get(vhdl, buf, buflen) == GRAPH_SUCCESS)
641                 return(buf);
642         else
643                 return(DEVNAME_UNKNOWN);
644 }
645
646
647 void
648 hwgraph_debug(char *file, const char * function, int line, vertex_hdl_t vhdl1, vertex_hdl_t vhdl2, char *format, ...)
649 {
650
651         int pos;
652         char *hwpath;
653         va_list ap;
654
655         if ( !hwgraph_debug_mask )
656                 return;
657
658         hwpath = kmalloc(MAXDEVNAME, GFP_KERNEL);
659         if (!hwpath) {
660                 printk("HWGRAPH_DEBUG kmalloc fails at %d ", __LINE__);
661                 return;
662         }
663
664         printk("HWGRAPH_DEBUG %s %s %d : ", file, function, line);
665
666         if (vhdl1){
667                 memset(hwpath, 0, MAXDEVNAME);
668                 pos = hwgfs_generate_path(vhdl1, hwpath, MAXDEVNAME);
669                 printk("vhdl1 = %s : ", &hwpath[pos]);
670         }
671
672         if (vhdl2){
673                 memset(hwpath, 0, MAXDEVNAME);
674                 pos = hwgfs_generate_path(vhdl2, hwpath, MAXDEVNAME);
675                 printk("vhdl2 = %s :", &hwpath[pos]);
676         }
677
678         memset(hwpath, 0, MAXDEVNAME);
679         va_start(ap, format);
680         vsnprintf(hwpath, 500, format, ap);
681         va_end(ap);
682         hwpath[MAXDEVNAME -1] = (char)0; /* Just in case. */
683         printk(" %s", hwpath);
684         kfree(hwpath);
685 }
686
687 EXPORT_SYMBOL(hwgraph_mk_dir);
688 EXPORT_SYMBOL(hwgraph_path_add);
689 EXPORT_SYMBOL(hwgraph_register);
690 EXPORT_SYMBOL(hwgraph_vertex_destroy);
691 EXPORT_SYMBOL(hwgraph_fastinfo_get);
692 EXPORT_SYMBOL(hwgraph_connectpt_get);
693 EXPORT_SYMBOL(hwgraph_info_add_LBL);
694 EXPORT_SYMBOL(hwgraph_info_remove_LBL);
695 EXPORT_SYMBOL(hwgraph_info_replace_LBL);
696 EXPORT_SYMBOL(hwgraph_info_get_LBL);
697 EXPORT_SYMBOL(hwgraph_info_get_exported_LBL);
698 EXPORT_SYMBOL(hwgraph_info_get_next_LBL);
699 EXPORT_SYMBOL(hwgraph_info_export_LBL);
700 EXPORT_SYMBOL(hwgraph_info_unexport_LBL);
701 EXPORT_SYMBOL(hwgraph_traverse);
702 EXPORT_SYMBOL(hwgraph_vertex_name_get);