devicename.c revision 86091
155714Skris/*-
255714Skris * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
355714Skris * All rights reserved.
455714Skris *
555714Skris * Redistribution and use in source and binary forms, with or without
655714Skris * modification, are permitted provided that the following conditions
755714Skris * are met:
855714Skris * 1. Redistributions of source code must retain the above copyright
955714Skris *    notice, this list of conditions and the following disclaimer.
1055714Skris * 2. Redistributions in binary form must reproduce the above copyright
1155714Skris *    notice, this list of conditions and the following disclaimer in the
1255714Skris *    documentation and/or other materials provided with the distribution.
1355714Skris *
1455714Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1568651Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1668651Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1755714Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1855714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1955714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2055714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2155714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2255714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2355714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2468651Skris * SUCH DAMAGE.
2568651Skris *
2655714Skris * $FreeBSD: head/sys/boot/i386/libi386/devicename.c 86091 2001-11-05 18:58:33Z jhb $
2755714Skris */
2855714Skris
2955714Skris#include <stand.h>
3055714Skris#include <string.h>
3155714Skris#include <sys/disklabel.h>
3255714Skris#include "bootstrap.h"
3355714Skris#include "libi386.h"
3455714Skris
3555714Skrisstatic int	i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path);
3655714Skris
3755714Skris/*
3855714Skris * Point (dev) at an allocated device specifier for the device matching the
3955714Skris * path in (devspec). If it contains an explicit device specification,
4055714Skris * use that.  If not, use the default device.
4155714Skris */
42160814Ssimonint
4355714Skrisi386_getdev(void **vdev, const char *devspec, const char **path)
4455714Skris{
4555714Skris    struct i386_devdesc **dev = (struct i386_devdesc **)vdev;
4655714Skris    int				rv;
4755714Skris
4855714Skris    /*
4955714Skris     * If it looks like this is just a path and no
5055714Skris     * device, go with the current device.
5155714Skris     */
5255714Skris    if ((devspec == NULL) ||
5355714Skris	(devspec[0] == '/') ||
5455714Skris	(strchr(devspec, ':') == NULL)) {
5555714Skris
5655714Skris	if (((rv = i386_parsedev(dev, getenv("currdev"), NULL)) == 0) &&
5755714Skris	    (path != NULL))
5855714Skris		*path = devspec;
5955714Skris	return(rv);
6055714Skris    }
6155714Skris
6255714Skris    /*
6355714Skris     * Try to parse the device name off the beginning of the devspec
6455714Skris     */
6555714Skris    return(i386_parsedev(dev, devspec, path));
6655714Skris}
6755714Skris
6855714Skris/*
6955714Skris * Point (dev) at an allocated device specifier matching the string version
7055714Skris * at the beginning of (devspec).  Return a pointer to the remaining
7155714Skris * text in (path).
7255714Skris *
7355714Skris * In all cases, the beginning of (devspec) is compared to the names
7455714Skris * of known devices in the device switch, and then any following text
75160814Ssimon * is parsed according to the rules applied to the device type.
7655714Skris *
7755714Skris * For disk-type devices, the syntax is:
7855714Skris *
7955714Skris * disk<unit>[s<slice>][<partition>]:
8055714Skris *
81160814Ssimon */
8255714Skrisstatic int
8355714Skrisi386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path)
8455714Skris{
8555714Skris    struct i386_devdesc *idev;
8655714Skris    struct devsw	*dv;
8755714Skris    int			i, unit, slice, partition, err;
8855714Skris    char		*cp;
8955714Skris    const char		*np;
9055714Skris
9155714Skris    /* minimum length check */
9255714Skris    if (strlen(devspec) < 2)
9355714Skris	return(EINVAL);
9455714Skris
9555714Skris    /* look for a device that matches */
9655714Skris    for (i = 0, dv = NULL; devsw[i] != NULL; i++) {
9755714Skris	if (!strncmp(devspec, devsw[i]->dv_name, strlen(devsw[i]->dv_name))) {
9855714Skris	    dv = devsw[i];
9955714Skris	    break;
10055714Skris	}
10155714Skris    }
10255714Skris    if (dv == NULL)
10355714Skris	return(ENOENT);
10455714Skris    idev = malloc(sizeof(struct i386_devdesc));
105    err = 0;
106    np = (devspec + strlen(dv->dv_name));
107
108    switch(dv->dv_type) {
109    case DEVT_NONE:			/* XXX what to do here?  Do we care? */
110	break;
111
112    case DEVT_DISK:
113	unit = -1;
114	slice = -1;
115	partition = -1;
116	if (*np && (*np != ':')) {
117	    unit = strtol(np, &cp, 10);	/* next comes the unit number */
118	    if (cp == np) {
119		err = EUNIT;
120		goto fail;
121	    }
122	    if (*cp == 's') {		/* got a slice number */
123		np = cp + 1;
124		slice = strtol(np, &cp, 10);
125		if (cp == np) {
126		    err = ESLICE;
127		    goto fail;
128		}
129	    }
130	    if (*cp && (*cp != ':')) {
131		partition = *cp - 'a';		/* get a partition number */
132		if ((partition < 0) || (partition >= MAXPARTITIONS)) {
133		    err = EPART;
134		    goto fail;
135		}
136		cp++;
137	    }
138	}
139	if (*cp && (*cp != ':')) {
140	    err = EINVAL;
141	    goto fail;
142	}
143
144	idev->d_kind.biosdisk.unit = unit;
145	idev->d_kind.biosdisk.slice = slice;
146	idev->d_kind.biosdisk.partition = partition;
147	if (path != NULL)
148	    *path = (*cp == 0) ? cp : cp + 1;
149	break;
150
151    case DEVT_CD:
152    case DEVT_NET:
153	unit = 0;
154
155	if (*np && (*np != ':')) {
156	    unit = strtol(np, &cp, 0);	/* get unit number if present */
157	    if (cp == np) {
158		err = EUNIT;
159		goto fail;
160	    }
161	}
162	if (*cp && (*cp != ':')) {
163	    err = EINVAL;
164	    goto fail;
165	}
166
167	if (dv->dv_type == DEVT_NET)
168	    idev->d_kind.netif.unit = unit;
169	else
170	    idev->d_kind.bioscd.unit = unit;
171	if (path != NULL)
172	    *path = (*cp == 0) ? cp : cp + 1;
173	break;
174
175    default:
176	err = EINVAL;
177	goto fail;
178    }
179    idev->d_dev = dv;
180    idev->d_type = dv->dv_type;
181    if (dev == NULL) {
182	free(idev);
183    } else {
184	*dev = idev;
185    }
186    return(0);
187
188 fail:
189    free(idev);
190    return(err);
191}
192
193
194char *
195i386_fmtdev(void *vdev)
196{
197    struct i386_devdesc	*dev = (struct i386_devdesc *)vdev;
198    static char		buf[128];	/* XXX device length constant? */
199    char		*cp;
200
201    switch(dev->d_type) {
202    case DEVT_NONE:
203	strcpy(buf, "(no device)");
204	break;
205
206    case DEVT_CD:
207	sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_kind.bioscd.unit);
208	break;
209
210    case DEVT_DISK:
211	cp = buf;
212	cp += sprintf(cp, "%s%d", dev->d_dev->dv_name, dev->d_kind.biosdisk.unit);
213	if (dev->d_kind.biosdisk.slice > 0)
214	    cp += sprintf(cp, "s%d", dev->d_kind.biosdisk.slice);
215	if (dev->d_kind.biosdisk.partition >= 0)
216	    cp += sprintf(cp, "%c", dev->d_kind.biosdisk.partition + 'a');
217	strcat(cp, ":");
218	break;
219
220    case DEVT_NET:
221	sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_kind.netif.unit);
222	break;
223    }
224    return(buf);
225}
226
227
228/*
229 * Set currdev to suit the value being supplied in (value)
230 */
231int
232i386_setcurrdev(struct env_var *ev, int flags, void *value)
233{
234    struct i386_devdesc	*ncurr;
235    int			rv;
236
237    if ((rv = i386_parsedev(&ncurr, value, NULL)) != 0)
238	return(rv);
239    free(ncurr);
240    env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
241    return(0);
242}
243
244