dev_rmscsi.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#if defined(_FIRMWARE_NEEDS_FDISK)
#endif
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <thread.h>
#include <synch.h>
#include "vold.h"
#ifdef P0_WA
#endif
static bool_t rmscsi_use(char *, char *);
static int rmscsi_getfd(dev_t);
static void rmscsi_devmap(vol_t *, int, int);
static bool_t rmscsi_testpath(char *);
static struct devsw rmscsidevsw = {
rmscsi_use, /* d_use */
rmscsi_error, /* d_error */
rmscsi_getfd, /* d_getfd */
NULL, /* d_poll */
rmscsi_devmap, /* d_devmap */
rmscsi_close, /* d_close */
NULL, /* d_eject */
NULL, /* d_find */
NULL, /* d_check */
RMSCSI_MTYPE, /* d_mtype */
DRIVE_CLASS, /* d_dtype */
(uid_t)0, /* d_uid */
(gid_t)0, /* d_gid */
(mode_t)0, /* d_mode */
rmscsi_testpath /* d_test */
};
dev_init()
{
return (TRUE);
}
static struct rmscsi_priv {
char *rs_rawpath[V_NUMPAR];
int rs_tid; /* thread id */
int rs_defpart;
#ifdef P0_WA
char *rs_blk_p0_path; /* the p0 blk name */
int rs_p0_part; /* slice to substitute p0 for */
#endif
#if defined(_FIRMWARE_NEEDS_FDISK)
#endif
};
#define RMSCSI_NAMEPROTO_DEFD "%sd0s%d"
#define RMSCSI_BASEPART DEFAULT_PARTITION
#define RMSCSI_NAMEPROTO "%ss%d"
#if defined(_FIRMWARE_NEEDS_FDISK)
#define RMSCSI_NAMEPROTO_P "%sp%d"
#ifdef P0_WA
#define RMSCSI_NAMEPROTO_P_ALL 0
#endif
static void rmscsi_open_exclusive(struct rmscsi_priv *, char *, char *);
#endif
/* thread stack size */
/*
* rmscsi_use -- this routine expects either a raw or block path that
* to a removable scsi disk (removability being detected
* using a SCSI inquiry command)
*
* it further expects that the supplied
*
* it finds the complimentary device by switching this
* group of block devices, then this routine will
*
* a thread is created which will handle this new group of
* interfaces to a device
*
* a devs struct is filled in and passed on to the thread
*
* return TRUE implies that the device is one which isn't
* currently managed, and needs to be
*/
static bool_t
{
char namebuf1[MAXPATHLEN];
char *path_trunc = path;
char namebuf[MAXNAMELEN];
struct rmscsi_priv *rsp;
char *s;
char *p;
int i;
/*
* we don't do an open for the device because it'll probably just
* return ENODEV if there isn't a device there
*
* instead, we just stat the device and make sure the device
* node is there and is a reasonable type
*/
/* just take a path if they hand it to us. */
/*
* we can accept a path of the form:
*
* /dev/{dsk,rdsk}/cNtN
*
* we fill in the rest by appending "d0sN"
*/
/* can't even find it with "d0sN" appended! */
return (FALSE);
}
} else {
/*
* the supplied path is complete -- truncate at the "slice"
* part of the name
*
* XXX: assume all rmscsi pathnames end in "sN"
*/
/* XXX: should make sure a slice number follows */
*s = '\0'; /* truncate at the "sN" */
} else {
/* the full path didn't have an "s" in it! */
return (FALSE);
}
}
/*
* check to see if this guy is already configured
*/
return (TRUE);
} else {
return (FALSE);
}
}
/*
* check the modes to make sure that the path is either
* a block or a character device
*/
"rmscsi: %s not block or char device (mode 0x%x)\n"),
return (FALSE);
}
/* create en "empty" 'rmscsi-private' data struct */
for (i = 0; i < V_NUMPAR; i++) {
}
#ifdef P0_WA
#endif
/* stick some good stuff in the device hierarchy */
/* he gave us a raw path (i.e. "rdsk" in it) */
/* save a pointer to the raw vv-node */
/* create the names for rawpath */
for (i = 0; i < V_NUMPAR; i++) {
path_trunc, i);
}
/* get the block path now from the raw one */
/* skip past "rdsk/" */
if ((p = strchr(s, '/')) != 0) {
p++;
} else {
/* no slash after rdsk? */
/* what else can we do? */
}
/* get the block vv-node */
#ifdef P0_WA
/* set up the p0 block pathname */
#endif
/* he gave us the block path */
/* save pointer to block vv-node */
#ifdef P0_WA
#endif
/* skip past "dsk/" */
if ((p = strchr(s, '/')) != 0) {
p++;
} else {
/* no slash after "dsk"? */
path);
/* what else can we do? */
}
/* save a pointer to the raw vv-node */
/* create the names for rawpath */
for (i = 0; i < V_NUMPAR; i++) {
}
} else {
return (FALSE);
}
#endif
return (FALSE);
}
#if defined(_FIRMWARE_NEEDS_FDISK)
/*
* serious hackery -- open the p? interfaces (so others can't
* get around us)
*/
#endif
if (thr_create(0, RMSCSI_STKSIZE,
return (FALSE);
}
#ifdef DEBUG
#endif
return (TRUE);
}
/*ARGSUSED*/
static void
{
struct rmscsi_priv *rsp;
#ifdef P0_WA
/* return P0 path for bigest slice that starts at 0 */
/* hack! -- use p0 instead of the requested slice */
} else {
/* return the actual slice requested */
}
#else
#endif
#ifdef DEBUG
#endif
}
static int
{
struct rmscsi_priv *rsp;
int fd;
return (fd);
}
/*ARGSUSED*/
static bool_t
{
return (TRUE);
}
/*
* State that must be cleaned up:
* name in the name space
* the "dp"
* any pointers to the media
* eject any existing media
* the priv structure
*/
/*
* XXX: a bug still exists here. we have a thread polling on this
* XXX: device in the kernel, we need to get rid of this also.
* XXX: since we're going to move the waiter thread up to the
* XXX: user level, it'll be easier to kill off as part of the
* XXX: cleanup of the device private data.
*/
static void
{
char namebuf[MAXNAMELEN];
struct rmscsi_priv *rsp;
int i;
return;
}
} else {
}
return;
}
/* get our private data */
/*
* take care of the listner thread
*/
/*
* if there is a volume inserted in this device ...
*/
/*
* clean up the name space and the device maps
* to remove references to any volume that might
* be in the device right now
*
* this crap with the flags is to keep the
* "poll" from being relaunched by this function
*
* yes, its a hack and there should be a better way
*/
} else {
}
return;
}
/* do the eject work */
}
/*
* clean up the names in the name space
*/
/*
* free the private data we've allocated
*/
for (i = 0; i < V_NUMPAR; i++) {
if (rsp->rs_rawpath[i]) {
}
}
}
#if defined(_FIRMWARE_NEEDS_FDISK)
for (i = 0; i < (FD_NUMPART+1); i++) {
if (rsp->rs_raw_pfd[i] >= 0) {
}
}
#endif
/*
* free the dp, so no one points at us anymore
*/
dev_freedp(dp);
}
#ifdef P0_WA
/*
* return the numnber of the slice that starts at zero and maps the largest
* portion of the device. if none found return a -1
*/
static int
part_to_hack(struct vtoc *v)
{
int i;
int part_no = -1;
int part_start = -1;
int part_size = 0;
int sz;
int st;
/* scan for lowest starting part that has biggest chunk */
for (i = 0; i < V_NUMPAR; i++) {
/* get size and start, ignoring this slice if no size */
continue;
}
/*
* 3 possible cases of choosing this partition over
* our previous best:
* -> we don't have a previous best
* -> this part starts earlier than previous best
* -> this part starts at same place but is larger
*/
if ((part_start < 0) ||
(st < part_start) ||
part_start = st;
part_no = i;
continue;
}
}
/* return slice found (or -1 if none) */
return (part_no);
}
#endif /* P0_WA */
static void
{
#ifdef DEBUG
static char *dkiostate_to_str(enum dkio_state);
#endif
static int reopen_rmscsi(struct rmscsi_priv *);
extern int vold_running;
extern cond_t running_cv;
extern mutex_t running_mutex;
int fd;
struct vioc_event vie;
enum dkio_state rmscsi_state;
int i;
/* ensure vold main stuff is running before continuing */
(void) mutex_lock(&running_mutex);
while (vold_running == 0) {
}
(void) mutex_unlock(&running_mutex);
/* open each slice of the media to cover all of the bases */
for (i = 0; i < V_NUMPAR; i++) {
rsp->rs_rawpath[i]);
/*
* just always open rdonly here, since the DKIOC_INSERTED
*/
noise("rmscsi: open of \"%s\"; %m\n",
rsp->rs_rawpath[i]);
goto errout;
}
/* XXX: should we do this? it seems to make sens (and work!) */
}
/*
* check to make sure device is a SCSI disk
*
* XXX: isn't this redundant with rmscsi_testpath() ?
*/
noise("rmscsi: %s DKIOCINFO failed; %m\n",
goto errout;
}
"rmscsi: %s is not a SCSI disk drive (disk type %d expected, %d found)\n",
goto errout;
}
/*CONSTCOND*/
while (1) {
/*
* this ioctl blocks until state changes.
*/
#ifdef DEBUG
debug(3,
"rmscsi_thread_wait: ioctl(DKIOCSTATE, \"%s\") on \"%s\"\n",
#else
#endif
debug(1,
"rmscsi_thread_wait: DKIOCSTATE of \"%s\"; %m\n",
goto errout;
}
(void) sleep(1);
continue;
}
#ifdef DEBUG
#endif
if (rmscsi_state == DKIO_NONE) {
continue; /* steady state -- ignore */
}
/*
* we have media in the drive
*/
if (rmscsi_state == DKIO_INSERTED) {
/*
* if we already know about the media in the
* device, just ignore the information
*/
continue;
}
/*
* find out the lowest partition that maps the
* beginning of the drive
*/
}
debug(1,
"rmscsi_thread_wait: rs_defpart now %d\n",
rsp->rs_defpart);
#ifdef P0_WA
debug(1,
"rmscsi_thread_wait: slice to hack = %d\n",
rsp->rs_p0_part);
#endif
}
/* generate an "insert" event */
}
/*
* we have NO media in the drive (it's just been ejected)
*/
if (rmscsi_state == DKIO_EJECTED) {
vol_t *v;
(void) mutex_lock(&vold_main_mutex);
/*
* if we already know about the ejection,
* just continue in our happy loop
*/
(void) mutex_unlock(&vold_main_mutex);
continue;
}
/*
* generate an eject event (if we have a unit)
*
* XXX: this doesn't work because the DKIOCSTATE ioctl
* never seems to return DKIO_EJECTED for some
* devices, such as the ZIP 100
*/
for (i = 0; i < (int)v->v_ndev; i++) {
break;
}
}
(void) mutex_unlock(&vold_main_mutex);
}
}
/*NOTREACHED*/
/*
* we get here if we have an error: close all the open fd's and
* return
*/
for (i = 0; i < V_NUMPAR; i++) {
}
}
}
/*
* close then open the default partition for the media
*/
static int
{
int rdonly = 0;
/*
* XXX: boy, is this a hack
*
* this works around a bug in scsi drivers were
* you can't seem to read from a file descriptor you've opened
* O_NDELAY where there wasn't any media in the drive
*
* this open can take forever, by the way ...
*/
#ifdef DEBUG
#endif
rdonly = 1;
} else {
}
}
}
/* set close-on-exec */
debug(1,
"reopen_rmscsi: fd = %d (slice %d), rdonly = %d (path \"%s\")\n",
return (rdonly);
}
#ifdef DEBUG
static char *
{
static char state_buf[30];
switch (st) {
case DKIO_NONE:
break;
case DKIO_INSERTED:
break;
case DKIO_EJECTED:
break;
default:
break;
}
return (state_buf);
}
#endif /* DEBUG */
static bool_t
rmscsi_testpath(char *path)
{
int fd = -1;
int removable;
/* check to see if we're already using it */
/* something's seriously wrong */
goto dun;
}
return (TRUE);
} else {
return (FALSE);
}
}
/* make sure our path is a raw device */
path);
goto dun;
}
/*
* if we can't open it, assume that it's because it's busy or
* something else is wrong
*
* in any event, dev_use couldn't open it either, so it's
* not worth trying to use the device
*/
goto dun;
}
/* check to make sure device is a SCSI device */
rp);
goto dun;
}
rp);
debug(5,
"rmscsi(probing): (disk type %d expected, %d found)\n",
goto dun;
}
/*
* if we stop here, we'll end up trying to manage hard disks that
* are found, since they also return DKC_SCSI_CCS,
* so do a DKIOCREMOVABLE ioctl (more general than trying
* to do a SCSI inquiry here, since this latter method won't
* work on non-SCSI devices, such as IDE disks)
*/
/* do the inquiry */
debug(5,
"rmscsi(probing): \"%s\" DKIOCREMOVABLE ioctl failed; %m\n",
rp);
goto dun;
}
if (removable == 0) {
rp);
goto dun;
}
#ifdef DEBUG
rp);
#endif
dun:
/* all done */
if (fd >= 0) {
}
}
return (res);
}
#if defined(_FIRMWARE_NEEDS_FDISK)
/*
* serious hackery -- attempt to open the p? interfaces of the specified
* device -- just to keep users from getting around volmgt
*
* If this fails, just ignore it.
*
* The supplied params path1 and path2 will be the block
* and char prototype paths (but not necessarily in that
* order).
*/
static void
{
char namebuf[MAXNAMELEN];
int i;
char *raw_proto; /* for the "rdsk" path */
/* initialized all of the fds */
for (i = 0; i < (FD_NUMPART+1); i++) {
}
/* find out which one is the raw path prototype */
} else {
return;
}
/* (attempt to) open each p device */
for (i = 0; i < (FD_NUMPART+1); i++) {
/* do the raw device */
#ifdef DEBUG
#endif
}
}
#endif /* _FIRMWARE_NEEDS_FDISK */