18b8a9b1dSJustin T. Gibbs /* 28b8a9b1dSJustin T. Gibbs * Common functions for CAM "type" (peripheral) drivers. 38b8a9b1dSJustin T. Gibbs * 48b8a9b1dSJustin T. Gibbs * Copyright (c) 1997, 1998 Justin T. Gibbs. 58b8a9b1dSJustin T. Gibbs * Copyright (c) 1997, 1998 Kenneth D. Merry. 68b8a9b1dSJustin T. Gibbs * All rights reserved. 78b8a9b1dSJustin T. Gibbs * 88b8a9b1dSJustin T. Gibbs * Redistribution and use in source and binary forms, with or without 98b8a9b1dSJustin T. Gibbs * modification, are permitted provided that the following conditions 108b8a9b1dSJustin T. Gibbs * are met: 118b8a9b1dSJustin T. Gibbs * 1. Redistributions of source code must retain the above copyright 128b8a9b1dSJustin T. Gibbs * notice, this list of conditions, and the following disclaimer, 138b8a9b1dSJustin T. Gibbs * without modification, immediately at the beginning of the file. 148b8a9b1dSJustin T. Gibbs * 2. The name of the author may not be used to endorse or promote products 158b8a9b1dSJustin T. Gibbs * derived from this software without specific prior written permission. 168b8a9b1dSJustin T. Gibbs * 178b8a9b1dSJustin T. Gibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 188b8a9b1dSJustin T. Gibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 198b8a9b1dSJustin T. Gibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 208b8a9b1dSJustin T. Gibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 218b8a9b1dSJustin T. Gibbs * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 228b8a9b1dSJustin T. Gibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 238b8a9b1dSJustin T. Gibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 248b8a9b1dSJustin T. Gibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 258b8a9b1dSJustin T. Gibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 268b8a9b1dSJustin T. Gibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 278b8a9b1dSJustin T. Gibbs * SUCH DAMAGE. 288b8a9b1dSJustin T. Gibbs * 29ee9c90c7SKenneth D. Merry * $Id: cam_periph.c,v 1.5 1998/10/15 17:46:18 ken Exp $ 308b8a9b1dSJustin T. Gibbs */ 318b8a9b1dSJustin T. Gibbs 328b8a9b1dSJustin T. Gibbs #include <sys/param.h> 338b8a9b1dSJustin T. Gibbs #include <sys/systm.h> 348b8a9b1dSJustin T. Gibbs #include <sys/types.h> 358b8a9b1dSJustin T. Gibbs #include <sys/malloc.h> 368b8a9b1dSJustin T. Gibbs #include <sys/kernel.h> 378b8a9b1dSJustin T. Gibbs #include <sys/buf.h> 388b8a9b1dSJustin T. Gibbs #include <sys/proc.h> 398b8a9b1dSJustin T. Gibbs #include <sys/devicestat.h> 408b8a9b1dSJustin T. Gibbs #include <vm/vm.h> 418b8a9b1dSJustin T. Gibbs #include <vm/vm_extern.h> 428b8a9b1dSJustin T. Gibbs 438b8a9b1dSJustin T. Gibbs #include <cam/cam.h> 448b8a9b1dSJustin T. Gibbs #include <cam/cam_conf.h> 458b8a9b1dSJustin T. Gibbs #include <cam/cam_ccb.h> 468b8a9b1dSJustin T. Gibbs #include <cam/cam_xpt_periph.h> 478b8a9b1dSJustin T. Gibbs #include <cam/cam_periph.h> 488b8a9b1dSJustin T. Gibbs #include <cam/cam_debug.h> 498b8a9b1dSJustin T. Gibbs 508b8a9b1dSJustin T. Gibbs #include <cam/scsi/scsi_all.h> 518b8a9b1dSJustin T. Gibbs #include <cam/scsi/scsi_message.h> 528b8a9b1dSJustin T. Gibbs #include <cam/scsi/scsi_da.h> 538b8a9b1dSJustin T. Gibbs #include <cam/scsi/scsi_pass.h> 548b8a9b1dSJustin T. Gibbs 558b8a9b1dSJustin T. Gibbs static u_int camperiphnextunit(struct periph_driver *p_drv, 568b8a9b1dSJustin T. Gibbs u_int newunit, int wired); 578b8a9b1dSJustin T. Gibbs static u_int camperiphunit(struct periph_driver *p_drv, 588b8a9b1dSJustin T. Gibbs path_id_t path_id_t, 598b8a9b1dSJustin T. Gibbs target_id_t target, lun_id_t lun); 608b8a9b1dSJustin T. Gibbs static void camperiphdone(struct cam_periph *periph, 618b8a9b1dSJustin T. Gibbs union ccb *done_ccb); 628b8a9b1dSJustin T. Gibbs static void camperiphfree(struct cam_periph *periph); 638b8a9b1dSJustin T. Gibbs 648b8a9b1dSJustin T. Gibbs cam_status 65ee9c90c7SKenneth D. Merry cam_periph_alloc(periph_ctor_t *periph_ctor, 66ee9c90c7SKenneth D. Merry periph_oninv_t *periph_oninvalidate, 67ee9c90c7SKenneth D. Merry periph_dtor_t *periph_dtor, periph_start_t *periph_start, 68ee9c90c7SKenneth D. Merry char *name, cam_periph_type type, struct cam_path *path, 69ee9c90c7SKenneth D. Merry ac_callback_t *ac_callback, ac_code code, void *arg) 708b8a9b1dSJustin T. Gibbs { 718b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 728b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 738b8a9b1dSJustin T. Gibbs struct cam_periph *cur_periph; 748b8a9b1dSJustin T. Gibbs path_id_t path_id; 758b8a9b1dSJustin T. Gibbs target_id_t target_id; 768b8a9b1dSJustin T. Gibbs lun_id_t lun_id; 778b8a9b1dSJustin T. Gibbs cam_status status; 788b8a9b1dSJustin T. Gibbs u_int init_level; 798b8a9b1dSJustin T. Gibbs int s; 808b8a9b1dSJustin T. Gibbs 818b8a9b1dSJustin T. Gibbs init_level = 0; 828b8a9b1dSJustin T. Gibbs /* 838b8a9b1dSJustin T. Gibbs * Handle Hot-Plug scenarios. If there is already a peripheral 848b8a9b1dSJustin T. Gibbs * of our type assigned to this path, we are likely waiting for 858b8a9b1dSJustin T. Gibbs * final close on an old, invalidated, peripheral. If this is 868b8a9b1dSJustin T. Gibbs * the case, queue up a deferred call to the peripheral's async 878b8a9b1dSJustin T. Gibbs * handler. If it looks like a mistaken re-alloation, complain. 888b8a9b1dSJustin T. Gibbs */ 898b8a9b1dSJustin T. Gibbs if ((periph = cam_periph_find(path, name)) != NULL) { 908b8a9b1dSJustin T. Gibbs 918b8a9b1dSJustin T. Gibbs if ((periph->flags & CAM_PERIPH_INVALID) != 0 928b8a9b1dSJustin T. Gibbs && (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) == 0) { 938b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_NEW_DEV_FOUND; 948b8a9b1dSJustin T. Gibbs periph->deferred_callback = ac_callback; 958b8a9b1dSJustin T. Gibbs periph->deferred_ac = code; 968b8a9b1dSJustin T. Gibbs return (CAM_REQ_INPROG); 978b8a9b1dSJustin T. Gibbs } else { 988b8a9b1dSJustin T. Gibbs printf("cam_periph_alloc: attempt to re-allocate " 998b8a9b1dSJustin T. Gibbs "valid device %s%d rejected\n", 1008b8a9b1dSJustin T. Gibbs periph->periph_name, periph->unit_number); 1018b8a9b1dSJustin T. Gibbs } 1028b8a9b1dSJustin T. Gibbs return (CAM_REQ_INVALID); 1038b8a9b1dSJustin T. Gibbs } 1048b8a9b1dSJustin T. Gibbs 1058b8a9b1dSJustin T. Gibbs periph = (struct cam_periph *)malloc(sizeof(*periph), M_DEVBUF, 1068b8a9b1dSJustin T. Gibbs M_NOWAIT); 1078b8a9b1dSJustin T. Gibbs 1088b8a9b1dSJustin T. Gibbs if (periph == NULL) 1098b8a9b1dSJustin T. Gibbs return (CAM_RESRC_UNAVAIL); 1108b8a9b1dSJustin T. Gibbs 1118b8a9b1dSJustin T. Gibbs init_level++; 1128b8a9b1dSJustin T. Gibbs 1138b8a9b1dSJustin T. Gibbs for (p_drv = (struct periph_driver **)periphdriver_set.ls_items; 1148b8a9b1dSJustin T. Gibbs *p_drv != NULL; p_drv++) { 1158b8a9b1dSJustin T. Gibbs if (strcmp((*p_drv)->driver_name, name) == 0) 1168b8a9b1dSJustin T. Gibbs break; 1178b8a9b1dSJustin T. Gibbs } 1188b8a9b1dSJustin T. Gibbs 1198b8a9b1dSJustin T. Gibbs path_id = xpt_path_path_id(path); 1208b8a9b1dSJustin T. Gibbs target_id = xpt_path_target_id(path); 1218b8a9b1dSJustin T. Gibbs lun_id = xpt_path_lun_id(path); 1228b8a9b1dSJustin T. Gibbs bzero(periph, sizeof(*periph)); 1238b8a9b1dSJustin T. Gibbs cam_init_pinfo(&periph->pinfo); 1248b8a9b1dSJustin T. Gibbs periph->periph_start = periph_start; 1258b8a9b1dSJustin T. Gibbs periph->periph_dtor = periph_dtor; 126ee9c90c7SKenneth D. Merry periph->periph_oninval = periph_oninvalidate; 1278b8a9b1dSJustin T. Gibbs periph->type = type; 1288b8a9b1dSJustin T. Gibbs periph->periph_name = name; 1298b8a9b1dSJustin T. Gibbs periph->unit_number = camperiphunit(*p_drv, path_id, target_id, lun_id); 1308b8a9b1dSJustin T. Gibbs periph->immediate_priority = CAM_PRIORITY_NONE; 1318b8a9b1dSJustin T. Gibbs periph->refcount = 0; 1328b8a9b1dSJustin T. Gibbs SLIST_INIT(&periph->ccb_list); 1338b8a9b1dSJustin T. Gibbs status = xpt_create_path(&path, periph, path_id, target_id, lun_id); 1348b8a9b1dSJustin T. Gibbs if (status != CAM_REQ_CMP) 1358b8a9b1dSJustin T. Gibbs goto failure; 1368b8a9b1dSJustin T. Gibbs 1378b8a9b1dSJustin T. Gibbs periph->path = path; 1388b8a9b1dSJustin T. Gibbs init_level++; 1398b8a9b1dSJustin T. Gibbs 1408b8a9b1dSJustin T. Gibbs status = xpt_add_periph(periph); 1418b8a9b1dSJustin T. Gibbs 1428b8a9b1dSJustin T. Gibbs if (status != CAM_REQ_CMP) 1438b8a9b1dSJustin T. Gibbs goto failure; 1448b8a9b1dSJustin T. Gibbs 1458b8a9b1dSJustin T. Gibbs s = splsoftcam(); 1468b8a9b1dSJustin T. Gibbs cur_periph = TAILQ_FIRST(&(*p_drv)->units); 1478b8a9b1dSJustin T. Gibbs while (cur_periph != NULL 1488b8a9b1dSJustin T. Gibbs && cur_periph->unit_number < periph->unit_number) 1498b8a9b1dSJustin T. Gibbs cur_periph = TAILQ_NEXT(cur_periph, unit_links); 1508b8a9b1dSJustin T. Gibbs 1518b8a9b1dSJustin T. Gibbs if (cur_periph != NULL) 1528b8a9b1dSJustin T. Gibbs TAILQ_INSERT_BEFORE(cur_periph, periph, unit_links); 1538b8a9b1dSJustin T. Gibbs else { 1548b8a9b1dSJustin T. Gibbs TAILQ_INSERT_TAIL(&(*p_drv)->units, periph, unit_links); 1558b8a9b1dSJustin T. Gibbs (*p_drv)->generation++; 1568b8a9b1dSJustin T. Gibbs } 1578b8a9b1dSJustin T. Gibbs 1588b8a9b1dSJustin T. Gibbs splx(s); 1598b8a9b1dSJustin T. Gibbs 1608b8a9b1dSJustin T. Gibbs init_level++; 1618b8a9b1dSJustin T. Gibbs 1628b8a9b1dSJustin T. Gibbs status = periph_ctor(periph, arg); 1638b8a9b1dSJustin T. Gibbs 1648b8a9b1dSJustin T. Gibbs if (status == CAM_REQ_CMP) 1658b8a9b1dSJustin T. Gibbs init_level++; 1668b8a9b1dSJustin T. Gibbs 1678b8a9b1dSJustin T. Gibbs failure: 1688b8a9b1dSJustin T. Gibbs switch (init_level) { 1698b8a9b1dSJustin T. Gibbs case 4: 1708b8a9b1dSJustin T. Gibbs /* Initialized successfully */ 1718b8a9b1dSJustin T. Gibbs break; 1728b8a9b1dSJustin T. Gibbs case 3: 1738b8a9b1dSJustin T. Gibbs s = splsoftcam(); 1748b8a9b1dSJustin T. Gibbs TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links); 1758b8a9b1dSJustin T. Gibbs splx(s); 1768b8a9b1dSJustin T. Gibbs xpt_remove_periph(periph); 1778b8a9b1dSJustin T. Gibbs case 2: 1788b8a9b1dSJustin T. Gibbs xpt_free_path(periph->path); 1798b8a9b1dSJustin T. Gibbs case 1: 1808b8a9b1dSJustin T. Gibbs free(periph, M_DEVBUF); 1818b8a9b1dSJustin T. Gibbs case 0: 1828b8a9b1dSJustin T. Gibbs /* No cleanup to perform. */ 1838b8a9b1dSJustin T. Gibbs break; 1848b8a9b1dSJustin T. Gibbs default: 1858b8a9b1dSJustin T. Gibbs panic("cam_periph_alloc: Unkown init level"); 1868b8a9b1dSJustin T. Gibbs } 1878b8a9b1dSJustin T. Gibbs return(status); 1888b8a9b1dSJustin T. Gibbs } 1898b8a9b1dSJustin T. Gibbs 1908b8a9b1dSJustin T. Gibbs /* 1918b8a9b1dSJustin T. Gibbs * Find a peripheral structure with the specified path, target, lun, 1928b8a9b1dSJustin T. Gibbs * and (optionally) type. If the name is NULL, this function will return 1938b8a9b1dSJustin T. Gibbs * the first peripheral driver that matches the specified path. 1948b8a9b1dSJustin T. Gibbs */ 1958b8a9b1dSJustin T. Gibbs struct cam_periph * 1968b8a9b1dSJustin T. Gibbs cam_periph_find(struct cam_path *path, char *name) 1978b8a9b1dSJustin T. Gibbs { 1988b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 1998b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 2008b8a9b1dSJustin T. Gibbs int s; 2018b8a9b1dSJustin T. Gibbs 2028b8a9b1dSJustin T. Gibbs for (p_drv = (struct periph_driver **)periphdriver_set.ls_items; 2038b8a9b1dSJustin T. Gibbs *p_drv != NULL; p_drv++) { 2048b8a9b1dSJustin T. Gibbs 2058b8a9b1dSJustin T. Gibbs if (name != NULL && (strcmp((*p_drv)->driver_name, name) != 0)) 2068b8a9b1dSJustin T. Gibbs continue; 2078b8a9b1dSJustin T. Gibbs 2088b8a9b1dSJustin T. Gibbs s = splsoftcam(); 2098b8a9b1dSJustin T. Gibbs for (periph = TAILQ_FIRST(&(*p_drv)->units); periph != NULL; 2108b8a9b1dSJustin T. Gibbs periph = TAILQ_NEXT(periph, unit_links)) { 2118b8a9b1dSJustin T. Gibbs if (xpt_path_comp(periph->path, path) == 0) { 2128b8a9b1dSJustin T. Gibbs splx(s); 2138b8a9b1dSJustin T. Gibbs return(periph); 2148b8a9b1dSJustin T. Gibbs } 2158b8a9b1dSJustin T. Gibbs } 2168b8a9b1dSJustin T. Gibbs splx(s); 2178b8a9b1dSJustin T. Gibbs if (name != NULL) 2188b8a9b1dSJustin T. Gibbs return(NULL); 2198b8a9b1dSJustin T. Gibbs } 2208b8a9b1dSJustin T. Gibbs return(NULL); 2218b8a9b1dSJustin T. Gibbs } 2228b8a9b1dSJustin T. Gibbs 2238b8a9b1dSJustin T. Gibbs cam_status 2248b8a9b1dSJustin T. Gibbs cam_periph_acquire(struct cam_periph *periph) 2258b8a9b1dSJustin T. Gibbs { 2268b8a9b1dSJustin T. Gibbs int s; 2278b8a9b1dSJustin T. Gibbs 2288b8a9b1dSJustin T. Gibbs if (periph == NULL) 2298b8a9b1dSJustin T. Gibbs return(CAM_REQ_CMP_ERR); 2308b8a9b1dSJustin T. Gibbs 2318b8a9b1dSJustin T. Gibbs s = splsoftcam(); 2328b8a9b1dSJustin T. Gibbs periph->refcount++; 2338b8a9b1dSJustin T. Gibbs splx(s); 2348b8a9b1dSJustin T. Gibbs 2358b8a9b1dSJustin T. Gibbs return(CAM_REQ_CMP); 2368b8a9b1dSJustin T. Gibbs } 2378b8a9b1dSJustin T. Gibbs 2388b8a9b1dSJustin T. Gibbs void 2398b8a9b1dSJustin T. Gibbs cam_periph_release(struct cam_periph *periph) 2408b8a9b1dSJustin T. Gibbs { 2418b8a9b1dSJustin T. Gibbs int s; 2428b8a9b1dSJustin T. Gibbs 2438b8a9b1dSJustin T. Gibbs if (periph == NULL) 2448b8a9b1dSJustin T. Gibbs return; 2458b8a9b1dSJustin T. Gibbs 2468b8a9b1dSJustin T. Gibbs s = splsoftcam(); 2478b8a9b1dSJustin T. Gibbs if ((--periph->refcount == 0) 2488b8a9b1dSJustin T. Gibbs && (periph->flags & CAM_PERIPH_INVALID)) { 2498b8a9b1dSJustin T. Gibbs camperiphfree(periph); 2508b8a9b1dSJustin T. Gibbs } 2518b8a9b1dSJustin T. Gibbs splx(s); 2528b8a9b1dSJustin T. Gibbs 2538b8a9b1dSJustin T. Gibbs } 2548b8a9b1dSJustin T. Gibbs 2558b8a9b1dSJustin T. Gibbs /* 2568b8a9b1dSJustin T. Gibbs * Look for the next unit number that is not currently in use for this 2578b8a9b1dSJustin T. Gibbs * peripheral type starting at "newunit". Also exclude unit numbers that 2588b8a9b1dSJustin T. Gibbs * are reserved by for future "hardwiring" unless we already know that this 2598b8a9b1dSJustin T. Gibbs * is a potential wired device. Only assume that the device is "wired" the 2608b8a9b1dSJustin T. Gibbs * first time through the loop since after that we'll be looking at unit 2618b8a9b1dSJustin T. Gibbs * numbers that did not match a wiring entry. 2628b8a9b1dSJustin T. Gibbs */ 2638b8a9b1dSJustin T. Gibbs static u_int 2648b8a9b1dSJustin T. Gibbs camperiphnextunit(struct periph_driver *p_drv, u_int newunit, int wired) 2658b8a9b1dSJustin T. Gibbs { 2668b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 2678b8a9b1dSJustin T. Gibbs struct cam_periph_config *periph_conf; 2688b8a9b1dSJustin T. Gibbs char *periph_name; 2698b8a9b1dSJustin T. Gibbs int s; 2708b8a9b1dSJustin T. Gibbs 2718b8a9b1dSJustin T. Gibbs s = splsoftcam(); 2728b8a9b1dSJustin T. Gibbs periph_name = p_drv->driver_name; 2738b8a9b1dSJustin T. Gibbs for (;;newunit++) { 2748b8a9b1dSJustin T. Gibbs 2758b8a9b1dSJustin T. Gibbs for (periph = TAILQ_FIRST(&p_drv->units); 2768b8a9b1dSJustin T. Gibbs periph != NULL && periph->unit_number != newunit; 2778b8a9b1dSJustin T. Gibbs periph = TAILQ_NEXT(periph, unit_links)) 2788b8a9b1dSJustin T. Gibbs ; 2798b8a9b1dSJustin T. Gibbs 2808b8a9b1dSJustin T. Gibbs if (periph != NULL && periph->unit_number == newunit) { 2818b8a9b1dSJustin T. Gibbs if (wired != 0) { 2828b8a9b1dSJustin T. Gibbs xpt_print_path(periph->path); 2838b8a9b1dSJustin T. Gibbs printf("Duplicate Wired Device entry!\n"); 2848b8a9b1dSJustin T. Gibbs xpt_print_path(periph->path); 2858b8a9b1dSJustin T. Gibbs printf("Second device will not be wired\n"); 2868b8a9b1dSJustin T. Gibbs wired = 0; 2878b8a9b1dSJustin T. Gibbs } 2888b8a9b1dSJustin T. Gibbs continue; 2898b8a9b1dSJustin T. Gibbs } 2908b8a9b1dSJustin T. Gibbs 2918b8a9b1dSJustin T. Gibbs for (periph_conf = cam_pinit; 2928b8a9b1dSJustin T. Gibbs wired == 0 && periph_conf->periph_name != NULL; 2938b8a9b1dSJustin T. Gibbs periph_conf++) { 2948b8a9b1dSJustin T. Gibbs 2958b8a9b1dSJustin T. Gibbs /* 2968b8a9b1dSJustin T. Gibbs * Don't match entries like "da 4" as a wired down 2978b8a9b1dSJustin T. Gibbs * device, but do match entries like "da 4 target 5" 2988b8a9b1dSJustin T. Gibbs * or even "da 4 scbus 1". 2998b8a9b1dSJustin T. Gibbs */ 3008b8a9b1dSJustin T. Gibbs if (IS_SPECIFIED(periph_conf->periph_unit) 3018b8a9b1dSJustin T. Gibbs && (!strcmp(periph_name, periph_conf->periph_name)) 3028b8a9b1dSJustin T. Gibbs && (IS_SPECIFIED(periph_conf->target) 3038b8a9b1dSJustin T. Gibbs || IS_SPECIFIED(periph_conf->pathid)) 3048b8a9b1dSJustin T. Gibbs && (newunit == periph_conf->periph_unit)) 3058b8a9b1dSJustin T. Gibbs break; 3068b8a9b1dSJustin T. Gibbs } 3078b8a9b1dSJustin T. Gibbs 3088b8a9b1dSJustin T. Gibbs if (wired != 0 || periph_conf->periph_name == NULL) 3098b8a9b1dSJustin T. Gibbs break; 3108b8a9b1dSJustin T. Gibbs } 3118b8a9b1dSJustin T. Gibbs splx(s); 3128b8a9b1dSJustin T. Gibbs return (newunit); 3138b8a9b1dSJustin T. Gibbs } 3148b8a9b1dSJustin T. Gibbs 3158b8a9b1dSJustin T. Gibbs static u_int 3168b8a9b1dSJustin T. Gibbs camperiphunit(struct periph_driver *p_drv, path_id_t pathid, 3178b8a9b1dSJustin T. Gibbs target_id_t target, lun_id_t lun) 3188b8a9b1dSJustin T. Gibbs { 3198b8a9b1dSJustin T. Gibbs struct cam_periph_config *periph_conf; 3208b8a9b1dSJustin T. Gibbs u_int unit; 3218b8a9b1dSJustin T. Gibbs int hit; 3228b8a9b1dSJustin T. Gibbs 3238b8a9b1dSJustin T. Gibbs unit = 0; 3248b8a9b1dSJustin T. Gibbs hit = 0; 3258b8a9b1dSJustin T. Gibbs 3268b8a9b1dSJustin T. Gibbs for (periph_conf = cam_pinit; 3278b8a9b1dSJustin T. Gibbs periph_conf->periph_name != NULL; 3288b8a9b1dSJustin T. Gibbs periph_conf++, hit = 0) { 3298b8a9b1dSJustin T. Gibbs 3308b8a9b1dSJustin T. Gibbs if (!strcmp(p_drv->driver_name, periph_conf->periph_name) 3318b8a9b1dSJustin T. Gibbs && IS_SPECIFIED(periph_conf->periph_unit)) { 3328b8a9b1dSJustin T. Gibbs 3338b8a9b1dSJustin T. Gibbs if (IS_SPECIFIED(periph_conf->pathid)) { 3348b8a9b1dSJustin T. Gibbs 3358b8a9b1dSJustin T. Gibbs if (pathid != periph_conf->pathid) 3368b8a9b1dSJustin T. Gibbs continue; 3378b8a9b1dSJustin T. Gibbs hit++; 3388b8a9b1dSJustin T. Gibbs } 3398b8a9b1dSJustin T. Gibbs 3408b8a9b1dSJustin T. Gibbs if (IS_SPECIFIED(periph_conf->target)) { 3418b8a9b1dSJustin T. Gibbs 3428b8a9b1dSJustin T. Gibbs if (target != periph_conf->target) 3438b8a9b1dSJustin T. Gibbs continue; 3448b8a9b1dSJustin T. Gibbs hit++; 3458b8a9b1dSJustin T. Gibbs } 3468b8a9b1dSJustin T. Gibbs 3478b8a9b1dSJustin T. Gibbs if (IS_SPECIFIED(periph_conf->lun)) { 3488b8a9b1dSJustin T. Gibbs 3498b8a9b1dSJustin T. Gibbs if (lun != periph_conf->lun) 3508b8a9b1dSJustin T. Gibbs continue; 3518b8a9b1dSJustin T. Gibbs hit++; 3528b8a9b1dSJustin T. Gibbs } 3538b8a9b1dSJustin T. Gibbs 3548b8a9b1dSJustin T. Gibbs if (hit != 0) { 3558b8a9b1dSJustin T. Gibbs unit = periph_conf->periph_unit; 3568b8a9b1dSJustin T. Gibbs break; 3578b8a9b1dSJustin T. Gibbs } 3588b8a9b1dSJustin T. Gibbs } 3598b8a9b1dSJustin T. Gibbs } 3608b8a9b1dSJustin T. Gibbs 3618b8a9b1dSJustin T. Gibbs /* 3628b8a9b1dSJustin T. Gibbs * Either start from 0 looking for the next unit or from 3638b8a9b1dSJustin T. Gibbs * the unit number given in the periph_conf. This way, 3648b8a9b1dSJustin T. Gibbs * if we have wildcard matches, we don't return the same 3658b8a9b1dSJustin T. Gibbs * unit number twice. 3668b8a9b1dSJustin T. Gibbs */ 3678b8a9b1dSJustin T. Gibbs unit = camperiphnextunit(p_drv, unit, /*wired*/hit); 3688b8a9b1dSJustin T. Gibbs 3698b8a9b1dSJustin T. Gibbs return (unit); 3708b8a9b1dSJustin T. Gibbs } 3718b8a9b1dSJustin T. Gibbs 3728b8a9b1dSJustin T. Gibbs void 3738b8a9b1dSJustin T. Gibbs cam_periph_invalidate(struct cam_periph *periph) 3748b8a9b1dSJustin T. Gibbs { 3758b8a9b1dSJustin T. Gibbs int s; 3768b8a9b1dSJustin T. Gibbs 377ee9c90c7SKenneth D. Merry s = splsoftcam(); 378ee9c90c7SKenneth D. Merry /* 379ee9c90c7SKenneth D. Merry * We only call this routine the first time a peripheral is 380ee9c90c7SKenneth D. Merry * invalidated. The oninvalidate() routine is always called at 381ee9c90c7SKenneth D. Merry * splsoftcam(). 382ee9c90c7SKenneth D. Merry */ 383ee9c90c7SKenneth D. Merry if (((periph->flags & CAM_PERIPH_INVALID) == 0) 384ee9c90c7SKenneth D. Merry && (periph->periph_oninval != NULL)) 385ee9c90c7SKenneth D. Merry periph->periph_oninval(periph); 386ee9c90c7SKenneth D. Merry 3878b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_INVALID; 3888b8a9b1dSJustin T. Gibbs periph->flags &= ~CAM_PERIPH_NEW_DEV_FOUND; 3898b8a9b1dSJustin T. Gibbs 3908b8a9b1dSJustin T. Gibbs if (periph->refcount == 0) 3918b8a9b1dSJustin T. Gibbs camperiphfree(periph); 3928b8a9b1dSJustin T. Gibbs else if (periph->refcount < 0) 3938b8a9b1dSJustin T. Gibbs printf("cam_invalidate_periph: refcount < 0!!\n"); 3948b8a9b1dSJustin T. Gibbs splx(s); 3958b8a9b1dSJustin T. Gibbs } 3968b8a9b1dSJustin T. Gibbs 3978b8a9b1dSJustin T. Gibbs static void 3988b8a9b1dSJustin T. Gibbs camperiphfree(struct cam_periph *periph) 3998b8a9b1dSJustin T. Gibbs { 4008b8a9b1dSJustin T. Gibbs int s; 4018b8a9b1dSJustin T. Gibbs struct periph_driver **p_drv; 4028b8a9b1dSJustin T. Gibbs 4038b8a9b1dSJustin T. Gibbs for (p_drv = (struct periph_driver **)periphdriver_set.ls_items; 4048b8a9b1dSJustin T. Gibbs *p_drv != NULL; p_drv++) { 4058b8a9b1dSJustin T. Gibbs if (strcmp((*p_drv)->driver_name, periph->periph_name) == 0) 4068b8a9b1dSJustin T. Gibbs break; 4078b8a9b1dSJustin T. Gibbs } 4088b8a9b1dSJustin T. Gibbs 4098b8a9b1dSJustin T. Gibbs if (periph->periph_dtor != NULL) 4108b8a9b1dSJustin T. Gibbs periph->periph_dtor(periph); 4118b8a9b1dSJustin T. Gibbs 4128b8a9b1dSJustin T. Gibbs s = splsoftcam(); 4138b8a9b1dSJustin T. Gibbs TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links); 4148b8a9b1dSJustin T. Gibbs (*p_drv)->generation++; 4158b8a9b1dSJustin T. Gibbs splx(s); 4168b8a9b1dSJustin T. Gibbs 4178b8a9b1dSJustin T. Gibbs xpt_remove_periph(periph); 4188b8a9b1dSJustin T. Gibbs 4198b8a9b1dSJustin T. Gibbs if (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) { 4208b8a9b1dSJustin T. Gibbs union ccb ccb; 4218b8a9b1dSJustin T. Gibbs void *arg; 4228b8a9b1dSJustin T. Gibbs 4238b8a9b1dSJustin T. Gibbs switch (periph->deferred_ac) { 4248b8a9b1dSJustin T. Gibbs case AC_FOUND_DEVICE: 4258b8a9b1dSJustin T. Gibbs ccb.ccb_h.func_code = XPT_GDEV_TYPE; 4268b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&ccb.ccb_h, periph->path, /*priority*/ 1); 4278b8a9b1dSJustin T. Gibbs xpt_action(&ccb); 4288b8a9b1dSJustin T. Gibbs arg = &ccb; 4298b8a9b1dSJustin T. Gibbs break; 4308b8a9b1dSJustin T. Gibbs case AC_PATH_REGISTERED: 4318b8a9b1dSJustin T. Gibbs ccb.ccb_h.func_code = XPT_PATH_INQ; 4328b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&ccb.ccb_h, periph->path, /*priority*/ 1); 4338b8a9b1dSJustin T. Gibbs xpt_action(&ccb); 4348b8a9b1dSJustin T. Gibbs arg = &ccb; 4358b8a9b1dSJustin T. Gibbs break; 4368b8a9b1dSJustin T. Gibbs default: 4378b8a9b1dSJustin T. Gibbs arg = NULL; 4388b8a9b1dSJustin T. Gibbs break; 4398b8a9b1dSJustin T. Gibbs } 4408b8a9b1dSJustin T. Gibbs periph->deferred_callback(NULL, periph->deferred_ac, 4418b8a9b1dSJustin T. Gibbs periph->path, arg); 4428b8a9b1dSJustin T. Gibbs } 4438b8a9b1dSJustin T. Gibbs xpt_free_path(periph->path); 4448b8a9b1dSJustin T. Gibbs free(periph, M_DEVBUF); 4458b8a9b1dSJustin T. Gibbs } 4468b8a9b1dSJustin T. Gibbs 4478b8a9b1dSJustin T. Gibbs /* 4488b8a9b1dSJustin T. Gibbs * Wait interruptibly for an exclusive lock. 4498b8a9b1dSJustin T. Gibbs */ 4508b8a9b1dSJustin T. Gibbs int 4518b8a9b1dSJustin T. Gibbs cam_periph_lock(struct cam_periph *periph, int priority) 4528b8a9b1dSJustin T. Gibbs { 4538b8a9b1dSJustin T. Gibbs int error; 4548b8a9b1dSJustin T. Gibbs 4558b8a9b1dSJustin T. Gibbs while ((periph->flags & CAM_PERIPH_LOCKED) != 0) { 4568b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_LOCK_WANTED; 4578b8a9b1dSJustin T. Gibbs if ((error = tsleep(periph, priority, "caplck", 0)) != 0) 4588b8a9b1dSJustin T. Gibbs return error; 4598b8a9b1dSJustin T. Gibbs } 4608b8a9b1dSJustin T. Gibbs 4618b8a9b1dSJustin T. Gibbs if (cam_periph_acquire(periph) != CAM_REQ_CMP) 4628b8a9b1dSJustin T. Gibbs return(ENXIO); 4638b8a9b1dSJustin T. Gibbs 4648b8a9b1dSJustin T. Gibbs periph->flags |= CAM_PERIPH_LOCKED; 4658b8a9b1dSJustin T. Gibbs return 0; 4668b8a9b1dSJustin T. Gibbs } 4678b8a9b1dSJustin T. Gibbs 4688b8a9b1dSJustin T. Gibbs /* 4698b8a9b1dSJustin T. Gibbs * Unlock and wake up any waiters. 4708b8a9b1dSJustin T. Gibbs */ 4718b8a9b1dSJustin T. Gibbs void 4728b8a9b1dSJustin T. Gibbs cam_periph_unlock(struct cam_periph *periph) 4738b8a9b1dSJustin T. Gibbs { 4748b8a9b1dSJustin T. Gibbs periph->flags &= ~CAM_PERIPH_LOCKED; 4758b8a9b1dSJustin T. Gibbs if ((periph->flags & CAM_PERIPH_LOCK_WANTED) != 0) { 4768b8a9b1dSJustin T. Gibbs periph->flags &= ~CAM_PERIPH_LOCK_WANTED; 4778b8a9b1dSJustin T. Gibbs wakeup(periph); 4788b8a9b1dSJustin T. Gibbs } 4798b8a9b1dSJustin T. Gibbs 4808b8a9b1dSJustin T. Gibbs cam_periph_release(periph); 4818b8a9b1dSJustin T. Gibbs } 4828b8a9b1dSJustin T. Gibbs 4838b8a9b1dSJustin T. Gibbs /* 4848b8a9b1dSJustin T. Gibbs * Map user virtual pointers into kernel virtual address space, so we can 4858b8a9b1dSJustin T. Gibbs * access the memory. This won't work on physical pointers, for now it's 4868b8a9b1dSJustin T. Gibbs * up to the caller to check for that. (XXX KDM -- should we do that here 4878b8a9b1dSJustin T. Gibbs * instead?) This also only works for up to MAXPHYS memory. Since we use 4888b8a9b1dSJustin T. Gibbs * buffers to map stuff in and out, we're limited to the buffer size. 4898b8a9b1dSJustin T. Gibbs */ 4908b8a9b1dSJustin T. Gibbs int 4918b8a9b1dSJustin T. Gibbs cam_periph_mapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) 4928b8a9b1dSJustin T. Gibbs { 4938b8a9b1dSJustin T. Gibbs int flags, numbufs, i; 4948b8a9b1dSJustin T. Gibbs u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; 4958b8a9b1dSJustin T. Gibbs u_int32_t lengths[CAM_PERIPH_MAXMAPS]; 4968b8a9b1dSJustin T. Gibbs u_int32_t dirs[CAM_PERIPH_MAXMAPS]; 4978b8a9b1dSJustin T. Gibbs 4988b8a9b1dSJustin T. Gibbs switch(ccb->ccb_h.func_code) { 4998b8a9b1dSJustin T. Gibbs case XPT_DEV_MATCH: 5008b8a9b1dSJustin T. Gibbs if (ccb->cdm.pattern_buf_len > MAXPHYS) { 5018b8a9b1dSJustin T. Gibbs printf("cam_periph_mapmem: attempt to map %u bytes, " 5028b8a9b1dSJustin T. Gibbs "which is greater than MAXPHYS(%d)\n", 5038b8a9b1dSJustin T. Gibbs ccb->cdm.pattern_buf_len, MAXPHYS); 5048b8a9b1dSJustin T. Gibbs return(E2BIG); 5058b8a9b1dSJustin T. Gibbs } else if (ccb->cdm.match_buf_len > MAXPHYS) { 5068b8a9b1dSJustin T. Gibbs printf("cam_periph_mapmem: attempt to map %u bytes, " 5078b8a9b1dSJustin T. Gibbs "which is greater than MAXPHYS(%d)\n", 5088b8a9b1dSJustin T. Gibbs ccb->cdm.match_buf_len, MAXPHYS); 5098b8a9b1dSJustin T. Gibbs return(E2BIG); 5108b8a9b1dSJustin T. Gibbs } 5118b8a9b1dSJustin T. Gibbs if (ccb->cdm.match_buf_len == 0) { 5128b8a9b1dSJustin T. Gibbs printf("cam_periph_mapmem: invalid match buffer " 5138b8a9b1dSJustin T. Gibbs "length 0\n"); 5148b8a9b1dSJustin T. Gibbs return(EINVAL); 5158b8a9b1dSJustin T. Gibbs } 5168b8a9b1dSJustin T. Gibbs if (ccb->cdm.pattern_buf_len > 0) { 5178b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; 5188b8a9b1dSJustin T. Gibbs lengths[0] = ccb->cdm.pattern_buf_len; 5198b8a9b1dSJustin T. Gibbs dirs[0] = CAM_DIR_OUT; 5208b8a9b1dSJustin T. Gibbs data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; 5218b8a9b1dSJustin T. Gibbs lengths[1] = ccb->cdm.match_buf_len; 5228b8a9b1dSJustin T. Gibbs dirs[1] = CAM_DIR_IN; 5238b8a9b1dSJustin T. Gibbs numbufs = 2; 5248b8a9b1dSJustin T. Gibbs } else { 5258b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; 5268b8a9b1dSJustin T. Gibbs lengths[0] = ccb->cdm.match_buf_len; 5278b8a9b1dSJustin T. Gibbs dirs[0] = CAM_DIR_IN; 5288b8a9b1dSJustin T. Gibbs numbufs = 1; 5298b8a9b1dSJustin T. Gibbs } 5308b8a9b1dSJustin T. Gibbs break; 5318b8a9b1dSJustin T. Gibbs case XPT_SCSI_IO: 5328b8a9b1dSJustin T. Gibbs if (ccb->csio.dxfer_len > MAXPHYS) { 5338b8a9b1dSJustin T. Gibbs printf("cam_periph_mapmem: attempt to map %u bytes, " 5348b8a9b1dSJustin T. Gibbs "which is greater than MAXPHYS(%d)\n", 5358b8a9b1dSJustin T. Gibbs ccb->csio.dxfer_len, MAXPHYS); 5368b8a9b1dSJustin T. Gibbs return(E2BIG); 5378b8a9b1dSJustin T. Gibbs } 5388b8a9b1dSJustin T. Gibbs 5398b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) 5408b8a9b1dSJustin T. Gibbs return(0); 5418b8a9b1dSJustin T. Gibbs 5428b8a9b1dSJustin T. Gibbs data_ptrs[0] = &ccb->csio.data_ptr; 5438b8a9b1dSJustin T. Gibbs lengths[0] = ccb->csio.dxfer_len;; 5448b8a9b1dSJustin T. Gibbs dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK; 5458b8a9b1dSJustin T. Gibbs numbufs = 1; 5468b8a9b1dSJustin T. Gibbs break; 5478b8a9b1dSJustin T. Gibbs default: 5488b8a9b1dSJustin T. Gibbs return(EINVAL); 5498b8a9b1dSJustin T. Gibbs break; /* NOTREACHED */ 5508b8a9b1dSJustin T. Gibbs } 5518b8a9b1dSJustin T. Gibbs 5528b8a9b1dSJustin T. Gibbs /* this keeps the current process from getting swapped */ 5538b8a9b1dSJustin T. Gibbs /* 5548b8a9b1dSJustin T. Gibbs * XXX KDM should I use P_NOSWAP instead? 5558b8a9b1dSJustin T. Gibbs */ 5568b8a9b1dSJustin T. Gibbs curproc->p_flag |= P_PHYSIO; 5578b8a9b1dSJustin T. Gibbs 5588b8a9b1dSJustin T. Gibbs for (i = 0; i < numbufs; i++) { 5598b8a9b1dSJustin T. Gibbs flags = 0; 5608b8a9b1dSJustin T. Gibbs 5618b8a9b1dSJustin T. Gibbs if (dirs[i] & CAM_DIR_IN) { 5628b8a9b1dSJustin T. Gibbs flags = B_READ; 5638b8a9b1dSJustin T. Gibbs if (useracc(*data_ptrs[i], lengths[i], B_READ) == 0){ 5648b8a9b1dSJustin T. Gibbs printf("cam_periph_mapmem: error, " 5652e8bf209SBruce Evans "address %p, length %lu isn't " 5668b8a9b1dSJustin T. Gibbs "user accessible for READ\n", 5672e8bf209SBruce Evans (void *)*data_ptrs[i], 5682e8bf209SBruce Evans (u_long)lengths[i]); 5698b8a9b1dSJustin T. Gibbs /* 5708b8a9b1dSJustin T. Gibbs * If we've already mapped one or more 5718b8a9b1dSJustin T. Gibbs * buffers for this CCB, unmap it (them). 5728b8a9b1dSJustin T. Gibbs */ 5738b8a9b1dSJustin T. Gibbs if (i > 0) 5748b8a9b1dSJustin T. Gibbs cam_periph_unmapmem(ccb, mapinfo); 5758b8a9b1dSJustin T. Gibbs else 5768b8a9b1dSJustin T. Gibbs curproc->p_flag &= ~P_PHYSIO; 5778b8a9b1dSJustin T. Gibbs 5788b8a9b1dSJustin T. Gibbs return(EACCES); 5798b8a9b1dSJustin T. Gibbs } 5808b8a9b1dSJustin T. Gibbs } 5818b8a9b1dSJustin T. Gibbs 5828b8a9b1dSJustin T. Gibbs /* 5838b8a9b1dSJustin T. Gibbs * XXX this check is really bogus, since B_WRITE currently 5848b8a9b1dSJustin T. Gibbs * is all 0's, and so it is "set" all the time. 5858b8a9b1dSJustin T. Gibbs */ 5868b8a9b1dSJustin T. Gibbs if (dirs[i] & CAM_DIR_OUT) { 5878b8a9b1dSJustin T. Gibbs flags |= B_WRITE; 5888b8a9b1dSJustin T. Gibbs if (useracc(*data_ptrs[i], lengths[i], B_WRITE) == 0){ 5898b8a9b1dSJustin T. Gibbs printf("cam_periph_mapmem: error, " 5902e8bf209SBruce Evans "address %p, length %lu isn't " 5918b8a9b1dSJustin T. Gibbs "user accessible for WRITE\n", 5922e8bf209SBruce Evans (void *)*data_ptrs[i], 5932e8bf209SBruce Evans (u_long)lengths[i]); 5948b8a9b1dSJustin T. Gibbs /* 5958b8a9b1dSJustin T. Gibbs * If we've already mapped one or more 5968b8a9b1dSJustin T. Gibbs * buffers for this CCB, unmap it (them). 5978b8a9b1dSJustin T. Gibbs */ 5988b8a9b1dSJustin T. Gibbs if (i > 0) 5998b8a9b1dSJustin T. Gibbs cam_periph_unmapmem(ccb, mapinfo); 6008b8a9b1dSJustin T. Gibbs else 6018b8a9b1dSJustin T. Gibbs curproc->p_flag &= ~P_PHYSIO; 6028b8a9b1dSJustin T. Gibbs 6038b8a9b1dSJustin T. Gibbs return(EACCES); 6048b8a9b1dSJustin T. Gibbs } 6058b8a9b1dSJustin T. Gibbs } 6068b8a9b1dSJustin T. Gibbs 6078b8a9b1dSJustin T. Gibbs /* 6088b8a9b1dSJustin T. Gibbs * Get the buffer. 6098b8a9b1dSJustin T. Gibbs */ 6108b8a9b1dSJustin T. Gibbs mapinfo->bp[i] = getpbuf(); 6118b8a9b1dSJustin T. Gibbs 6128b8a9b1dSJustin T. Gibbs /* save the buffer's data address */ 6138b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_saveaddr = mapinfo->bp[i]->b_data; 6148b8a9b1dSJustin T. Gibbs 6158b8a9b1dSJustin T. Gibbs /* put our pointer in the data slot */ 6168b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_data = *data_ptrs[i]; 6178b8a9b1dSJustin T. Gibbs 6188b8a9b1dSJustin T. Gibbs /* set the transfer length, we know it's < 64K */ 6198b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_bufsize = lengths[i]; 6208b8a9b1dSJustin T. Gibbs 6218b8a9b1dSJustin T. Gibbs /* set the flags */ 6228b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_flags = flags | B_PHYS | B_BUSY; 6238b8a9b1dSJustin T. Gibbs 6248b8a9b1dSJustin T. Gibbs /* map the buffer into kernel memory */ 6258b8a9b1dSJustin T. Gibbs vmapbuf(mapinfo->bp[i]); 6268b8a9b1dSJustin T. Gibbs 6278b8a9b1dSJustin T. Gibbs /* set our pointer to the new mapped area */ 6288b8a9b1dSJustin T. Gibbs *data_ptrs[i] = mapinfo->bp[i]->b_data; 6298b8a9b1dSJustin T. Gibbs 6308b8a9b1dSJustin T. Gibbs mapinfo->num_bufs_used++; 6318b8a9b1dSJustin T. Gibbs } 6328b8a9b1dSJustin T. Gibbs 6338b8a9b1dSJustin T. Gibbs return(0); 6348b8a9b1dSJustin T. Gibbs } 6358b8a9b1dSJustin T. Gibbs 6368b8a9b1dSJustin T. Gibbs /* 6378b8a9b1dSJustin T. Gibbs * Unmap memory segments mapped into kernel virtual address space by 6388b8a9b1dSJustin T. Gibbs * cam_periph_mapmem(). 6398b8a9b1dSJustin T. Gibbs */ 6408b8a9b1dSJustin T. Gibbs void 6418b8a9b1dSJustin T. Gibbs cam_periph_unmapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) 6428b8a9b1dSJustin T. Gibbs { 6438b8a9b1dSJustin T. Gibbs int numbufs, i; 6448b8a9b1dSJustin T. Gibbs u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; 6458b8a9b1dSJustin T. Gibbs 6468b8a9b1dSJustin T. Gibbs if (mapinfo->num_bufs_used <= 0) { 6478b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 6488b8a9b1dSJustin T. Gibbs curproc->p_flag &= ~P_PHYSIO; 6498b8a9b1dSJustin T. Gibbs return; 6508b8a9b1dSJustin T. Gibbs } 6518b8a9b1dSJustin T. Gibbs 6528b8a9b1dSJustin T. Gibbs switch (ccb->ccb_h.func_code) { 6538b8a9b1dSJustin T. Gibbs case XPT_DEV_MATCH: 6548b8a9b1dSJustin T. Gibbs numbufs = min(mapinfo->num_bufs_used, 2); 6558b8a9b1dSJustin T. Gibbs 6568b8a9b1dSJustin T. Gibbs if (numbufs == 1) { 6578b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; 6588b8a9b1dSJustin T. Gibbs } else { 6598b8a9b1dSJustin T. Gibbs data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; 6608b8a9b1dSJustin T. Gibbs data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; 6618b8a9b1dSJustin T. Gibbs } 6628b8a9b1dSJustin T. Gibbs break; 6638b8a9b1dSJustin T. Gibbs case XPT_SCSI_IO: 6648b8a9b1dSJustin T. Gibbs data_ptrs[0] = &ccb->csio.data_ptr; 6658b8a9b1dSJustin T. Gibbs numbufs = min(mapinfo->num_bufs_used, 1); 6668b8a9b1dSJustin T. Gibbs break; 6678b8a9b1dSJustin T. Gibbs default: 6688b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 6698b8a9b1dSJustin T. Gibbs curproc->p_flag &= ~P_PHYSIO; 6708b8a9b1dSJustin T. Gibbs return; 6718b8a9b1dSJustin T. Gibbs break; /* NOTREACHED */ 6728b8a9b1dSJustin T. Gibbs } 6738b8a9b1dSJustin T. Gibbs 6748b8a9b1dSJustin T. Gibbs for (i = 0; i < numbufs; i++) { 6758b8a9b1dSJustin T. Gibbs /* Set the user's pointer back to the original value */ 6768b8a9b1dSJustin T. Gibbs *data_ptrs[i] = mapinfo->bp[i]->b_saveaddr; 6778b8a9b1dSJustin T. Gibbs 6788b8a9b1dSJustin T. Gibbs /* unmap the buffer */ 6798b8a9b1dSJustin T. Gibbs vunmapbuf(mapinfo->bp[i]); 6808b8a9b1dSJustin T. Gibbs 6818b8a9b1dSJustin T. Gibbs /* clear the flags we set above */ 6828b8a9b1dSJustin T. Gibbs mapinfo->bp[i]->b_flags &= ~(B_PHYS|B_BUSY); 6838b8a9b1dSJustin T. Gibbs 6848b8a9b1dSJustin T. Gibbs /* release the buffer */ 6858b8a9b1dSJustin T. Gibbs relpbuf(mapinfo->bp[i]); 6868b8a9b1dSJustin T. Gibbs } 6878b8a9b1dSJustin T. Gibbs 6888b8a9b1dSJustin T. Gibbs /* allow ourselves to be swapped once again */ 6898b8a9b1dSJustin T. Gibbs curproc->p_flag &= ~P_PHYSIO; 6908b8a9b1dSJustin T. Gibbs } 6918b8a9b1dSJustin T. Gibbs 6928b8a9b1dSJustin T. Gibbs union ccb * 6938b8a9b1dSJustin T. Gibbs cam_periph_getccb(struct cam_periph *periph, u_int32_t priority) 6948b8a9b1dSJustin T. Gibbs { 6958b8a9b1dSJustin T. Gibbs struct ccb_hdr *ccb_h; 6968b8a9b1dSJustin T. Gibbs int s; 6978b8a9b1dSJustin T. Gibbs 6988b8a9b1dSJustin T. Gibbs CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdgetccb\n")); 6998b8a9b1dSJustin T. Gibbs 7008b8a9b1dSJustin T. Gibbs s = splsoftcam(); 7018b8a9b1dSJustin T. Gibbs 7028b8a9b1dSJustin T. Gibbs while (periph->ccb_list.slh_first == NULL) { 7038b8a9b1dSJustin T. Gibbs if (periph->immediate_priority > priority) 7048b8a9b1dSJustin T. Gibbs periph->immediate_priority = priority; 7058b8a9b1dSJustin T. Gibbs xpt_schedule(periph, priority); 7068b8a9b1dSJustin T. Gibbs if ((periph->ccb_list.slh_first != NULL) 7078b8a9b1dSJustin T. Gibbs && (periph->ccb_list.slh_first->pinfo.priority == priority)) 7088b8a9b1dSJustin T. Gibbs break; 7098b8a9b1dSJustin T. Gibbs tsleep(&periph->ccb_list, PRIBIO, "cgticb", 0); 7108b8a9b1dSJustin T. Gibbs } 7118b8a9b1dSJustin T. Gibbs 7128b8a9b1dSJustin T. Gibbs ccb_h = periph->ccb_list.slh_first; 7138b8a9b1dSJustin T. Gibbs SLIST_REMOVE_HEAD(&periph->ccb_list, periph_links.sle); 7148b8a9b1dSJustin T. Gibbs splx(s); 7158b8a9b1dSJustin T. Gibbs return ((union ccb *)ccb_h); 7168b8a9b1dSJustin T. Gibbs } 7178b8a9b1dSJustin T. Gibbs 7188b8a9b1dSJustin T. Gibbs void 7198b8a9b1dSJustin T. Gibbs cam_periph_ccbwait(union ccb *ccb) 7208b8a9b1dSJustin T. Gibbs { 7218b8a9b1dSJustin T. Gibbs int s; 7228b8a9b1dSJustin T. Gibbs 7238b8a9b1dSJustin T. Gibbs s = splsoftcam(); 7248b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.pinfo.index != CAM_UNQUEUED_INDEX) 7258b8a9b1dSJustin T. Gibbs || ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG)) 7268b8a9b1dSJustin T. Gibbs tsleep(&ccb->ccb_h.cbfcnp, PRIBIO, "cbwait", 0); 7278b8a9b1dSJustin T. Gibbs 7288b8a9b1dSJustin T. Gibbs splx(s); 7298b8a9b1dSJustin T. Gibbs } 7308b8a9b1dSJustin T. Gibbs 7318b8a9b1dSJustin T. Gibbs int 7328b8a9b1dSJustin T. Gibbs cam_periph_ioctl(struct cam_periph *periph, int cmd, caddr_t addr, 7338b8a9b1dSJustin T. Gibbs int (*error_routine)(union ccb *ccb, 7348b8a9b1dSJustin T. Gibbs cam_flags camflags, 7358b8a9b1dSJustin T. Gibbs u_int32_t sense_flags)) 7368b8a9b1dSJustin T. Gibbs { 7378b8a9b1dSJustin T. Gibbs union ccb *ccb; 7388b8a9b1dSJustin T. Gibbs int error; 7398b8a9b1dSJustin T. Gibbs int found; 7408b8a9b1dSJustin T. Gibbs 7418b8a9b1dSJustin T. Gibbs error = found = 0; 7428b8a9b1dSJustin T. Gibbs 7438b8a9b1dSJustin T. Gibbs switch(cmd){ 7448b8a9b1dSJustin T. Gibbs case CAMGETPASSTHRU: 7458b8a9b1dSJustin T. Gibbs ccb = cam_periph_getccb(periph, /* priority */ 1); 7468b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&ccb->ccb_h, 7478b8a9b1dSJustin T. Gibbs ccb->ccb_h.path, 7488b8a9b1dSJustin T. Gibbs /*priority*/1); 7498b8a9b1dSJustin T. Gibbs ccb->ccb_h.func_code = XPT_GDEVLIST; 7508b8a9b1dSJustin T. Gibbs 7518b8a9b1dSJustin T. Gibbs /* 7528b8a9b1dSJustin T. Gibbs * Basically, the point of this is that we go through 7538b8a9b1dSJustin T. Gibbs * getting the list of devices, until we find a passthrough 7548b8a9b1dSJustin T. Gibbs * device. In the current version of the CAM code, the 7558b8a9b1dSJustin T. Gibbs * only way to determine what type of device we're dealing 7568b8a9b1dSJustin T. Gibbs * with is by its name. 7578b8a9b1dSJustin T. Gibbs */ 7588b8a9b1dSJustin T. Gibbs while (found == 0) { 7598b8a9b1dSJustin T. Gibbs ccb->cgdl.index = 0; 7608b8a9b1dSJustin T. Gibbs ccb->cgdl.status = CAM_GDEVLIST_MORE_DEVS; 7618b8a9b1dSJustin T. Gibbs while (ccb->cgdl.status == CAM_GDEVLIST_MORE_DEVS) { 7628b8a9b1dSJustin T. Gibbs 7638b8a9b1dSJustin T. Gibbs /* we want the next device in the list */ 7648b8a9b1dSJustin T. Gibbs xpt_action(ccb); 7658b8a9b1dSJustin T. Gibbs if (strncmp(ccb->cgdl.periph_name, 7668b8a9b1dSJustin T. Gibbs "pass", 4) == 0){ 7678b8a9b1dSJustin T. Gibbs found = 1; 7688b8a9b1dSJustin T. Gibbs break; 7698b8a9b1dSJustin T. Gibbs } 7708b8a9b1dSJustin T. Gibbs } 7718b8a9b1dSJustin T. Gibbs if ((ccb->cgdl.status == CAM_GDEVLIST_LAST_DEVICE) && 7728b8a9b1dSJustin T. Gibbs (found == 0)) { 7738b8a9b1dSJustin T. Gibbs ccb->cgdl.periph_name[0] = '\0'; 7748b8a9b1dSJustin T. Gibbs ccb->cgdl.unit_number = 0; 7758b8a9b1dSJustin T. Gibbs break; 7768b8a9b1dSJustin T. Gibbs } 7778b8a9b1dSJustin T. Gibbs } 7788b8a9b1dSJustin T. Gibbs 7798b8a9b1dSJustin T. Gibbs /* copy the result back out */ 7808b8a9b1dSJustin T. Gibbs bcopy(ccb, addr, sizeof(union ccb)); 7818b8a9b1dSJustin T. Gibbs 7828b8a9b1dSJustin T. Gibbs /* and release the ccb */ 7838b8a9b1dSJustin T. Gibbs xpt_release_ccb(ccb); 7848b8a9b1dSJustin T. Gibbs 7858b8a9b1dSJustin T. Gibbs break; 7868b8a9b1dSJustin T. Gibbs default: 7878b8a9b1dSJustin T. Gibbs error = ENOTTY; 7888b8a9b1dSJustin T. Gibbs break; 7898b8a9b1dSJustin T. Gibbs } 7908b8a9b1dSJustin T. Gibbs return(error); 7918b8a9b1dSJustin T. Gibbs } 7928b8a9b1dSJustin T. Gibbs 7938b8a9b1dSJustin T. Gibbs int 7948b8a9b1dSJustin T. Gibbs cam_periph_runccb(union ccb *ccb, 7958b8a9b1dSJustin T. Gibbs int (*error_routine)(union ccb *ccb, 7968b8a9b1dSJustin T. Gibbs cam_flags camflags, 7978b8a9b1dSJustin T. Gibbs u_int32_t sense_flags), 7988b8a9b1dSJustin T. Gibbs cam_flags camflags, u_int32_t sense_flags, 7998b8a9b1dSJustin T. Gibbs struct devstat *ds) 8008b8a9b1dSJustin T. Gibbs { 8018b8a9b1dSJustin T. Gibbs int error; 8028b8a9b1dSJustin T. Gibbs 8038b8a9b1dSJustin T. Gibbs error = 0; 8048b8a9b1dSJustin T. Gibbs 8058b8a9b1dSJustin T. Gibbs /* 8068b8a9b1dSJustin T. Gibbs * If the user has supplied a stats structure, and if we understand 8078b8a9b1dSJustin T. Gibbs * this particular type of ccb, record the transaction start. 8088b8a9b1dSJustin T. Gibbs */ 8098b8a9b1dSJustin T. Gibbs if ((ds != NULL) && (ccb->ccb_h.func_code == XPT_SCSI_IO)) 8108b8a9b1dSJustin T. Gibbs devstat_start_transaction(ds); 8118b8a9b1dSJustin T. Gibbs 8128b8a9b1dSJustin T. Gibbs xpt_action(ccb); 8138b8a9b1dSJustin T. Gibbs 8148b8a9b1dSJustin T. Gibbs do { 8158b8a9b1dSJustin T. Gibbs cam_periph_ccbwait(ccb); 8168b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) 8178b8a9b1dSJustin T. Gibbs error = 0; 8188b8a9b1dSJustin T. Gibbs else if (error_routine != NULL) 8198b8a9b1dSJustin T. Gibbs error = (*error_routine)(ccb, camflags, sense_flags); 8208b8a9b1dSJustin T. Gibbs else 8218b8a9b1dSJustin T. Gibbs error = 0; 8228b8a9b1dSJustin T. Gibbs 8238b8a9b1dSJustin T. Gibbs } while (error == ERESTART); 8248b8a9b1dSJustin T. Gibbs 8258b8a9b1dSJustin T. Gibbs if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) 8268b8a9b1dSJustin T. Gibbs cam_release_devq(ccb->ccb_h.path, 8278b8a9b1dSJustin T. Gibbs /* relsim_flags */0, 8288b8a9b1dSJustin T. Gibbs /* openings */0, 8298b8a9b1dSJustin T. Gibbs /* timeout */0, 8308b8a9b1dSJustin T. Gibbs /* getcount_only */ FALSE); 8318b8a9b1dSJustin T. Gibbs 8328b8a9b1dSJustin T. Gibbs if ((ds != NULL) && (ccb->ccb_h.func_code == XPT_SCSI_IO)) 8338b8a9b1dSJustin T. Gibbs devstat_end_transaction(ds, 8348b8a9b1dSJustin T. Gibbs ccb->csio.dxfer_len, 8358b8a9b1dSJustin T. Gibbs ccb->csio.tag_action & 0xf, 8368b8a9b1dSJustin T. Gibbs ((ccb->ccb_h.flags & CAM_DIR_MASK) == 8378b8a9b1dSJustin T. Gibbs CAM_DIR_NONE) ? DEVSTAT_NO_DATA : 8388b8a9b1dSJustin T. Gibbs (ccb->ccb_h.flags & CAM_DIR_OUT) ? 8398b8a9b1dSJustin T. Gibbs DEVSTAT_WRITE : 8408b8a9b1dSJustin T. Gibbs DEVSTAT_READ); 8418b8a9b1dSJustin T. Gibbs 8428b8a9b1dSJustin T. Gibbs return(error); 8438b8a9b1dSJustin T. Gibbs } 8448b8a9b1dSJustin T. Gibbs 8458b8a9b1dSJustin T. Gibbs u_int32_t 8468b8a9b1dSJustin T. Gibbs cam_release_devq(struct cam_path *path, u_int32_t relsim_flags, 8478b8a9b1dSJustin T. Gibbs u_int32_t openings, u_int32_t timeout, 8488b8a9b1dSJustin T. Gibbs int getcount_only) 8498b8a9b1dSJustin T. Gibbs { 8508b8a9b1dSJustin T. Gibbs struct ccb_relsim crs; 8518b8a9b1dSJustin T. Gibbs 8528b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&crs.ccb_h, path, 8538b8a9b1dSJustin T. Gibbs /*priority*/1); 8548b8a9b1dSJustin T. Gibbs crs.ccb_h.func_code = XPT_REL_SIMQ; 8558b8a9b1dSJustin T. Gibbs crs.ccb_h.flags = getcount_only ? CAM_DEV_QFREEZE : 0; 8568b8a9b1dSJustin T. Gibbs crs.release_flags = relsim_flags; 8578b8a9b1dSJustin T. Gibbs crs.openings = openings; 8588b8a9b1dSJustin T. Gibbs crs.release_timeout = timeout; 8598b8a9b1dSJustin T. Gibbs xpt_action((union ccb *)&crs); 8608b8a9b1dSJustin T. Gibbs return (crs.qfrozen_cnt); 8618b8a9b1dSJustin T. Gibbs } 8628b8a9b1dSJustin T. Gibbs 8638b8a9b1dSJustin T. Gibbs #define saved_ccb_ptr ppriv_ptr0 8648b8a9b1dSJustin T. Gibbs static void 8658b8a9b1dSJustin T. Gibbs camperiphdone(struct cam_periph *periph, union ccb *done_ccb) 8668b8a9b1dSJustin T. Gibbs { 8678b8a9b1dSJustin T. Gibbs cam_status status; 8688b8a9b1dSJustin T. Gibbs int frozen; 8698b8a9b1dSJustin T. Gibbs int sense; 8708b8a9b1dSJustin T. Gibbs struct scsi_start_stop_unit *scsi_cmd; 8718b8a9b1dSJustin T. Gibbs u_int32_t relsim_flags, timeout; 8728b8a9b1dSJustin T. Gibbs u_int32_t qfrozen_cnt; 8738b8a9b1dSJustin T. Gibbs 8748b8a9b1dSJustin T. Gibbs status = done_ccb->ccb_h.status; 8758b8a9b1dSJustin T. Gibbs frozen = (status & CAM_DEV_QFRZN) != 0; 8768b8a9b1dSJustin T. Gibbs sense = (status & CAM_AUTOSNS_VALID) != 0; 8778b8a9b1dSJustin T. Gibbs status &= CAM_STATUS_MASK; 8788b8a9b1dSJustin T. Gibbs 8798b8a9b1dSJustin T. Gibbs timeout = 0; 8808b8a9b1dSJustin T. Gibbs relsim_flags = 0; 8818b8a9b1dSJustin T. Gibbs 8828b8a9b1dSJustin T. Gibbs /* 8838b8a9b1dSJustin T. Gibbs * Unfreeze the queue once if it is already frozen.. 8848b8a9b1dSJustin T. Gibbs */ 8858b8a9b1dSJustin T. Gibbs if (frozen != 0) { 8868b8a9b1dSJustin T. Gibbs qfrozen_cnt = cam_release_devq(done_ccb->ccb_h.path, 8878b8a9b1dSJustin T. Gibbs /*relsim_flags*/0, 8888b8a9b1dSJustin T. Gibbs /*openings*/0, 8898b8a9b1dSJustin T. Gibbs /*timeout*/0, 8908b8a9b1dSJustin T. Gibbs /*getcount_only*/0); 8918b8a9b1dSJustin T. Gibbs } 8928b8a9b1dSJustin T. Gibbs 8938b8a9b1dSJustin T. Gibbs switch (status) { 8948b8a9b1dSJustin T. Gibbs 8958b8a9b1dSJustin T. Gibbs case CAM_REQ_CMP: 8968b8a9b1dSJustin T. Gibbs 8978b8a9b1dSJustin T. Gibbs /* 8988b8a9b1dSJustin T. Gibbs * If we have successfully taken a device from the not 8998b8a9b1dSJustin T. Gibbs * ready to ready state, re-scan the device and re-get the 9008b8a9b1dSJustin T. Gibbs * inquiry information. Many devices (mostly disks) don't 9018b8a9b1dSJustin T. Gibbs * properly report their inquiry information unless they 9028b8a9b1dSJustin T. Gibbs * are spun up. 9038b8a9b1dSJustin T. Gibbs */ 9048b8a9b1dSJustin T. Gibbs if (done_ccb->ccb_h.func_code == XPT_SCSI_IO) { 9058b8a9b1dSJustin T. Gibbs scsi_cmd = (struct scsi_start_stop_unit *) 9068b8a9b1dSJustin T. Gibbs &done_ccb->csio.cdb_io.cdb_bytes; 9078b8a9b1dSJustin T. Gibbs 9088b8a9b1dSJustin T. Gibbs if (scsi_cmd->opcode == START_STOP_UNIT) 9098b8a9b1dSJustin T. Gibbs xpt_async(AC_INQ_CHANGED, 9108b8a9b1dSJustin T. Gibbs done_ccb->ccb_h.path, NULL); 9118b8a9b1dSJustin T. Gibbs } 9128b8a9b1dSJustin T. Gibbs bcopy(done_ccb->ccb_h.saved_ccb_ptr, done_ccb, 9138b8a9b1dSJustin T. Gibbs sizeof(union ccb)); 9148b8a9b1dSJustin T. Gibbs 91560a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 91660a899a0SKenneth D. Merry 9178b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 9188b8a9b1dSJustin T. Gibbs 9198b8a9b1dSJustin T. Gibbs break; 9208b8a9b1dSJustin T. Gibbs case CAM_SCSI_STATUS_ERROR: 9218b8a9b1dSJustin T. Gibbs scsi_cmd = (struct scsi_start_stop_unit *) 9228b8a9b1dSJustin T. Gibbs &done_ccb->csio.cdb_io.cdb_bytes; 9238b8a9b1dSJustin T. Gibbs if (sense != 0) { 9248b8a9b1dSJustin T. Gibbs struct scsi_sense_data *sense; 9258b8a9b1dSJustin T. Gibbs int error_code, sense_key, asc, ascq; 9268b8a9b1dSJustin T. Gibbs 9278b8a9b1dSJustin T. Gibbs sense = &done_ccb->csio.sense_data; 9288b8a9b1dSJustin T. Gibbs scsi_extract_sense(sense, &error_code, 9298b8a9b1dSJustin T. Gibbs &sense_key, &asc, &ascq); 9308b8a9b1dSJustin T. Gibbs 9318b8a9b1dSJustin T. Gibbs /* 9328b8a9b1dSJustin T. Gibbs * If the error is "invalid field in CDB", 9338b8a9b1dSJustin T. Gibbs * and the load/eject flag is set, turn the 9348b8a9b1dSJustin T. Gibbs * flag off and try again. This is just in 9358b8a9b1dSJustin T. Gibbs * case the drive in question barfs on the 9368b8a9b1dSJustin T. Gibbs * load eject flag. The CAM code should set 9378b8a9b1dSJustin T. Gibbs * the load/eject flag by default for 9388b8a9b1dSJustin T. Gibbs * removable media. 9398b8a9b1dSJustin T. Gibbs */ 9408b8a9b1dSJustin T. Gibbs 9418b8a9b1dSJustin T. Gibbs /* XXX KDM 9428b8a9b1dSJustin T. Gibbs * Should we check to see what the specific 9438b8a9b1dSJustin T. Gibbs * scsi status is?? Or does it not matter 9448b8a9b1dSJustin T. Gibbs * since we already know that there was an 9458b8a9b1dSJustin T. Gibbs * error, and we know what the specific 9468b8a9b1dSJustin T. Gibbs * error code was, and we know what the 9478b8a9b1dSJustin T. Gibbs * opcode is.. 9488b8a9b1dSJustin T. Gibbs */ 9498b8a9b1dSJustin T. Gibbs if ((scsi_cmd->opcode == START_STOP_UNIT) && 9508b8a9b1dSJustin T. Gibbs ((scsi_cmd->how & SSS_LOEJ) != 0) && 9518b8a9b1dSJustin T. Gibbs (asc == 0x24) && (ascq == 0x00) && 9528b8a9b1dSJustin T. Gibbs (done_ccb->ccb_h.retry_count > 0)) { 9538b8a9b1dSJustin T. Gibbs 9548b8a9b1dSJustin T. Gibbs scsi_cmd->how &= ~SSS_LOEJ; 9558b8a9b1dSJustin T. Gibbs 9568b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 9578b8a9b1dSJustin T. Gibbs 9588b8a9b1dSJustin T. Gibbs } else if (done_ccb->ccb_h.retry_count > 0) { 9598b8a9b1dSJustin T. Gibbs /* 9608b8a9b1dSJustin T. Gibbs * In this case, the error recovery 9618b8a9b1dSJustin T. Gibbs * command failed, but we've got 9628b8a9b1dSJustin T. Gibbs * some retries left on it. Give 9638b8a9b1dSJustin T. Gibbs * it another try. 9648b8a9b1dSJustin T. Gibbs */ 9658b8a9b1dSJustin T. Gibbs 9668b8a9b1dSJustin T. Gibbs /* set the timeout to .5 sec */ 9678b8a9b1dSJustin T. Gibbs relsim_flags = 9688b8a9b1dSJustin T. Gibbs RELSIM_RELEASE_AFTER_TIMEOUT; 9698b8a9b1dSJustin T. Gibbs timeout = 500; 9708b8a9b1dSJustin T. Gibbs 9718b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 9728b8a9b1dSJustin T. Gibbs 9738b8a9b1dSJustin T. Gibbs break; 9748b8a9b1dSJustin T. Gibbs 9758b8a9b1dSJustin T. Gibbs } else { 9768b8a9b1dSJustin T. Gibbs /* 9778b8a9b1dSJustin T. Gibbs * Copy the original CCB back and 9788b8a9b1dSJustin T. Gibbs * send it back to the caller. 9798b8a9b1dSJustin T. Gibbs */ 9808b8a9b1dSJustin T. Gibbs bcopy(done_ccb->ccb_h.saved_ccb_ptr, 9818b8a9b1dSJustin T. Gibbs done_ccb, sizeof(union ccb)); 9828b8a9b1dSJustin T. Gibbs 98360a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 98460a899a0SKenneth D. Merry 9858b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 9868b8a9b1dSJustin T. Gibbs } 9878b8a9b1dSJustin T. Gibbs } else { 9888b8a9b1dSJustin T. Gibbs /* 9898b8a9b1dSJustin T. Gibbs * Eh?? The command failed, but we don't 9908b8a9b1dSJustin T. Gibbs * have any sense. What's up with that? 9918b8a9b1dSJustin T. Gibbs * Fire the CCB again to return it to the 9928b8a9b1dSJustin T. Gibbs * caller. 9938b8a9b1dSJustin T. Gibbs */ 9948b8a9b1dSJustin T. Gibbs bcopy(done_ccb->ccb_h.saved_ccb_ptr, 9958b8a9b1dSJustin T. Gibbs done_ccb, sizeof(union ccb)); 9968b8a9b1dSJustin T. Gibbs 99760a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 99860a899a0SKenneth D. Merry 9998b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 10008b8a9b1dSJustin T. Gibbs 10018b8a9b1dSJustin T. Gibbs } 10028b8a9b1dSJustin T. Gibbs break; 10038b8a9b1dSJustin T. Gibbs default: 10048b8a9b1dSJustin T. Gibbs bcopy(done_ccb->ccb_h.saved_ccb_ptr, done_ccb, 10058b8a9b1dSJustin T. Gibbs sizeof(union ccb)); 10068b8a9b1dSJustin T. Gibbs 100760a899a0SKenneth D. Merry periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; 100860a899a0SKenneth D. Merry 10098b8a9b1dSJustin T. Gibbs xpt_action(done_ccb); 10108b8a9b1dSJustin T. Gibbs 10118b8a9b1dSJustin T. Gibbs break; 10128b8a9b1dSJustin T. Gibbs } 10138b8a9b1dSJustin T. Gibbs 10148b8a9b1dSJustin T. Gibbs /* decrement the retry count */ 10158b8a9b1dSJustin T. Gibbs if (done_ccb->ccb_h.retry_count > 0) 10168b8a9b1dSJustin T. Gibbs done_ccb->ccb_h.retry_count--; 10178b8a9b1dSJustin T. Gibbs 10188b8a9b1dSJustin T. Gibbs qfrozen_cnt = cam_release_devq(done_ccb->ccb_h.path, 10198b8a9b1dSJustin T. Gibbs /*relsim_flags*/relsim_flags, 10208b8a9b1dSJustin T. Gibbs /*openings*/0, 10218b8a9b1dSJustin T. Gibbs /*timeout*/timeout, 10228b8a9b1dSJustin T. Gibbs /*getcount_only*/0); 10238b8a9b1dSJustin T. Gibbs } 10248b8a9b1dSJustin T. Gibbs 10258b8a9b1dSJustin T. Gibbs /* 10268b8a9b1dSJustin T. Gibbs * Generic error handler. Peripheral drivers usually filter 10278b8a9b1dSJustin T. Gibbs * out the errors that they handle in a unique mannor, then 10288b8a9b1dSJustin T. Gibbs * call this function. 10298b8a9b1dSJustin T. Gibbs */ 10308b8a9b1dSJustin T. Gibbs int 10318b8a9b1dSJustin T. Gibbs cam_periph_error(union ccb *ccb, cam_flags camflags, 10328b8a9b1dSJustin T. Gibbs u_int32_t sense_flags, union ccb *save_ccb) 10338b8a9b1dSJustin T. Gibbs { 10348b8a9b1dSJustin T. Gibbs cam_status status; 10358b8a9b1dSJustin T. Gibbs int frozen; 10368b8a9b1dSJustin T. Gibbs int sense; 10378b8a9b1dSJustin T. Gibbs int error; 10388b8a9b1dSJustin T. Gibbs int openings; 10398b8a9b1dSJustin T. Gibbs int retry; 10408b8a9b1dSJustin T. Gibbs u_int32_t relsim_flags; 10418b8a9b1dSJustin T. Gibbs u_int32_t timeout; 10428b8a9b1dSJustin T. Gibbs 10438b8a9b1dSJustin T. Gibbs status = ccb->ccb_h.status; 10448b8a9b1dSJustin T. Gibbs frozen = (status & CAM_DEV_QFRZN) != 0; 10458b8a9b1dSJustin T. Gibbs sense = (status & CAM_AUTOSNS_VALID) != 0; 10468b8a9b1dSJustin T. Gibbs status &= CAM_STATUS_MASK; 10478b8a9b1dSJustin T. Gibbs relsim_flags = 0; 10488b8a9b1dSJustin T. Gibbs 10498b8a9b1dSJustin T. Gibbs 10508b8a9b1dSJustin T. Gibbs switch (status) { 10518b8a9b1dSJustin T. Gibbs case CAM_REQ_CMP: 10528b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 10538b8a9b1dSJustin T. Gibbs retry = ccb->ccb_h.retry_count > 0; 10548b8a9b1dSJustin T. Gibbs if (retry) 10558b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 10568b8a9b1dSJustin T. Gibbs error = 0; 10578b8a9b1dSJustin T. Gibbs break; 10588b8a9b1dSJustin T. Gibbs case CAM_SCSI_STATUS_ERROR: 10598b8a9b1dSJustin T. Gibbs 10608b8a9b1dSJustin T. Gibbs switch (ccb->csio.scsi_status) { 10618b8a9b1dSJustin T. Gibbs case SCSI_STATUS_OK: 10628b8a9b1dSJustin T. Gibbs case SCSI_STATUS_COND_MET: 10638b8a9b1dSJustin T. Gibbs case SCSI_STATUS_INTERMED: 10648b8a9b1dSJustin T. Gibbs case SCSI_STATUS_INTERMED_COND_MET: 10658b8a9b1dSJustin T. Gibbs error = 0; 10668b8a9b1dSJustin T. Gibbs break; 10678b8a9b1dSJustin T. Gibbs case SCSI_STATUS_CMD_TERMINATED: 10688b8a9b1dSJustin T. Gibbs case SCSI_STATUS_CHECK_COND: 10698b8a9b1dSJustin T. Gibbs if (sense != 0) { 10708b8a9b1dSJustin T. Gibbs struct scsi_sense_data *sense; 10718b8a9b1dSJustin T. Gibbs int error_code, sense_key, asc, ascq; 10728b8a9b1dSJustin T. Gibbs struct cam_periph *periph; 10738b8a9b1dSJustin T. Gibbs scsi_sense_action err_action; 10748b8a9b1dSJustin T. Gibbs struct ccb_getdev cgd; 10758b8a9b1dSJustin T. Gibbs 10768b8a9b1dSJustin T. Gibbs sense = &ccb->csio.sense_data; 10778b8a9b1dSJustin T. Gibbs scsi_extract_sense(sense, &error_code, 10788b8a9b1dSJustin T. Gibbs &sense_key, &asc, &ascq); 10798b8a9b1dSJustin T. Gibbs periph = xpt_path_periph(ccb->ccb_h.path); 10808b8a9b1dSJustin T. Gibbs 10818b8a9b1dSJustin T. Gibbs /* 10828b8a9b1dSJustin T. Gibbs * Grab the inquiry data for this device. 10838b8a9b1dSJustin T. Gibbs */ 10848b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&cgd.ccb_h, ccb->ccb_h.path, 10858b8a9b1dSJustin T. Gibbs /*priority*/ 1); 10868b8a9b1dSJustin T. Gibbs cgd.ccb_h.func_code = XPT_GDEV_TYPE; 10878b8a9b1dSJustin T. Gibbs xpt_action((union ccb *)&cgd); 10888b8a9b1dSJustin T. Gibbs 10898b8a9b1dSJustin T. Gibbs err_action = scsi_error_action(asc, ascq, 10908b8a9b1dSJustin T. Gibbs &cgd.inq_data); 10918b8a9b1dSJustin T. Gibbs 10928b8a9b1dSJustin T. Gibbs /* 10938b8a9b1dSJustin T. Gibbs * Send a Test Unit Ready to the device. 10948b8a9b1dSJustin T. Gibbs * If the 'many' flag is set, we send 120 10958b8a9b1dSJustin T. Gibbs * test unit ready commands, one every half 10968b8a9b1dSJustin T. Gibbs * second. Otherwise, we just send one TUR. 10978b8a9b1dSJustin T. Gibbs * We only want to do this if the retry 10988b8a9b1dSJustin T. Gibbs * count has not been exhausted. 10998b8a9b1dSJustin T. Gibbs */ 11008b8a9b1dSJustin T. Gibbs if (((err_action & SS_MASK) == SS_TUR) 11018b8a9b1dSJustin T. Gibbs && save_ccb != NULL 11028b8a9b1dSJustin T. Gibbs && ccb->ccb_h.retry_count > 0) { 11038b8a9b1dSJustin T. Gibbs 110460a899a0SKenneth D. Merry /* 110560a899a0SKenneth D. Merry * Since error recovery is already 110660a899a0SKenneth D. Merry * in progress, don't attempt to 110760a899a0SKenneth D. Merry * process this error. It is probably 110860a899a0SKenneth D. Merry * related to the error that caused 110960a899a0SKenneth D. Merry * the currently active error recovery 111060a899a0SKenneth D. Merry * action. Also, we only have 111160a899a0SKenneth D. Merry * space for one saved CCB, so if we 111260a899a0SKenneth D. Merry * had two concurrent error recovery 111360a899a0SKenneth D. Merry * actions, we would end up 111460a899a0SKenneth D. Merry * over-writing one error recovery 111560a899a0SKenneth D. Merry * CCB with another one. 111660a899a0SKenneth D. Merry */ 111760a899a0SKenneth D. Merry if (periph->flags & 111860a899a0SKenneth D. Merry CAM_PERIPH_RECOVERY_INPROG) { 111960a899a0SKenneth D. Merry error = ERESTART; 112060a899a0SKenneth D. Merry break; 112160a899a0SKenneth D. Merry } 112260a899a0SKenneth D. Merry 112360a899a0SKenneth D. Merry periph->flags |= 112460a899a0SKenneth D. Merry CAM_PERIPH_RECOVERY_INPROG; 112560a899a0SKenneth D. Merry 11268b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 11278b8a9b1dSJustin T. Gibbs if ((err_action & 11288b8a9b1dSJustin T. Gibbs SSQ_DECREMENT_COUNT) != 0) { 11298b8a9b1dSJustin T. Gibbs retry = 1; 11308b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 11318b8a9b1dSJustin T. Gibbs } 11328b8a9b1dSJustin T. Gibbs 11338b8a9b1dSJustin T. Gibbs bcopy(ccb, save_ccb, sizeof(*save_ccb)); 11348b8a9b1dSJustin T. Gibbs 11358b8a9b1dSJustin T. Gibbs /* 11368b8a9b1dSJustin T. Gibbs * We retry this one every half 11378b8a9b1dSJustin T. Gibbs * second for a minute. If the 11388b8a9b1dSJustin T. Gibbs * device hasn't become ready in a 11398b8a9b1dSJustin T. Gibbs * minute's time, it's unlikely to 11408b8a9b1dSJustin T. Gibbs * ever become ready. If the table 11418b8a9b1dSJustin T. Gibbs * doesn't specify SSQ_MANY, we can 11428b8a9b1dSJustin T. Gibbs * only try this once. Oh well. 11438b8a9b1dSJustin T. Gibbs */ 11448b8a9b1dSJustin T. Gibbs if ((err_action & SSQ_MANY) != 0) 11458b8a9b1dSJustin T. Gibbs scsi_test_unit_ready(&ccb->csio, 11468b8a9b1dSJustin T. Gibbs /*retries*/120, 11478b8a9b1dSJustin T. Gibbs camperiphdone, 11488b8a9b1dSJustin T. Gibbs MSG_SIMPLE_Q_TAG, 11498b8a9b1dSJustin T. Gibbs SSD_FULL_SIZE, 11508b8a9b1dSJustin T. Gibbs /*timeout*/5000); 11518b8a9b1dSJustin T. Gibbs else 11528b8a9b1dSJustin T. Gibbs scsi_test_unit_ready(&ccb->csio, 11538b8a9b1dSJustin T. Gibbs /*retries*/1, 11548b8a9b1dSJustin T. Gibbs camperiphdone, 11558b8a9b1dSJustin T. Gibbs MSG_SIMPLE_Q_TAG, 11568b8a9b1dSJustin T. Gibbs SSD_FULL_SIZE, 11578b8a9b1dSJustin T. Gibbs /*timeout*/5000); 11588b8a9b1dSJustin T. Gibbs 11598b8a9b1dSJustin T. Gibbs /* release the queue after .5 sec. */ 11608b8a9b1dSJustin T. Gibbs relsim_flags = 11618b8a9b1dSJustin T. Gibbs RELSIM_RELEASE_AFTER_TIMEOUT; 11628b8a9b1dSJustin T. Gibbs timeout = 500; 11638b8a9b1dSJustin T. Gibbs /* 11648b8a9b1dSJustin T. Gibbs * Drop the priority to 0 so that 11658b8a9b1dSJustin T. Gibbs * we are the first to execute. Also 11668b8a9b1dSJustin T. Gibbs * freeze the queue after this command 11678b8a9b1dSJustin T. Gibbs * is sent so that we can restore the 11688b8a9b1dSJustin T. Gibbs * old csio and have it queued in the 11698b8a9b1dSJustin T. Gibbs * proper order before we let normal 11708b8a9b1dSJustin T. Gibbs * transactions go to the drive. 11718b8a9b1dSJustin T. Gibbs */ 11728b8a9b1dSJustin T. Gibbs ccb->ccb_h.pinfo.priority = 0; 11738b8a9b1dSJustin T. Gibbs ccb->ccb_h.flags |= CAM_DEV_QFREEZE; 11748b8a9b1dSJustin T. Gibbs 11758b8a9b1dSJustin T. Gibbs /* 11768b8a9b1dSJustin T. Gibbs * Save a pointer to the original 11778b8a9b1dSJustin T. Gibbs * CCB in the new CCB. 11788b8a9b1dSJustin T. Gibbs */ 11798b8a9b1dSJustin T. Gibbs ccb->ccb_h.saved_ccb_ptr = save_ccb; 11808b8a9b1dSJustin T. Gibbs 11818b8a9b1dSJustin T. Gibbs error = ERESTART; 11828b8a9b1dSJustin T. Gibbs } 11838b8a9b1dSJustin T. Gibbs /* 11848b8a9b1dSJustin T. Gibbs * Send a start unit command to the device, 11858b8a9b1dSJustin T. Gibbs * and then retry the command. We only 11868b8a9b1dSJustin T. Gibbs * want to do this if the retry count has 11878b8a9b1dSJustin T. Gibbs * not been exhausted. If the user 11888b8a9b1dSJustin T. Gibbs * specified 0 retries, then we follow 11898b8a9b1dSJustin T. Gibbs * their request and do not retry. 11908b8a9b1dSJustin T. Gibbs */ 11918b8a9b1dSJustin T. Gibbs else if (((err_action & SS_MASK) == SS_START) 11928b8a9b1dSJustin T. Gibbs && save_ccb != NULL 11938b8a9b1dSJustin T. Gibbs && ccb->ccb_h.retry_count > 0) { 11948b8a9b1dSJustin T. Gibbs int le; 11958b8a9b1dSJustin T. Gibbs 119660a899a0SKenneth D. Merry /* 119760a899a0SKenneth D. Merry * Only one error recovery action 119860a899a0SKenneth D. Merry * at a time. See above. 119960a899a0SKenneth D. Merry */ 120060a899a0SKenneth D. Merry if (periph->flags & 120160a899a0SKenneth D. Merry CAM_PERIPH_RECOVERY_INPROG) { 120260a899a0SKenneth D. Merry error = ERESTART; 120360a899a0SKenneth D. Merry break; 120460a899a0SKenneth D. Merry } 120560a899a0SKenneth D. Merry 120660a899a0SKenneth D. Merry periph->flags |= 120760a899a0SKenneth D. Merry CAM_PERIPH_RECOVERY_INPROG; 120860a899a0SKenneth D. Merry 12098b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 12108b8a9b1dSJustin T. Gibbs retry = 1; 12118b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 12128b8a9b1dSJustin T. Gibbs 12138b8a9b1dSJustin T. Gibbs /* 12148b8a9b1dSJustin T. Gibbs * Check for removable media and 12158b8a9b1dSJustin T. Gibbs * set load/eject flag 12168b8a9b1dSJustin T. Gibbs * appropriately. 12178b8a9b1dSJustin T. Gibbs */ 12188b8a9b1dSJustin T. Gibbs if (SID_IS_REMOVABLE(&cgd.inq_data)) 12198b8a9b1dSJustin T. Gibbs le = TRUE; 12208b8a9b1dSJustin T. Gibbs else 12218b8a9b1dSJustin T. Gibbs le = FALSE; 12228b8a9b1dSJustin T. Gibbs 12238b8a9b1dSJustin T. Gibbs /* 12248b8a9b1dSJustin T. Gibbs * Attempt to start the drive up. 12258b8a9b1dSJustin T. Gibbs * 12268b8a9b1dSJustin T. Gibbs * Save the current ccb so it can 12278b8a9b1dSJustin T. Gibbs * be restored and retried once the 12288b8a9b1dSJustin T. Gibbs * drive is started up. 12298b8a9b1dSJustin T. Gibbs */ 12308b8a9b1dSJustin T. Gibbs bcopy(ccb, save_ccb, sizeof(*save_ccb)); 12318b8a9b1dSJustin T. Gibbs 12328b8a9b1dSJustin T. Gibbs scsi_start_stop(&ccb->csio, 12338b8a9b1dSJustin T. Gibbs /*retries*/1, 12348b8a9b1dSJustin T. Gibbs camperiphdone, 12358b8a9b1dSJustin T. Gibbs MSG_SIMPLE_Q_TAG, 12368b8a9b1dSJustin T. Gibbs /*start*/TRUE, 12378b8a9b1dSJustin T. Gibbs /*load/eject*/le, 12388b8a9b1dSJustin T. Gibbs /*immediate*/FALSE, 12398b8a9b1dSJustin T. Gibbs SSD_FULL_SIZE, 12408b8a9b1dSJustin T. Gibbs /*timeout*/50000); 12418b8a9b1dSJustin T. Gibbs /* 12428b8a9b1dSJustin T. Gibbs * Drop the priority to 0 so that 12438b8a9b1dSJustin T. Gibbs * we are the first to execute. Also 12448b8a9b1dSJustin T. Gibbs * freeze the queue after this command 12458b8a9b1dSJustin T. Gibbs * is sent so that we can restore the 12468b8a9b1dSJustin T. Gibbs * old csio and have it queued in the 12478b8a9b1dSJustin T. Gibbs * proper order before we let normal 12488b8a9b1dSJustin T. Gibbs * transactions go to the drive. 12498b8a9b1dSJustin T. Gibbs */ 12508b8a9b1dSJustin T. Gibbs ccb->ccb_h.pinfo.priority = 0; 12518b8a9b1dSJustin T. Gibbs ccb->ccb_h.flags |= CAM_DEV_QFREEZE; 12528b8a9b1dSJustin T. Gibbs 12538b8a9b1dSJustin T. Gibbs /* 12548b8a9b1dSJustin T. Gibbs * Save a pointer to the original 12558b8a9b1dSJustin T. Gibbs * CCB in the new CCB. 12568b8a9b1dSJustin T. Gibbs */ 12578b8a9b1dSJustin T. Gibbs ccb->ccb_h.saved_ccb_ptr = save_ccb; 12588b8a9b1dSJustin T. Gibbs 12598b8a9b1dSJustin T. Gibbs error = ERESTART; 12608b8a9b1dSJustin T. Gibbs } else if ((sense_flags & SF_RETRY_UA) != 0) { 12618b8a9b1dSJustin T. Gibbs /* 12628b8a9b1dSJustin T. Gibbs * XXX KDM this is a *horrible* 12638b8a9b1dSJustin T. Gibbs * hack. 12648b8a9b1dSJustin T. Gibbs */ 12658b8a9b1dSJustin T. Gibbs error = scsi_interpret_sense(ccb, 12668b8a9b1dSJustin T. Gibbs sense_flags, 12678b8a9b1dSJustin T. Gibbs &relsim_flags, 12688b8a9b1dSJustin T. Gibbs &openings, 12698b8a9b1dSJustin T. Gibbs &timeout, 12708b8a9b1dSJustin T. Gibbs err_action); 12718b8a9b1dSJustin T. Gibbs } 12728b8a9b1dSJustin T. Gibbs 12738b8a9b1dSJustin T. Gibbs /* 12748b8a9b1dSJustin T. Gibbs * Theoretically, this code should send a 12758b8a9b1dSJustin T. Gibbs * test unit ready to the given device, and 12768b8a9b1dSJustin T. Gibbs * if it returns and error, send a start 12778b8a9b1dSJustin T. Gibbs * unit command. Since we don't yet have 12788b8a9b1dSJustin T. Gibbs * the capability to do two-command error 12798b8a9b1dSJustin T. Gibbs * recovery, just send a start unit. 12808b8a9b1dSJustin T. Gibbs * XXX KDM fix this! 12818b8a9b1dSJustin T. Gibbs */ 12828b8a9b1dSJustin T. Gibbs else if (((err_action & SS_MASK) == SS_TURSTART) 12838b8a9b1dSJustin T. Gibbs && save_ccb != NULL 12848b8a9b1dSJustin T. Gibbs && ccb->ccb_h.retry_count > 0) { 12858b8a9b1dSJustin T. Gibbs int le; 12868b8a9b1dSJustin T. Gibbs 128760a899a0SKenneth D. Merry /* 128860a899a0SKenneth D. Merry * Only one error recovery action 128960a899a0SKenneth D. Merry * at a time. See above. 129060a899a0SKenneth D. Merry */ 129160a899a0SKenneth D. Merry if (periph->flags & 129260a899a0SKenneth D. Merry CAM_PERIPH_RECOVERY_INPROG) { 129360a899a0SKenneth D. Merry error = ERESTART; 129460a899a0SKenneth D. Merry break; 129560a899a0SKenneth D. Merry } 129660a899a0SKenneth D. Merry 129760a899a0SKenneth D. Merry periph->flags |= 129860a899a0SKenneth D. Merry CAM_PERIPH_RECOVERY_INPROG; 129960a899a0SKenneth D. Merry 13008b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 13018b8a9b1dSJustin T. Gibbs retry = 1; 13028b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 13038b8a9b1dSJustin T. Gibbs 13048b8a9b1dSJustin T. Gibbs /* 13058b8a9b1dSJustin T. Gibbs * Check for removable media and 13068b8a9b1dSJustin T. Gibbs * set load/eject flag 13078b8a9b1dSJustin T. Gibbs * appropriately. 13088b8a9b1dSJustin T. Gibbs */ 13098b8a9b1dSJustin T. Gibbs if (SID_IS_REMOVABLE(&cgd.inq_data)) 13108b8a9b1dSJustin T. Gibbs le = TRUE; 13118b8a9b1dSJustin T. Gibbs else 13128b8a9b1dSJustin T. Gibbs le = FALSE; 13138b8a9b1dSJustin T. Gibbs 13148b8a9b1dSJustin T. Gibbs /* 13158b8a9b1dSJustin T. Gibbs * Attempt to start the drive up. 13168b8a9b1dSJustin T. Gibbs * 13178b8a9b1dSJustin T. Gibbs * Save the current ccb so it can 13188b8a9b1dSJustin T. Gibbs * be restored and retried once the 13198b8a9b1dSJustin T. Gibbs * drive is started up. 13208b8a9b1dSJustin T. Gibbs */ 13218b8a9b1dSJustin T. Gibbs bcopy(ccb, save_ccb, sizeof(*save_ccb)); 13228b8a9b1dSJustin T. Gibbs 13238b8a9b1dSJustin T. Gibbs scsi_start_stop(&ccb->csio, 13248b8a9b1dSJustin T. Gibbs /*retries*/1, 13258b8a9b1dSJustin T. Gibbs camperiphdone, 13268b8a9b1dSJustin T. Gibbs MSG_SIMPLE_Q_TAG, 13278b8a9b1dSJustin T. Gibbs /*start*/TRUE, 13288b8a9b1dSJustin T. Gibbs /*load/eject*/le, 13298b8a9b1dSJustin T. Gibbs /*immediate*/FALSE, 13308b8a9b1dSJustin T. Gibbs SSD_FULL_SIZE, 13318b8a9b1dSJustin T. Gibbs /*timeout*/50000); 13328b8a9b1dSJustin T. Gibbs 13338b8a9b1dSJustin T. Gibbs /* release the queue after .5 sec. */ 13348b8a9b1dSJustin T. Gibbs relsim_flags = 13358b8a9b1dSJustin T. Gibbs RELSIM_RELEASE_AFTER_TIMEOUT; 13368b8a9b1dSJustin T. Gibbs timeout = 500; 13378b8a9b1dSJustin T. Gibbs /* 13388b8a9b1dSJustin T. Gibbs * Drop the priority to 0 so that 13398b8a9b1dSJustin T. Gibbs * we are the first to execute. Also 13408b8a9b1dSJustin T. Gibbs * freeze the queue after this command 13418b8a9b1dSJustin T. Gibbs * is sent so that we can restore the 13428b8a9b1dSJustin T. Gibbs * old csio and have it queued in the 13438b8a9b1dSJustin T. Gibbs * proper order before we let normal 13448b8a9b1dSJustin T. Gibbs * transactions go to the drive. 13458b8a9b1dSJustin T. Gibbs */ 13468b8a9b1dSJustin T. Gibbs ccb->ccb_h.pinfo.priority = 0; 13478b8a9b1dSJustin T. Gibbs ccb->ccb_h.flags |= CAM_DEV_QFREEZE; 13488b8a9b1dSJustin T. Gibbs 13498b8a9b1dSJustin T. Gibbs /* 13508b8a9b1dSJustin T. Gibbs * Save a pointer to the original 13518b8a9b1dSJustin T. Gibbs * CCB in the new CCB. 13528b8a9b1dSJustin T. Gibbs */ 13538b8a9b1dSJustin T. Gibbs ccb->ccb_h.saved_ccb_ptr = save_ccb; 13548b8a9b1dSJustin T. Gibbs 13558b8a9b1dSJustin T. Gibbs error = ERESTART; 13568b8a9b1dSJustin T. Gibbs } else { 13578b8a9b1dSJustin T. Gibbs error = scsi_interpret_sense(ccb, 13588b8a9b1dSJustin T. Gibbs sense_flags, 13598b8a9b1dSJustin T. Gibbs &relsim_flags, 13608b8a9b1dSJustin T. Gibbs &openings, 13618b8a9b1dSJustin T. Gibbs &timeout, 13628b8a9b1dSJustin T. Gibbs err_action); 13638b8a9b1dSJustin T. Gibbs } 13648b8a9b1dSJustin T. Gibbs } else if (ccb->csio.scsi_status == 13658b8a9b1dSJustin T. Gibbs SCSI_STATUS_CHECK_COND) { 13668b8a9b1dSJustin T. Gibbs /* no point in decrementing the retry count */ 13678b8a9b1dSJustin T. Gibbs panic("cam_periph_error: scsi status of " 13688b8a9b1dSJustin T. Gibbs "CHECK COND returned but no sense " 13698b8a9b1dSJustin T. Gibbs "information is availible. " 13708b8a9b1dSJustin T. Gibbs "Controller should have returned " 13718b8a9b1dSJustin T. Gibbs "CAM_AUTOSENSE_FAILED"); 13728b8a9b1dSJustin T. Gibbs /* NOTREACHED */ 13738b8a9b1dSJustin T. Gibbs error = EIO; 13748b8a9b1dSJustin T. Gibbs } else if (ccb->ccb_h.retry_count > 0) { 13758b8a9b1dSJustin T. Gibbs /* 13768b8a9b1dSJustin T. Gibbs * XXX KDM shouldn't there be a better 13778b8a9b1dSJustin T. Gibbs * argument to return?? 13788b8a9b1dSJustin T. Gibbs */ 13798b8a9b1dSJustin T. Gibbs error = EIO; 13808b8a9b1dSJustin T. Gibbs } else { 13818b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 13828b8a9b1dSJustin T. Gibbs retry = ccb->ccb_h.retry_count > 0; 13838b8a9b1dSJustin T. Gibbs if (retry) 13848b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 13858b8a9b1dSJustin T. Gibbs /* 13868b8a9b1dSJustin T. Gibbs * If it was aborted with no 13878b8a9b1dSJustin T. Gibbs * clue as to the reason, just 13888b8a9b1dSJustin T. Gibbs * retry it again. 13898b8a9b1dSJustin T. Gibbs */ 13908b8a9b1dSJustin T. Gibbs error = ERESTART; 13918b8a9b1dSJustin T. Gibbs } 13928b8a9b1dSJustin T. Gibbs break; 13938b8a9b1dSJustin T. Gibbs case SCSI_STATUS_QUEUE_FULL: 13948b8a9b1dSJustin T. Gibbs { 13958b8a9b1dSJustin T. Gibbs /* no decrement */ 13968b8a9b1dSJustin T. Gibbs struct ccb_getdev cgd; 13978b8a9b1dSJustin T. Gibbs 13988b8a9b1dSJustin T. Gibbs /* 13998b8a9b1dSJustin T. Gibbs * First off, find out what the current 14008b8a9b1dSJustin T. Gibbs * transaction counts are. 14018b8a9b1dSJustin T. Gibbs */ 14028b8a9b1dSJustin T. Gibbs xpt_setup_ccb(&cgd.ccb_h, 14038b8a9b1dSJustin T. Gibbs ccb->ccb_h.path, 14048b8a9b1dSJustin T. Gibbs /*priority*/1); 14058b8a9b1dSJustin T. Gibbs cgd.ccb_h.func_code = XPT_GDEV_TYPE; 14068b8a9b1dSJustin T. Gibbs xpt_action((union ccb *)&cgd); 14078b8a9b1dSJustin T. Gibbs 14088b8a9b1dSJustin T. Gibbs /* 14098b8a9b1dSJustin T. Gibbs * If we were the only transaction active, treat 14108b8a9b1dSJustin T. Gibbs * the QUEUE FULL as if it were a BUSY condition. 14118b8a9b1dSJustin T. Gibbs */ 14128b8a9b1dSJustin T. Gibbs if (cgd.dev_active != 0) { 14138b8a9b1dSJustin T. Gibbs /* 14148b8a9b1dSJustin T. Gibbs * Reduce the number of openings to 14158b8a9b1dSJustin T. Gibbs * be 1 less than the amount it took 14168b8a9b1dSJustin T. Gibbs * to get a queue full bounded by the 14178b8a9b1dSJustin T. Gibbs * minimum allowed tag count for this 14188b8a9b1dSJustin T. Gibbs * device. 14198b8a9b1dSJustin T. Gibbs */ 14208b8a9b1dSJustin T. Gibbs openings = cgd.dev_active; 14218b8a9b1dSJustin T. Gibbs if (openings < cgd.mintags) 14228b8a9b1dSJustin T. Gibbs openings = cgd.mintags; 14238b8a9b1dSJustin T. Gibbs if (openings < cgd.dev_active+cgd.dev_openings) 14248b8a9b1dSJustin T. Gibbs relsim_flags = RELSIM_ADJUST_OPENINGS; 14258b8a9b1dSJustin T. Gibbs else { 14268b8a9b1dSJustin T. Gibbs /* 14278b8a9b1dSJustin T. Gibbs * Some devices report queue full for 14288b8a9b1dSJustin T. Gibbs * temporary resource shortages. For 14298b8a9b1dSJustin T. Gibbs * this reason, we allow a minimum 14308b8a9b1dSJustin T. Gibbs * tag count to be entered via a 14318b8a9b1dSJustin T. Gibbs * quirk entry to prevent the queue 14328b8a9b1dSJustin T. Gibbs * count on these devices from falling 14338b8a9b1dSJustin T. Gibbs * to a pessimisticly low value. We 14348b8a9b1dSJustin T. Gibbs * still wait for the next successful 14358b8a9b1dSJustin T. Gibbs * completion, however, before queueing 14368b8a9b1dSJustin T. Gibbs * more transactions to the device. 14378b8a9b1dSJustin T. Gibbs */ 14388b8a9b1dSJustin T. Gibbs relsim_flags = 14398b8a9b1dSJustin T. Gibbs RELSIM_RELEASE_AFTER_CMDCMPLT; 14408b8a9b1dSJustin T. Gibbs } 14418b8a9b1dSJustin T. Gibbs timeout = 0; 14428b8a9b1dSJustin T. Gibbs error = ERESTART; 14438b8a9b1dSJustin T. Gibbs break; 14448b8a9b1dSJustin T. Gibbs } 14458b8a9b1dSJustin T. Gibbs /* FALLTHROUGH */ 14468b8a9b1dSJustin T. Gibbs } 14478b8a9b1dSJustin T. Gibbs case SCSI_STATUS_BUSY: 14488b8a9b1dSJustin T. Gibbs /* 14498b8a9b1dSJustin T. Gibbs * Restart the queue after either another 14508b8a9b1dSJustin T. Gibbs * command completes or a 1 second timeout. 14518b8a9b1dSJustin T. Gibbs */ 14528b8a9b1dSJustin T. Gibbs /* 14538b8a9b1dSJustin T. Gibbs * XXX KDM ask JTG about this again, do we need to 14548b8a9b1dSJustin T. Gibbs * be looking at the retry count here? 14558b8a9b1dSJustin T. Gibbs */ 14568b8a9b1dSJustin T. Gibbs error = ERESTART; 14578b8a9b1dSJustin T. Gibbs relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT 14588b8a9b1dSJustin T. Gibbs | RELSIM_RELEASE_AFTER_CMDCMPLT; 14598b8a9b1dSJustin T. Gibbs timeout = 1000; 14608b8a9b1dSJustin T. Gibbs break; 14618b8a9b1dSJustin T. Gibbs case SCSI_STATUS_RESERV_CONFLICT: 14628b8a9b1dSJustin T. Gibbs error = EIO; 14638b8a9b1dSJustin T. Gibbs break; 14648b8a9b1dSJustin T. Gibbs default: 14658b8a9b1dSJustin T. Gibbs error = EIO; 14668b8a9b1dSJustin T. Gibbs break; 14678b8a9b1dSJustin T. Gibbs } 14688b8a9b1dSJustin T. Gibbs break; 14698b8a9b1dSJustin T. Gibbs case CAM_REQ_CMP_ERR: 14708b8a9b1dSJustin T. Gibbs case CAM_AUTOSENSE_FAIL: 14718b8a9b1dSJustin T. Gibbs case CAM_CMD_TIMEOUT: 14728b8a9b1dSJustin T. Gibbs case CAM_UNEXP_BUSFREE: 14738b8a9b1dSJustin T. Gibbs case CAM_UNCOR_PARITY: 14748b8a9b1dSJustin T. Gibbs case CAM_DATA_RUN_ERR: 14758b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 14768b8a9b1dSJustin T. Gibbs retry = ccb->ccb_h.retry_count > 0; 14778b8a9b1dSJustin T. Gibbs if (retry) { 14788b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 14798b8a9b1dSJustin T. Gibbs error = ERESTART; 14808b8a9b1dSJustin T. Gibbs } else { 14818b8a9b1dSJustin T. Gibbs error = EIO; 14828b8a9b1dSJustin T. Gibbs } 14838b8a9b1dSJustin T. Gibbs break; 14848b8a9b1dSJustin T. Gibbs case CAM_UA_ABORT: 14858b8a9b1dSJustin T. Gibbs case CAM_UA_TERMIO: 14868b8a9b1dSJustin T. Gibbs case CAM_MSG_REJECT_REC: 14878b8a9b1dSJustin T. Gibbs /* XXX Don't know that these are correct */ 14888b8a9b1dSJustin T. Gibbs error = EIO; 14898b8a9b1dSJustin T. Gibbs break; 14908b8a9b1dSJustin T. Gibbs case CAM_SEL_TIMEOUT: 14918b8a9b1dSJustin T. Gibbs { 1492e471e974SJustin T. Gibbs /* 1493e471e974SJustin T. Gibbs * XXX 1494e471e974SJustin T. Gibbs * A single selection timeout should not be enough 1495e471e974SJustin T. Gibbs * to invalidate a device. We should retry for multiple 1496e471e974SJustin T. Gibbs * seconds assuming this isn't a probe. We'll probably 1497e471e974SJustin T. Gibbs * need a special flag for that. 1498e471e974SJustin T. Gibbs */ 1499e471e974SJustin T. Gibbs #if 0 15008b8a9b1dSJustin T. Gibbs struct cam_path *newpath; 15018b8a9b1dSJustin T. Gibbs 15028b8a9b1dSJustin T. Gibbs /* Should we do more if we can't create the path?? */ 15038b8a9b1dSJustin T. Gibbs if (xpt_create_path(&newpath, xpt_path_periph(ccb->ccb_h.path), 15048b8a9b1dSJustin T. Gibbs xpt_path_path_id(ccb->ccb_h.path), 15058b8a9b1dSJustin T. Gibbs xpt_path_target_id(ccb->ccb_h.path), 15068b8a9b1dSJustin T. Gibbs CAM_LUN_WILDCARD) != CAM_REQ_CMP) 15078b8a9b1dSJustin T. Gibbs break; 15088b8a9b1dSJustin T. Gibbs /* 15098b8a9b1dSJustin T. Gibbs * Let peripheral drivers know that this device has gone 15108b8a9b1dSJustin T. Gibbs * away. 15118b8a9b1dSJustin T. Gibbs */ 15128b8a9b1dSJustin T. Gibbs xpt_async(AC_LOST_DEVICE, newpath, NULL); 15138b8a9b1dSJustin T. Gibbs xpt_free_path(newpath); 1514e471e974SJustin T. Gibbs #endif 1515e471e974SJustin T. Gibbs error = ENXIO; 15168b8a9b1dSJustin T. Gibbs break; 15178b8a9b1dSJustin T. Gibbs } 15188b8a9b1dSJustin T. Gibbs case CAM_REQ_INVALID: 15198b8a9b1dSJustin T. Gibbs case CAM_PATH_INVALID: 15208b8a9b1dSJustin T. Gibbs case CAM_DEV_NOT_THERE: 15218b8a9b1dSJustin T. Gibbs case CAM_NO_HBA: 15228b8a9b1dSJustin T. Gibbs case CAM_PROVIDE_FAIL: 15238b8a9b1dSJustin T. Gibbs case CAM_REQ_TOO_BIG: 15248b8a9b1dSJustin T. Gibbs error = EINVAL; 15258b8a9b1dSJustin T. Gibbs break; 15268b8a9b1dSJustin T. Gibbs case CAM_SCSI_BUS_RESET: 15278b8a9b1dSJustin T. Gibbs case CAM_BDR_SENT: 15288b8a9b1dSJustin T. Gibbs case CAM_REQUEUE_REQ: 15298b8a9b1dSJustin T. Gibbs /* Unconditional requeue, dammit */ 15308b8a9b1dSJustin T. Gibbs error = ERESTART; 15318b8a9b1dSJustin T. Gibbs break; 15328b8a9b1dSJustin T. Gibbs case CAM_RESRC_UNAVAIL: 15338b8a9b1dSJustin T. Gibbs case CAM_BUSY: 15348b8a9b1dSJustin T. Gibbs /* timeout??? */ 15358b8a9b1dSJustin T. Gibbs default: 15368b8a9b1dSJustin T. Gibbs /* decrement the number of retries */ 15378b8a9b1dSJustin T. Gibbs retry = ccb->ccb_h.retry_count > 0; 15388b8a9b1dSJustin T. Gibbs if (retry) { 15398b8a9b1dSJustin T. Gibbs ccb->ccb_h.retry_count--; 15408b8a9b1dSJustin T. Gibbs error = ERESTART; 15418b8a9b1dSJustin T. Gibbs } else { 15428b8a9b1dSJustin T. Gibbs /* Check the sense codes */ 15438b8a9b1dSJustin T. Gibbs error = EIO; 15448b8a9b1dSJustin T. Gibbs } 15458b8a9b1dSJustin T. Gibbs break; 15468b8a9b1dSJustin T. Gibbs } 15478b8a9b1dSJustin T. Gibbs 15488b8a9b1dSJustin T. Gibbs /* Attempt a retry */ 15498b8a9b1dSJustin T. Gibbs if (error == ERESTART || error == 0) { 15508b8a9b1dSJustin T. Gibbs if (frozen != 0) 15518b8a9b1dSJustin T. Gibbs ccb->ccb_h.status &= ~CAM_DEV_QFRZN; 15528b8a9b1dSJustin T. Gibbs 15538b8a9b1dSJustin T. Gibbs if (error == ERESTART) 15548b8a9b1dSJustin T. Gibbs xpt_action(ccb); 15558b8a9b1dSJustin T. Gibbs 15568b8a9b1dSJustin T. Gibbs if (frozen != 0) { 15578b8a9b1dSJustin T. Gibbs cam_release_devq(ccb->ccb_h.path, 15588b8a9b1dSJustin T. Gibbs relsim_flags, 15598b8a9b1dSJustin T. Gibbs openings, 15608b8a9b1dSJustin T. Gibbs timeout, 15618b8a9b1dSJustin T. Gibbs /*getcount_only*/0); 15628b8a9b1dSJustin T. Gibbs } 15638b8a9b1dSJustin T. Gibbs } 15648b8a9b1dSJustin T. Gibbs 15658b8a9b1dSJustin T. Gibbs 15668b8a9b1dSJustin T. Gibbs return (error); 15678b8a9b1dSJustin T. Gibbs } 1568