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 + -
显示快捷键?