Changes from Andrew
[external/binutils.git] / sim / ppc / device.c
1 /*  This file is part of the program psim.
2
3     Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14  
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  
19     */
20
21
22 #ifndef _DEVICE_C_
23 #define _DEVICE_C_
24
25 #include <stdio.h>
26
27 #include "device_table.h"
28
29 #ifdef HAVE_STDLIB_H
30 #include <stdlib.h>
31 #endif
32
33 #ifdef HAVE_STRING_H
34 #include <string.h>
35 #else
36 #ifdef HAVE_STRINGS_H
37 #include <strings.h>
38 #endif
39 #endif
40
41 #include <ctype.h>
42
43
44
45 typedef struct _device_property_entry device_property_entry;
46 struct _device_property_entry {
47   const char *name;
48   device_property_entry *next;
49   device_property *value;
50 };
51
52
53 /* A device */
54 struct _device {
55   /* my name is ... */
56   const char *name;
57   const char *full_name;
58   /* device tree */
59   device *parent;
60   device *children;
61   device *sibling;
62   /* hw/sw callbacks */
63   void *data; /* device specific data */
64   const device_callbacks *callback;
65   /* device properties */
66   device_property_entry *properties;
67 };
68
69
70 device INLINE_DEVICE *
71 device_create(const char *name,
72               device *parent)
73 {
74   device_descriptor *descr;
75   int name_len;
76   char *chp;
77   chp = strchr(name, '@');
78   name_len = (chp == NULL ? strlen(name) : chp - name);
79   for (descr = device_table; descr->name != NULL; descr++) {
80     if (strncmp(name, descr->name, name_len) == 0
81         && (descr->name[name_len] == '\0'
82             || descr->name[name_len] == '@')) {
83       void *data = (descr->creator != NULL
84                     ? descr->creator(name, parent)
85                     : NULL);
86       return device_create_from(name, data, descr->callbacks, parent);
87     }
88   }
89   error("device_create() unknown device %s\n", name);
90   return NULL;
91 }
92
93 device INLINE_DEVICE *
94 device_create_from(const char *name,
95                    void *data,
96                    const device_callbacks *callbacks,
97                    device *parent)
98 {
99   device *new_device = ZALLOC(device);
100   new_device->data = data;
101   new_device->name = strdup(name);
102   new_device->callback = callbacks;
103   new_device->parent = parent;
104   return new_device;
105 }
106
107
108 device INLINE_DEVICE *
109 device_parent(device *me)
110 {
111   return me->parent;
112 }
113
114 const char INLINE_DEVICE *
115 device_name(device *me)
116 {
117   return me->name;
118 }
119
120 void INLINE_DEVICE *
121 device_data(device *me)
122 {
123   return me->data;
124 }
125
126 void INLINE_DEVICE
127 device_traverse_properties(device *me,
128                            device_traverse_property_function *traverse,
129                            void *data)
130 {
131   device_property_entry *entry = me->properties;
132   while (entry != NULL) {
133     traverse(me, entry->name, data);
134     entry = entry->next;
135   }
136 }
137
138 void INLINE_DEVICE
139 device_init(device *me,
140             psim *system)
141 {
142   me->callback->init(me, system);
143 }
144
145 void INLINE_DEVICE
146 device_attach_address(device *me,
147                       const char *name,
148                       attach_type attach,
149                       int space,
150                       unsigned_word addr,
151                       unsigned nr_bytes,
152                       access_type access,
153                       device *who) /*callback/default*/
154 {
155   me->callback->attach_address(me, name, attach, space,
156                                 addr, nr_bytes, access, who);
157 }
158
159 void INLINE_DEVICE
160 device_detach_address(device *me,
161                       const char *name,
162                       attach_type attach,
163                       int space,
164                       unsigned_word addr,
165                       unsigned nr_bytes,
166                       access_type access,
167                       device *who) /*callback/default*/
168 {
169   me->callback->detach_address(me, name, attach, space,
170                                 addr, nr_bytes, access, who);
171 }
172
173 unsigned INLINE_DEVICE
174 device_io_read_buffer(device *me,
175                       void *dest,
176                       int space,
177                       unsigned_word addr,
178                       unsigned nr_bytes,
179                       cpu *processor,
180                       unsigned_word cia)
181 {
182   return me->callback->io_read_buffer(me, dest, space,
183                                        addr, nr_bytes,
184                                        processor, cia);
185 }
186
187 unsigned INLINE_DEVICE
188 device_io_write_buffer(device *me,
189                        const void *source,
190                        int space,
191                        unsigned_word addr,
192                        unsigned nr_bytes,
193                        cpu *processor,
194                        unsigned_word cia)
195 {
196   return me->callback->io_write_buffer(me, source, space,
197                                        addr, nr_bytes,
198                                        processor, cia);
199 }
200
201 unsigned INLINE_DEVICE
202 device_dma_read_buffer(device *me,
203                        void *dest,
204                        int space,
205                        unsigned_word addr,
206                        unsigned nr_bytes)
207 {
208   return me->callback->dma_read_buffer(me, dest, space,
209                                        addr, nr_bytes);
210 }
211
212 unsigned INLINE_DEVICE
213 device_dma_write_buffer(device *me,
214                         const void *source,
215                         int space,
216                         unsigned_word addr,
217                         unsigned nr_bytes,
218                         int violate_read_only_section)
219 {
220   return me->callback->dma_write_buffer(me, source, space,
221                                         addr, nr_bytes,
222                                         violate_read_only_section);
223 }
224
225 void INLINE_DEVICE
226 device_attach_interrupt(device *me,
227                         device *who,
228                         int interrupt_line,
229                         const char *name)
230 {
231   me->callback->attach_interrupt(me, who, interrupt_line, name);
232 }
233
234 void INLINE_DEVICE
235 device_detach_interrupt(device *me,
236                         device *who,
237                         int interrupt_line,
238                         const char *name)
239 {
240   me->callback->detach_interrupt(me, who, interrupt_line, name);
241 }
242
243 void INLINE_DEVICE
244 device_interrupt(device *me,
245                  device *who,
246                  int interrupt_line,
247                  int interrupt_status,
248                  cpu *processor,
249                  unsigned_word cia)
250 {
251   me->callback->interrupt(me, who, interrupt_line, interrupt_status,
252                            processor, cia);
253 }
254
255 void INLINE_DEVICE
256 device_interrupt_ack(device *me,
257                      int interrupt_line,
258                      int interrupt_status)
259 {
260   me->callback->interrupt_ack(me, interrupt_line, interrupt_status);
261 }
262
263 void EXTERN_DEVICE
264 device_ioctl(device *me,
265              psim *system,
266              cpu *processor,
267              unsigned_word cia,
268              ...)
269 {
270   va_list ap;
271   va_start(ap, cia);
272   me->callback->ioctl(me, system, processor, cia, ap);
273   va_end(ap);
274 }
275       
276
277 /* Manipulate properties attached to devices */
278
279 device_property STATIC_INLINE_DEVICE *
280 device_add_property(device *me,
281                     const char *property,
282                     device_property_type type,
283                     const void *array,
284                     int sizeof_array)
285 {
286   device_property_entry *new_entry = 0;
287   device_property *new_value = 0;
288   void *new_array = 0;
289   /* find the list end */
290   device_property_entry **insertion_point = &me->properties;
291   while (*insertion_point != NULL) {
292     if (strcmp((**insertion_point).name, property) == 0)
293       return (**insertion_point).value;
294     insertion_point = &(**insertion_point).next;
295   }
296   /* alloc data for the new property */
297   new_entry = ZALLOC(device_property_entry);
298   new_value = ZALLOC(device_property);
299   new_array = (sizeof_array > 0
300                ? zalloc(sizeof_array)
301                : (void*)0);
302   /* insert the new property into the list */
303   *insertion_point = new_entry;
304   new_entry->name = strdup(property);
305   new_entry->value = new_value;
306   new_value->type = type;
307   new_value->sizeof_array = sizeof_array;
308   new_value->array = new_array;
309   if (sizeof_array > 0)
310     memcpy(new_array, array, sizeof_array);
311   return new_value;
312 }
313
314 void INLINE_DEVICE
315 device_add_array_property(device *me,
316                           const char *property,
317                           const void *array,
318                           int sizeof_array)
319 {
320   TRACE(trace_devices,
321         ("device_add_array_property(me=0x%lx, property=%s, ...)\n",
322          (long)me, property));
323   device_add_property(me, property,
324                       array_property, array, sizeof_array);
325 }
326
327 void INLINE_DEVICE
328 device_add_integer_property(device *me,
329                             const char *property,
330                             signed32 integer)
331 {
332   TRACE(trace_devices,
333         ("device_add_integer_property(me=0x%lx, property=%s, integer=%ld)\n",
334          (long)me, property, (long)integer));
335   H2BE(integer);
336   device_add_property(me, property, integer_property,
337                       &integer, sizeof(integer));
338 }
339
340 void INLINE_DEVICE
341 device_add_boolean_property(device *me,
342                             const char *property,
343                             int boolean)
344 {
345   signed32 new_boolean = (boolean ? -1 : 0);
346   TRACE(trace_devices,
347         ("device_add_boolean(me=0x%lx, property=%s, boolean=%d)\n",
348          (long)me, property, boolean));
349   device_add_property(me, property, boolean_property,
350                       &new_boolean, sizeof(new_boolean));
351 }
352
353 void INLINE_DEVICE
354 device_add_null_property(device *me,
355                          const char *property)
356 {
357   TRACE(trace_devices,
358         ("device_add_null(me=0x%lx, property=%s)\n",
359          (long)me, property));
360   device_add_property(me, property, null_property,
361                       NULL, 0);
362 }
363
364 void INLINE_DEVICE
365 device_add_string_property(device *me,
366                            const char *property,
367                            const char *string)
368 {
369
370   TRACE(trace_devices,
371         ("device_add_property(me=0x%lx, property=%s, string=%s)\n",
372          (long)me, property, string));
373   device_add_property(me, property, string_property,
374                       string, strlen(string) + 1);
375 }
376
377 const device_property INLINE_DEVICE *
378 device_find_property(device *me,
379                      const char *property)
380 {
381   if (me != (device*)0) {
382     device_property_entry *entry = me->properties;
383     while (entry != (device_property_entry*)0) {
384       if (strcmp(entry->name, property) == 0)
385         return entry->value;
386       entry = entry->next;
387     }
388   }
389   return (device_property*)0;
390 }
391
392 const char INLINE_DEVICE *
393 device_find_next_property(device *me,
394                           const char *property)
395 {
396   if (me != NULL) {
397     if (property == NULL || strcmp(property, "") == 0) {
398       return (me->properties != NULL
399               ? me->properties->name
400               : NULL);
401     }
402     else {
403       device_property_entry *entry = me->properties;
404       while (entry != NULL) {
405         if (strcmp(entry->name, property) == 0)
406           return (entry->next != NULL
407                   ? entry->next->name
408                   : NULL);
409         entry = entry->next;
410       }
411     }
412   }
413   return NULL;
414 }
415
416 const device_property INLINE_DEVICE *
417 device_find_array_property(device *me,
418                            const char *property)
419 {
420   const device_property *node;
421   TRACE(trace_devices,
422         ("device_find_integer(me=0x%lx, property=%s)\n",
423          (long)me, property));
424   node = device_find_property(me, property);
425   if (node == (device_property*)0
426       || node->type != array_property)
427     error("%s property %s not found or of wrong type\n",
428           me->name, property);
429   return node;
430 }
431
432 signed_word INLINE_DEVICE
433 device_find_integer_property(device *me,
434                              const char *property)
435 {
436   const device_property *node;
437   signed32 integer;
438   TRACE(trace_devices,
439         ("device_find_integer(me=0x%lx, property=%s)\n",
440          (long)me, property));
441   node = device_find_property(me, property);
442   if (node == (device_property*)0
443       || node->type != integer_property)
444     error("%s property %s not found or of wrong type\n",
445           me->name, property);
446   ASSERT(sizeof(integer) == node->sizeof_array);
447   memcpy(&integer, node->array, sizeof(integer));
448   BE2H(integer);
449   return integer;
450 }
451
452 int INLINE_DEVICE
453 device_find_boolean_property(device *me,
454                              const char *property)
455 {
456   const device_property *node;
457   unsigned32 boolean;
458   TRACE(trace_devices,
459         ("device_find_boolean(me=0x%lx, property=%s)\n",
460          (long)me, property));
461   node = device_find_property(me, property);
462   if (node == (device_property*)0
463       || node->type != boolean_property)
464     error("%s property %s not found or of wrong type\n",
465           me->name, property);
466   ASSERT(sizeof(boolean) == node->sizeof_array);
467   memcpy(&boolean, node->array, sizeof(boolean));
468   return boolean;
469 }
470
471 const char INLINE_DEVICE *
472 device_find_string_property(device *me,
473                             const char *property)
474 {
475   const device_property *node;
476   const char *string;
477   TRACE(trace_devices,
478         ("device_find_string(me=0x%lx, property=%s)\n",
479          (long)me, property));
480   node = device_find_property(me, property);
481   if (node == (device_property*)0
482       || node->type != string_property)
483     error("%s property %s not found or of wrong type\n",
484           me->name, property);
485   string = node->array;
486   ASSERT(strlen(string) + 1 == node->sizeof_array);
487   return string;
488 }
489
490
491 /* determine the full name of the device.  If buf is specified it is
492    stored in there.  Failing that, a safe area of memory is allocated */
493 const char STATIC_INLINE_DEVICE *
494 device_tree_full_name(device *leaf,
495                       char *buf,
496                       unsigned sizeof_buf)
497 {
498   /* get a buffer */
499   char full_name[1024];
500   if (buf == (char*)0) {
501     buf = full_name;
502     sizeof_buf = sizeof(full_name);
503   }
504
505   /* construct a name */
506   if (leaf->parent == NULL) {
507     if (sizeof_buf < 1)
508       error("device_full_name() buffer overflow\n");
509     *buf = '\0';
510   }
511   else {
512     device_tree_full_name(leaf->parent, buf, sizeof_buf);
513     if (strlen(buf) + strlen("/") + strlen(leaf->name) + 1 > sizeof_buf)
514       error("device_full_name() buffer overflow\n");
515     strcat(buf, "/");
516     strcat(buf, leaf->name);
517   }
518   
519   /* return it usefully */
520   if (buf == full_name)
521     buf = strdup(full_name);
522   return buf;
523 }
524
525
526 /* find/create a node in the device tree */
527
528 typedef enum {
529   device_tree_return_null = 2,
530   device_tree_abort = 3,
531 } device_tree_action;
532
533 device STATIC_INLINE_DEVICE *
534 device_tree_find_node(device *root,
535                       const char *path,
536                       const char *full_path,
537                       device_tree_action action)
538 {
539   const char *name;
540   int strlen_name;
541   device *child;
542
543   /* strip off any leading `/', `../' or `./' */
544   while (1) {
545     if (strncmp(path, "/", strlen("/")) == 0) {
546       while (root != NULL && root->parent != NULL)
547         root = root->parent;
548       path += strlen("/");
549     }
550     else if (strncmp(path, "./", strlen("./")) == 0) {
551       root = root;
552       path += strlen("./");
553     }
554     else if (strncmp(path, "../", strlen("../")) == 0) {
555       if (root != NULL && root->parent != NULL)
556         root = root->parent;
557       path += strlen("../");
558     }
559     else {
560       break;
561     }
562   }
563
564   /* parse the driver_name/unit-address */
565   ASSERT(*path != '/');
566   name = path;
567   while (isalnum(*path)
568          || *path == ',' || *path == ',' || *path == '_'
569          || *path == '+' || *path == '-')
570     path++;
571   if ((*path != '/' && *path != '@' && *path != ':' && *path != '\0')
572       || (name == path && *name != '\0'))
573     error("device_tree: path %s invalid at %s\n", full_path, path);
574
575   /* parse the unit-address */
576   if (*path == '@') {
577     path++;
578     while ((*path != '\0' && *path != ':' && *path != '/')
579            || (*path == ':' && path[-1] == '\\')
580            || (*path == '/' && path[-1] == '\\'))
581       path++;
582   }
583   strlen_name = path - name;
584
585   /* skip the device-arguments */
586   if (*path == ':') {
587     path++;
588     while ((*path != '\0' && *path != '/' && *path != ':' && *path != '@')
589            || (*path == '/' && path[-1] == '\\')
590            || (*path == ':' && path[-1] == '\\')
591            || (*path == '@' && path[-1] == '\\'))
592       path++;
593   }
594
595   /* sanity checks */
596   if (*path != '\0' && *path != '/')
597     error("device_tree: path %s invalid at %s\n", full_path, path);
598
599   /* leaf? and growing? */
600   if (name[0] == '\0') {
601     return root;
602   }
603   else if (root != NULL) {
604     for (child = root->children;
605          child != NULL;
606          child = child->sibling) {
607       if (strncmp(name, child->name, strlen_name) == 0
608           && strlen(child->name) == strlen_name) {
609         if (*path == '\0')
610           return child;
611         else
612           return device_tree_find_node(child,
613                                        path + 1/* / */,
614                                        full_path,
615                                        action);
616       }
617     }
618   }
619
620   /* search failed, take default action */
621   switch (action) {
622   case device_tree_return_null:
623     return NULL;
624   case device_tree_abort:
625     error("device_tree_find_node() could not find %s in tree\n",
626           full_path);
627     return NULL;
628   default:
629     error("device_tree_find_node() invalid default action %d\n", action);
630     return NULL;
631   }
632 }
633
634
635 /* grow the device tree */
636
637 device INLINE_DEVICE *
638 device_tree_add_device(device *root,
639                        const char *prefix,
640                        device *new_sub_tree)
641 {
642   device *parent;
643   TRACE(trace_device_tree,
644         ("device_tree_add_device(root=0x%lx, prefix=%s, dev=0x%lx)\n",
645          (long)root, prefix, (long)new_sub_tree));
646
647   /* find our parent */
648   parent = device_tree_find_node(root,
649                                  prefix,
650                                  prefix, /* full-path */
651                                  device_tree_abort);
652
653   /* create/insert a new child */
654   new_sub_tree->parent = parent;
655   if (parent != NULL) {
656     device **sibling = &parent->children;
657     while ((*sibling) != NULL)
658       sibling = &(*sibling)->sibling;
659     *sibling = new_sub_tree;
660   }
661
662   return new_sub_tree;
663 }
664
665 device INLINE_DEVICE *
666 device_tree_find_device(device *root,
667                         const char *path)
668 {
669   device *node;
670   TRACE(trace_device_tree,
671         ("device_tree_find_device_tree(root=0x%lx, path=%s)\n",
672          (long)root, path));
673   node = device_tree_find_node(root,
674                                path,
675                                path, /* full-name */
676                                device_tree_return_null);
677   return node;
678 }
679
680
681 /* init all the devices */
682
683 void STATIC_INLINE_DEVICE
684 device_tree_init_device(device *root,
685                         void *data)
686 {
687   psim *system;
688   system = (psim*)data;
689   TRACE(trace_device_tree,
690         ("device_tree_init() initializing device=0x%lx:%s\n",
691          (long)root, root->full_name));
692   device_init(root, system);
693 }
694
695
696 void INLINE_DEVICE
697 device_tree_init(device *root,
698                  psim *system)
699 {
700   TRACE(trace_device_tree,
701         ("device_tree_init(root=0x%lx, system=0x%lx)\n", (long)root, (long)system));
702   device_tree_traverse(root, device_tree_init_device, NULL, system);
703   TRACE(trace_device_tree,
704         ("device_tree_init() = void\n"));
705 }
706
707
708 /* traverse a device tree applying prefix/postfix functions to it */
709
710 void INLINE_DEVICE
711 device_tree_traverse(device *root,
712                      device_tree_traverse_function *prefix,
713                      device_tree_traverse_function *postfix,
714                      void *data)
715 {
716   device *child;
717   if (prefix != NULL)
718     prefix(root, data);
719   for (child = root->children; child != NULL; child = child->sibling) {
720     device_tree_traverse(child, prefix, postfix, data);
721   }
722   if (postfix != NULL)
723     postfix(root, data);
724 }
725
726
727 /* dump out a device node and addresses */
728
729 void INLINE_DEVICE
730 device_tree_dump(device *device,
731                  void *ignore_data_argument)
732 {
733   printf_filtered("(device_tree@0x%lx\n", (long)device);
734   printf_filtered(" (parent 0x%lx)\n", (long)device->parent);
735   printf_filtered(" (children 0x%lx)\n", (long)device->children);
736   printf_filtered(" (sibling 0x%lx)\n", (long)device->sibling);
737   printf_filtered(" (name %s)\n", device->name);
738   error("FIXME - need to print out properties\n");
739   printf_filtered(")\n");
740 }
741
742
743 /* lookup/create a device various formats */
744
745 void STATIC_INLINE_DEVICE
746 u_strcat(char *buf,
747          unsigned_word uw)
748 {
749   if (MASKED64(uw, 32, 63) == uw
750       || WITH_HOST_WORD_BITSIZE == 64) {
751     char *end = strchr(buf, '\0');
752     sprintf(end, "0x%x", (unsigned)uw);
753   }
754   else {
755     char *end = strchr(buf, '\0');
756     sprintf(end, "0x%x%08x",
757             (unsigned)EXTRACTED64(uw, 0, 31),
758             (unsigned)EXTRACTED64(uw, 32, 63));
759   }
760 }
761
762 void STATIC_INLINE_DEVICE
763 c_strcat(char *buf,
764         const char *c)
765 {
766   char *end = strchr(buf, '\0');
767   while (*c) {
768     if (*c == '/' || *c == ',')
769       *end++ = '\\';
770     *end++ = *c++;
771   }
772   *end = '\0';
773 }
774
775 device INLINE_DEVICE *
776 device_tree_add_found(device *root,
777                       const char *prefix,
778                       const char *name)
779 {
780   device *parent;
781   device *new_device;
782   device *new_node;
783   TRACE(trace_device_tree,
784         ("device_tree_add_found(root=0x%lx, prefix=%s, name=%x)\n",
785          (long)root, prefix, name));
786   parent = device_tree_find_node(root, prefix, prefix,
787                                  device_tree_abort);
788   new_device = device_tree_find_device(parent, name);
789   if (new_device != NULL)
790     return new_device;
791   else {
792     new_device = device_create(name, parent);
793     new_node = device_tree_add_device(parent, "", new_device);
794     ASSERT(new_device == new_node);
795     return new_node;
796   }
797 }
798
799 device INLINE_DEVICE *
800 device_tree_add_found_c(device *root,
801                         const char *prefix,
802                         const char *name,
803                         const char *c1)
804 {
805   char buf[1024];
806   strcpy(buf, name);
807   strcat(buf, "@");
808   c_strcat(buf, c1);
809   if (strlen(buf) + 1 >= sizeof(buf))
810     error("device_tree_add_found_c - buffer overflow\n");
811   return device_tree_add_found(root, prefix, buf);
812 }
813
814 device INLINE_DEVICE *
815 device_tree_add_found_c_uw(device *root,
816                            const char *prefix,
817                            const char *name,
818                            const char *c1,
819                            unsigned_word uw2)
820 {
821   char buf[1024];
822   strcpy(buf, name);
823   strcat(buf, "@");
824   c_strcat(buf, c1);
825   strcat(buf, ",");
826   u_strcat(buf, uw2);
827   if (strlen(buf) + 1 >= sizeof(buf))
828     error("device_tree_add_found_* - buffer overflow\n");
829   return device_tree_add_found(root, prefix, buf);
830 }
831
832 device INLINE_DEVICE *
833 device_tree_add_found_uw_u(device *root,
834                            const char *prefix,
835                            const char *name,
836                            unsigned_word uw1,
837                            unsigned u2)
838 {
839   char buf[1024];
840   strcpy(buf, name);
841   strcat(buf, "@");
842   u_strcat(buf, uw1);
843   strcat(buf, ",");
844   u_strcat(buf, u2);
845   if (strlen(buf) + 1 >= sizeof(buf))
846     error("device_tree_add_found_* - buffer overflow\n");
847   return device_tree_add_found(root, prefix, buf);
848 }
849
850 device INLINE_DEVICE *
851 device_tree_add_found_uw_u_u(device *root,
852                              const char *prefix,
853                              const char *name,
854                              unsigned_word uw1,
855                              unsigned u2,
856                              unsigned u3)
857 {
858   char buf[1024];
859   strcpy(buf, name);
860   strcat(buf, "@");
861   u_strcat(buf, uw1);
862   strcat(buf, ",");
863   u_strcat(buf, u2);
864   strcat(buf, ",");
865   u_strcat(buf, u3);
866   if (strlen(buf) + 1 >= sizeof(buf))
867     error("device_tree_add_found_* - buffer overflow\n");
868   return device_tree_add_found(root, prefix, buf);
869 }
870
871 device INLINE_DEVICE *
872 device_tree_add_found_uw_u_u_c(device *root,
873                                const char *prefix,
874                                const char *name,
875                                unsigned_word uw1,
876                                unsigned u2,
877                                unsigned u3,
878                                const char *c4)
879 {
880   char buf[1024];
881   strcpy(buf, name);
882   strcat(buf, "@");
883   u_strcat(buf, uw1);
884   strcat(buf, ",");
885   u_strcat(buf, u2);
886   strcat(buf, ",");
887   u_strcat(buf, u3);
888   strcat(buf, ",");
889   c_strcat(buf, c4);
890   if (strlen(buf) + 1 >= sizeof(buf))
891     error("device_tree_add_found_* - buffer overflow\n");
892   return device_tree_add_found(root, prefix, buf);
893 }
894
895 device INLINE_DEVICE *
896 device_tree_add_found_uw_uw_u_u_c(device *root,
897                                   const char *prefix,
898                                   const char *name,
899                                   unsigned_word uw1,
900                                   unsigned_word uw2,
901                                   unsigned u3,
902                                   unsigned u4,
903                                   const char *c5)
904 {
905   char buf[1024];
906   strcpy(buf, name);
907   strcat(buf, "@");
908   u_strcat(buf, uw1);
909   strcat(buf, ",");
910   u_strcat(buf, uw2);
911   strcat(buf, ",");
912   u_strcat(buf, u3);
913   strcat(buf, ",");
914   u_strcat(buf, u4);
915   strcat(buf, ",");
916   c_strcat(buf, c5);
917   if (strlen(buf) + 1 >= sizeof(buf))
918     error("device_tree_add_found_* - buffer overflow\n");
919   return device_tree_add_found(root, prefix, buf);
920 }
921
922 device INLINE_DEVICE *
923 device_tree_add_found_uw_uw_u_u_u(device *root,
924                                   const char *prefix,
925                                   const char *name,
926                                   unsigned_word uw1,
927                                   unsigned_word uw2,
928                                   unsigned u3,
929                                   unsigned u4,
930                                   unsigned u5)
931 {
932   char buf[1024];
933   strcpy(buf, name);
934   strcat(buf, "@");
935   u_strcat(buf, uw1);
936   strcat(buf, ",");
937   u_strcat(buf, uw2);
938   strcat(buf, ",");
939   u_strcat(buf, u3);
940   strcat(buf, ",");
941   u_strcat(buf, u4);
942   strcat(buf, ",");
943   u_strcat(buf, u5);
944   if (strlen(buf) + 1 >= sizeof(buf))
945     error("device_tree_add_found_* - buffer overflow\n");
946   return device_tree_add_found(root, prefix, buf);
947 }
948
949
950 /* Parse a device name, various formats */
951
952 #define SCAN_INIT(NAME) \
953   char *START = (char*)0; \
954   char *END = (char*)0; \
955   int COUNT = -1; \
956   /* find the first element */ \
957   END = strchr(NAME, '@'); \
958   if (END == (char*)0) \
959     return COUNT; \
960   COUNT += 1; \
961   START = END + 1
962
963 #define SCAN_END \
964   return COUNT
965
966 #define SCAN_U(U) \
967 do { \
968   *U = strtoul(START, &END, 0); \
969   if (START == END) \
970     return COUNT; \
971   COUNT += 1; \
972   if (*END != ',') \
973     return COUNT; \
974   START = END + 1; \
975 } while (0)
976
977 #define SCAN_P(P) \
978 do { \
979   *P = (void*)(unsigned)strtouq(START, END, 0); \
980   if (START == END) \
981     return COUNT; \
982   COUNT += 1; \
983   if (*END != ',') \
984     return COUNT; \
985   START = END + 1; \
986 } while (0)
987
988 #define SCAN_C(C, SIZE) \
989 do { \
990   char *chp = C; \
991   END = START; \
992   while (*END != '\0' && *END != ',') { \
993     if (*END == '\\') \
994       END += 1; \
995     *chp = *END; \
996     chp += 1; \
997     END += 1; \
998     if ((SIZE) <= ((END) - (START))) \
999       return COUNT; /* overflow */ \
1000   } \
1001   *chp = '\0'; \
1002   if (START == END) \
1003     return COUNT; \
1004   COUNT += 1; \
1005   if (*END != ',') \
1006     return COUNT; \
1007   START = END + 1; \
1008 } while (0)
1009
1010 int INLINE_DEVICE
1011 scand_c(const char *name,
1012         char *c1,
1013         unsigned c1size)
1014 {
1015   SCAN_INIT(name);
1016   SCAN_C(c1, c1size);
1017   SCAN_END;
1018 }
1019
1020 int INLINE_DEVICE
1021 scand_c_uw_u(const char *name,
1022              char *c1,
1023              unsigned c1size,
1024              unsigned_word *uw2,
1025              unsigned *u3)
1026 {
1027   SCAN_INIT(name);
1028   SCAN_C(c1, c1size);
1029   SCAN_U(uw2);
1030   SCAN_U(u3);
1031   SCAN_END;
1032 }
1033
1034 int INLINE_DEVICE
1035 scand_uw(const char *name,
1036          unsigned_word *uw1)
1037 {
1038   SCAN_INIT(name);
1039   SCAN_U(uw1);
1040   SCAN_END;
1041 }
1042
1043 int INLINE_DEVICE
1044 scand_uw_c(const char *name,
1045            unsigned_word *uw1,
1046            char *c2,
1047            unsigned c2size)
1048 {
1049   SCAN_INIT(name);
1050   SCAN_U(uw1);
1051   SCAN_C(c2, c2size);
1052   SCAN_END;
1053 }
1054
1055 int INLINE_DEVICE
1056 scand_uw_u(const char *name,
1057            unsigned_word *uw1,
1058            unsigned *u2)
1059 {
1060   SCAN_INIT(name);
1061   SCAN_U(uw1);
1062   SCAN_U(u2);
1063   SCAN_END;
1064 }
1065
1066 int INLINE_DEVICE
1067 scand_uw_u_u(const char *name,
1068              unsigned_word *uw1,
1069              unsigned *u2,
1070              unsigned *u3)
1071 {
1072   SCAN_INIT(name);
1073   SCAN_U(uw1);
1074   SCAN_U(u2);
1075   SCAN_U(u3);
1076   SCAN_END;
1077 }
1078
1079 int INLINE_DEVICE
1080 scand_uw_u_u_c(const char *name,
1081                unsigned_word *uw1,
1082                unsigned *u2,
1083                unsigned *u3,
1084                char *c4,
1085                unsigned c4size)
1086 {
1087   SCAN_INIT(name);
1088   SCAN_U(uw1);
1089   SCAN_U(u2);
1090   SCAN_U(u3);
1091   SCAN_C(c4, c4size);
1092   SCAN_END;
1093 }
1094
1095 int INLINE_DEVICE
1096 scand_uw_uw(const char *name,
1097             unsigned_word *uw1,
1098             unsigned_word *uw2)
1099 {
1100   SCAN_INIT(name);
1101   SCAN_U(uw1);
1102   SCAN_U(uw2);
1103   SCAN_END;
1104 }
1105
1106 int INLINE_DEVICE
1107 scand_uw_uw_u(const char *name,
1108               unsigned_word *uw1,
1109               unsigned_word *uw2,
1110               unsigned *u3)
1111 {
1112   SCAN_INIT(name);
1113   SCAN_U(uw1);
1114   SCAN_U(uw2);
1115   SCAN_U(u3);
1116   SCAN_END;
1117 }
1118
1119 int INLINE_DEVICE
1120 scand_uw_uw_u_u_c(const char *name,
1121                   unsigned_word *uw1,
1122                   unsigned_word *uw2,
1123                   unsigned *u3,
1124                   unsigned *u4,
1125                   char *c5,
1126                   unsigned c5size)
1127 {
1128   SCAN_INIT(name);
1129   SCAN_U(uw1);
1130   SCAN_U(uw2);
1131   SCAN_U(u3);
1132   SCAN_U(u4);
1133   SCAN_C(c5, c5size);
1134   SCAN_END;
1135 }
1136
1137 int INLINE_DEVICE
1138 scand_uw_uw_u_u_u(const char *name,
1139                   unsigned_word *uw1,
1140                   unsigned_word *uw2,
1141                   unsigned *u3,
1142                   unsigned *u4,
1143                   unsigned *u5)
1144 {
1145   SCAN_INIT(name);
1146   SCAN_U(uw1);
1147   SCAN_U(uw2);
1148   SCAN_U(u3);
1149   SCAN_U(u4);
1150   SCAN_U(u5);
1151   SCAN_END;
1152 }
1153
1154
1155 #endif /* _DEVICE_C_ */