source: trunk/src/os2ahci/ioctl.c@ 125

Last change on this file since 125 was 125, checked in by cjm, 14 years ago

Version 1.21


  • Triggered by reports of performance loss with NCQ
  • New command line flag "/F" to force using write buffers even when upstream I/O requested non-buffered I/O; the primary purpose of this flag is to debug the NCQ performance drop but the flag may or may not remain.
File size: 20.8 KB
Line 
1/******************************************************************************
2 * ioctl.c - Generic IOCTL command processing
3 *
4 * Copyright (c) 2011 thi.guten Software Development
5 * Copyright (c) 2011 Mensys B.V.
6 *
7 * Authors: Christian Mueller, Markus Thielen
8 *
9 * Parts copied from/inspired by the Linux AHCI driver;
10 * those parts are (c) Linux AHCI/ATA maintainers
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
27#include "os2ahci.h"
28#include "atapi.h"
29#include "ata.h"
30#include "ioctl.h"
31
32#include <scsi.h>
33
34#pragma pack(1)
35
36/* -------------------------- macros and constants ------------------------- */
37
38/* ------------------------ typedefs and structures ------------------------ */
39
40/* Memory area for IOCTLs which send IORBs downstream; currently only
41 * OS2AHCI_IOCTL_PASSTHROUGH falls into this category, thus we're simply
42 * reusing the IORB_ADAPTER_PASSTHRU structure for now. If this ever changes,
43 * we'll need to define a union to cover all IORB types in question.
44 */
45#define CMDLEN sizeof(((OS2AHCI_PASSTHROUGH *) 0)->cmd)
46typedef struct {
47 IORB_ADAPTER_PASSTHRU iorb; /* IORB */
48 SCSI_STATUS_BLOCK ssb; /* SCSI status block */
49 UCHAR cmd[CMDLEN]; /* copy of passthrough cmd
50 * (need to fill ATA return
51 * registers at interrupt time)
52 */
53 UCHAR sense[ATAPI_SENSE_LEN]; /* sense buffer */
54 SCATGATENTRY sg_lst[AHCI_MAX_SG / 2]; /* scatter/gather list */
55 ULONG sg_cnt; /* number of S/G elements */
56 UCHAR lh[16]; /* lock handle for VMLock() */
57} IOCTL_CONTEXT;
58
59/* -------------------------- function prototypes -------------------------- */
60
61static USHORT do_smart (BYTE unit, BYTE sub_func, BYTE parm, void _far *buf);
62static int map_unit (BYTE unit, USHORT _far *a, USHORT _far *p,
63 USHORT _far *d);
64static LIN lin (void _far *p);
65
66IORBH _far * _far _cdecl ioctl_wakeup(IORBH _far *iorb);
67
68/* ------------------------ global/static variables ------------------------ */
69
70/* ----------------------------- start of code ----------------------------- */
71
72/******************************************************************************
73 * Return device list to allow the ring 3 application to figure out which
74 * adapter/port/device combinations are available.
75 */
76USHORT ioctl_get_devlist(RP_GENIOCTL _far *ioctl)
77{
78 OS2AHCI_DEVLIST _far *devlst = (OS2AHCI_DEVLIST _far *) ioctl->DataPacket;
79 USHORT maxcnt = 0;
80 USHORT cnt = 0;
81 USHORT a;
82 USHORT p;
83 USHORT d;
84
85 /* verify addressability of parm buffer (number of devlst elements) */
86 if (DevHelp_VerifyAccess((SEL) ((ULONG) ioctl->ParmPacket >> 16),
87 sizeof(USHORT),
88 (USHORT) (ULONG) ioctl->ParmPacket,
89 VERIFY_READONLY) != 0) {
90 return(STDON | STERR | 0x05);
91 }
92
93 maxcnt = *((USHORT _far *) ioctl->ParmPacket);
94
95 /* verify addressability of return buffer (OS2AHCI_DEVLIST) */
96 if (DevHelp_VerifyAccess((SEL) ((ULONG) devlst >> 16),
97 offsetof(OS2AHCI_DEVLIST, devs) +
98 sizeof(devlst->devs) * maxcnt,
99 (USHORT) (ULONG) devlst,
100 VERIFY_READWRITE) != 0) {
101 return(STDON | STERR | 0x05);
102 }
103
104 /* fill-in device list */
105 for (a = 0; a < ad_info_cnt; a++) {
106 AD_INFO *ai = ad_infos + a;
107
108 for (p = 0; p <= ai->port_max; p++) {
109 P_INFO *pi = ai->ports + p;
110
111 for (d = 0; d <= pi->dev_max; d++) {
112 if (pi->devs[d].present) {
113 /* add this device to the device list */
114 if (cnt >= maxcnt) {
115 /* not enough room in devlst */
116 goto ioctl_get_device_done;
117 }
118
119 devlst->devs[cnt].adapter = a;
120 devlst->devs[cnt].port = p;
121 devlst->devs[cnt].device = d;
122 devlst->devs[cnt].type = pi->devs[d].dev_type;
123 devlst->devs[cnt].ncq_max = pi->devs[d].ncq_max;
124
125 if (pi->devs[d].lba48) devlst->devs[cnt].flags |= DF_LBA48;
126 if (pi->devs[d].atapi) devlst->devs[cnt].flags |= DF_ATAPI;
127 if (pi->devs[d].atapi_16) devlst->devs[cnt].flags |= DF_ATAPI_16;
128 if (pi->devs[d].removable) devlst->devs[cnt].flags |= DF_REMOVABLE;
129 cnt++;
130 }
131 }
132 }
133 }
134
135ioctl_get_device_done:
136 devlst->cnt = cnt;
137 return(STDON);
138}
139
140/******************************************************************************
141 * Adapter passthrough IOCTL. This IOCTL covers both ATA and ATAPI passthrough
142 * requests.
143 */
144USHORT ioctl_passthrough(RP_GENIOCTL _far *ioctl)
145{
146 OS2AHCI_PASSTHROUGH _far *req = (OS2AHCI_PASSTHROUGH _far *) ioctl->ParmPacket;
147 char _far *sense_buf = (char _far *) ioctl->DataPacket;
148 IOCTL_CONTEXT *ic;
149 USHORT ret;
150 USHORT a;
151 USHORT p;
152 USHORT d;
153
154 /* verify addressability of parm buffer (OS2AHCI_PASSTHROUGH) */
155 if (DevHelp_VerifyAccess((SEL) ((ULONG) req >> 16),
156 sizeof(OS2AHCI_PASSTHROUGH),
157 (USHORT) (ULONG) req,
158 VERIFY_READWRITE) != 0) {
159 return(STDON | STERR | 0x05);
160 }
161
162 /* verify addressability of data buffer (sense data) */
163 if (req->sense_len > 0) {
164 if (DevHelp_VerifyAccess((SEL) ((ULONG) sense_buf >> 16),
165 req->sense_len,
166 (USHORT) (ULONG) sense_buf,
167 VERIFY_READWRITE) != 0) {
168 return(STDON | STERR | 0x05);
169 }
170 }
171
172 /* Verify basic request parameters such as adapter/port/device, size of
173 * DMA buffer (the S/G list can't have more than AHCI_MAX_SG / 2 entries), ...
174 */
175 a = req->adapter;
176 p = req->port;
177 d = req->device;
178 if (a >= ad_info_cnt || p > ad_infos[a].port_max ||
179 d > ad_infos[a].ports[p].dev_max || !ad_infos[a].ports[p].devs[d].present) {
180 return(STDON | STERR | ERROR_I24_BAD_UNIT);
181 }
182 if ((req->buflen + 4095) / 4096 + 1 > AHCI_MAX_SG / 2 ||
183 req->cmdlen < 6 || req->cmdlen > sizeof(req->cmd)) {
184 return(STDON | STERR | ERROR_I24_INVALID_PARAMETER);
185 }
186
187 /* allocate IOCTL context data */
188 if ((ic = malloc(sizeof(*ic))) == NULL) {
189 return(STDON | STERR | ERROR_I24_GEN_FAILURE);
190 }
191 memset(ic, 0x00, sizeof(*ic));
192
193 /* lock DMA transfer buffer into memory and construct S/G list */
194 if (req->buflen > 0) {
195 if (DevHelp_VMLock(VMDHL_LONG | !((req->flags & PT_WRITE) ? VMDHL_WRITE : 0),
196 req->buf, req->buflen, lin(ic->sg_lst), lin(&ic->lh),
197 &ic->sg_cnt) != 0) {
198 /* couldn't lock buffer and/or produce a S/G list */
199 free(ic);
200 return(STDON | STERR | ERROR_I24_INVALID_PARAMETER);
201 }
202 }
203
204 /* fill in adapter passthrough fields */
205 ic->iorb.iorbh.Length = sizeof(ic->iorb);
206 ic->iorb.iorbh.UnitHandle = iorb_unit(a, p, d);
207 ic->iorb.iorbh.CommandCode = IOCC_ADAPTER_PASSTHRU;
208 ic->iorb.iorbh.CommandModifier = (req->flags & PT_ATAPI) ? IOCM_EXECUTE_CDB
209 : IOCM_EXECUTE_ATA;
210 ic->iorb.iorbh.RequestControl = IORB_ASYNC_POST;
211 ic->iorb.iorbh.Timeout = req->timeout;
212 ic->iorb.iorbh.NotifyAddress = ioctl_wakeup;
213
214 ic->iorb.cSGList = ic->sg_cnt;
215 ic->iorb.pSGList = ic->sg_lst;
216 ic->iorb.ppSGLIST = virt_to_phys(ic->sg_lst);
217
218 memcpy(ic->cmd, req->cmd.cdb, sizeof(ic->cmd));
219 ic->iorb.ControllerCmdLen = req->cmdlen;
220 ic->iorb.pControllerCmd = ic->cmd;
221 ic->iorb.Flags = (req->flags & PT_WRITE) ? 0 : PT_DIRECTION_IN;
222
223 if (req->sense_len > 0) {
224 /* initialize SCSI status block to allow getting sense data */
225 ic->iorb.iorbh.pStatusBlock = (BYTE *) &ic->ssb;
226 ic->iorb.iorbh.StatusBlockLen = sizeof(ic->ssb);
227 ic->ssb.SenseData = (SCSI_REQSENSE_DATA _far *) ic->sense;
228 ic->ssb.ReqSenseLen = sizeof(ic->sense);
229 ic->iorb.iorbh.RequestControl |= IORB_REQ_STATUSBLOCK;
230 }
231
232 /* send IORB on its way */
233 add_entry(&ic->iorb.iorbh);
234
235 /* Wait for IORB completion. On SMP kernels, ProcBlock will release
236 * all spinlocks currently held, in addition to re-enabling interrupts,
237 * so we can use spinlocks to protect the time between checking the
238 * IORB_DONE flag and calling ProcBlock.
239 *
240 * However, if OS2AHCI_SMP is not defined, we're emulating spinlocks
241 * via disabling interrupts and there's a sanity check in the emulation
242 * code to catch recursive spinlocks, thus we need to reset the emulated
243 * spinlock status before calling ProcBlock. Otherwise, the driver will
244 * will get caught in the sanity checks when processing the next IORB or
245 * interrupt while we're waiting.
246 */
247 spin_lock(drv_lock);
248 while (!(ic->iorb.iorbh.Status & IORB_DONE)) {
249
250# ifndef OS2AHCI_SMP
251 drv_lock = 0;
252# endif
253
254 DevHelp_ProcBlock((ULONG) (void _far *) &ic->iorb.iorbh, 30000, 1);
255 spin_lock(drv_lock);
256 }
257 spin_unlock(drv_lock);
258
259 ret = STDON;
260
261 /* map IORB error codes to device driver error codes */
262 if (ic->iorb.iorbh.Status & IORB_ERROR) {
263 ret |= STERR;
264
265 switch (ic->iorb.iorbh.ErrorCode) {
266
267 case IOERR_UNIT_NOT_READY:
268 ret |= ERROR_I24_NOT_READY;
269 break;
270
271 case IOERR_MEDIA_CHANGED:
272 ret |= ERROR_I24_DISK_CHANGE;
273 break;
274
275 case IOERR_MEDIA:
276 case IOERR_MEDIA_NOT_FORMATTED:
277 ret |= ERROR_I24_CRC;
278 break;
279
280 case IOERR_CMD_SYNTAX:
281 case IOERR_CMD_NOT_SUPPORTED:
282 ret |= ERROR_I24_BAD_COMMAND;
283 break;
284
285 case IOERR_MEDIA_WRITE_PROTECT:
286 ret |= ERROR_I24_WRITE_PROTECT;
287 break;
288
289 case IOERR_CMD_ABORTED:
290 ret |= ERROR_I24_CHAR_CALL_INTERRUPTED;
291 break;
292
293 case IOERR_RBA_ADDRESSING_ERROR:
294 ret |= ERROR_I24_SEEK;
295 break;
296
297 case IOERR_RBA_LIMIT:
298 ret |= ERROR_I24_SECTOR_NOT_FOUND;
299 break;
300
301 case IOERR_CMD_SGLIST_BAD:
302 ret |= ERROR_I24_INVALID_PARAMETER;
303 break;
304
305 case IOERR_DEVICE_NONSPECIFIC:
306 case IOERR_ADAPTER_TIMEOUT:
307 case IOERR_ADAPTER_DEVICEBUSCHECK:
308 case IOERR_CMD_ADD_SOFTWARE_FAILURE:
309 case IOERR_CMD_SW_RESOURCE:
310 default:
311 ret |= ERROR_I24_GEN_FAILURE;
312 break;
313 }
314
315 /* copy sense information, if there is any */
316 if ((ic->iorb.iorbh.Status & IORB_STATUSBLOCK_AVAIL) &&
317 (ic->ssb.Flags | STATUS_SENSEDATA_VALID)) {
318 memcpy(sense_buf, ic->ssb.SenseData,
319 min(ic->ssb.ReqSenseLen, req->sense_len));
320 }
321
322 } else if ((req->flags & PT_ATAPI) == 0) {
323 /* Copy ATA cmd back to IOCTL request (ATA commands are effectively
324 * registers which are sometimes used to indicate return conditions,
325 * e.g. when requesting the smart status)
326 */
327 memcpy(&req->cmd.ata, ic->cmd, sizeof(req->cmd.ata));
328 }
329
330 free(ic);
331 if (req->buflen > 0) {
332 DevHelp_VMUnLock(lin(ic->lh));
333 }
334 return(ret);
335}
336
337/******************************************************************************
338 * Generic disk IOCTL handler; this IOCTL category has originally been defined
339 * in IBM1S506; the code has been more or less copied from DANIS506.
340 *
341 * NOTE: Only a subset of the IOCTL calls are implemented in OS2AHCI at this
342 * point, basically those calls required to get HDMON working.
343 */
344USHORT ioctl_gen_dsk(RP_GENIOCTL _far *ioctl)
345{
346 DSKSP_CommandParameters _far *cp = (DSKSP_CommandParameters _far *) ioctl->ParmPacket;
347 UnitInformationData _far *ui;
348 OS2AHCI_PASSTHROUGH pt;
349 RP_GENIOCTL tmp_ioctl;
350 USHORT size = 0;
351 USHORT ret;
352 USHORT a;
353 USHORT p;
354 USHORT d;
355 UCHAR unit;
356
357 /* verify addressability of parm buffer (DSKSP_CommandParameters) */
358 if (DevHelp_VerifyAccess((SEL) ((ULONG) cp >> 16),
359 sizeof(DSKSP_CommandParameters),
360 (USHORT) (ULONG) cp,
361 VERIFY_READONLY) != 0) {
362 return(STDON | STERR | 0x05);
363 }
364 unit = cp->byPhysicalUnit;
365
366 /* verify addressability of data buffer (depends on function code) */
367 switch (ioctl->Function) {
368
369 case DSKSP_GEN_GET_COUNTERS:
370 size = sizeof(DeviceCountersData);
371 break;
372
373 case DSKSP_GET_UNIT_INFORMATION:
374 size = sizeof(UnitInformationData);
375 break;
376
377 case DSKSP_GET_INQUIRY_DATA:
378 size = ATA_ID_WORDS * sizeof(u16);
379 break;
380 }
381
382 if (size > 0) {
383 if (DevHelp_VerifyAccess((SEL) ((ULONG) ioctl->DataPacket >> 16),
384 size, (USHORT) (ULONG) ioctl->DataPacket,
385 VERIFY_READWRITE) != 0) {
386 return(STDON | STERR | 0x05);
387 }
388 }
389
390 if (map_unit(unit, &a, &p, &d)) {
391 return(STDON | STERR | ERROR_I24_BAD_UNIT);
392 }
393
394 /* execute generic disk request */
395 switch (ioctl->Function) {
396
397 case DSKSP_GEN_GET_COUNTERS:
398 /* Not supported, yet; we would need dynamically allocated device
399 * structures to cope with the memory requirements of the corresponding
400 * statistics buffer. For the time being, we'll return an empty buffer.
401 */
402 memset(ioctl->DataPacket, 0x00, sizeof(DeviceCountersData));
403 ret = STDON;
404 break;
405
406 case DSKSP_GET_UNIT_INFORMATION:
407 /* get unit information; things like port addresses won't fit so we don't
408 * even bother returning those.
409 */
410 ui = (UnitInformationData _far *) ioctl->DataPacket;
411 memset(ui, 0x00, sizeof(*ui));
412
413 ui->wRevisionNumber = 1;
414 ui->wIRQ = ad_infos[a].irq;
415 ui->wFlags = UIF_VALID;
416 ui->wFlags |= UIF_RUNNING_BMDMA;
417 ui->wFlags |= (unit & 0x0001) ? UIF_SLAVE : 0;
418 ui->wFlags |= (ad_infos[a].ports[p].devs[d].atapi) ? UIF_ATAPI : 0;
419 ui->wFlags |= UIF_SATA;
420
421 ret = STDON;
422 break;
423
424 case DSKSP_GET_INQUIRY_DATA:
425 /* return ATA ID buffer */
426 memset(&tmp_ioctl, 0x00, sizeof(tmp_ioctl));
427 tmp_ioctl.Category = OS2AHCI_IOCTL_CATEGORY;
428 tmp_ioctl.Function = OS2AHCI_IOCTL_PASSTHROUGH;
429 tmp_ioctl.ParmPacket = (void _far *) &pt;
430
431 memset(&pt, 0x00, sizeof(pt));
432 pt.adapter = a;
433 pt.port = p;
434 pt.device = d;
435 pt.cmdlen = sizeof(pt.cmd.ata);
436 pt.cmd.ata.cmd = (ad_infos[a].ports[p].devs[d].atapi) ?
437 ATA_CMD_ID_ATAPI : ATA_CMD_ID_ATA;
438 pt.buflen = size;
439 pt.buf = lin(ioctl->DataPacket);
440
441 ret = gen_ioctl(&tmp_ioctl);
442 break;
443
444 default:
445 ret = STDON | STATUS_ERR_UNKCMD;
446 break;
447 }
448
449 return(ret);
450}
451
452/******************************************************************************
453 * SMART IOCTL handler; this IOCTL category has originally been defined in
454 * IBM1S506; the code has been more or less copied from DANIS506.
455 */
456USHORT ioctl_smart(RP_GENIOCTL _far *ioctl)
457{
458 DSKSP_CommandParameters _far *cp = (DSKSP_CommandParameters _far *) ioctl->ParmPacket;
459 USHORT size = 0;
460 USHORT ret;
461 UCHAR unit;
462 UCHAR parm;
463
464 /* verify addressability of parm buffer (DSKSP_CommandParameters) */
465 if (DevHelp_VerifyAccess((SEL) ((ULONG) cp >> 16),
466 sizeof(DSKSP_CommandParameters),
467 (USHORT) (ULONG) cp,
468 VERIFY_READONLY) != 0) {
469 return(STDON | STERR | 0x05);
470 }
471 unit = cp->byPhysicalUnit;
472
473 /* verify addressability of data buffer (depends on SMART function) */
474 switch (ioctl->Function) {
475
476 case DSKSP_SMART_GETSTATUS:
477 size = sizeof(ULONG);
478 break;
479
480 case DSKSP_SMART_GET_ATTRIBUTES:
481 case DSKSP_SMART_GET_THRESHOLDS:
482 case DSKSP_SMART_GET_LOG:
483 size = 512;
484 break;
485 }
486
487 if (size > 0) {
488 if (DevHelp_VerifyAccess((SEL) ((ULONG) ioctl->DataPacket >> 16),
489 size, (USHORT) (ULONG) ioctl->DataPacket,
490 VERIFY_READWRITE) != 0) {
491 return(STDON | STERR | 0x05);
492 }
493 parm = ioctl->DataPacket[0];
494 }
495
496 /* execute SMART request */
497 switch (ioctl->Function) {
498
499 case DSKSP_SMART_ONOFF:
500 ret = do_smart(unit,
501 (BYTE) ((parm) ? ATA_SMART_ENABLE
502 : ATA_SMART_DISABLE),
503 0, NULL);
504 break;
505
506 case DSKSP_SMART_AUTOSAVE_ONOFF:
507 ret = do_smart(unit, ATA_SMART_AUTOSAVE,
508 (BYTE) ((parm) ? (BYTE) 0xf1 : 0),
509 NULL);
510 break;
511
512 case DSKSP_SMART_AUTO_OFFLINE:
513 ret = do_smart(unit, ATA_SMART_AUTO_OFFLINE, parm, NULL);
514 break;
515
516 case DSKSP_SMART_EXEC_OFFLINE:
517 ret = do_smart(unit, ATA_SMART_IMMEDIATE_OFFLINE, parm, NULL);
518 break;
519
520 case DSKSP_SMART_SAVE:
521 ret = do_smart(unit, ATA_SMART_SAVE, 0, NULL);
522 break;
523
524 case DSKSP_SMART_GETSTATUS:
525 ret = do_smart(unit, ATA_SMART_STATUS, 0, ioctl->DataPacket);
526 break;
527
528 case DSKSP_SMART_GET_ATTRIBUTES:
529 ret = do_smart(unit, ATA_SMART_READ_VALUES, 0, ioctl->DataPacket);
530 break;
531
532 case DSKSP_SMART_GET_THRESHOLDS:
533 ret = do_smart(unit, ATA_SMART_READ_THRESHOLDS, 0, ioctl->DataPacket);
534 break;
535
536 case DSKSP_SMART_GET_LOG:
537 ret = do_smart(unit, ATA_SMART_READ_LOG, parm, ioctl->DataPacket);
538 break;
539
540 default:
541 ret = STDON | STATUS_ERR_UNKCMD;
542 }
543
544 return(ret);
545}
546
547/******************************************************************************
548 * Perform SMART request. The code has been more or less copied from DANIS506.
549 */
550static USHORT do_smart(BYTE unit, BYTE sub_func, BYTE parm, void _far *buf)
551{
552 OS2AHCI_PASSTHROUGH pt;
553 RP_GENIOCTL ioctl;
554 USHORT ret;
555 USHORT a;
556 USHORT p;
557 USHORT d;
558
559 if (map_unit(unit, &a, &p, &d)) {
560 return(STDON | STERR | ERROR_I24_BAD_UNIT);
561 }
562
563 /* Perform SMART request using the existing OS2AHCI_IOTCL_PASSTHROUGH IOCTL
564 * interface which already takes care of allocating an IORB, s/g lists, etc.
565 */
566 memset(&ioctl, 0x00, sizeof(ioctl));
567 ioctl.Category = OS2AHCI_IOCTL_CATEGORY;
568 ioctl.Function = OS2AHCI_IOCTL_PASSTHROUGH;
569 ioctl.ParmPacket = (void _far *) &pt;
570
571 memset(&pt, 0x00, sizeof(pt));
572 pt.adapter = a;
573 pt.port = p;
574 pt.device = d;
575 pt.cmdlen = sizeof(pt.cmd.ata);
576 pt.cmd.ata.features = sub_func;
577 pt.cmd.ata.count = parm;
578 pt.cmd.ata.lba_l = (0xc24fL << 8) | parm;
579 pt.cmd.ata.cmd = ATA_CMD_SMART;
580
581 if (buf != NULL && sub_func != ATA_SMART_STATUS) {
582 pt.buflen = 512;
583 pt.buf = lin(buf);
584 }
585
586 if (((ret = gen_ioctl(&ioctl)) & STERR) == 0 &&
587 sub_func == ATA_SMART_STATUS) {
588
589 /* ATA_SMART_STATUS doesn't transfer anything but instead relies on the
590 * returned D2H FIS, mapped to the ATA CMD, to have a certain value
591 * (0xf42c); the IOCTL result is expected to be returned as a ULONG in
592 * the data buffer.
593 */
594 if (((pt.cmd.ata.lba_l >> 8) & 0xffff) == 0xf42c) {
595 *((ULONG _far *) buf) = 1;
596 } else {
597 *((ULONG _far *) buf) = 0;
598 }
599 }
600
601 return(ret);
602}
603
604/******************************************************************************
605 * Map DSKSP unit number to corresponding adapter/port/device number. Units
606 * are identified by an 8-bit adapter/device number with the lowest bit
607 * selecting between master (0) and slave (1). This number is mapped to our
608 * ATA/ATAPI units sequentially.
609 */
610static int map_unit(BYTE unit, USHORT _far *a, USHORT _far *p, USHORT _far *d)
611{
612 USHORT _a;
613 USHORT _p;
614 USHORT _d;
615
616 /* map unit to adapter/port/device */
617 for (_a = 0; _a < ad_info_cnt; _a++) {
618 AD_INFO *ai = ad_infos + _a;
619
620 for (_p = 0; _p <= ai->port_max; _p++) {
621 P_INFO *pi = ai->ports + _p;
622
623 for (_d = 0; _d <= pi->dev_max; _d++) {
624 if (pi->devs[_d].present) {
625 if (--unit == 0) {
626 /* found the device */
627 *a = _a;
628 *p = _p;
629 *d = _d;
630 return(0);
631 }
632 }
633 }
634 }
635 }
636
637 /* unit not found */
638 return(-1);
639}
640
641/******************************************************************************
642 * Get linear address for specified virtual address.
643 */
644static LIN lin(void _far *p)
645{
646 LIN l;
647
648 if (DevHelp_VirtToLin((SEL) ((ULONG) p >> 16), (USHORT) (ULONG) p, &l) != 0) {
649 return(0);
650 }
651
652 return(l);
653}
654
655/******************************************************************************
656 * IORB notification routine; used to wake up the sleeping application thread
657 * when the IOCTL IORB is complete.
658 */
659IORBH _far * _far _cdecl ioctl_wakeup(IORBH _far *iorb)
660{
661 USHORT awake_count;
662
663 DevHelp_ProcRun((ULONG) iorb, &awake_count);
664
665 return(NULL);
666}
667
Note: See TracBrowser for help on using the repository browser.