BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / bus_managers / ide / pio.cpp
blob672cd9bce9199c226214ffa1af1f2e070e7aeb5a
1 /*
2 * Copyright 2004-2011, Haiku, Inc. All rights reserved.
3 * Copyright 2002-2004, Thomas Kurschel. All rights reserved.
5 * Distributed under the terms of the MIT License.
6 */
8 /*
9 PIO data transmission
11 This file is more difficult then you might expect as the SCSI system
12 uses physical addresses everywhere which have to be mapped into
13 virtual address space during transmission. Additionally, during ATAPI
14 commands we may have to transmit more data then exist because the
15 data len specified by the command doesn't need to be the same as
16 of the data buffer provided.
18 The handling of S/G entries of odd size may look superfluous as the
19 SCSI bus manager can take care of that. In general, this would be possible
20 as most controllers need even alignment for DMA as well, but some can
21 handle _any_ S/G list and it wouldn't be sensitive to enforce stricter
22 alignement just for some rare PIO transmissions.
24 Little hint for the meaning of "transferred": this is the number of bytes
25 sent over the bus. For read-transmissions, this may be one more then copied
26 into the buffer (the extra byte read is stored in device->odd_byte), for
27 write-transmissions, this may be one less (the waiting byte is pending in
28 device->odd_byte).
30 In terms of error handling: we don't bother checking transmission of every
31 single byte via read/write_pio(). At least at the end of the request, when
32 the status bits are verified, we will see that something has gone wrong.
34 TBD: S/G entries may have odd start address. For non-Intel architecture
35 we either have to copy data to an aligned buffer or have to modify
36 PIO-handling in controller drivers.
39 #include "ide_internal.h"
40 #include "ide_sim.h"
42 #include <thread.h>
43 #include <vm/vm.h>
45 #include <string.h>
48 // internal error code if scatter gather table is too short
49 #define ERR_TOO_BIG (B_ERRORS_END + 1)
52 /*! Prepare PIO transfer */
53 void
54 prep_PIO_transfer(ide_device_info *device, ide_qrequest *qrequest)
56 SHOW_FLOW0(4, "");
58 device->left_sg_elem = qrequest->request->sg_count;
59 device->cur_sg_elem = qrequest->request->sg_list;
60 device->cur_sg_ofs = 0;
61 device->has_odd_byte = false;
62 qrequest->request->data_resid = qrequest->request->data_length;
66 /*! Transfer virtually continuous data */
67 static inline status_t
68 transfer_PIO_virtcont(ide_device_info *device, uint8 *virtualAddress,
69 int length, bool write, int *transferred)
71 ide_bus_info *bus = device->bus;
72 ide_controller_interface *controller = bus->controller;
73 void * channel_cookie = bus->channel_cookie;
75 if (write) {
76 // if there is a byte left from last chunk, transmit it together
77 // with the first byte of the current chunk (IDE requires 16 bits
78 // to be transmitted at once)
79 if (device->has_odd_byte) {
80 uint8 buffer[2];
82 buffer[0] = device->odd_byte;
83 buffer[1] = *virtualAddress++;
85 controller->write_pio(channel_cookie, (uint16 *)buffer, 1, false);
87 --length;
88 *transferred += 2;
91 controller->write_pio(channel_cookie, (uint16 *)virtualAddress,
92 length / 2, false);
94 // take care if chunk size was odd, which means that 1 byte remains
95 virtualAddress += length & ~1;
96 *transferred += length & ~1;
98 device->has_odd_byte = (length & 1) != 0;
100 if (device->has_odd_byte)
101 device->odd_byte = *virtualAddress;
102 } else {
103 // if we read one byte too much last time, push it into current chunk
104 if (device->has_odd_byte) {
105 *virtualAddress++ = device->odd_byte;
106 --length;
109 SHOW_FLOW(4, "Reading PIO to %p, %d bytes", virtualAddress, length);
111 controller->read_pio(channel_cookie, (uint16 *)virtualAddress,
112 length / 2, false);
114 // take care of odd chunk size;
115 // in this case we read 1 byte to few!
116 virtualAddress += length & ~1;
117 *transferred += length & ~1;
119 device->has_odd_byte = (length & 1) != 0;
121 if (device->has_odd_byte) {
122 uint8 buffer[2];
124 // now read the missing byte; as we have to read 2 bytes at once,
125 // we'll read one byte too much
126 controller->read_pio(channel_cookie, (uint16 *)buffer, 1, false);
128 *virtualAddress = buffer[0];
129 device->odd_byte = buffer[1];
131 *transferred += 2;
135 return B_OK;
139 /*! Transmit physically continuous data */
140 static inline status_t
141 transfer_PIO_physcont(ide_device_info *device, addr_t physicalAddress,
142 int length, bool write, int *transferred)
144 // we must split up chunk into B_PAGE_SIZE blocks as we can map only
145 // one page into address space at once
146 while (length > 0) {
147 addr_t virtualAddress;
148 void* handle;
149 int page_left, cur_len;
150 status_t err;
151 Thread* thread = thread_get_current_thread();
153 SHOW_FLOW(4, "Transmitting to/from physical address %lx, %d bytes left",
154 physicalAddress, length);
156 thread_pin_to_current_cpu(thread);
157 if (vm_get_physical_page_current_cpu(physicalAddress, &virtualAddress,
158 &handle) != B_OK) {
159 thread_unpin_from_current_cpu(thread);
160 // ouch: this should never ever happen
161 set_sense(device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_INTERNAL_FAILURE);
162 return B_ERROR;
165 // if chunks starts in the middle of a page, we have even less then
166 // a page left
167 page_left = B_PAGE_SIZE - physicalAddress % B_PAGE_SIZE;
169 SHOW_FLOW(4, "page_left=%d", page_left);
171 cur_len = min_c(page_left, length);
173 SHOW_FLOW(4, "cur_len=%d", cur_len);
175 err = transfer_PIO_virtcont(device, (uint8 *)virtualAddress,
176 cur_len, write, transferred);
178 vm_put_physical_page_current_cpu(virtualAddress, handle);
179 thread_unpin_from_current_cpu(thread);
181 if (err != B_OK)
182 return err;
184 length -= cur_len;
185 physicalAddress += cur_len;
188 return B_OK;
192 /*! Transfer PIO block from/to buffer */
193 static inline int
194 transfer_PIO_block(ide_device_info *device, int length, bool write, int *transferred)
196 // data is usually split up into multiple scatter/gather blocks
197 while (length > 0) {
198 int left_bytes, cur_len;
199 status_t err;
201 if (device->left_sg_elem == 0)
202 // ups - buffer too small (for ATAPI data, this is OK)
203 return ERR_TOO_BIG;
205 // we might have transmitted part of a scatter/entry already!
206 left_bytes = device->cur_sg_elem->size - device->cur_sg_ofs;
208 cur_len = min_c(left_bytes, length);
210 err = transfer_PIO_physcont(device,
211 (addr_t)device->cur_sg_elem->address + device->cur_sg_ofs,
212 cur_len, write, transferred);
214 if (err != B_OK)
215 return err;
217 if (left_bytes <= length) {
218 // end of one scatter/gather block reached
219 device->cur_sg_ofs = 0;
220 ++device->cur_sg_elem;
221 --device->left_sg_elem;
222 } else {
223 // still in the same block
224 device->cur_sg_ofs += cur_len;
227 length -= cur_len;
230 return B_OK;
234 /*! Write zero data (required for ATAPI if we ran out of data) */
236 static void
237 write_discard_PIO(ide_device_info *device, int length)
239 ide_bus_info *bus = device->bus;
240 uint8 buffer[32];
242 memset(buffer, 0, sizeof(buffer));
244 // we transmit 32 zero-bytes at once
245 // (not very efficient but easy to implement - you get what you deserve
246 // when you don't provide enough buffer)
247 while (length > 0) {
248 int cur_len;
250 // if device asks for odd number of bytes, append an extra byte to
251 // make length even (this is the "length + 1" term)
252 cur_len = min_c(length + 1, (int)(sizeof(buffer))) / 2;
254 bus->controller->write_pio(bus->channel_cookie, (uint16 *)buffer, cur_len, false);
256 length -= cur_len * 2;
261 /*! Read PIO data and discard it (required for ATAPI if buffer was too small) */
262 static void
263 read_discard_PIO(ide_device_info *device, int length)
265 ide_bus_info *bus = device->bus;
266 uint8 buffer[32];
268 // discard 32 bytes at once (see write_discard_PIO)
269 while (length > 0) {
270 int cur_len;
272 // read extra byte if length is odd (that's the "length + 1")
273 cur_len = min_c(length + 1, (int)sizeof(buffer)) / 2;
275 bus->controller->read_pio(bus->channel_cookie, (uint16 *)buffer, cur_len, false);
277 length -= cur_len * 2;
282 /*! write PIO data
283 return: there are 3 possible results
284 NO_ERROR - everything's nice and groovy
285 ERR_TOO_BIG - data buffer was too short, remaining data got discarded
286 B_ERROR - something serious went wrong, sense data was set
288 status_t
289 write_PIO_block(ide_qrequest *qrequest, int length)
291 ide_device_info *device = qrequest->device;
292 int transferred;
293 status_t err;
295 transferred = 0;
296 err = transfer_PIO_block(device, length, true, &transferred);
298 qrequest->request->data_resid -= transferred;
300 if (err != ERR_TOO_BIG)
301 return err;
303 // there may be a pending odd byte - transmit that now
304 if (qrequest->device->has_odd_byte) {
305 uint8 buffer[2];
307 buffer[0] = device->odd_byte;
308 buffer[1] = 0;
310 device->has_odd_byte = false;
312 qrequest->request->data_resid -= 1;
313 transferred += 2;
315 device->bus->controller->write_pio(device->bus->channel_cookie, (uint16 *)buffer, 1, false);
318 // "transferred" may actually be larger then length because the last odd-byte
319 // is sent together with an extra zero-byte
320 if (transferred >= length)
321 return err;
323 // Ouch! the device asks for data but we haven't got any left.
324 // Sadly, this behaviour is OK for ATAPI packets, but there is no
325 // way to tell the device that we don't have any data left;
326 // only solution is to send zero bytes, though it's BAD BAD BAD
327 write_discard_PIO(qrequest->device, length - transferred);
328 return ERR_TOO_BIG;
332 /*! read PIO data
333 return: see write_PIO_block
335 status_t
336 read_PIO_block(ide_qrequest *qrequest, int length)
338 ide_device_info *device = qrequest->device;
339 int transferred;
340 status_t err;
342 transferred = 0;
343 err = transfer_PIO_block(qrequest->device, length, false, &transferred);
345 qrequest->request->data_resid -= transferred;
347 // if length was odd, there's an extra byte waiting in device->odd_byte
348 if (device->has_odd_byte) {
349 // discard byte
350 device->has_odd_byte = false;
351 // adjust res_id as the extra byte didn't reach the buffer
352 ++qrequest->request->data_resid;
355 if (err != ERR_TOO_BIG)
356 return err;
358 // the device returns more data then the buffer can store;
359 // for ATAPI this is OK - we just discard remaining bytes (there
360 // is no way to tell ATAPI about that, but we "only" waste time)
362 // perhaps discarding the extra odd-byte was sufficient
363 if (transferred >= length)
364 return err;
366 SHOW_FLOW(3, "discarding after %d bytes", transferred);
367 read_discard_PIO(qrequest->device, length - transferred);
368 return ERR_TOO_BIG;