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

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

Major reorganization

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