cam_periph.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,577 行 · 第 1/3 页

C
1,577
字号
/* * Common functions for CAM "type" (peripheral) drivers. * * Copyright (c) 1997, 1998 Justin T. Gibbs. * Copyright (c) 1997, 1998 Kenneth D. Merry. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions, and the following disclaimer, *    without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products *    derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * *      $Id: cam_periph.c,v 1.9.2.2 1999/05/09 01:27:21 ken Exp $ */#include <sys/param.h>#include <sys/systm.h>#include <sys/types.h>#include <sys/malloc.h>#include <sys/linker_set.h>#include <sys/buf.h>#include <sys/proc.h>#include <sys/devicestat.h>#include <vm/vm.h>#include <vm/vm_extern.h>#include <cam/cam.h>#include <cam/cam_conf.h>#include <cam/cam_ccb.h>#include <cam/cam_xpt_periph.h>#include <cam/cam_periph.h>#include <cam/cam_debug.h>#include <cam/scsi/scsi_all.h>#include <cam/scsi/scsi_message.h>#include <cam/scsi/scsi_da.h>#include <cam/scsi/scsi_pass.h>static	u_int		camperiphnextunit(struct periph_driver *p_drv,					  u_int newunit, int wired);static	u_int		camperiphunit(struct periph_driver *p_drv,				      path_id_t path_id_t,				      target_id_t target, lun_id_t lun); static	void		camperiphdone(struct cam_periph *periph, 					union ccb *done_ccb);static  void		camperiphfree(struct cam_periph *periph);cam_statuscam_periph_alloc(periph_ctor_t *periph_ctor,		 periph_oninv_t *periph_oninvalidate,		 periph_dtor_t *periph_dtor, periph_start_t *periph_start,		 char *name, cam_periph_type type, struct cam_path *path,		 ac_callback_t *ac_callback, ac_code code, void *arg){	struct		periph_driver **p_drv;	struct		cam_periph *periph;	struct		cam_periph *cur_periph;	path_id_t	path_id;	target_id_t	target_id;	lun_id_t	lun_id;	cam_status	status;	u_int		init_level;	int s;	init_level = 0;	/*	 * Handle Hot-Plug scenarios.  If there is already a peripheral	 * of our type assigned to this path, we are likely waiting for	 * final close on an old, invalidated, peripheral.  If this is	 * the case, queue up a deferred call to the peripheral's async	 * handler.  If it looks like a mistaken re-alloation, complain.	 */	if ((periph = cam_periph_find(path, name)) != NULL) {		if ((periph->flags & CAM_PERIPH_INVALID) != 0		 && (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) == 0) {			periph->flags |= CAM_PERIPH_NEW_DEV_FOUND;			periph->deferred_callback = ac_callback;			periph->deferred_ac = code;			return (CAM_REQ_INPROG);		} else {			printf("cam_periph_alloc: attempt to re-allocate "			       "valid device %s%d rejected\n",			       periph->periph_name, periph->unit_number);		}		return (CAM_REQ_INVALID);	}		periph = (struct cam_periph *)malloc(sizeof(*periph), M_DEVBUF,					     M_NOWAIT);	if (periph == NULL)		return (CAM_RESRC_UNAVAIL);		init_level++;	for (p_drv = (struct periph_driver **)periphdriver_set.ls_items;	     *p_drv != NULL; p_drv++) {		if (strcmp((*p_drv)->driver_name, name) == 0)			break;	}		path_id = xpt_path_path_id(path);	target_id = xpt_path_target_id(path);	lun_id = xpt_path_lun_id(path);	bzero(periph, sizeof(*periph));	cam_init_pinfo(&periph->pinfo);	periph->periph_start = periph_start;	periph->periph_dtor = periph_dtor;	periph->periph_oninval = periph_oninvalidate;	periph->type = type;	periph->periph_name = name;	periph->unit_number = camperiphunit(*p_drv, path_id, target_id, lun_id);	periph->immediate_priority = CAM_PRIORITY_NONE;	periph->refcount = 0;	SLIST_INIT(&periph->ccb_list);	status = xpt_create_path(&path, periph, path_id, target_id, lun_id);	if (status != CAM_REQ_CMP)		goto failure;	periph->path = path;	init_level++;	status = xpt_add_periph(periph);	if (status != CAM_REQ_CMP)		goto failure;	s = splsoftcam();	cur_periph = TAILQ_FIRST(&(*p_drv)->units);	while (cur_periph != NULL	    && cur_periph->unit_number < periph->unit_number)		cur_periph = TAILQ_NEXT(cur_periph, unit_links);	if (cur_periph != NULL)		TAILQ_INSERT_BEFORE(cur_periph, periph, unit_links);	else {		TAILQ_INSERT_TAIL(&(*p_drv)->units, periph, unit_links);		(*p_drv)->generation++;	}	splx(s);	init_level++;	status = periph_ctor(periph, arg);	if (status == CAM_REQ_CMP)		init_level++;failure:	switch (init_level) {	case 4:		/* Initialized successfully */		break;	case 3:		s = splsoftcam();		TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links);		splx(s);		xpt_remove_periph(periph);	case 2:		xpt_free_path(periph->path);	case 1:		free(periph, M_DEVBUF);	case 0:		/* No cleanup to perform. */		break;	default:		panic("cam_periph_alloc: Unkown init level");	}	return(status);}/* * Find a peripheral structure with the specified path, target, lun,  * and (optionally) type.  If the name is NULL, this function will return * the first peripheral driver that matches the specified path. */struct cam_periph *cam_periph_find(struct cam_path *path, char *name){	struct periph_driver **p_drv;	struct cam_periph *periph;	int s;	for (p_drv = (struct periph_driver **)periphdriver_set.ls_items;	     *p_drv != NULL; p_drv++) {		if (name != NULL && (strcmp((*p_drv)->driver_name, name) != 0))			continue;		s = splsoftcam();		for (periph = TAILQ_FIRST(&(*p_drv)->units); periph != NULL;		     periph = TAILQ_NEXT(periph, unit_links)) {			if (xpt_path_comp(periph->path, path) == 0) {				splx(s);				return(periph);			}		}		splx(s);		if (name != NULL)			return(NULL);	}	return(NULL);}cam_statuscam_periph_acquire(struct cam_periph *periph){	int s;	if (periph == NULL)		return(CAM_REQ_CMP_ERR);	s = splsoftcam();	periph->refcount++;	splx(s);	return(CAM_REQ_CMP);}voidcam_periph_release(struct cam_periph *periph){	int s;	if (periph == NULL)		return;	s = splsoftcam();	if ((--periph->refcount == 0)	 && (periph->flags & CAM_PERIPH_INVALID)) {		camperiphfree(periph);	}	splx(s);}/* * Look for the next unit number that is not currently in use for this * peripheral type starting at "newunit".  Also exclude unit numbers that * are reserved by for future "hardwiring" unless we already know that this * is a potential wired device.  Only assume that the device is "wired" the * first time through the loop since after that we'll be looking at unit * numbers that did not match a wiring entry. */static u_intcamperiphnextunit(struct periph_driver *p_drv, u_int newunit, int wired){	struct	cam_periph *periph;	struct	cam_periph_config *periph_conf;	char	*periph_name;	int	s;	s = splsoftcam();	periph_name = p_drv->driver_name;	for (;;newunit++) {		for (periph = TAILQ_FIRST(&p_drv->units);		     periph != NULL && periph->unit_number != newunit;		     periph = TAILQ_NEXT(periph, unit_links))			;		if (periph != NULL && periph->unit_number == newunit) {			if (wired != 0) {				xpt_print_path(periph->path);				printf("Duplicate Wired Device entry!\n");				xpt_print_path(periph->path);				printf("Second device will not be wired\n");				wired = 0;			}			continue;		}		for (periph_conf = cam_pinit;		     wired == 0 && periph_conf->periph_name != NULL;		     periph_conf++) {			/*			 * Don't match entries like "da 4" as a wired down			 * device, but do match entries like "da 4 target 5"			 * or even "da 4 scbus 1". 			 */			if (IS_SPECIFIED(periph_conf->periph_unit)			 && (!strcmp(periph_name, periph_conf->periph_name))			 && (IS_SPECIFIED(periph_conf->target)			  || IS_SPECIFIED(periph_conf->pathid))			 && (newunit == periph_conf->periph_unit))				break;		}		if (wired != 0 || periph_conf->periph_name == NULL)			break;	}	splx(s);	return (newunit);}static u_intcamperiphunit(struct periph_driver *p_drv, path_id_t pathid,	      target_id_t target, lun_id_t lun){	struct cam_periph_config *periph_conf;	u_int unit;	int hit;	unit = 0;	hit = 0;	for (periph_conf = cam_pinit;	     periph_conf->periph_name != NULL;	     periph_conf++, hit = 0) {		if (!strcmp(p_drv->driver_name, periph_conf->periph_name)		 && IS_SPECIFIED(periph_conf->periph_unit)) {			if (IS_SPECIFIED(periph_conf->pathid)) {				if (pathid != periph_conf->pathid)					continue;				hit++;			}			if (IS_SPECIFIED(periph_conf->target)) {				if (target != periph_conf->target)					continue;				hit++;			}			if (IS_SPECIFIED(periph_conf->lun)) {				if (lun != periph_conf->lun)					continue;				hit++;			}			if (hit != 0) {				unit = periph_conf->periph_unit;				break;			}		}	}	/*	 * Either start from 0 looking for the next unit or from	 * the unit number given in the periph_conf.  This way,	 * if we have wildcard matches, we don't return the same	 * unit number twice.	 */	unit = camperiphnextunit(p_drv, unit, /*wired*/hit);	return (unit);}voidcam_periph_invalidate(struct cam_periph *periph){	int s;	s = splsoftcam();	/*	 * We only call this routine the first time a peripheral is	 * invalidated.  The oninvalidate() routine is always called at	 * splsoftcam().	 */	if (((periph->flags & CAM_PERIPH_INVALID) == 0)	 && (periph->periph_oninval != NULL))		periph->periph_oninval(periph);	periph->flags |= CAM_PERIPH_INVALID;	periph->flags &= ~CAM_PERIPH_NEW_DEV_FOUND;	if (periph->refcount == 0)		camperiphfree(periph);	else if (periph->refcount < 0)		printf("cam_invalidate_periph: refcount < 0!!\n");	splx(s);}static voidcamperiphfree(struct cam_periph *periph){	int s;	struct periph_driver **p_drv;	for (p_drv = (struct periph_driver **)periphdriver_set.ls_items;	     *p_drv != NULL; p_drv++) {		if (strcmp((*p_drv)->driver_name, periph->periph_name) == 0)			break;	}		if (periph->periph_dtor != NULL)		periph->periph_dtor(periph);		s = splsoftcam();	TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links);	(*p_drv)->generation++;	splx(s);	xpt_remove_periph(periph);	if (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) {		union ccb ccb;		void *arg;		switch (periph->deferred_ac) {		case AC_FOUND_DEVICE:			ccb.ccb_h.func_code = XPT_GDEV_TYPE;			xpt_setup_ccb(&ccb.ccb_h, periph->path, /*priority*/ 1);			xpt_action(&ccb);			arg = &ccb;			break;		case AC_PATH_REGISTERED:			ccb.ccb_h.func_code = XPT_PATH_INQ;			xpt_setup_ccb(&ccb.ccb_h, periph->path, /*priority*/ 1);			xpt_action(&ccb);			arg = &ccb;			break;		default:			arg = NULL;			break;		}		periph->deferred_callback(NULL, periph->deferred_ac,					  periph->path, arg);	}	xpt_free_path(periph->path);	free(periph, M_DEVBUF);}/* * Wait interruptibly for an exclusive lock. */intcam_periph_lock(struct cam_periph *periph, int priority){	int error;	while ((periph->flags & CAM_PERIPH_LOCKED) != 0) {		periph->flags |= CAM_PERIPH_LOCK_WANTED;		if ((error = tsleep(periph, priority, "caplck", 0)) != 0)			return error;	}	if (cam_periph_acquire(periph) != CAM_REQ_CMP)		return(ENXIO);	periph->flags |= CAM_PERIPH_LOCKED;	return 0;}/* * Unlock and wake up any waiters. */voidcam_periph_unlock(struct cam_periph *periph){	periph->flags &= ~CAM_PERIPH_LOCKED;	if ((periph->flags & CAM_PERIPH_LOCK_WANTED) != 0) {		periph->flags &= ~CAM_PERIPH_LOCK_WANTED;		wakeup(periph);	}	cam_periph_release(periph);}/* * Map user virtual pointers into kernel virtual address space, so we can * access the memory.  This won't work on physical pointers, for now it's * up to the caller to check for that.  (XXX KDM -- should we do that here * instead?)  This also only works for up to MAXPHYS memory.  Since we use * buffers to map stuff in and out, we're limited to the buffer size. */intcam_periph_mapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo){	int numbufs, i;	int flags[CAM_PERIPH_MAXMAPS];	u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS];	u_int32_t lengths[CAM_PERIPH_MAXMAPS];	u_int32_t dirs[CAM_PERIPH_MAXMAPS];	switch(ccb->ccb_h.func_code) {	case XPT_DEV_MATCH:		if (ccb->cdm.match_buf_len == 0) {			printf("cam_periph_mapmem: invalid match buffer "			       "length 0\n");			return(EINVAL);		}		if (ccb->cdm.pattern_buf_len > 0) {			data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns;			lengths[0] = ccb->cdm.pattern_buf_len;			dirs[0] = CAM_DIR_OUT;			data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches;			lengths[1] = ccb->cdm.match_buf_len;			dirs[1] = CAM_DIR_IN;			numbufs = 2;		} else {			data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches;			lengths[0] = ccb->cdm.match_buf_len;			dirs[0] = CAM_DIR_IN;			numbufs = 1;		}		break;	case XPT_SCSI_IO:		if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE)			return(0);		data_ptrs[0] = &ccb->csio.data_ptr;		lengths[0] = ccb->csio.dxfer_len;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?