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

Last change on this file since 181 was 181, checked in by David Azarewicz, 9 years ago

Debugging changes

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