/[gxemul]/upstream/0.4.4/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.4/src/device.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 35 - (show annotations)
Mon Oct 8 16:21:26 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 11993 byte(s)
0.4.4
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.32 2006/12/30 13:30:51 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 device_entries = realloc(device_entries, sizeof(struct device_entry)
94 * (n_device_entries + 1));
95 if (device_entries == NULL) {
96 fprintf(stderr, "device_register(): out of memory\n");
97 exit(1);
98 }
99
100 memset(&device_entries[n_device_entries], 0,
101 sizeof(struct device_entry));
102
103 device_entries[n_device_entries].name = strdup(name);
104 device_entries[n_device_entries].initf = initf;
105
106 device_entries_sorted = 0;
107 n_device_entries ++;
108 return 1;
109 }
110
111
112 /*
113 * pci_register():
114 *
115 * Registers a pci device. The pci device is added to the pci_entries array.
116 *
117 * Return value is 1 if the pci device was registered. If it was not
118 * added, this function does not return.
119 */
120 int pci_register(char *name, void (*initf)(struct machine *, struct memory *,
121 struct pci_device *))
122 {
123 pci_entries = realloc(pci_entries, sizeof(struct pci_entry)
124 * (n_pci_entries + 1));
125 if (pci_entries == NULL) {
126 fprintf(stderr, "pci_register(): out of memory\n");
127 exit(1);
128 }
129
130 memset(&pci_entries[n_pci_entries], 0, sizeof(struct pci_entry));
131
132 pci_entries[n_pci_entries].name = strdup(name);
133 pci_entries[n_pci_entries].initf = initf;
134 n_pci_entries ++;
135 return 1;
136 }
137
138
139 /*
140 * pci_lookup_initf():
141 *
142 * Find a pci device init function by scanning the pci_entries array.
143 *
144 * Return value is a function pointer, or NULL if the name was not found.
145 */
146 void (*pci_lookup_initf(const char *name))(struct machine *machine,
147 struct memory *mem, struct pci_device *pd)
148 {
149 int i;
150
151 if (name == NULL) {
152 fprintf(stderr, "pci_lookup_initf(): name = NULL\n");
153 exit(1);
154 }
155
156 for (i=0; i<n_pci_entries; i++)
157 if (strcmp(name, pci_entries[i].name) == 0)
158 return pci_entries[i].initf;
159 return NULL;
160 }
161
162
163 /*
164 * device_lookup():
165 *
166 * Lookup a device name by scanning the device_entries array (as a binary
167 * search tree).
168 *
169 * Return value is a pointer to the device_entry on success, or a NULL pointer
170 * if there was no such device.
171 */
172 struct device_entry *device_lookup(char *name)
173 {
174 int hi, lo;
175
176 if (name == NULL) {
177 fprintf(stderr, "device_lookup(): NULL ptr\n");
178 exit(1);
179 }
180
181 if (!device_entries_sorted)
182 sort_entries();
183
184 if (n_device_entries == 0)
185 return NULL;
186
187 lo = 0; hi = n_device_entries - 1;
188
189 while (lo <= hi) {
190 int r, i = (lo + hi) / 2;
191
192 /* printf("device_lookup(): i=%i (lo=%i hi=%i)\n", i, lo, hi);
193 printf(" name='%s', '%s'\n", name,
194 device_entries[i].name); */
195
196 r = strcmp(name, device_entries[i].name);
197 if (r == 0) {
198 /* Found it! */
199 return &device_entries[i];
200 }
201
202 /* Try left or right half: */
203 if (r < 0)
204 hi = i - 1;
205 if (r > 0)
206 lo = i + 1;
207 }
208
209 return NULL;
210 }
211
212
213 /*
214 * device_unregister():
215 *
216 * Unregisters a device.
217 *
218 * Return value is 1 if a device was unregistered, 0 otherwise.
219 */
220 int device_unregister(char *name)
221 {
222 ssize_t i;
223 struct device_entry *p = device_lookup(name);
224
225 if (p == NULL) {
226 fatal("device_unregister(): no such device (\"%s\")\n", name);
227 return 0;
228 }
229
230 i = (size_t)p - (size_t)device_entries;
231 i /= sizeof(struct device_entry);
232
233 free(device_entries[i].name);
234 device_entries[i].name = NULL;
235
236 if (i == n_device_entries-1) {
237 /* Do nothing if we're removing the last array element. */
238 } else {
239 /* Remove array element i by copying the last element
240 to i's position: */
241 device_entries[i] = device_entries[n_device_entries-1];
242
243 /* The array is not sorted anymore: */
244 device_entries_sorted = 0;
245 }
246
247 n_device_entries --;
248
249 /* TODO: realloc? */
250 return 1;
251 }
252
253
254 /*
255 * device_add():
256 *
257 * Add a device to a machine. For example: "kn210 addr=0x12340000" adds a
258 * device called "kn210" at a specific address.
259 *
260 * TODO: This function is quite ugly, and should be cleaned up.
261 * name_and_params should be const.
262 */
263 void *device_add(struct machine *machine, char *name_and_params)
264 {
265 struct device_entry *p;
266 struct devinit devinit;
267 char *s2, *s3;
268 size_t len, interrupt_path_len = strlen(machine->path) + 100;
269 int quoted;
270
271 memset(&devinit, 0, sizeof(struct devinit));
272 devinit.machine = machine;
273
274 /* Default values: */
275 devinit.addr_mult = 1;
276 devinit.in_use = 1;
277
278 /* Get the device name first: */
279 s2 = name_and_params;
280 while (s2[0] != ',' && s2[0] != ' ' && s2[0] != '\0')
281 s2 ++;
282
283 len = (size_t)s2 - (size_t)name_and_params;
284 devinit.name = malloc(len + 1);
285 if (devinit.name == NULL) {
286 fprintf(stderr, "device_add(): out of memory\n");
287 exit(1);
288 }
289 memcpy(devinit.name, name_and_params, len);
290 devinit.name[len] = '\0';
291
292 /* Allocate space for the default interrupt name: */
293 devinit.interrupt_path = malloc(interrupt_path_len + 1);
294 if (devinit.interrupt_path == NULL) {
295 fprintf(stderr, "device_add(): out of memory\n");
296 exit(1);
297 }
298 snprintf(devinit.interrupt_path, interrupt_path_len,
299 "%s.cpu[%i]", machine->path, machine->bootstrap_cpu);
300
301 p = device_lookup(devinit.name);
302 if (p == NULL) {
303 fatal("no such device (\"%s\")\n", devinit.name);
304 if (device_exit_on_error)
305 exit(1);
306 else
307 goto return_fail;
308 }
309
310 /* Get params from name_and_params: */
311 while (*s2 != '\0') {
312 /* Skip spaces, commas, and semicolons: */
313 while (*s2 == ' ' || *s2 == ',' || *s2 == ';')
314 s2 ++;
315
316 if (*s2 == '\0')
317 break;
318
319 /* s2 now points to the next param. eg "addr=1234" */
320
321 /* Get a word (until there is a '=' sign): */
322 s3 = s2;
323 while (*s3 != '=' && *s3 != '\0')
324 s3 ++;
325 if (s3 == s2) {
326 fatal("weird param: %s\n", s2);
327 if (device_exit_on_error)
328 exit(1);
329 else
330 goto return_fail;
331 }
332 s3 ++;
333 /* s3 now points to the parameter value ("1234") */
334
335 if (strncmp(s2, "addr=", 5) == 0) {
336 devinit.addr = mystrtoull(s3, NULL, 0);
337 } else if (strncmp(s2, "addr2=", 6) == 0) {
338 devinit.addr2 = mystrtoull(s3, NULL, 0);
339 } else if (strncmp(s2, "len=", 4) == 0) {
340 devinit.len = mystrtoull(s3, NULL, 0);
341 } else if (strncmp(s2, "addr_mult=", 10) == 0) {
342 devinit.addr_mult = mystrtoull(s3, NULL, 0);
343 } else if (strncmp(s2, "pci_little_endian=", 18) == 0) {
344 devinit.pci_little_endian = mystrtoull(s3, NULL, 0);
345 switch (devinit.pci_little_endian) {
346 case 0: break;
347 case 1: devinit.pci_little_endian =
348 MEM_PCI_LITTLE_ENDIAN;
349 break;
350 default:fatal("Bad pci_little_endian value.\n");
351 exit(1);
352 }
353 } else if (strncmp(s2, "irq=", 4) == 0) {
354 devinit.irq_nr = mystrtoull(s3, NULL, 0);
355
356 /* New-style interrupt path: */
357 snprintf(devinit.interrupt_path, interrupt_path_len,s3);
358 if (strchr(devinit.interrupt_path, ' ') != NULL)
359 *strchr(devinit.interrupt_path, ' ') = '\0';
360
361 if (strncmp(s3, "none", 4) == 0)
362 devinit.interrupt_path[0] = '\0';
363 } else if (strncmp(s2, "in_use=", 7) == 0) {
364 devinit.in_use = mystrtoull(s3, NULL, 0);
365 } else if (strncmp(s2, "name2=", 6) == 0) {
366 char *h = s2 + 6;
367 size_t len = 0;
368 quoted = 0;
369 while (*h) {
370 if (*h == '\'')
371 quoted = !quoted;
372 h++, len++;
373 if (!quoted && *h == ' ')
374 break;
375 }
376 devinit.name2 = malloc(len + 1);
377 if (devinit.name2 == NULL) {
378 fprintf(stderr, "out of memory\n");
379 exit(1);
380 }
381 h = s2 + 6;
382 if (*h == '\'')
383 len -= 2, h++;
384 snprintf(devinit.name2, len + 1, h);
385 } else {
386 fatal("unknown param: %s\n", s2);
387 if (device_exit_on_error)
388 exit(1);
389 else
390 goto return_fail;
391 }
392
393 /* skip to the next param: */
394 s2 = s3;
395 quoted = 0;
396 while (*s2 != '\0' && (*s2 != ' ' || quoted) &&
397 *s2 != ',' && *s2 != ';') {
398 if (*s2 == '\'')
399 quoted = !quoted;
400 s2 ++;
401 }
402 }
403
404
405 /*
406 * Call the init function for this device:
407 */
408
409 devinit.return_ptr = NULL;
410
411 if (!p->initf(&devinit)) {
412 fatal("error adding device (\"%s\")\n", name_and_params);
413 if (device_exit_on_error)
414 exit(1);
415 else
416 goto return_fail;
417 }
418
419 free(devinit.interrupt_path);
420 free(devinit.name);
421 return devinit.return_ptr;
422
423 return_fail:
424 free(devinit.name);
425 return NULL;
426 }
427
428
429 /*
430 * device_dumplist():
431 *
432 * Dump a list of all registered devices. (If the list is not sorted when
433 * this function is called, it is implicitly sorted.)
434 */
435 void device_dumplist(void)
436 {
437 int i;
438
439 if (!device_entries_sorted)
440 sort_entries();
441
442 for (i=0; i<n_device_entries; i++) {
443 debug(" %s", device_entries[i].name);
444
445 /* TODO: flags? */
446
447 debug("\n");
448 }
449 }
450
451
452 /*
453 * device_set_exit_on_error():
454 *
455 * This function selects the behaviour of the emulator when a device is not
456 * found. During startup, it is nicest to abort the whole emulator session,
457 * but if a device addition is attempted from within the debugger, then it is
458 * nicer to just print a warning and continue.
459 */
460 void device_set_exit_on_error(int exit_on_error)
461 {
462 device_exit_on_error = exit_on_error;
463 }
464
465
466 /*
467 * device_init():
468 *
469 * Initialize the device registry, and call autodev_init() to automatically
470 * add all normal devices (from the src/devices/ directory).
471 *
472 * This function should be called before any other device_*() function is used.
473 */
474 void device_init(void)
475 {
476 device_entries = NULL;
477 device_entries_sorted = 0;
478 n_device_entries = 0;
479
480 autodev_init();
481 }
482

  ViewVC Help
Powered by ViewVC 1.1.26