BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / bus_managers / ide / devices.c
blobc9464fc146034ceef3de699cf63cda243e4139da
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 */
7 /*
8 Part of Open IDE bus manager
10 Device manager
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.
17 #include "ide_internal.h"
18 #include "ide_sim.h"
19 #include "ide_cmds.h"
21 #include <string.h>
22 #include <malloc.h>
23 #include <ByteOrder.h>
25 #define TRACE(x...) dprintf("IDE: " x)
27 /** cleanup links devices on one bus when <device> is deleted */
29 static void
30 cleanup_device_links(ide_device_info *device)
32 ide_bus_info *bus = device->bus;
34 TRACE("cleanup_device_links: device %p\n", device);
36 bus->devices[device->is_device1] = NULL;
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;
46 device->other_device = NULL;
50 /** destroy device info */
52 static void
53 destroy_device(ide_device_info *device)
55 TRACE("destroy_device: device %p\n", device);
57 // paranoia
58 device->exec_io = NULL;
59 cancel_timer(&device->reconnect_timer.te);
61 scsi->free_dpc(device->reconnect_timeout_dpc);
63 cleanup_device_links(device);
65 destroy_qreq_array(device);
67 uninit_synced_pc(&device->reconnect_timeout_synced_pc);
69 free(device);
73 /** setup links between the devices on one bus */
75 static void
76 setup_device_links(ide_bus_info *bus, ide_device_info *device)
78 TRACE("setup_device_links: bus %p, device %p\n", bus, device);
80 device->bus = bus;
81 bus->devices[device->is_device1] = device;
83 device->other_device = device;
85 if (device->is_device1) {
86 if (bus->devices[0]) {
87 device->other_device = bus->devices[0];
88 bus->devices[0]->other_device = device;
90 } else {
91 if (bus->devices[1]) {
92 device->other_device = bus->devices[1];
93 bus->devices[1]->other_device = device;
97 if (bus->first_device == NULL)
98 bus->first_device = device;
102 /** create device info */
104 static ide_device_info *
105 create_device(ide_bus_info *bus, bool is_device1)
107 ide_device_info *device;
109 TRACE("create_device: bus %p, device-number %d\n", bus, is_device1);
111 device = (ide_device_info *)malloc(sizeof(*device));
112 if (device == NULL)
113 return NULL;
115 memset(device, 0, sizeof(*device));
117 device->is_device1 = is_device1;
118 device->target_id = is_device1;
120 setup_device_links(bus, device);
122 device->DMA_failures = 0;
123 device->CQ_failures = 0;
124 device->num_failed_send = 0;
126 device->combined_sense = 0;
128 device->num_running_reqs = 0;
130 device->reconnect_timer.device = device;
132 init_synced_pc(&device->reconnect_timeout_synced_pc,
133 reconnect_timeout_worker);
135 if (scsi->alloc_dpc(&device->reconnect_timeout_dpc) != B_OK)
136 goto err;
138 device->total_sectors = 0;
139 return device;
141 err:
142 destroy_device(device);
143 return NULL;
146 #if B_HOST_IS_LENDIAN
148 #define B_BENDIAN_TO_HOST_MULTI(v, n) do { \
149 size_t __swap16_multi_n = (n); \
150 uint16 *__swap16_multi_v = (v); \
152 while( __swap16_multi_n ) { \
153 *__swap16_multi_v = B_SWAP_INT16(*__swap16_multi_v); \
154 __swap16_multi_v++; \
155 __swap16_multi_n--; \
157 } while (0)
159 #else
161 #define B_BENDIAN_TO_HOST_MULTI(v, n)
163 #endif
166 /** prepare infoblock for further use, i.e. fix endianess */
168 static void
169 prep_infoblock(ide_device_info *device)
171 ide_device_infoblock *infoblock = &device->infoblock;
173 B_BENDIAN_TO_HOST_MULTI((uint16 *)infoblock->serial_number,
174 sizeof(infoblock->serial_number) / 2);
176 B_BENDIAN_TO_HOST_MULTI( (uint16 *)infoblock->firmware_version,
177 sizeof(infoblock->firmware_version) / 2);
179 B_BENDIAN_TO_HOST_MULTI( (uint16 *)infoblock->model_number,
180 sizeof(infoblock->model_number) / 2);
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);
187 /** read info block of ATA or ATAPI device */
189 static bool
190 scan_device_int(ide_device_info *device, bool atapi)
192 ide_bus_info *bus = device->bus;
193 int status;
195 TRACE("scan_device_int: device %p, atapi %d\n", device, atapi);
197 device->tf_param_mask = 0;
198 device->tf.write.command = atapi ? IDE_CMD_IDENTIFY_PACKET_DEVICE
199 : IDE_CMD_IDENTIFY_DEVICE;
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;
209 device->tf.lba.device = device->is_device1;
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;
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;
223 TRACE("scan_device_int: no fast response to inquiry\n");
225 // check the busy flag - if it's still set, there's probably no device
226 IDE_LOCK(bus);
228 status = bus->controller->get_altstatus(bus->channel_cookie);
229 cont = (status & ide_status_bsy) == ide_status_bsy;
231 IDE_UNLOCK(bus);
233 TRACE("scan_device_int: status %#04x\n", status);
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);
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);
243 TRACE("scan_device_int: aborting because busy bit not set\n");
244 return false;
247 TRACE("scan_device_int: busy bit set, give device more time\n");
249 // there is something, so wait for it
250 acquire_sem(bus->sync_wait_sem);
252 TRACE("scan_device_int: got a fast response\n");
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);
258 if (bus->sync_wait_timeout) {
259 TRACE("scan_device_int: aborting on sync_wait_timeout\n");
260 return false;
263 ide_wait(device, ide_status_drq, ide_status_bsy, true, 1000);
265 status = bus->controller->get_altstatus(bus->channel_cookie);
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;
273 // get the infoblock
274 bus->controller->read_pio(bus->channel_cookie, (uint16 *)&device->infoblock,
275 sizeof(device->infoblock) / sizeof(uint16), false);
277 if (!wait_for_drqdown(device)) {
278 TRACE("scan_device_int: wait_for_drqdown failed\n");
279 return false;
282 TRACE("scan_device_int: device found\n");
284 prep_infoblock(device);
285 return true;
289 /** scan one device */
291 void
292 scan_device_worker(ide_bus_info *bus, void *arg)
294 int is_device1 = (int)arg;
295 ide_device_info *device;
297 TRACE("scan_device_worker: bus %p, device-number %d\n", bus, is_device1);
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]);
306 device = create_device(bus, is_device1);
308 // reset status so we can see what goes wrong during detection
309 device->subsys_status = SCSI_REQ_CMP;
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;
323 bus->state = ide_state_idle;
324 release_sem(bus->scan_device_sem);
325 return;
327 err:
328 destroy_device(device);
330 bus->state = ide_state_idle;
331 release_sem(bus->scan_device_sem);