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