Add Pentium 4 Thermal Control Circuit support.
[dragonfly.git] / release / sysinstall / devices.c
1 /*
2  * The new sysinstall program.
3  *
4  * This is probably the last program in the `sysinstall' line - the next
5  * generation being essentially a complete rewrite.
6  *
7  * $FreeBSD: src/release/sysinstall/devices.c,v 1.117.2.19 2003/01/29 20:59:10 trhodes Exp $
8  * $DragonFly: src/release/sysinstall/Attic/devices.c,v 1.2 2003/06/17 04:27:21 dillon Exp $
9  *
10  * Copyright (c) 1995
11  *      Jordan Hubbard.  All rights reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer,
18  *    verbatim and that no modifications are made prior to this
19  *    point in the file.
20  * 2. Redistributions in binary form must reproduce the above copyright
21  *    notice, this list of conditions and the following disclaimer in the
22  *    documentation and/or other materials provided with the distribution.
23  *
24  * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  */
37
38 #include "sysinstall.h"
39 #include <sys/fcntl.h>
40 #include <sys/param.h>
41 #include <sys/socket.h>
42 #include <sys/ioctl.h>
43 #include <sys/errno.h>
44 #include <sys/time.h>
45 #include <net/if.h>
46 #include <net/if_var.h>
47 #include <net/if_dl.h>
48 #include <netinet/in.h>
49 #include <netinet/in_var.h>
50 #include <arpa/inet.h>
51 #include <ctype.h>
52
53 /* how much to bias minor number for a given /dev/<ct#><un#>s<s#> slice */
54 #define SLICE_DELTA     (0x10000)
55
56 static Device *Devices[DEV_MAX];
57 static int numDevs;
58
59 static struct _devname {
60     DeviceType type;
61     char *name;
62     char *description;
63     int major, minor, delta, max;
64 } device_names[] = {
65     { DEVICE_TYPE_CDROM,        "cd%dc",        "SCSI CDROM drive",     15, 2, 8, 4                             },
66     { DEVICE_TYPE_CDROM,        "mcd%da",       "Mitsumi (old model) CDROM drive",      29, 0, 8, 4             },
67     { DEVICE_TYPE_CDROM,        "scd%da",       "Sony CDROM drive - CDU31/33A type",    45, 0, 8, 4             },
68 #ifdef notdef
69     { DEVICE_TYPE_CDROM,        "matcd%da",     "Matsushita CDROM ('sound blaster' type)", 46, 0, 8, 4          },
70 #endif
71     { DEVICE_TYPE_CDROM,        "acd%dc",       "ATAPI/IDE CDROM",      117, 0, 8, 4                            },
72     { DEVICE_TYPE_TAPE,         "rsa%d",        "SCSI tape drive",      14, 0, 16, 4                            },
73     { DEVICE_TYPE_TAPE,         "rwt%d",        "Wangtek tape drive",   10, 0, 1, 4                             },
74     { DEVICE_TYPE_DISK,         "da%d",         "SCSI disk device",     13, 65538, 8, 16                        },
75     { DEVICE_TYPE_DISK,         "ad%d",         "ATA/IDE disk device",  116, 65538, 8, 16                       },
76     { DEVICE_TYPE_DISK,         "ar%d",         "ATA/IDE RAID device",  157, 65538, 8, 16                       },
77     { DEVICE_TYPE_DISK,         "fla%d",        "M-Systems DiskOnChip Flash devicee",   102, 65538, 8, 16       },
78     { DEVICE_TYPE_DISK,         "afd%d",        "ATAPI/IDE floppy device",      118, 65538, 8, 4                },
79     { DEVICE_TYPE_DISK,         "mlxd%d",       "Mylex RAID disk",      131, 65538, 8, 4                        },
80     { DEVICE_TYPE_DISK,         "amrd%d",       "AMI MegaRAID drive",   133, 65538, 8, 4                        },
81     { DEVICE_TYPE_DISK,         "idad%d",       "Compaq RAID array",    109, 65538, 8, 4                        },
82     { DEVICE_TYPE_DISK,         "twed%d",       "3ware ATA RAID array", 147, 65538, 8, 4                        },
83     { DEVICE_TYPE_DISK,         "aacd%d",       "Adaptec FSA RAID array", 151, 65538, 8, 4                      },
84     { DEVICE_TYPE_FLOPPY,       "fd%d",         "floppy drive unit A",  9, 0, 64, 4                             },
85     { DEVICE_TYPE_NETWORK,      "an",           "Aironet 4500/4800 802.11 wireless adapter"                     },
86     { DEVICE_TYPE_NETWORK,      "aue",          "ADMtek USB ethernet adapter"                                   },
87     { DEVICE_TYPE_NETWORK,      "bge",          "Broadcom BCM570x PCI gigabit ethernet card"                            },
88     { DEVICE_TYPE_NETWORK,      "cue",          "CATC USB ethernet adapter"                                     },
89     { DEVICE_TYPE_NETWORK,      "fpa",          "DEC DEFPA PCI FDDI card"                                       },
90     { DEVICE_TYPE_NETWORK,      "sr",           "SDL T1/E1 sync serial PCI card"                                },
91     { DEVICE_TYPE_NETWORK,      "cc3i",         "SDL HSSI sync serial PCI card"                                 },
92     { DEVICE_TYPE_NETWORK,      "en",           "Efficient Networks ATM PCI card"                               },
93     { DEVICE_TYPE_NETWORK,      "dc",           "DEC/Intel 21143 (and clones) PCI fast ethernet card"           },
94     { DEVICE_TYPE_NETWORK,      "de",           "DEC DE435 PCI NIC or other DC21040-AA based card"              },
95     { DEVICE_TYPE_NETWORK,      "fxp",          "Intel EtherExpress Pro/100B PCI Fast Ethernet card"            },
96     { DEVICE_TYPE_NETWORK,      "ed",           "Novell NE1000/2000; 3C503; NE2000-compatible PCMCIA"           },
97     { DEVICE_TYPE_NETWORK,      "ep",           "3Com 3C509 ethernet card/3C589 PCMCIA"                         },
98     { DEVICE_TYPE_NETWORK,      "el",           "3Com 3C501 ethernet card"                                      },
99     { DEVICE_TYPE_NETWORK,      "em",           "Intel(R) PRO/1000 ethernet card"                               },
100     { DEVICE_TYPE_NETWORK,      "ex",           "Intel EtherExpress Pro/10 ethernet card"                       },
101     { DEVICE_TYPE_NETWORK,      "fe",           "Fujitsu MB86960A/MB86965A ethernet card"                       },
102     { DEVICE_TYPE_NETWORK,      "ie",           "AT&T StarLAN 10 and EN100; 3Com 3C507; NI5210"                 },
103     { DEVICE_TYPE_NETWORK,      "ix",           "Intel Etherexpress ethernet card"                              },
104     { DEVICE_TYPE_NETWORK,      "kue",          "Kawasaki LSI USB ethernet adapter"                             },
105     { DEVICE_TYPE_NETWORK,      "le",           "DEC EtherWorks 2 or 3 ethernet card"                           },
106     { DEVICE_TYPE_NETWORK,      "lnc",          "Lance/PCnet (Isolan/Novell NE2100/NE32-VL) ethernet"           },
107     { DEVICE_TYPE_NETWORK,      "lge",          "Level 1 LXT1001 gigabit ethernet card"                         },
108     { DEVICE_TYPE_NETWORK,      "nge",          "NatSemi PCI gigabit ethernet card"                             },
109     { DEVICE_TYPE_NETWORK,      "pcn",          "AMD Am79c79x PCI ethernet card"                                },
110     { DEVICE_TYPE_NETWORK,      "ray",          "Raytheon Raylink 802.11 wireless adaptor"                      },
111     { DEVICE_TYPE_NETWORK,      "rl",           "RealTek 8129/8139 PCI ethernet card"                           },
112     { DEVICE_TYPE_NETWORK,      "sf",           "Adaptec AIC-6915 PCI ethernet card"                            },
113     { DEVICE_TYPE_NETWORK,      "sis",          "SiS 900/SiS 7016 PCI ethernet card"                            },
114 #ifdef PC98
115     { DEVICE_TYPE_NETWORK,      "snc",          "SONIC ethernet card"                                           },
116 #endif
117     { DEVICE_TYPE_NETWORK,      "sn",           "SMC/Megahertz ethernet card"                                   },
118     { DEVICE_TYPE_NETWORK,      "ste",          "Sundance ST201 PCI ethernet card"                              },
119     { DEVICE_TYPE_NETWORK,      "sk",           "SysKonnect PCI gigabit ethernet card"                          },
120     { DEVICE_TYPE_NETWORK,      "tx",           "SMC 9432TX ethernet card"                                      },
121     { DEVICE_TYPE_NETWORK,      "txp",          "3Com 3cR990 ethernet card"                                     },
122     { DEVICE_TYPE_NETWORK,      "ti",           "Alteon Networks PCI gigabit ethernet card"                     },
123     { DEVICE_TYPE_NETWORK,      "tl",           "Texas Instruments ThunderLAN PCI ethernet card"                },
124     { DEVICE_TYPE_NETWORK,      "vr",           "VIA VT3043/VT86C100A Rhine PCI ethernet card"                  },
125     { DEVICE_TYPE_NETWORK,      "vx",           "3COM 3c590 / 3c595 ethernet card"                              },
126     { DEVICE_TYPE_NETWORK,      "wb",           "Winbond W89C840F PCI ethernet card"                            },
127     { DEVICE_TYPE_NETWORK,      "wi",           "Lucent WaveLAN/IEEE 802.11 wireless adapter"                   },
128     { DEVICE_TYPE_NETWORK,      "wx",           "Intel Gigabit Ethernet (82452) card"                   },
129     { DEVICE_TYPE_NETWORK,      "xe",           "Xircom/Intel EtherExpress Pro100/16 ethernet card"             },
130     { DEVICE_TYPE_NETWORK,      "xl",           "3COM 3c90x / 3c90xB PCI ethernet card"                         },
131     { DEVICE_TYPE_NETWORK,      "cuaa%d",       "%s on device %s (COM%d)",      28, 128, 1, 16                  },
132     { DEVICE_TYPE_NETWORK,      "lp",           "Parallel Port IP (PLIP) peer connection"                       },
133     { DEVICE_TYPE_NETWORK,      "lo",           "Loop-back (local) network interface"                           },
134 #ifdef PC98
135     { DEVICE_TYPE_DISK,         "wd%d",         "IDE disk device",              3, 65538, 8, 16                 },
136     { DEVICE_TYPE_CDROM,        "wcd%dc",       "ATAPI IDE CDROM",              69, 2, 8, 4                     },
137     { DEVICE_TYPE_FLOPPY,       "wfd%d",        "ATAPI floppy drive unit A",    87, 0, 8, 4                     },
138     { DEVICE_TYPE_DISK,         "wfd%d",        "ATAPI floppy device",          87, 65538, 8, 4                 },
139 #endif
140     { 0 },
141 };
142
143 Device *
144 new_device(char *name)
145 {
146     Device *dev;
147
148     dev = safe_malloc(sizeof(Device));
149     bzero(dev, sizeof(Device));
150     if (name)
151         SAFE_STRCPY(dev->name, name);
152     return dev;
153 }
154
155 /* Stubs for unimplemented strategy routines */
156 Boolean
157 dummyInit(Device *dev)
158 {
159     return TRUE;
160 }
161
162 FILE *
163 dummyGet(Device *dev, char *dist, Boolean probe)
164 {
165     return NULL;
166 }
167
168 void
169 dummyShutdown(Device *dev)
170 {
171     return;
172 }
173
174 static int
175 deviceTry(struct _devname dev, char *try, int i)
176 {
177     int fd;
178     char unit[80];
179     mode_t m;
180     dev_t d;
181     int fail;
182
183     snprintf(unit, sizeof unit, dev.name, i);
184     snprintf(try, FILENAME_MAX, "/dev/%s", unit);
185     if (isDebug())
186         msgDebug("deviceTry: attempting to open %s\n", try);
187     fd = open(try, O_RDONLY);
188     if (fd >= 0) {
189         if (isDebug())
190             msgDebug("deviceTry: open of %s succeeded on first try.\n", try);
191         return fd;
192     }
193     m = 0640 | S_IFCHR;
194     d = makedev(dev.major, dev.minor + (i * dev.delta));
195     if (isDebug())
196         msgDebug("deviceTry: Making %s device for %s [%d, %d]\n", m & S_IFCHR ? "raw" : "block", try, dev.major, dev.minor + (i * dev.delta));
197     fail = mknod(try, m, d);
198     fd = open(try, O_RDONLY);
199     if (fd >= 0) {
200         if (isDebug())
201             msgDebug("deviceTry: open of %s succeeded on second try.\n", try);
202         return fd;
203     }
204     else if (!fail)
205         (void)unlink(try);
206     /* Don't try a "make-under" here since we're using a fixit floppy in this case */
207     snprintf(try, FILENAME_MAX, "/mnt/dev/%s", unit);
208     fd = open(try, O_RDONLY);
209     if (isDebug())
210         msgDebug("deviceTry: final attempt for %s returns %d\n", try, fd);
211     return fd;
212 }
213
214 /* Register a new device in the devices array */
215 Device *
216 deviceRegister(char *name, char *desc, char *devname, DeviceType type, Boolean enabled,
217                Boolean (*init)(Device *), FILE * (*get)(Device *, char *, Boolean),
218                void (*shutdown)(Device *), void *private)
219 {
220     Device *newdev = NULL;
221
222     if (numDevs == DEV_MAX)
223         msgFatal("Too many devices found!");
224     else {
225         newdev = new_device(name);
226         newdev->description = desc;
227         newdev->devname = devname;
228         newdev->type = type;
229         newdev->enabled = enabled;
230         newdev->init = init ? init : dummyInit;
231         newdev->get = get ? get : dummyGet;
232         newdev->shutdown = shutdown ? shutdown : dummyShutdown;
233         newdev->private = private;
234         Devices[numDevs] = newdev;
235         Devices[++numDevs] = NULL;
236     }
237     return newdev;
238 }
239
240 /* Reset the registered device chain */
241 void
242 deviceReset(void)
243 {
244     int i;
245
246     for (i = 0; i < numDevs; i++) {
247         DEVICE_SHUTDOWN(Devices[i]);
248
249         /* XXX this potentially leaks Devices[i]->private if it's being
250          * used to point to something dynamic, but you're not supposed
251          * to call this routine at such times that some open instance
252          * has its private ptr pointing somewhere anyway. XXX
253          */
254         free(Devices[i]);
255     }
256     Devices[numDevs = 0] = NULL;
257 }
258
259 /* Get all device information for devices we have attached */
260 void
261 deviceGetAll(void)
262 {
263     int i, j, fd, s;
264     struct ifconf ifc;
265     struct ifreq *ifptr, *end;
266     int ifflags;
267     char buffer[INTERFACE_MAX * sizeof(struct ifreq)];
268     char **names;
269
270     msgNotify("Probing devices, please wait (this can take a while)...");
271     /* First go for the network interfaces.  Stolen shamelessly from ifconfig! */
272     ifc.ifc_len = sizeof(buffer);
273     ifc.ifc_buf = buffer;
274
275     s = socket(AF_INET, SOCK_DGRAM, 0);
276     if (s < 0)
277         goto skipif;    /* Jump over network iface probing */
278
279     if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0)
280         goto skipif;    /* Jump over network iface probing */
281
282     close(s);
283     ifflags = ifc.ifc_req->ifr_flags;
284     end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
285     for (ifptr = ifc.ifc_req; ifptr < end; ifptr++) {
286         char *descr;
287
288         /* If it's not a link entry, forget it */
289         if (ifptr->ifr_ifru.ifru_addr.sa_family != AF_LINK)
290             goto loopend;
291
292         /* Eliminate network devices that don't make sense */
293         if (!strncmp(ifptr->ifr_name, "lo", 2))
294             goto loopend;
295
296         /* If we have a slip device, don't register it */
297         if (!strncmp(ifptr->ifr_name, "sl", 2)) {
298             goto loopend;
299         }
300         /* And the same for ppp */
301         if (!strncmp(ifptr->ifr_name, "tun", 3) || !strncmp(ifptr->ifr_name, "ppp", 3)) {
302             goto loopend;
303         }
304         /* Try and find its description */
305         for (i = 0, descr = NULL; device_names[i].name; i++) {
306             int len = strlen(device_names[i].name);
307
308             if (!ifptr->ifr_name || !ifptr->ifr_name[0])
309                 continue;
310             else if (!strncmp(ifptr->ifr_name, device_names[i].name, len)) {
311                 descr = device_names[i].description;
312                 break;
313             }
314         }
315         if (!descr)
316             descr = "<unknown network interface type>";
317
318         deviceRegister(ifptr->ifr_name, descr, strdup(ifptr->ifr_name), DEVICE_TYPE_NETWORK, TRUE,
319                        mediaInitNetwork, NULL, mediaShutdownNetwork, NULL);
320         if (isDebug())
321             msgDebug("Found a network device named %s\n", ifptr->ifr_name);
322         close(s);
323         if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
324             continue;
325
326 loopend:
327         if (ifptr->ifr_addr.sa_len)     /* I'm not sure why this is here - it's inherited */
328             ifptr = (struct ifreq *)((caddr_t)ifptr + ifptr->ifr_addr.sa_len - sizeof(struct sockaddr));
329         close(s);
330     }
331
332 skipif:
333     /* Next, try to find all the types of devices one might need
334      * during the second stage of the installation.
335      */
336     for (i = 0; device_names[i].name; i++) {
337         for (j = 0; j < device_names[i].max; j++) {
338             char try[FILENAME_MAX];
339
340             switch(device_names[i].type) {
341             case DEVICE_TYPE_CDROM:
342                 fd = deviceTry(device_names[i], try, j);
343                 if (fd >= 0 || errno == EBUSY) {        /* EBUSY if already mounted */
344                     char n[BUFSIZ];
345
346                     if (fd >= 0) close(fd);
347                     snprintf(n, sizeof n, device_names[i].name, j);
348                     deviceRegister(strdup(n), device_names[i].description, strdup(try),
349                                          DEVICE_TYPE_CDROM, TRUE, mediaInitCDROM, mediaGetCDROM,
350                                          mediaShutdownCDROM, NULL);
351                     if (isDebug())
352                         msgDebug("Found a CDROM device for %s\n", try);
353                 }
354                 break;
355
356             case DEVICE_TYPE_TAPE:
357                 fd = deviceTry(device_names[i], try, j);
358                 if (fd >= 0) {
359                     char n[BUFSIZ];
360
361                     close(fd);
362                     snprintf(n, sizeof n, device_names[i].name, j);
363                     deviceRegister(strdup(n), device_names[i].description, strdup(try),
364                                    DEVICE_TYPE_TAPE, TRUE, mediaInitTape, mediaGetTape, mediaShutdownTape, NULL);
365                     if (isDebug())
366                         msgDebug("Found a TAPE device for %s\n", try);
367                 }
368                 break;
369
370             case DEVICE_TYPE_DISK:
371                 fd = deviceTry(device_names[i], try, j);
372                 if (fd >= 0 && RunningAsInit) {
373                     dev_t d;
374                     mode_t m;
375                     int s, fail;
376                     char unit[80], slice[80];
377
378                     close(fd);
379                     /* Make associated slice entries */
380                     for (s = 1; s < 8; s++) {
381                         snprintf(unit, sizeof unit, device_names[i].name, j);
382                         snprintf(slice, sizeof slice, "/dev/%ss%d", unit, s);
383                         d = makedev(device_names[i].major, device_names[i].minor +
384                                     (j * device_names[i].delta) + (s * SLICE_DELTA));
385                         m = 0640 | S_IFCHR;
386                         fail = mknod(slice, m, d);
387                         fd = open(slice, O_RDONLY);
388                         if (fd >= 0)
389                             close(fd);
390                         else if (!fail)
391                             (void)unlink(slice);
392                     }
393                 }
394                 break;
395
396             case DEVICE_TYPE_FLOPPY:
397                 fd = deviceTry(device_names[i], try, j);
398                 if (fd >= 0) {
399                     char n[BUFSIZ];
400
401                     close(fd);
402                     snprintf(n, sizeof n, device_names[i].name, j);
403                     deviceRegister(strdup(n), device_names[i].description, strdup(try),
404                                    DEVICE_TYPE_FLOPPY, TRUE, mediaInitFloppy, mediaGetFloppy,
405                                    mediaShutdownFloppy, NULL);
406                     if (isDebug())
407                         msgDebug("Found a floppy device for %s\n", try);
408                 }
409                 break;
410
411             case DEVICE_TYPE_NETWORK:
412                 fd = deviceTry(device_names[i], try, j);
413                 /* The only network devices that you can open this way are serial ones */
414                 if (fd >= 0) {
415                     char *newdesc, *cp;
416
417                     close(fd);
418                     cp = device_names[i].description;
419                     /* Serial devices get a slip and ppp device each, if supported */
420                     newdesc = safe_malloc(strlen(cp) + 40);
421                     sprintf(newdesc, cp, "SLIP interface", try, j + 1);
422                     deviceRegister("sl0", newdesc, strdup(try), DEVICE_TYPE_NETWORK, TRUE, mediaInitNetwork,
423                                    NULL, mediaShutdownNetwork, NULL);
424                     msgDebug("Add mapping for %s to sl0\n", try);
425                     newdesc = safe_malloc(strlen(cp) + 50);
426                     sprintf(newdesc, cp, "PPP interface", try, j + 1);
427                     deviceRegister("ppp0", newdesc, strdup(try), DEVICE_TYPE_NETWORK, TRUE, mediaInitNetwork,
428                                    NULL, mediaShutdownNetwork, NULL);
429                     if (isDebug())
430                         msgDebug("Add mapping for %s to ppp0\n", try);
431                 }
432                 break;
433
434             default:
435                 break;
436             }
437         }
438     }
439
440     /* Finally, go get the disks and look for DOS partitions to register */
441     if ((names = Disk_Names()) != NULL) {
442         int i;
443
444         for (i = 0; names[i]; i++) {
445             Chunk *c1;
446             Disk *d;
447
448             /* Ignore memory disks */
449             if (!strncmp(names[i], "md", 2))
450                 continue;
451
452             d = Open_Disk(names[i]);
453             if (!d) {
454                 msgDebug("Unable to open disk %s", names[i]);
455                 continue;
456             }
457
458             deviceRegister(names[i], names[i], d->name, DEVICE_TYPE_DISK, FALSE,
459                            dummyInit, dummyGet, dummyShutdown, d);
460             if (isDebug())
461                 msgDebug("Found a disk device named %s\n", names[i]);
462
463             /* Look for existing DOS partitions to register as "DOS media devices" */
464             for (c1 = d->chunks->part; c1; c1 = c1->next) {
465                 if (c1->type == fat || c1->type == extended) {
466                     Device *dev;
467                     char devname[80];
468
469                     /* Got one! */
470                     snprintf(devname, sizeof devname, "/dev/%s", c1->name);
471                     dev = deviceRegister(c1->name, c1->name, strdup(devname), DEVICE_TYPE_DOS, TRUE,
472                                          mediaInitDOS, mediaGetDOS, mediaShutdownDOS, NULL);
473                     dev->private = c1;
474                     if (isDebug())
475                         msgDebug("Found a DOS partition %s on drive %s\n", c1->name, d->name);
476                 }
477             }
478         }
479         free(names);
480     }
481     dialog_clear_norefresh();
482 }
483
484 /* Rescan all devices, after closing previous set - convenience function */
485 void
486 deviceRescan(void)
487 {
488     deviceReset();
489     deviceGetAll();
490 }
491
492 /*
493  * Find all devices that match the criteria, allowing "wildcarding" as well
494  * by allowing NULL or ANY values to match all.  The array returned is static
495  * and may be used until the next invocation of deviceFind().
496  */
497 Device **
498 deviceFind(char *name, DeviceType class)
499 {
500     static Device *found[DEV_MAX];
501     int i, j;
502
503     j = 0;
504     for (i = 0; i < numDevs; i++) {
505         if ((!name || !strcmp(Devices[i]->name, name))
506             && (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
507             found[j++] = Devices[i];
508     }
509     found[j] = NULL;
510     return j ? found : NULL;
511 }
512
513 Device **
514 deviceFindDescr(char *name, char *desc, DeviceType class)
515 {
516     static Device *found[DEV_MAX];
517     int i, j;
518
519     j = 0;
520     for (i = 0; i < numDevs; i++) {
521         if ((!name || !strcmp(Devices[i]->name, name)) &&
522             (!desc || !strcmp(Devices[i]->description, desc)) &&
523             (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
524             found[j++] = Devices[i];
525     }
526     found[j] = NULL;
527     return j ? found : NULL;
528 }
529
530 int
531 deviceCount(Device **devs)
532 {
533     int i;
534
535     if (!devs)
536         return 0;
537     for (i = 0; devs[i]; i++);
538     return i;
539 }
540
541 /*
542  * Create a menu listing all the devices of a certain type in the system.
543  * The passed-in menu is expected to be a "prototype" from which the new
544  * menu is cloned.
545  */
546 DMenu *
547 deviceCreateMenu(DMenu *menu, DeviceType type, int (*hook)(dialogMenuItem *d), int (*check)(dialogMenuItem *d))
548 {
549     Device **devs;
550     int numdevs;
551     DMenu *tmp = NULL;
552     int i, j;
553
554     devs = deviceFind(NULL, type);
555     numdevs = deviceCount(devs);
556     if (!numdevs)
557         return NULL;
558     tmp = (DMenu *)safe_malloc(sizeof(DMenu) + (sizeof(dialogMenuItem) * (numdevs + 1)));
559     bcopy(menu, tmp, sizeof(DMenu));
560     for (i = 0; devs[i]; i++) {
561         tmp->items[i].prompt = devs[i]->name;
562         for (j = 0; j < numDevs; j++) {
563             if (devs[i] == Devices[j]) {
564                 tmp->items[i].title = Devices[j]->description;
565                 break;
566             }
567         }
568         if (j == numDevs)
569             tmp->items[i].title = "<unknown device type>";
570         tmp->items[i].fire = hook;
571         tmp->items[i].checked = check;
572     }
573     tmp->items[i].title = NULL;
574     return tmp;
575 }