1 /* 2 * Copyright 2004-2011, Haiku, Inc. All RightsReserved. 3 * Copyright 2002-2003, Thomas Kurschel. All rights reserved. 4 * 5 * Distributed under the terms of the MIT License. 6 */ 7 8 9 //! Handling of removable media. 10 11 12 #include "scsi_periph_int.h" 13 14 #include <string.h> 15 16 17 void 18 periph_media_changed(scsi_periph_device_info *device, scsi_ccb *request) 19 { 20 uint32 backup_flags; 21 uint8 backup_cdb[SCSI_MAX_CDB_SIZE]; 22 uchar backup_cdb_len; 23 int64 backup_sort; 24 bigtime_t backup_timeout; 25 uchar *backup_data; 26 const physical_entry *backup_sg_list; 27 uint16 backup_sg_count; 28 uint32 backup_data_len; 29 30 // if there is no hook, the driver doesn't handle removal devices 31 if (!device->removable) { 32 SHOW_ERROR0( 1, "Driver doesn't support medium changes, but there occured one!?" ); 33 return; 34 } 35 36 // when medium has changed, tell all handles 37 periph_media_changed_public(device); 38 39 // the peripheral driver may need a fresh ccb; sadly, we cannot allocate one 40 // as this may lead to a deadlock if all ccb are in use already; thus, we 41 // have to backup all relevant data of current ccb and use it instead of a 42 // new one - not pretty but working (and performance is not an issue in this 43 // path) 44 backup_flags = request->flags; 45 memcpy(backup_cdb, request->cdb, SCSI_MAX_CDB_SIZE); 46 backup_cdb_len = request->cdb_length; 47 backup_sort = request->sort; 48 backup_timeout = request->timeout; 49 backup_data = request->data; 50 backup_sg_list = request->sg_list; 51 backup_sg_count = request->sg_count; 52 backup_data_len = request->data_length; 53 54 if (device->callbacks->media_changed != NULL) 55 device->callbacks->media_changed(device->periph_device, request); 56 57 request->flags = backup_flags; 58 memcpy(request->cdb, backup_cdb, SCSI_MAX_CDB_SIZE); 59 request->cdb_length = backup_cdb_len; 60 request->sort = backup_sort; 61 request->timeout = backup_timeout; 62 request->data = backup_data; 63 request->sg_list = backup_sg_list; 64 request->sg_count = backup_sg_count; 65 request->data_length = backup_data_len; 66 } 67 68 69 void 70 periph_media_changed_public(scsi_periph_device_info *device) 71 { 72 scsi_periph_handle_info *handle; 73 74 mutex_lock(&device->mutex); 75 76 // when medium has changed, tell all handles 77 // (this must be atomic for each handle!) 78 for (handle = device->handles; handle; handle = handle->next) 79 handle->pending_error = B_DEV_MEDIA_CHANGED; 80 81 mutex_unlock(&device->mutex); 82 } 83 84 85 /** send TUR */ 86 87 static err_res 88 send_tur(scsi_periph_device_info *device, scsi_ccb *request) 89 { 90 scsi_cmd_tur *cmd = (scsi_cmd_tur *)request->cdb; 91 92 request->flags = SCSI_DIR_NONE | SCSI_ORDERED_QTAG; 93 94 request->data = NULL; 95 request->sg_list = NULL; 96 request->data_length = 0; 97 request->timeout = device->std_timeout; 98 request->sort = -1; 99 request->sg_list = NULL; 100 101 memset(cmd, 0, sizeof(*cmd)); 102 cmd->opcode = SCSI_OP_TEST_UNIT_READY; 103 104 request->cdb_length = sizeof(*cmd); 105 106 device->scsi->sync_io(request); 107 108 return periph_check_error(device, request); 109 } 110 111 112 /** wait until device is ready */ 113 114 static err_res 115 wait_for_ready(scsi_periph_device_info *device, scsi_ccb *request) 116 { 117 int retries = 0; 118 119 while (true) { 120 err_res res; 121 122 // we send TURs until the device is OK or all hope is lost 123 res = send_tur(device, request); 124 125 switch (res.action) { 126 case err_act_ok: 127 return MK_ERROR(err_act_ok, B_OK); 128 129 case err_act_retry: 130 if (++retries >= 3) 131 return MK_ERROR(err_act_fail, res.error_code); 132 break; 133 134 case err_act_many_retries: 135 if (++retries >= 30) 136 return MK_ERROR(err_act_fail, res.error_code); 137 break; 138 139 default: 140 SHOW_FLOW( 3, "action: %x, error: %x", (int)res.action, (int)res.error_code); 141 return res; 142 } 143 } 144 } 145 146 147 status_t 148 periph_get_media_status(scsi_periph_handle_info *handle) 149 { 150 scsi_periph_device_info *device = handle->device; 151 scsi_ccb *request; 152 err_res res; 153 status_t err; 154 155 mutex_lock(&device->mutex); 156 157 // removal requests are returned to exactly one handle 158 // (no real problem, as noone check medias status "by mistake") 159 if (device->removal_requested) { 160 device->removal_requested = false; 161 err = B_DEV_MEDIA_CHANGE_REQUESTED; 162 goto err; 163 } 164 165 // if there is a pending error (read: media has changed), return once per handle 166 err = handle->pending_error; 167 if (err != B_OK) { 168 handle->pending_error = B_OK; 169 goto err; 170 } 171 172 SHOW_FLOW0( 3, "" ); 173 174 mutex_unlock(&device->mutex); 175 176 // finally, ask the device itself 177 178 request = device->scsi->alloc_ccb(device->scsi_device); 179 if (request == NULL) 180 return B_NO_MEMORY; 181 182 res = wait_for_ready(device, request); 183 184 device->scsi->free_ccb(request); 185 186 SHOW_FLOW(3, "error_code: %x", (int)res.error_code); 187 188 return res.error_code; 189 190 err: 191 mutex_unlock(&device->mutex); 192 return err; 193 } 194 195 196 /*! Send START/STOP command to device 197 start - true for start, false for stop 198 withLoadEject - if true, then lock drive on start and eject on stop 199 */ 200 err_res 201 periph_send_start_stop(scsi_periph_device_info *device, scsi_ccb *request, 202 bool start, bool withLoadEject) 203 { 204 scsi_cmd_ssu *cmd = (scsi_cmd_ssu *)request->cdb; 205 206 // this must be ordered, so all previous commands are really finished 207 request->flags = SCSI_DIR_NONE | SCSI_ORDERED_QTAG; 208 209 request->data = NULL; 210 request->sg_list = NULL; 211 request->data_length = 0; 212 request->timeout = device->std_timeout; 213 request->sort = -1; 214 request->sg_list = NULL; 215 216 memset(cmd, 0, sizeof(*cmd)); 217 cmd->opcode = SCSI_OP_START_STOP; 218 // we don't want to poll; we give a long timeout instead 219 // (well - the default timeout _is_ large) 220 cmd->immediately = 0; 221 cmd->start = start; 222 cmd->load_eject = withLoadEject; 223 224 request->cdb_length = sizeof(*cmd); 225 226 device->scsi->sync_io(request); 227 228 return periph_check_error(device, request); 229 } 230