]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.sbin/sysinstall/devices.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / usr.sbin / 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$
8  *
9  * Copyright (c) 1995
10  *      Jordan Hubbard.  All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer,
17  *    verbatim and that no modifications are made prior to this
18  *    point in the file.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  */
36
37 #include "sysinstall.h"
38 #include <sys/fcntl.h>
39 #include <sys/param.h>
40 #include <sys/socket.h>
41 #include <sys/ioctl.h>
42 #include <sys/errno.h>
43 #include <sys/time.h>
44 #include <net/if.h>
45 #include <net/if_var.h>
46 #include <net/if_dl.h>
47 #include <netinet/in.h>
48 #include <netinet/in_var.h>
49 #include <arpa/inet.h>
50 #include <ctype.h>
51 #include <libdisk.h>
52
53 static Device *Devices[DEV_MAX];
54 static int numDevs;
55
56 #define DEVICE_ENTRY(type, name, descr, max)    { type, name, descr, max }
57
58 #define CDROM(name, descr, max)                                 \
59         DEVICE_ENTRY(DEVICE_TYPE_CDROM, name, descr, max)
60 #define DISK(name, descr, max)                                          \
61         DEVICE_ENTRY(DEVICE_TYPE_DISK, name, descr, max)
62 #define FLOPPY(name, descr, max)                                        \
63         DEVICE_ENTRY(DEVICE_TYPE_FLOPPY, name, descr, max)
64 #define NETWORK(name, descr)                                            \
65         DEVICE_ENTRY(DEVICE_TYPE_NETWORK, name, descr, 0)
66 #define SERIAL(name, descr, max)                                        \
67         DEVICE_ENTRY(DEVICE_TYPE_NETWORK, name, descr, max)
68 #define USB(name, descr, max)                                           \
69         DEVICE_ENTRY(DEVICE_TYPE_USB, name, descr, max)
70
71 static struct _devname {
72     DeviceType type;
73     char *name;
74     char *description;
75     int max;
76 } device_names[] = {
77     CDROM("cd%d",       "SCSI CDROM drive",                     4),
78     CDROM("mcd%d",      "Mitsumi (old model) CDROM drive",      4),
79     CDROM("scd%d",      "Sony CDROM drive - CDU31/33A type",    4),
80     CDROM("acd%d",      "ATAPI/IDE CDROM",                      4),
81     DISK("da%d",        "SCSI disk device",                     16),
82     DISK("ad%d",        "ATA/IDE disk device",                  16),
83     DISK("ada%d",       "SATA disk device",                     16),
84     DISK("ar%d",        "ATA/IDE RAID device",                  16),
85     DISK("afd%d",       "ATAPI/IDE floppy device",              4),
86     DISK("mlxd%d",      "Mylex RAID disk",                      4),
87     DISK("amrd%d",      "AMI MegaRAID drive",                   4),
88     DISK("idad%d",      "Compaq RAID array",                    4),
89     DISK("twed%d",      "3ware ATA RAID array",                 4),
90     DISK("aacd%d",      "Adaptec FSA RAID array",               4),
91     DISK("ipsd%d",      "IBM ServeRAID RAID array",             4),
92     DISK("mfid%d",      "LSI MegaRAID SAS array",               4),
93     FLOPPY("fd%d",      "floppy drive unit A",                  4),
94     SERIAL("cuau%d",    "%s on device %s (COM%d)",              16),
95     USB("da%da",        "USB Mass Storage Device",              16),
96     NETWORK("ae",       "Attansic/Atheros L2 Fast Ethernet"),
97     NETWORK("age",      "Attansic/Atheros L1 Gigabit Ethernet"),
98     NETWORK("alc",      "Atheros AR8131/AR8132 PCIe Ethernet"),
99     NETWORK("ale",      "Atheros AR8121/AR8113/AR8114 PCIe Ethernet"),
100     NETWORK("an",       "Aironet 4500/4800 802.11 wireless adapter"),
101     NETWORK("ath",      "Atheros IEEE 802.11 wireless adapter"),
102     NETWORK("aue",      "ADMtek USB Ethernet adapter"),
103     NETWORK("axe",      "ASIX Electronics USB Ethernet adapter"),
104     NETWORK("bce",      "Broadcom NetXtreme II Gigabit Ethernet card"),
105     NETWORK("bfe",      "Broadcom BCM440x PCI Ethernet card"),
106     NETWORK("bge",      "Broadcom BCM570x PCI Gigabit Ethernet card"),
107     NETWORK("bm",       "Apple BMAC Built-in Ethernet"),
108     NETWORK("bwn",      "Broadcom BCM43xx IEEE 802.11 wireless adapter"),
109     NETWORK("bxe",      "Broadcom NetXtreme II 10Gb Ethernet card"),
110     NETWORK("cas",      "Sun Cassini/Cassini+ or NS DP83065 Saturn Ethernet"),
111     NETWORK("cue",      "CATC USB Ethernet adapter"),
112     NETWORK("cxgb",     "Chelsio T3 10Gb Ethernet card"),
113     NETWORK("cxgbe",    "Chelsio T4 10Gb Ethernet card"),
114     NETWORK("fpa",      "DEC DEFPA PCI FDDI card"),
115     NETWORK("sr",       "SDL T1/E1 sync serial PCI card"),
116     NETWORK("cc3i",     "SDL HSSI sync serial PCI card"),
117     NETWORK("en",       "Efficient Networks ATM PCI card"),
118     NETWORK("dc",       "DEC/Intel 21143 (and clones) PCI Fast Ethernet card"),
119     NETWORK("de",       "DEC DE435 PCI NIC or other DC21040-AA based card"),
120     NETWORK("fxp",      "Intel EtherExpress Pro/100B PCI Fast Ethernet card"),
121     NETWORK("ed",       "Novell NE1000/2000; 3C503; NE2000-compatible PCMCIA"),
122     NETWORK("ep",       "3Com 3C509 Ethernet card/3C589 PCMCIA"),
123     NETWORK("em",       "Intel(R) PRO/1000 Ethernet card"),
124     NETWORK("et",       "Agere ET1310 based PCI Express Gigabit Ethernet card"),
125     NETWORK("ex",       "Intel EtherExpress Pro/10 Ethernet card"),
126     NETWORK("fe",       "Fujitsu MB86960A/MB86965A Ethernet card"),
127     NETWORK("gem",      "Apple GMAC or Sun ERI/GEM Ethernet adapter"),
128     NETWORK("hme",      "Sun HME (Happy Meal Ethernet) Ethernet adapter"),
129     NETWORK("ie",       "AT&T StarLAN 10 and EN100; 3Com 3C507; NI5210"),
130     NETWORK("igb",      "Intel(R) PRO/1000 PCI Express Gigabit Ethernet card"),
131     NETWORK("ipw",      "Intel PRO/Wireless 2100 IEEE 802.11 adapter"),
132     NETWORK("iwi",      "Intel PRO/Wireless 2200BG/2225BG/2915ABG adapter"),
133     NETWORK("iwn",      "Intel Wireless WiFi Link 4965AGN IEEE 802.11n adapter"),
134     NETWORK("ixgb",     "Intel(R) PRO/10Gb Ethernet card"),
135     NETWORK("ixgbe",    "Intel(R) PRO/10Gb Ethernet card"),
136     NETWORK("jme",      "JMicron JMC250 Gigabit/JMC260 Fast Ethernet"),
137     NETWORK("kue",      "Kawasaki LSI USB Ethernet adapter"),
138     NETWORK("le",       "AMD Am7900 LANCE or Am79C9xx PCnet Ethernet adapter"),
139     NETWORK("lge",      "Level 1 LXT1001 Gigabit Ethernet card"),
140     NETWORK("malo",     "Marvell Libertas 88W8335 802.11 wireless adapter"),
141     NETWORK("msk",      "Marvell/SysKonnect Yukon II Gigabit Ethernet"),
142     NETWORK("mxge",     "Myricom Myri10GE 10Gb Ethernet card"),
143     NETWORK("nfe",      "NVIDIA nForce MCP Ethernet"),
144     NETWORK("nge",      "NatSemi PCI Gigabit Ethernet card"),
145     NETWORK("nve",      "NVIDIA nForce MCP Ethernet"),
146     NETWORK("nxge",     "Neterion Xframe 10GbE Server/Storage adapter"),
147     NETWORK("pcn",      "AMD Am79c79x PCI Ethernet card"),
148     NETWORK("ral",      "Ralink Technology IEEE 802.11 wireless adapter"),
149     NETWORK("ray",      "Raytheon Raylink 802.11 wireless adapter"),
150     NETWORK("re",       "RealTek 8139C+/8169/8169S/8110S PCI Ethernet card"),
151     NETWORK("rl",       "RealTek 8129/8139 PCI Ethernet card"),
152     NETWORK("rue",      "RealTek USB Ethernet card"),
153     NETWORK("rum",      "Ralink Technology USB IEEE 802.11 wireless adapter"),
154     NETWORK("sf",       "Adaptec AIC-6915 PCI Ethernet card"),
155     NETWORK("sge",      "Silicon Integrated Systems SiS190/191 Ethernet"),
156     NETWORK("sis",      "SiS 900/SiS 7016 PCI Ethernet card"),
157 #ifdef PC98
158     NETWORK("snc",      "SONIC Ethernet card"),
159 #endif
160     NETWORK("sn",       "SMC/Megahertz Ethernet card"),
161     NETWORK("ste",      "Sundance ST201 PCI Ethernet card"),
162     NETWORK("stge",     "Sundance/Tamarack TC9021 Gigabit Ethernet"),
163     NETWORK("sk",       "SysKonnect PCI Gigabit Ethernet card"),
164     NETWORK("tx",       "SMC 9432TX Ethernet card"),
165     NETWORK("txp",      "3Com 3cR990 Ethernet card"),
166     NETWORK("ti",       "Alteon Networks PCI Gigabit Ethernet card"),
167     NETWORK("tl",       "Texas Instruments ThunderLAN PCI Ethernet card"),
168     NETWORK("uath",     "Atheros AR5005UG and AR5005UX USB wireless adapter"),
169     NETWORK("upgt",     "Conexant/Intersil PrismGT USB wireless adapter"),
170     NETWORK("ural",     "Ralink Technology RT2500USB 802.11 wireless adapter"),
171     NETWORK("urtw",     "Realtek 8187L USB wireless adapter"),
172     NETWORK("vge",      "VIA VT612x PCI Gigabit Ethernet card"),
173     NETWORK("vr",       "VIA VT3043/VT86C100A Rhine PCI Ethernet card"),
174     NETWORK("vte",      "DM&P Vortex86 RDC R6040 Fast Ethernet"),
175     NETWORK("vlan",     "IEEE 802.1Q VLAN network interface"),
176     NETWORK("vx",       "3COM 3c590 / 3c595 Ethernet card"),
177     NETWORK("wb",       "Winbond W89C840F PCI Ethernet card"),
178     NETWORK("wi",       "Lucent WaveLAN/IEEE 802.11 wireless adapter"),
179     NETWORK("wpi",      "Intel 3945ABG IEEE 802.11 wireless adapter"),
180     NETWORK("xe",       "Xircom/Intel EtherExpress Pro100/16 Ethernet card"),
181     NETWORK("xl",       "3COM 3c90x / 3c90xB PCI Ethernet card"),
182     NETWORK("zyd",      "ZyDAS ZD1211/ZD1211B USB 802.11 wireless adapter"),
183     NETWORK("fwe",      "FireWire Ethernet emulation"),
184     NETWORK("fwip",     "IP over FireWire"),
185     NETWORK("plip",     "Parallel Port IP (PLIP) peer connection"),
186     NETWORK("lo",       "Loop-back (local) network interface"),
187     NETWORK("disc",     "Software discard network interface"),
188     { 0, NULL, NULL, 0 }
189 };
190
191 Device *
192 new_device(char *name)
193 {
194     Device *dev;
195
196     dev = safe_malloc(sizeof(Device));
197     bzero(dev, sizeof(Device));
198     if (name)
199         SAFE_STRCPY(dev->name, name);
200     return dev;
201 }
202
203 /* Stubs for unimplemented strategy routines */
204 Boolean
205 dummyInit(Device *dev)
206 {
207     return TRUE;
208 }
209
210 FILE *
211 dummyGet(Device *dev, char *dist, Boolean probe)
212 {
213     return NULL;
214 }
215
216 void
217 dummyShutdown(Device *dev)
218 {
219     return;
220 }
221
222 static int
223 deviceTry(struct _devname dev, char *try, int i)
224 {
225     int fd;
226     char unit[80];
227
228     snprintf(unit, sizeof unit, dev.name, i);
229     snprintf(try, FILENAME_MAX, "/dev/%s", unit);
230     if (isDebug())
231         msgDebug("deviceTry: attempting to open %s\n", try);
232     fd = open(try, O_RDONLY);
233     if (fd >= 0) {
234         if (isDebug())
235             msgDebug("deviceTry: open of %s succeeded on first try.\n", try);
236     } else {
237         if (isDebug())
238             msgDebug("deviceTry: open of %s failed.\n", try);
239     }
240     return fd;
241 }
242
243 /* Register a new device in the devices array */
244 Device *
245 deviceRegister(char *name, char *desc, char *devname, DeviceType type, Boolean enabled,
246                Boolean (*init)(Device *), FILE * (*get)(Device *, char *, Boolean),
247                void (*shutdown)(Device *), void *private)
248 {
249     Device *newdev = NULL;
250
251     if (numDevs == DEV_MAX)
252         msgFatal("Too many devices found!");
253     else {
254         newdev = new_device(name);
255         newdev->description = desc;
256         newdev->devname = devname;
257         newdev->type = type;
258         newdev->enabled = enabled;
259         newdev->init = init ? init : dummyInit;
260         newdev->get = get ? get : dummyGet;
261         newdev->shutdown = shutdown ? shutdown : dummyShutdown;
262         newdev->private = private;
263         Devices[numDevs] = newdev;
264         Devices[++numDevs] = NULL;
265     }
266     return newdev;
267 }
268
269 /* Reset the registered device chain */
270 void
271 deviceReset(void)
272 {
273     int i;
274
275     for (i = 0; i < numDevs; i++) {
276         DEVICE_SHUTDOWN(Devices[i]);
277
278         /* XXX this potentially leaks Devices[i]->private if it's being
279          * used to point to something dynamic, but you're not supposed
280          * to call this routine at such times that some open instance
281          * has its private ptr pointing somewhere anyway. XXX
282          */
283         free(Devices[i]);
284     }
285     Devices[numDevs = 0] = NULL;
286 }
287
288 /* Get all device information for devices we have attached */
289 void
290 deviceGetAll(void)
291 {
292     int i, j, fd, s;
293     struct ifconf ifc;
294     struct ifreq *ifptr, *end;
295     int ifflags;
296     char buffer[INTERFACE_MAX * sizeof(struct ifreq)];
297     char **names;
298
299     msgNotify("Probing devices, please wait (this can take a while)...");
300     /* First go for the network interfaces.  Stolen shamelessly from ifconfig! */
301     memset(&ifc, 0, sizeof(ifc));
302     memset(buffer, 0, INTERFACE_MAX * sizeof(struct ifreq));
303     ifc.ifc_len = sizeof(buffer);
304     ifc.ifc_buf = buffer;
305
306     s = socket(AF_INET, SOCK_DGRAM, 0);
307     if (s < 0)
308         goto skipif;    /* Jump over network iface probing */
309
310     if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0)
311         goto skipif;    /* Jump over network iface probing */
312
313     close(s);
314     ifflags = ifc.ifc_req->ifr_flags;
315     end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
316     for (ifptr = ifc.ifc_req; ifptr < end; ifptr++) {
317         char *descr;
318
319         /* If it's not a link entry, forget it */
320         if (ifptr->ifr_ifru.ifru_addr.sa_family != AF_LINK)
321             goto loopend;
322
323         /* Eliminate network devices that don't make sense */
324         if (!strncmp(ifptr->ifr_name, "lo", 2))
325             goto loopend;
326
327         /* Try and find its description */
328         for (i = 0, descr = NULL; device_names[i].name; i++) {
329             int len = strlen(device_names[i].name);
330
331             if (!ifptr->ifr_name || !ifptr->ifr_name[0])
332                 continue;
333             else if (!strncmp(ifptr->ifr_name, device_names[i].name, len)) {
334                 descr = device_names[i].description;
335                 break;
336             }
337         }
338         if (!descr)
339             descr = "<unknown network interface type>";
340
341         deviceRegister(ifptr->ifr_name, descr, strdup(ifptr->ifr_name), DEVICE_TYPE_NETWORK, TRUE,
342                        mediaInitNetwork, NULL, mediaShutdownNetwork, NULL);
343         if (isDebug())
344             msgDebug("Found a network device named %s\n", ifptr->ifr_name);
345         close(s);
346         if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
347             continue;
348
349 loopend:
350         if (ifptr->ifr_addr.sa_len)     /* I'm not sure why this is here - it's inherited */
351             ifptr = (struct ifreq *)((caddr_t)ifptr + ifptr->ifr_addr.sa_len - sizeof(struct sockaddr));
352         close(s);
353     }
354
355 skipif:
356     /* Next, try to find all the types of devices one might need
357      * during the second stage of the installation.
358      */
359     for (i = 0; device_names[i].name; i++) {
360         for (j = 0; j < device_names[i].max; j++) {
361             char try[FILENAME_MAX];
362
363             switch(device_names[i].type) {
364             case DEVICE_TYPE_CDROM:
365                 fd = deviceTry(device_names[i], try, j);
366                 if (fd >= 0 || errno == EBUSY) {        /* EBUSY if already mounted */
367                     char n[BUFSIZ];
368
369                     if (fd >= 0) close(fd);
370                     snprintf(n, sizeof n, device_names[i].name, j);
371                     deviceRegister(n, device_names[i].description, strdup(try),
372                                          DEVICE_TYPE_CDROM, TRUE, mediaInitCDROM, mediaGetCDROM,
373                                          mediaShutdownCDROM, NULL);
374                     if (isDebug())
375                         msgDebug("Found a CDROM device for %s\n", try);
376                 }
377                 break;
378
379             case DEVICE_TYPE_DISK:
380                 /* nothing to do */
381                 break;
382
383             case DEVICE_TYPE_FLOPPY:
384                 fd = deviceTry(device_names[i], try, j);
385                 if (fd >= 0) {
386                     char n[BUFSIZ];
387
388                     close(fd);
389                     snprintf(n, sizeof n, device_names[i].name, j);
390                     deviceRegister(n, device_names[i].description, strdup(try),
391                                    DEVICE_TYPE_FLOPPY, TRUE, mediaInitFloppy, mediaGetFloppy,
392                                    mediaShutdownFloppy, NULL);
393                     if (isDebug())
394                         msgDebug("Found a floppy device for %s\n", try);
395                 }
396                 break;
397
398             case DEVICE_TYPE_USB:
399                 fd = deviceTry(device_names[i], try, j);
400                 if (fd >= 0) {
401                         char n[BUFSIZ];
402
403                         close(fd);
404                         snprintf(n, sizeof(n), device_names[i].name, j);
405                         deviceRegister(n, device_names[i].description,
406                             strdup(try), DEVICE_TYPE_USB, TRUE, mediaInitUSB,
407                             mediaGetUSB, mediaShutdownUSB, NULL);
408
409                         if (isDebug())
410                                 msgDebug("Found a USB disk for %s\n", try);
411                 }
412                 break;
413
414             default:
415                 break;
416             }
417         }
418     }
419
420     /* Finally, go get the disks and look for partitions to register */
421     if ((names = Disk_Names()) != NULL) {
422         int i;
423
424         for (i = 0; names[i]; i++) {
425             Chunk *c1;
426             Disk *d;
427
428             /* Ignore memory disks */
429             if (!strncmp(names[i], "md", 2))
430                 continue;
431
432             /*
433              * XXX 
434              *  Due to unknown reasons, Disk_Names() returns SCSI CDROM as a
435              * valid disk. This is main reason why sysinstall presents SCSI
436              * CDROM to available disks in Fdisk/Label menu. In addition,
437              * adding a blank SCSI CDROM to the menu generates floating point
438              * exception in sparc64. Disk_Names() just extracts sysctl
439              * "kern.disks". Why GEOM treats SCSI CDROM as a disk is beyond
440              * me and that should be investigated.
441              * For temporary workaround, ignore SCSI CDROM device.
442              */
443             if (!strncmp(names[i], "cd", 2))
444                 continue;
445
446             d = Open_Disk(names[i]);
447             if (!d) {
448                 msgDebug("Unable to open disk %s\n", names[i]);
449                 continue;
450             }
451
452             deviceRegister(names[i], names[i], d->name, DEVICE_TYPE_DISK, FALSE,
453                            dummyInit, dummyGet, dummyShutdown, d);
454             if (isDebug())
455                 msgDebug("Found a disk device named %s\n", names[i]);
456
457             /* Look for existing DOS partitions to register as "DOS media devices"
458              * XXX: libdisks handling of extended partitions is too
459              * simplistic - it does not handle them containing (for
460              * example) UFS partitions
461              */
462             for (c1 = d->chunks->part; c1; c1 = c1->next) {
463                 if (c1->type == fat || c1->type == efi || c1->type == extended) {
464                     Device *dev;
465                     char devname[80];
466
467                     /* Got one! */
468                     snprintf(devname, sizeof devname, "/dev/%s", c1->name);
469                     dev = deviceRegister(c1->name, c1->name, strdup(devname), DEVICE_TYPE_DOS, TRUE,
470                                          mediaInitDOS, mediaGetDOS, mediaShutdownDOS, NULL);
471                     dev->private = c1;
472                     if (isDebug())
473                         msgDebug("Found a DOS partition %s\n", c1->name);
474                 } else if (c1->type == freebsd) {
475                     Device *dev;
476                     char devname[80];
477                     Chunk *c2;
478                         
479                     for (c2 = c1->part; c2; c2 = c2->next) {
480                         if (c2->type != part || c2->subtype != 7)
481                             continue;
482                         /* Got one! */
483                         snprintf(devname, sizeof devname, "/dev/%s", c1->name);
484                         dev = deviceRegister(c2->name, c2->name, strdup(devname), DEVICE_TYPE_UFS, TRUE,
485                                              mediaInitUFS, mediaGetUFS, mediaShutdownUFS, NULL);
486                         dev->private = c2;
487                         if (isDebug())
488                             msgDebug("Found a UFS sub-partition %s\n", c2->name);
489                     }
490                 }
491                 
492             }
493         }
494         free(names);
495     }
496     dialog_clear_norefresh();
497 }
498
499 /* Rescan all devices, after closing previous set - convenience function */
500 void
501 deviceRescan(void)
502 {
503     deviceReset();
504     deviceGetAll();
505 }
506
507 /*
508  * Find all devices that match the criteria, allowing "wildcarding" as well
509  * by allowing NULL or ANY values to match all.  The array returned is static
510  * and may be used until the next invocation of deviceFind().
511  */
512 Device **
513 deviceFind(char *name, DeviceType class)
514 {
515     static Device *found[DEV_MAX];
516     int i, j;
517
518     j = 0;
519     for (i = 0; i < numDevs; i++) {
520         if ((!name || !strcmp(Devices[i]->name, name))
521             && (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
522             found[j++] = Devices[i];
523     }
524     found[j] = NULL;
525     return j ? found : NULL;
526 }
527
528 Device **
529 deviceFindDescr(char *name, char *desc, DeviceType class)
530 {
531     static Device *found[DEV_MAX];
532     int i, j;
533
534     j = 0;
535     for (i = 0; i < numDevs; i++) {
536         if ((!name || !strcmp(Devices[i]->name, name)) &&
537             (!desc || !strcmp(Devices[i]->description, desc)) &&
538             (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
539             found[j++] = Devices[i];
540     }
541     found[j] = NULL;
542     return j ? found : NULL;
543 }
544
545 int
546 deviceCount(Device **devs)
547 {
548     int i;
549
550     if (!devs)
551         return 0;
552     for (i = 0; devs[i]; i++);
553     return i;
554 }
555
556 /*
557  * Create a menu listing all the devices of a certain type in the system.
558  * The passed-in menu is expected to be a "prototype" from which the new
559  * menu is cloned.
560  */
561 DMenu *
562 deviceCreateMenu(DMenu *menu, DeviceType type, int (*hook)(dialogMenuItem *d), int (*check)(dialogMenuItem *d))
563 {
564     Device **devs;
565     int numdevs;
566     DMenu *tmp = NULL;
567     int i, j;
568
569     devs = deviceFind(NULL, type);
570     numdevs = deviceCount(devs);
571     if (!numdevs)
572         return NULL;
573     tmp = (DMenu *)safe_malloc(sizeof(DMenu) + (sizeof(dialogMenuItem) * (numdevs + 1)));
574     bcopy(menu, tmp, sizeof(DMenu));
575     for (i = 0; devs[i]; i++) {
576         tmp->items[i].prompt = devs[i]->name;
577         for (j = 0; j < numDevs; j++) {
578             if (devs[i] == Devices[j]) {
579                 tmp->items[i].title = Devices[j]->description;
580                 break;
581             }
582         }
583         if (j == numDevs)
584             tmp->items[i].title = "<unknown device type>";
585         tmp->items[i].fire = hook;
586         tmp->items[i].checked = check;
587     }
588     tmp->items[i].title = NULL;
589     return tmp;
590 }