/[gxemul]/upstream/0.4.6/src/device.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /upstream/0.4.6/src/device.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 43 - (show annotations)
Mon Oct 8 16:22:43 2007 UTC (16 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 11478 byte(s)
0.4.6
1 /*
2 * Copyright (C) 2005-2007 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * $Id: device.c,v 1.34 2007/06/15 17:02:37 debug Exp $
29 *
30 * Device registry framework.
31 */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include "device.h"
38 #include "machine.h"
39 #include "memory.h"
40 #include "misc.h"
41
42
43 static struct device_entry *device_entries = NULL;
44 static int device_entries_sorted = 0;
45 static int n_device_entries = 0;
46 static int device_exit_on_error = 1;
47
48 static struct pci_entry *pci_entries = NULL;
49 static int n_pci_entries = 0;
50
51
52 /*
53 * device_entry_compar():
54 *
55 * Internal function, used by sort_entries().
56 */
57 static int device_entry_compar(const void *a, const void *b)
58 {
59 struct device_entry *pa = (struct device_entry *) a;
60 struct device_entry *pb = (struct device_entry *) b;
61
62 return strcmp(pa->name, pb->name);
63 }
64
65
66 /*
67 * sort_entries():
68 *
69 * Internal function. Sorts the device_entries array in alphabetic order.
70 */
71 static void sort_entries(void)
72 {
73 qsort(device_entries, n_device_entries, sizeof(struct device_entry),
74 device_entry_compar);
75
76 device_entries_sorted = 1;
77 }
78
79
80 /*
81 * device_register():
82 *
83 * Registers a device. The device is added to the end of the device_entries
84 * array, and the sorted flag is set to zero.
85 *
86 * NOTE: It would be a bad thing if two devices had the same name. However,
87 * that isn't checked here, it is up to the caller!
88 *
89 * Return value is 1 if the device was registered, 0 otherwise.
90 */
91 int device_register(char *name, int (*initf)(struct devinit *))
92 {
93 CHECK_ALLOCATION(device_entries = realloc(device_entries,
94 sizeof(struct device_entry) * (n_device_entries + 1)));
95
96 memset(&device_entries[n_device_entries], 0,
97 sizeof(struct device_entry));
98
99 CHECK_ALLOCATION(device_entries[n_device_entries].name = strdup(name));
100 device_entries[n_device_entries].initf = initf;
101
102 device_entries_sorted = 0;
103 n_device_entries ++;
104 return 1;
105 }
106
107
108 /*
109 * pci_register():
110 *
111 * Registers a pci device. The pci device is added to the pci_entries array.
112 *
113 * Return value is 1 if the pci device was registered. If it was not
114 * added, this function does not return.
115 */
116 int pci_register(char *name, void (*initf)(struct machine *, struct memory *,
117 struct pci_device *))
118 {
119 CHECK_ALLOCATION(pci_entries = realloc(pci_entries,
120 sizeof(struct pci_entry) * (n_pci_entries + 1)));
121
122 memset(&pci_entries[n_pci_entries], 0, sizeof(struct pci_entry));
123
124 CHECK_ALLOCATION(pci_entries[n_pci_entries].name = strdup(name));
125 pci_entries[n_pci_entries].initf = initf;
126 n_pci_entries ++;
127 return 1;
128 }
129
130
131 /*
132 * pci_lookup_initf():
133 *
134 * Find a pci device init function by scanning the pci_entries array.
135 *
136 * Return value is a function pointer, or NULL if the name was not found.
137 */
138 void (*pci_lookup_initf(const char *name))(struct machine *machine,
139 struct memory *mem, struct pci_device *pd)
140 {
141 int i;
142
143 if (name == NULL) {
144 fprintf(stderr, "pci_lookup_initf(): name = NULL\n");
145 exit(1);
146 }
147
148 for (i=0; i<n_pci_entries; i++)
149 if (strcmp(name, pci_entries[i].name) == 0)
150 return pci_entries[i].initf;
151 return NULL;
152 }
153
154
155 /*
156 * device_lookup():
157 *
158 * Lookup a device name by scanning the device_entries array (as a binary
159 * search tree).
160 *
161 * Return value is a pointer to the device_entry on success, or a NULL pointer
162 * if there was no such device.
163 */
164 struct device_entry *device_lookup(char *name)
165 {
166 int hi, lo;
167
168 if (name == NULL) {
169 fprintf(stderr, "device_lookup(): NULL ptr\n");
170 exit(1);
171 }
172
173 if (!device_entries_sorted)
174 sort_entries();
175
176 if (n_device_entries == 0)
177 return NULL;
178
179 lo = 0; hi = n_device_entries - 1;
180
181 while (lo <= hi) {
182 int r, i = (lo + hi) / 2;
183
184 /* printf("device_lookup(): i=%i (lo=%i hi=%i)\n", i, lo, hi);
185 printf(" name='%s', '%s'\n", name,
186 device_entries[i].name); */
187
188 r = strcmp(name, device_entries[i].name);
189 if (r == 0) {
190 /* Found it! */
191 return &device_entries[i];
192 }
193
194 /* Try left or right half: */
195 if (r < 0)
196 hi = i - 1;
197 if (r > 0)
198 lo = i + 1;
199 }
200
201 return NULL;
202 }
203
204
205 /*
206 * device_unregister():
207 *
208 * Unregisters a device.
209 *
210 * Return value is 1 if a device was unregistered, 0 otherwise.
211 */
212 int device_unregister(char *name)
213 {
214 ssize_t i;
215 struct device_entry *p = device_lookup(name);
216
217 if (p == NULL) {
218 fatal("device_unregister(): no such device (\"%s\")\n", name);
219 return 0;
220 }
221
222 i = (size_t)p - (size_t)device_entries;
223 i /= sizeof(struct device_entry);
224
225 free(device_entries[i].name);
226 device_entries[i].name = NULL;
227
228 if (i == n_device_entries-1) {
229 /* Do nothing if we're removing the last array element. */
230 } else {
231 /* Remove array element i by copying the last element
232 to i's position: */
233 device_entries[i] = device_entries[n_device_entries-1];
234
235 /* The array is not sorted anymore: */
236 device_entries_sorted = 0;
237 }
238
239 n_device_entries --;
240
241 /* TODO: realloc? */
242 return 1;
243 }
244
245
246 /*
247 * device_add():
248 *
249 * Add a device to a machine. For example: "kn210 addr=0x12340000" adds a
250 * device called "kn210" at a specific address.
251 *
252 * TODO: This function is quite ugly, and should be cleaned up.
253 * name_and_params should be const.
254 */
255 void *device_add(struct machine *machine, char *name_and_params)
256 {
257 struct device_entry *p;
258 struct devinit devinit;
259 char *s2, *s3;
260 size_t len, interrupt_path_len = strlen(machine->path) + 100;
261 int quoted;
262
263 memset(&devinit, 0, sizeof(struct devinit));
264 devinit.machine = machine;
265
266 /* Default values: */
267 devinit.addr_mult = 1;
268 devinit.in_use = 1;
269
270 /* Get the device name first: */
271 s2 = name_and_params;
272 while (s2[0] != ',' && s2[0] != ' ' && s2[0] != '\0')
273 s2 ++;
274
275 len = (size_t)s2 - (size_t)name_and_params;
276 CHECK_ALLOCATION(devinit.name = malloc(len + 1));
277 memcpy(devinit.name, name_and_params, len);
278 devinit.name[len] = '\0';
279
280 /* Allocate space for the default interrupt name: */
281 CHECK_ALLOCATION(devinit.interrupt_path =
282 malloc(interrupt_path_len + 1));
283 snprintf(devinit.interrupt_path, interrupt_path_len,
284 "%s.cpu[%i]", machine->path, machine->bootstrap_cpu);
285
286 p = device_lookup(devinit.name);
287 if (p == NULL) {
288 fatal("no such device (\"%s\")\n", devinit.name);
289 if (device_exit_on_error)
290 exit(1);
291 else
292 goto return_fail;
293 }
294
295 /* Get params from name_and_params: */
296 while (*s2 != '\0') {
297 /* Skip spaces, commas, and semicolons: */
298 while (*s2 == ' ' || *s2 == ',' || *s2 == ';')
299 s2 ++;
300
301 if (*s2 == '\0')
302 break;
303
304 /* s2 now points to the next param. eg "addr=1234" */
305
306 /* Get a word (until there is a '=' sign): */
307 s3 = s2;
308 while (*s3 != '=' && *s3 != '\0')
309 s3 ++;
310 if (s3 == s2) {
311 fatal("weird param: %s\n", s2);
312 if (device_exit_on_error)
313 exit(1);
314 else
315 goto return_fail;
316 }
317 s3 ++;
318 /* s3 now points to the parameter value ("1234") */
319
320 if (strncmp(s2, "addr=", 5) == 0) {
321 devinit.addr = mystrtoull(s3, NULL, 0);
322 } else if (strncmp(s2, "addr2=", 6) == 0) {
323 devinit.addr2 = mystrtoull(s3, NULL, 0);
324 } else if (strncmp(s2, "len=", 4) == 0) {
325 devinit.len = mystrtoull(s3, NULL, 0);
326 } else if (strncmp(s2, "addr_mult=", 10) == 0) {
327 devinit.addr_mult = mystrtoull(s3, NULL, 0);
328 } else if (strncmp(s2, "pci_little_endian=", 18) == 0) {
329 devinit.pci_little_endian = mystrtoull(s3, NULL, 0);
330 switch (devinit.pci_little_endian) {
331 case 0: break;
332 case 1: devinit.pci_little_endian =
333 MEM_PCI_LITTLE_ENDIAN;
334 break;
335 default:fatal("Bad pci_little_endian value.\n");
336 exit(1);
337 }
338 } else if (strncmp(s2, "irq=", 4) == 0) {
339 snprintf(devinit.interrupt_path, interrupt_path_len,s3);
340 if (strchr(devinit.interrupt_path, ' ') != NULL)
341 *strchr(devinit.interrupt_path, ' ') = '\0';
342 } else if (strncmp(s2, "in_use=", 7) == 0) {
343 devinit.in_use = mystrtoull(s3, NULL, 0);
344 } else if (strncmp(s2, "name2=", 6) == 0) {
345 char *h = s2 + 6;
346 size_t len = 0;
347 quoted = 0;
348 while (*h) {
349 if (*h == '\'')
350 quoted = !quoted;
351 h++, len++;
352 if (!quoted && *h == ' ')
353 break;
354 }
355 CHECK_ALLOCATION(devinit.name2 = malloc(len + 1));
356 h = s2 + 6;
357 if (*h == '\'')
358 len -= 2, h++;
359 snprintf(devinit.name2, len + 1, h);
360 } else {
361 fatal("unknown param: %s\n", s2);
362 if (device_exit_on_error)
363 exit(1);
364 else
365 goto return_fail;
366 }
367
368 /* skip to the next param: */
369 s2 = s3;
370 quoted = 0;
371 while (*s2 != '\0' && (*s2 != ' ' || quoted) &&
372 *s2 != ',' && *s2 != ';') {
373 if (*s2 == '\'')
374 quoted = !quoted;
375 s2 ++;
376 }
377 }
378
379
380 /*
381 * Call the init function for this device:
382 */
383
384 devinit.return_ptr = NULL;
385
386 if (!p->initf(&devinit)) {
387 fatal("error adding device (\"%s\")\n", name_and_params);
388 if (device_exit_on_error)
389 exit(1);
390 else
391 goto return_fail;
392 }
393
394 free(devinit.interrupt_path);
395 free(devinit.name);
396 return devinit.return_ptr;
397
398 return_fail:
399 free(devinit.name);
400 return NULL;
401 }
402
403
404 /*
405 * device_dumplist():
406 *
407 * Dump a list of all registered devices. (If the list is not sorted when
408 * this function is called, it is implicitly sorted.)
409 */
410 void device_dumplist(void)
411 {
412 int i;
413
414 if (!device_entries_sorted)
415 sort_entries();
416
417 for (i=0; i<n_device_entries; i++) {
418 debug(" %s", device_entries[i].name);
419
420 /* TODO: flags? */
421
422 debug("\n");
423 }
424 }
425
426
427 /*
428 * device_set_exit_on_error():
429 *
430 * This function selects the behaviour of the emulator when a device is not
431 * found. During startup, it is nicest to abort the whole emulator session,
432 * but if a device addition is attempted from within the debugger, then it is
433 * nicer to just print a warning and continue.
434 */
435 void device_set_exit_on_error(int exit_on_error)
436 {
437 device_exit_on_error = exit_on_error;
438 }
439
440
441 /*
442 * device_init():
443 *
444 * Initialize the device registry, and call autodev_init() to automatically
445 * add all normal devices (from the src/devices/ directory).
446 *
447 * This function should be called before any other device_*() function is used.
448 */
449 void device_init(void)
450 {
451 device_entries = NULL;
452 device_entries_sorted = 0;
453 n_device_entries = 0;
454
455 autodev_init();
456 }
457

  ViewVC Help
Powered by ViewVC 1.1.26