1/* 2** Copyright 2007, Marcus Overhagen. All rights reserved. 3** Copyright 2002/03, Thomas Kurschel. All rights reserved. 4** Distributed under the terms of the Haiku License. 5*/ 6 7/* 8 Part of Open IDE bus manager 9 10 Device manager 11 12 As the IDE bus manager is an SCSI to IDE translater, it 13 has to know a bit more about connected devices then a standard 14 SIM. This file contains device detection and classification. 15*/ 16 17#include "ide_internal.h" 18#include "ide_sim.h" 19#include "ide_cmds.h" 20 21#include <string.h> 22#include <malloc.h> 23#include <ByteOrder.h> 24 25#define TRACE(x...) dprintf("IDE: " x) 26 27/** cleanup links devices on one bus when <device> is deleted */ 28 29static void 30cleanup_device_links(ide_device_info *device) 31{ 32 ide_bus_info *bus = device->bus; 33 34 TRACE("cleanup_device_links: device %p\n", device); 35 36 bus->devices[device->is_device1] = NULL; 37 38 if (device->other_device) { 39 if (device->other_device != device) { 40 device->other_device->other_device = device->other_device; 41 bus->first_device = device->other_device; 42 } else 43 bus->first_device = NULL; 44 } 45 46 device->other_device = NULL; 47} 48 49 50/** destroy device info */ 51 52static void 53destroy_device(ide_device_info *device) 54{ 55 TRACE("destroy_device: device %p\n", device); 56 57 // paranoia 58 device->exec_io = NULL; 59 cancel_timer(&device->reconnect_timer.te); 60 61 scsi->free_dpc(device->reconnect_timeout_dpc); 62 63 cleanup_device_links(device); 64 65 destroy_qreq_array(device); 66 67 uninit_synced_pc(&device->reconnect_timeout_synced_pc); 68 69 free(device); 70} 71 72 73/** setup links between the devices on one bus */ 74 75static void 76setup_device_links(ide_bus_info *bus, ide_device_info *device) 77{ 78 TRACE("setup_device_links: bus %p, device %p\n", bus, device); 79 80 device->bus = bus; 81 bus->devices[device->is_device1] = device; 82 83 device->other_device = device; 84 85 if (device->is_device1) { 86 if (bus->devices[0]) { 87 device->other_device = bus->devices[0]; 88 bus->devices[0]->other_device = device; 89 } 90 } else { 91 if (bus->devices[1]) { 92 device->other_device = bus->devices[1]; 93 bus->devices[1]->other_device = device; 94 } 95 } 96 97 if (bus->first_device == NULL) 98 bus->first_device = device; 99} 100 101 102/** create device info */ 103 104static ide_device_info * 105create_device(ide_bus_info *bus, bool is_device1) 106{ 107 ide_device_info *device; 108 109 TRACE("create_device: bus %p, device-number %d\n", bus, is_device1); 110 111 device = (ide_device_info *)malloc(sizeof(*device)); 112 if (device == NULL) 113 return NULL; 114 115 memset(device, 0, sizeof(*device)); 116 117 device->is_device1 = is_device1; 118 device->target_id = is_device1; 119 120 setup_device_links(bus, device); 121 122 device->DMA_failures = 0; 123 device->CQ_failures = 0; 124 device->num_failed_send = 0; 125 126 device->combined_sense = 0; 127 128 device->num_running_reqs = 0; 129 130 device->reconnect_timer.device = device; 131 132 init_synced_pc(&device->reconnect_timeout_synced_pc, 133 reconnect_timeout_worker); 134 135 if (scsi->alloc_dpc(&device->reconnect_timeout_dpc) != B_OK) 136 goto err; 137 138 device->total_sectors = 0; 139 return device; 140 141err: 142 destroy_device(device); 143 return NULL; 144} 145 146#if B_HOST_IS_LENDIAN 147 148#define B_BENDIAN_TO_HOST_MULTI(v, n) do { \ 149 size_t __swap16_multi_n = (n); \ 150 uint16 *__swap16_multi_v = (v); \ 151 \ 152 while( __swap16_multi_n ) { \ 153 *__swap16_multi_v = B_SWAP_INT16(*__swap16_multi_v); \ 154 __swap16_multi_v++; \ 155 __swap16_multi_n--; \ 156 } \ 157} while (0) 158 159#else 160 161#define B_BENDIAN_TO_HOST_MULTI(v, n) 162 163#endif 164 165 166/** prepare infoblock for further use, i.e. fix endianess */ 167 168static void 169prep_infoblock(ide_device_info *device) 170{ 171 ide_device_infoblock *infoblock = &device->infoblock; 172 173 B_BENDIAN_TO_HOST_MULTI((uint16 *)infoblock->serial_number, 174 sizeof(infoblock->serial_number) / 2); 175 176 B_BENDIAN_TO_HOST_MULTI( (uint16 *)infoblock->firmware_version, 177 sizeof(infoblock->firmware_version) / 2); 178 179 B_BENDIAN_TO_HOST_MULTI( (uint16 *)infoblock->model_number, 180 sizeof(infoblock->model_number) / 2); 181 182 infoblock->LBA_total_sectors = B_LENDIAN_TO_HOST_INT32(infoblock->LBA_total_sectors); 183 infoblock->LBA48_total_sectors = B_LENDIAN_TO_HOST_INT64(infoblock->LBA48_total_sectors); 184} 185 186 187/** read info block of ATA or ATAPI device */ 188 189static bool 190scan_device_int(ide_device_info *device, bool atapi) 191{ 192 ide_bus_info *bus = device->bus; 193 int status; 194 195 TRACE("scan_device_int: device %p, atapi %d\n", device, atapi); 196 197 device->tf_param_mask = 0; 198 device->tf.write.command = atapi ? IDE_CMD_IDENTIFY_PACKET_DEVICE 199 : IDE_CMD_IDENTIFY_DEVICE; 200 201 // initialize device selection flags, 202 // this is the only place where this bit gets initialized in the task file 203 if (bus->controller->read_command_block_regs(bus->channel_cookie, &device->tf, 204 ide_mask_device_head) != B_OK) { 205 TRACE("scan_device_int: read_command_block_regs failed\n"); 206 return false; 207 } 208 209 device->tf.lba.device = device->is_device1; 210 211 if (!send_command(device, NULL, atapi ? false : true, 20, ide_state_sync_waiting)) { 212 TRACE("scan_device_int: send_command failed\n"); 213 return false; 214 } 215 216 // do a short wait first - if there's no device at all we could wait forever 217 // ToDo: have a look at this; if it times out (when the time is too short), 218 // the kernel seems to crash a little later)! 219 TRACE("scan_device_int: waiting 100ms...\n"); 220 if (acquire_sem_etc(bus->sync_wait_sem, 1, B_RELATIVE_TIMEOUT, 100000) == B_TIMED_OUT) { 221 bool cont; 222 223 TRACE("scan_device_int: no fast response to inquiry\n"); 224 225 // check the busy flag - if it's still set, there's probably no device 226 IDE_LOCK(bus); 227 228 status = bus->controller->get_altstatus(bus->channel_cookie); 229 cont = (status & ide_status_bsy) == ide_status_bsy; 230 231 IDE_UNLOCK(bus); 232 233 TRACE("scan_device_int: status %#04x\n", status); 234 235 if (!cont) { 236 TRACE("scan_device_int: busy bit not set after 100ms - probably noone there\n"); 237 // no reaction -> abort waiting 238 cancel_irq_timeout(bus); 239 240 // timeout or irq may have been fired, reset semaphore just is case 241 acquire_sem_etc(bus->sync_wait_sem, 1, B_RELATIVE_TIMEOUT, 0); 242 243 TRACE("scan_device_int: aborting because busy bit not set\n"); 244 return false; 245 } 246 247 TRACE("scan_device_int: busy bit set, give device more time\n"); 248 249 // there is something, so wait for it 250 acquire_sem(bus->sync_wait_sem); 251 } 252 TRACE("scan_device_int: got a fast response\n"); 253 254 // cancel the timeout manually; usually this is done by wait_for_sync(), but 255 // we've used the wait semaphore directly 256 cancel_irq_timeout(bus); 257 258 if (bus->sync_wait_timeout) { 259 TRACE("scan_device_int: aborting on sync_wait_timeout\n"); 260 return false; 261 } 262 263 ide_wait(device, ide_status_drq, ide_status_bsy, true, 1000); 264 265 status = bus->controller->get_altstatus(bus->channel_cookie); 266 267 if ((status & ide_status_err) != 0) { 268 // if there's no device, all bits including the error bit are set 269 TRACE("scan_device_int: error bit set - no device or wrong type (status: %#04x)\n", status); 270 return false; 271 } 272 273 // get the infoblock 274 bus->controller->read_pio(bus->channel_cookie, (uint16 *)&device->infoblock, 275 sizeof(device->infoblock) / sizeof(uint16), false); 276 277 if (!wait_for_drqdown(device)) { 278 TRACE("scan_device_int: wait_for_drqdown failed\n"); 279 return false; 280 } 281 282 TRACE("scan_device_int: device found\n"); 283 284 prep_infoblock(device); 285 return true; 286} 287 288 289/** scan one device */ 290 291void 292scan_device_worker(ide_bus_info *bus, void *arg) 293{ 294 int is_device1 = (int)arg; 295 ide_device_info *device; 296 297 TRACE("scan_device_worker: bus %p, device-number %d\n", bus, is_device1); 298 299 // forget everything we know about the device; 300 // don't care about peripheral drivers using this device 301 // as the device info is only used by us and not published 302 // directly or indirectly to the SCSI bus manager 303 if (bus->devices[is_device1]) 304 destroy_device(bus->devices[is_device1]); 305 306 device = create_device(bus, is_device1); 307 308 // reset status so we can see what goes wrong during detection 309 device->subsys_status = SCSI_REQ_CMP; 310 311 if (scan_device_int(device, false)) { 312 if (!prep_ata(device)) 313 goto err; 314 } else if (device->subsys_status != SCSI_TID_INVALID 315 && scan_device_int(device, true)) { 316 // only try ATAPI if there is at least some device 317 // (see send_command - this error code must be unique!) 318 if (!prep_atapi(device)) 319 goto err; 320 } else 321 goto err; 322 323 bus->state = ide_state_idle; 324 release_sem(bus->scan_device_sem); 325 return; 326 327err: 328 destroy_device(device); 329 330 bus->state = ide_state_idle; 331 release_sem(bus->scan_device_sem); 332} 333