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

Last change on this file since 129 was 129, checked in by cjm, 14 years ago
  • Finishing touches to SMART support in the context of tests with smartctl. Needed to separate 'parm' into 'cnt' and 'lba_l' for do_smart() because the "read smart log" command expects the count to be the number of sectors; setting both count and lba_l to the same value may result in DMA transfer overruns or underruns in the AHCI controller.
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 cnt, BYTE lba_l,
62 void _far *buf);
63static int map_unit (BYTE unit, USHORT _far *a, USHORT _far *p,
64 USHORT _far *d);
65static LIN lin (void _far *p);
66
67IORBH _far * _far _cdecl ioctl_wakeup(IORBH _far *iorb);
68
69/* ------------------------ global/static variables ------------------------ */
70
71/* ----------------------------- start of code ----------------------------- */
72
73/******************************************************************************
74 * Return device list to allow the ring 3 application to figure out which
75 * adapter/port/device combinations are available.
76 */
77USHORT ioctl_get_devlist(RP_GENIOCTL _far *ioctl)
78{
79 OS2AHCI_DEVLIST _far *devlst = (OS2AHCI_DEVLIST _far *) ioctl->DataPacket;
80 USHORT maxcnt = 0;
81 USHORT cnt = 0;
82 USHORT a;
83 USHORT p;
84 USHORT d;
85
86 /* verify addressability of parm buffer (number of devlst elements) */
87 if (DevHelp_VerifyAccess((SEL) ((ULONG) ioctl->ParmPacket >> 16),
88 sizeof(USHORT),
89 (USHORT) (ULONG) ioctl->ParmPacket,
90 VERIFY_READONLY) != 0) {
91 return(STDON | STERR | 0x05);
92 }
93
94 maxcnt = *((USHORT _far *) ioctl->ParmPacket);
95
96 /* verify addressability of return buffer (OS2AHCI_DEVLIST) */
97 if (DevHelp_VerifyAccess((SEL) ((ULONG) devlst >> 16),
98 offsetof(OS2AHCI_DEVLIST, devs) +
99 sizeof(devlst->devs) * maxcnt,
100 (USHORT) (ULONG) devlst,
101 VERIFY_READWRITE) != 0) {
102 return(STDON | STERR | 0x05);
103 }
104
105 /* fill-in device list */
106 for (a = 0; a < ad_info_cnt; a++) {
107 AD_INFO *ai = ad_infos + a;
108
109 for (p = 0; p <= ai->port_max; p++) {
110 P_INFO *pi = ai->ports + p;
111
112 for (d = 0; d <= pi->dev_max; d++) {
113 if (pi->devs[d].present) {
114 /* add this device to the device list */
115 if (cnt >= maxcnt) {
116 /* not enough room in devlst */
117 goto ioctl_get_device_done;
118 }
119
120 devlst->devs[cnt].adapter = a;
121 devlst->devs[cnt].port = p;
122 devlst->devs[cnt].device = d;
123 devlst->devs[cnt].type = pi->devs[d].dev_type;
124 devlst->devs[cnt].ncq_max = pi->devs[d].ncq_max;
125
126 if (pi->devs[d].lba48) devlst->devs[cnt].flags |= DF_LBA48;
127 if (pi->devs[d].atapi) devlst->devs[cnt].flags |= DF_ATAPI;
128 if (pi->devs[d].atapi_16) devlst->devs[cnt].flags |= DF_ATAPI_16;
129 if (pi->devs[d].removable) devlst->devs[cnt].flags |= DF_REMOVABLE;
130 cnt++;
131 }
132 }
133 }
134 }
135
136ioctl_get_device_done:
137 devlst->cnt = cnt;
138 return(STDON);
139}
140
141/******************************************************************************
142 * Adapter passthrough IOCTL. This IOCTL covers both ATA and ATAPI passthrough
143 * requests.
144 */
145USHORT ioctl_passthrough(RP_GENIOCTL _far *ioctl)
146{
147 OS2AHCI_PASSTHROUGH _far *req = (OS2AHCI_PASSTHROUGH _far *) ioctl->ParmPacket;
148 char _far *sense_buf = (char _far *) ioctl->DataPacket;
149 IOCTL_CONTEXT *ic;
150 USHORT ret;
151 USHORT a;
152 USHORT p;
153 USHORT d;
154
155 /* verify addressability of parm buffer (OS2AHCI_PASSTHROUGH) */
156 if (DevHelp_VerifyAccess((SEL) ((ULONG) req >> 16),
157 sizeof(OS2AHCI_PASSTHROUGH),
158 (USHORT) (ULONG) req,
159 VERIFY_READWRITE) != 0) {
160 return(STDON | STERR | 0x05);
161 }
162
163 /* verify addressability of data buffer (sense data) */
164 if (req->sense_len > 0) {
165 if (DevHelp_VerifyAccess((SEL) ((ULONG) sense_buf >> 16),
166 req->sense_len,
167 (USHORT) (ULONG) sense_buf,
168 VERIFY_READWRITE) != 0) {
169 return(STDON | STERR | 0x05);
170 }
171 }
172
173 /* Verify basic request parameters such as adapter/port/device, size of
174 * DMA buffer (the S/G list can't have more than AHCI_MAX_SG / 2 entries), ...
175 */
176 a = req->adapter;
177 p = req->port;
178 d = req->device;
179 if (a >= ad_info_cnt || p > ad_infos[a].port_max ||
180 d > ad_infos[a].ports[p].dev_max || !ad_infos[a].ports[p].devs[d].present) {
181 return(STDON | STERR | ERROR_I24_BAD_UNIT);
182 }
183 if ((req->buflen + 4095) / 4096 + 1 > AHCI_MAX_SG / 2 ||
184 req->cmdlen < 6 || req->cmdlen > sizeof(req->cmd)) {
185 return(STDON | STERR | ERROR_I24_INVALID_PARAMETER);
186 }
187
188 /* allocate IOCTL context data */
189 if ((ic = malloc(sizeof(*ic))) == NULL) {
190 return(STDON | STERR | ERROR_I24_GEN_FAILURE);
191 }
192 memset(ic, 0x00, sizeof(*ic));
193
194 /* lock DMA transfer buffer into memory and construct S/G list */
195 if (req->buflen > 0) {
196 if (DevHelp_VMLock(VMDHL_LONG | !((req->flags & PT_WRITE) ? VMDHL_WRITE : 0),
197 req->buf, req->buflen, lin(ic->sg_lst), lin(&ic->lh),
198 &ic->sg_cnt) != 0) {
199 /* couldn't lock buffer and/or produce a S/G list */
200 free(ic);
201 return(STDON | STERR | ERROR_I24_INVALID_PARAMETER);
202 }
203 }
204
205 /* fill in adapter passthrough fields */
206 ic->iorb.iorbh.Length = sizeof(ic->iorb);
207 ic->iorb.iorbh.UnitHandle = iorb_unit(a, p, d);
208 ic->iorb.iorbh.CommandCode = IOCC_ADAPTER_PASSTHRU;
209 ic->iorb.iorbh.CommandModifier = (req->flags & PT_ATAPI) ? IOCM_EXECUTE_CDB
210 : IOCM_EXECUTE_ATA;
211 ic->iorb.iorbh.RequestControl = IORB_ASYNC_POST;
212 ic->iorb.iorbh.Timeout = req->timeout;
213 ic->iorb.iorbh.NotifyAddress = ioctl_wakeup;
214
215 ic->iorb.cSGList = ic->sg_cnt;
216 ic->iorb.pSGList = ic->sg_lst;
217 ic->iorb.ppSGLIST = virt_to_phys(ic->sg_lst);
218
219 memcpy(ic->cmd, req->cmd.cdb, sizeof(ic->cmd));
220 ic->iorb.ControllerCmdLen = req->cmdlen;
221 ic->iorb.pControllerCmd = ic->cmd;
222 ic->iorb.Flags = (req->flags & PT_WRITE) ? 0 : PT_DIRECTION_IN;
223
224 if (req->sense_len > 0) {
225 /* initialize SCSI status block to allow getting sense data */
226 ic->iorb.iorbh.pStatusBlock = (BYTE *) &ic->ssb;
227 ic->iorb.iorbh.StatusBlockLen = sizeof(ic->ssb);
228 ic->ssb.SenseData = (SCSI_REQSENSE_DATA _far *) ic->sense;
229 ic->ssb.ReqSenseLen = sizeof(ic->sense);
230 ic->iorb.iorbh.RequestControl |= IORB_REQ_STATUSBLOCK;
231 }
232
233 /* send IORB on its way */
234 add_entry(&ic->iorb.iorbh);
235
236 /* Wait for IORB completion. On SMP kernels, ProcBlock will release
237 * all spinlocks currently held, in addition to re-enabling interrupts,
238 * so we can use spinlocks to protect the time between checking the
239 * IORB_DONE flag and calling ProcBlock.
240 *
241 * However, if OS2AHCI_SMP is not defined, we're emulating spinlocks
242 * via disabling interrupts and there's a sanity check in the emulation
243 * code to catch recursive spinlocks, thus we need to reset the emulated
244 * spinlock status before calling ProcBlock. Otherwise, the driver will
245 * will get caught in the sanity checks when processing the next IORB or
246 * interrupt while we're waiting.
247 */
248 spin_lock(drv_lock);
249 while (!(ic->iorb.iorbh.Status & IORB_DONE)) {
250
251# ifndef OS2AHCI_SMP
252 drv_lock = 0;
253# endif
254
255 DevHelp_ProcBlock((ULONG) (void _far *) &ic->iorb.iorbh, 30000, 1);
256 spin_lock(drv_lock);
257 }
258 spin_unlock(drv_lock);
259
260 ret = STDON;
261
262 /* map IORB error codes to device driver error codes */
263 if (ic->iorb.iorbh.Status & IORB_ERROR) {
264 ret |= STERR;
265
266 switch (ic->iorb.iorbh.ErrorCode) {
267
268 case IOERR_UNIT_NOT_READY:
269 ret |= ERROR_I24_NOT_READY;
270 break;
271
272 case IOERR_MEDIA_CHANGED:
273 ret |= ERROR_I24_DISK_CHANGE;
274 break;
275
276 case IOERR_MEDIA:
277 case IOERR_MEDIA_NOT_FORMATTED:
278 ret |= ERROR_I24_CRC;
279 break;
280
281 case IOERR_CMD_SYNTAX:
282 case IOERR_CMD_NOT_SUPPORTED:
283 ret |= ERROR_I24_BAD_COMMAND;
284 break;
285
286 case IOERR_MEDIA_WRITE_PROTECT:
287 ret |= ERROR_I24_WRITE_PROTECT;
288 break;
289
290 case IOERR_CMD_ABORTED:
291 ret |= ERROR_I24_CHAR_CALL_INTERRUPTED;
292 break;
293
294 case IOERR_RBA_ADDRESSING_ERROR:
295 ret |= ERROR_I24_SEEK;
296 break;
297
298 case IOERR_RBA_LIMIT:
299 ret |= ERROR_I24_SECTOR_NOT_FOUND;
300 break;
301
302 case IOERR_CMD_SGLIST_BAD:
303 ret |= ERROR_I24_INVALID_PARAMETER;
304 break;
305
306 case IOERR_DEVICE_NONSPECIFIC:
307 case IOERR_ADAPTER_TIMEOUT:
308 case IOERR_ADAPTER_DEVICEBUSCHECK:
309 case IOERR_CMD_ADD_SOFTWARE_FAILURE:
310 case IOERR_CMD_SW_RESOURCE:
311 default:
312 ret |= ERROR_I24_GEN_FAILURE;
313 break;
314 }
315
316 /* copy sense information, if there is any */
317 if ((ic->iorb.iorbh.Status & IORB_STATUSBLOCK_AVAIL) &&
318 (ic->ssb.Flags | STATUS_SENSEDATA_VALID)) {
319 memcpy(sense_buf, ic->ssb.SenseData,
320 min(ic->ssb.ReqSenseLen, req->sense_len));
321 }
322
323 } else if ((req->flags & PT_ATAPI) == 0) {
324 /* Copy ATA cmd back to IOCTL request (ATA commands are effectively
325 * registers which are sometimes used to indicate return conditions,
326 * e.g. when requesting the smart status)
327 */
328 memcpy(&req->cmd.ata, ic->cmd, sizeof(req->cmd.ata));
329 }
330
331 free(ic);
332 if (req->buflen > 0) {
333 DevHelp_VMUnLock(lin(ic->lh));
334 }
335 return(ret);
336}
337
338/******************************************************************************
339 * Generic disk IOCTL handler; this IOCTL category has originally been defined
340 * in IBM1S506; the code has been more or less copied from DANIS506.
341 *
342 * NOTE: Only a subset of the IOCTL calls are implemented in OS2AHCI at this
343 * point, basically those calls required to get HDMON working.
344 */
345USHORT ioctl_gen_dsk(RP_GENIOCTL _far *ioctl)
346{
347 DSKSP_CommandParameters _far *cp = (DSKSP_CommandParameters _far *) ioctl->ParmPacket;
348 UnitInformationData _far *ui;
349 OS2AHCI_PASSTHROUGH pt;
350 RP_GENIOCTL tmp_ioctl;
351 USHORT size = 0;
352 USHORT ret;
353 USHORT a;
354 USHORT p;
355 USHORT d;
356 UCHAR unit;
357
358 /* verify addressability of parm buffer (DSKSP_CommandParameters) */
359 if (DevHelp_VerifyAccess((SEL) ((ULONG) cp >> 16),
360 sizeof(DSKSP_CommandParameters),
361 (USHORT) (ULONG) cp,
362 VERIFY_READONLY) != 0) {
363 return(STDON | STERR | 0x05);
364 }
365 unit = cp->byPhysicalUnit;
366
367 /* verify addressability of data buffer (depends on function code) */
368 switch (ioctl->Function) {
369
370 case DSKSP_GEN_GET_COUNTERS:
371 size = sizeof(DeviceCountersData);
372 break;
373
374 case DSKSP_GET_UNIT_INFORMATION:
375 size = sizeof(UnitInformationData);
376 break;
377
378 case DSKSP_GET_INQUIRY_DATA:
379 size = ATA_ID_WORDS * sizeof(u16);
380 break;
381 }
382
383 if (size > 0) {
384 if (DevHelp_VerifyAccess((SEL) ((ULONG) ioctl->DataPacket >> 16),
385 size, (USHORT) (ULONG) ioctl->DataPacket,
386 VERIFY_READWRITE) != 0) {
387 return(STDON | STERR | 0x05);
388 }
389 }
390
391 if (map_unit(unit, &a, &p, &d)) {
392 return(STDON | STERR | ERROR_I24_BAD_UNIT);
393 }
394
395 /* execute generic disk request */
396 switch (ioctl->Function) {
397
398 case DSKSP_GEN_GET_COUNTERS:
399 /* Not supported, yet; we would need dynamically allocated device
400 * structures to cope with the memory requirements of the corresponding
401 * statistics buffer. For the time being, we'll return an empty buffer.
402 */
403 memset(ioctl->DataPacket, 0x00, sizeof(DeviceCountersData));
404 ret = STDON;
405 break;
406
407 case DSKSP_GET_UNIT_INFORMATION:
408 /* get unit information; things like port addresses won't fit so we don't
409 * even bother returning those.
410 */
411 ui = (UnitInformationData _far *) ioctl->DataPacket;
412 memset(ui, 0x00, sizeof(*ui));
413
414 ui->wRevisionNumber = 1;
415 ui->wIRQ = ad_infos[a].irq;
416 ui->wFlags = UIF_VALID;
417 ui->wFlags |= UIF_RUNNING_BMDMA;
418 ui->wFlags |= (unit & 0x0001) ? UIF_SLAVE : 0;
419 ui->wFlags |= (ad_infos[a].ports[p].devs[d].atapi) ? UIF_ATAPI : 0;
420 ui->wFlags |= UIF_SATA;
421
422 ret = STDON;
423 break;
424
425 case DSKSP_GET_INQUIRY_DATA:
426 /* return ATA ID buffer */
427 memset(&tmp_ioctl, 0x00, sizeof(tmp_ioctl));
428 tmp_ioctl.Category = OS2AHCI_IOCTL_CATEGORY;
429 tmp_ioctl.Function = OS2AHCI_IOCTL_PASSTHROUGH;
430 tmp_ioctl.ParmPacket = (void _far *) &pt;
431
432 memset(&pt, 0x00, sizeof(pt));
433 pt.adapter = a;
434 pt.port = p;
435 pt.device = d;
436 pt.cmdlen = sizeof(pt.cmd.ata);
437 pt.cmd.ata.cmd = (ad_infos[a].ports[p].devs[d].atapi) ?
438 ATA_CMD_ID_ATAPI : ATA_CMD_ID_ATA;
439 pt.buflen = size;
440 pt.buf = lin(ioctl->DataPacket);
441
442 ret = gen_ioctl(&tmp_ioctl);
443 break;
444
445 default:
446 ret = STDON | STATUS_ERR_UNKCMD;
447 break;
448 }
449
450 return(ret);
451}
452
453/******************************************************************************
454 * SMART IOCTL handler; this IOCTL category has originally been defined in
455 * IBM1S506; the code has been more or less copied from DANIS506.
456 */
457USHORT ioctl_smart(RP_GENIOCTL _far *ioctl)
458{
459 DSKSP_CommandParameters _far *cp = (DSKSP_CommandParameters _far *) ioctl->ParmPacket;
460 USHORT size = 0;
461 USHORT ret;
462 UCHAR unit;
463 UCHAR parm;
464
465 /* verify addressability of parm buffer (DSKSP_CommandParameters) */
466 if (DevHelp_VerifyAccess((SEL) ((ULONG) cp >> 16),
467 sizeof(DSKSP_CommandParameters),
468 (USHORT) (ULONG) cp,
469 VERIFY_READONLY) != 0) {
470 return(STDON | STERR | 0x05);
471 }
472 unit = cp->byPhysicalUnit;
473
474 /* verify addressability of data buffer (depends on SMART function) */
475 switch (ioctl->Function) {
476
477 case DSKSP_SMART_GETSTATUS:
478 size = sizeof(ULONG);
479 break;
480
481 case DSKSP_SMART_GET_ATTRIBUTES:
482 case DSKSP_SMART_GET_THRESHOLDS:
483 case DSKSP_SMART_GET_LOG:
484 size = 512;
485 break;
486 }
487
488 if (size > 0) {
489 if (DevHelp_VerifyAccess((SEL) ((ULONG) ioctl->DataPacket >> 16),
490 size, (USHORT) (ULONG) ioctl->DataPacket,
491 VERIFY_READWRITE) != 0) {
492 return(STDON | STERR | 0x05);
493 }
494 parm = ioctl->DataPacket[0];
495 }
496
497 /* execute SMART request */
498 switch (ioctl->Function) {
499
500 case DSKSP_SMART_ONOFF:
501 ret = do_smart(unit, (BYTE) ((parm) ? ATA_SMART_ENABLE
502 : ATA_SMART_DISABLE),
503 0, 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), 0, NULL);
509 break;
510
511 case DSKSP_SMART_AUTO_OFFLINE:
512 ret = do_smart(unit, ATA_SMART_AUTO_OFFLINE, parm, 0, NULL);
513 break;
514
515 case DSKSP_SMART_EXEC_OFFLINE:
516 ret = do_smart(unit, ATA_SMART_IMMEDIATE_OFFLINE, 0, parm, NULL);
517 break;
518
519 case DSKSP_SMART_SAVE:
520 ret = do_smart(unit, ATA_SMART_SAVE, 0, 0, NULL);
521 break;
522
523 case DSKSP_SMART_GETSTATUS:
524 ret = do_smart(unit, ATA_SMART_STATUS, 0, 0, ioctl->DataPacket);
525 break;
526
527 case DSKSP_SMART_GET_ATTRIBUTES:
528 ret = do_smart(unit, ATA_SMART_READ_VALUES, 0, 0, ioctl->DataPacket);
529 break;
530
531 case DSKSP_SMART_GET_THRESHOLDS:
532 ret = do_smart(unit, ATA_SMART_READ_THRESHOLDS, 0, 0, ioctl->DataPacket);
533 break;
534
535 case DSKSP_SMART_GET_LOG:
536 ret = do_smart(unit, ATA_SMART_READ_LOG, 1, parm, ioctl->DataPacket);
537 break;
538
539 default:
540 ret = STDON | STATUS_ERR_UNKCMD;
541 }
542
543 return(ret);
544}
545
546/******************************************************************************
547 * Perform SMART request. The code has been more or less copied from DANIS506.
548 */
549static USHORT do_smart(BYTE unit, BYTE sub_func, BYTE cnt, BYTE lba_l,
550 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 = cnt;
578 pt.cmd.ata.lba_l = (0xc24fL << 8) | lba_l;
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.