source: trunk/src/os2ahci/os2ahci.c@ 211

Last change on this file since 211 was 211, checked in by David Azarewicz, 2 years ago

Added workaround to help with VirtualBox issues.
Improved diagnostic messages.
Changed how timeouts are reset and how ctx hooks are triggered.
Added quirk for devices with issues executing some standard commands.
Changed to make /N the default.

File size: 50.1 KB
Line 
1/**
2 * os2ahci.c - main file for os2ahci driver
3 *
4 * Copyright (c) 2011 thi.guten Software Development
5 * Copyright (c) 2011 Mensys B.V.
6 * Copyright (c) 2013-2023 David Azarewicz <david@88watts.net>
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#define INCL_LONGLONG
29#include "os2ahci.h"
30#include "ioctl.h"
31#include "version.h"
32#include "devhdr.h"
33
34/* set two-dimensional array of port options */
35#define set_port_option(opt, val) \
36 if (adapter_index == -1) { \
37 /* set option for all adapters and ports */ \
38 memset(opt, val, sizeof(opt)); \
39 } else if (port_index == -1) { \
40 /* set option for all ports on current adapter */ \
41 memset(opt[adapter_index], val, sizeof(*opt)); \
42 } else { \
43 /* set option for specific port */ \
44 opt[adapter_index][port_index] = val; \
45 }
46
47#define FLAG_KRNL_EXIT_ADD 0x1000
48#define FLAG_KRNL_EXIT_REMOVE 0x2000
49
50#define TYPE_KRNL_EXIT_NMI 0x0000 /* non masked interrupts */
51#define TYPE_KRNL_EXIT_SFF 0x0001 /* system fatal faults */
52#define TYPE_KRNL_EXIT_PROCDUMP 0x0002
53#define TYPE_KRNL_EXIT_DYN 0x0003
54#define TYPE_KRNL_EXIT_INT13 0x0004 /* enable int13 IO */
55
56extern int SetPsdPutc(void);
57static int add_unit_info(IORB_CONFIGURATION *iorb_conf, int dt_ai, int a, int p, int d, int scsi_id);
58
59int thorough_scan = 1; /* if != 0, perform thorough PCI scan */
60int init_reset = 1; /* if != 0, reset ports during init */
61int force_write_cache = 0; /* if != 0, force write cache */
62int iVerbose = 0; /* default is quiet. 1=show sign on banner, >1=show adapter info during boot */
63int use_mbr_test = 1;
64
65HDRIVER rm_drvh; /* resource manager driver handle */
66USHORT add_handle; /* driver handle (RegisterDeviceClass) */
67char drv_name[] = "OS2AHCI"; /* driver name as string */
68
69/* resource manager driver information structure */
70static DRIVERSTRUCT rm_drvinfo =
71{
72 NULL, /* We cannot do Flat to Far16 conversion at compile time */
73 NULL, /* so we put NULLs in all the Far16 fields and then fill */
74 NULL, /* them in at run time */
75 DMAJOR,
76 DMINOR,
77 BLD_YEAR, BLD_MONTH, BLD_DAY,
78 0,
79 DRT_ADDDM,
80 DRS_ADD,
81 NULL
82};
83
84SpinLock_t drv_lock; /* driver-level spinlock */
85IORB_QUEUE driver_queue; /* driver-level IORB queue */
86AD_INFO ad_infos[MAX_AD]; /* adapter information list */
87int ad_info_cnt; /* number of entries in ad_infos[] */
88u16 ad_ignore; /* bitmap with adapter indexes to ignore */
89int init_complete; /* if != 0, initialization has completed */
90int suspended;
91int resume_sleep_flag;
92AHCISTATS AhciStats;
93
94/* apapter/port-specific options saved when parsing the command line */
95u8 emulate_scsi[MAX_AD][AHCI_MAX_PORTS];
96u8 enable_ncq[MAX_AD][AHCI_MAX_PORTS];
97u8 link_speed[MAX_AD][AHCI_MAX_PORTS];
98u8 link_power[MAX_AD][AHCI_MAX_PORTS];
99u8 track_size[MAX_AD][AHCI_MAX_PORTS];
100u8 port_ignore[MAX_AD][AHCI_MAX_PORTS];
101
102char BldLevel[] = BLDLEVEL;
103
104/******************************************************************************
105 * OS/2 device driver main strategy function.
106 *
107 * NOTE: this is also used as the IDC entry point. We expect an IOCTL request
108 * packet for IDC calls, so they can be handled by gen_ioctl.
109 */
110void StrategyHandler(REQPACKET *prp)
111{
112 u16 rc;
113
114 switch (prp->bCommand)
115 {
116 case STRATEGY_BASEDEVINIT:
117 rc = init_drv(prp);
118 break;
119
120 case STRATEGY_SHUTDOWN:
121 rc = exit_drv(prp->save_restore.Function);
122 break;
123
124 case STRATEGY_GENIOCTL:
125 rc = gen_ioctl(prp);
126 break;
127
128 case STRATEGY_OPEN:
129 build_user_info();
130 rc = RPDONE;
131 break;
132
133 case STRATEGY_READ:
134 rc = char_dev_input(prp);
135 break;
136
137 case STRATEGY_SAVERESTORE:
138 rc = sr_drv(prp->save_restore.Function);
139 break;
140
141 case STRATEGY_INITCOMPLETE:
142 case STRATEGY_CLOSE:
143 case STRATEGY_INPUTSTATUS:
144 case STRATEGY_FLUSHINPUT:
145 /* noop */
146 rc = RPDONE;
147 break;
148
149 default:
150 rc = RPDONE | RPERR_BADCOMMAND;
151 break;
152 }
153
154 prp->usStatus = rc;
155}
156
157void IdcHandler(REQPACKET *prp)
158{
159 StrategyHandler(prp);
160}
161
162/******************************************************************************
163 * Intialize the os2ahci driver. This includes command line parsing, scanning
164 * the PCI bus for supported AHCI adapters, etc.
165 */
166USHORT init_drv(REQPACKET *req)
167{
168 static int init_drv_called;
169 static int init_drv_failed;
170 APIRET rmrc;
171 const char *pszCmdLine, *cmd_line;
172 int adapter_index = -1;
173 int port_index = -1;
174 int iInvertOption;
175 int iStatus;
176
177 if (init_drv_called)
178 {
179 /* This is the init call for the second (IBMS506$) character
180 * device driver. If the main driver failed initialization, fail this
181 * one as well.
182 */
183 return(RPDONE | ((init_drv_failed) ? RPERR_INITFAIL : 0));
184 }
185 #ifdef DEBUG
186 D32g_DbgLevel = 0x80000000 | DEBUG;
187 #endif
188 init_drv_called = 1;
189 suspended = 0;
190 resume_sleep_flag = 0;
191 memset(ad_infos, 0, sizeof(ad_infos));
192 memset(emulate_scsi, 1, sizeof(emulate_scsi)); /* set default enabled */
193 memset(enable_ncq, 1, sizeof(enable_ncq)); /* set default enabled */
194 UtSetDriverName("OS2AHCI$");
195 Header.ulCaps |= DEV_ADAPTER_DD; /* DAZ This flag is not really needed. */
196 memset(&AhciStats, 0, sizeof(AhciStats));
197 AhciStats.ulSize = sizeof(AhciStats);
198 AhciStats.ulVersion = 0x101;
199
200 /* create driver-level spinlock */
201 KernAllocSpinLock(&drv_lock);
202
203 /* register driver with resource manager */
204 rm_drvinfo.DrvrName = drv_name;
205 rm_drvinfo.DrvrDescript = "AHCI SATA Driver";
206 rm_drvinfo.VendorName = DVENDOR;
207 if ((rmrc = RMCreateDriver(&rm_drvinfo, &rm_drvh)) != RMRC_SUCCESS)
208 {
209 iprintf("OS2AHCI.ADD: failed to register driver with resource manager (rc = %d)", rmrc);
210 goto init_fail;
211 }
212
213 pszCmdLine = cmd_line = req->init_in.szArgs;
214 iStatus = 0;
215 while (*pszCmdLine)
216 {
217 if (*pszCmdLine++ != '/') continue; /* Ignore anything that doesn't start with '/' */
218 /* pszCmdLine now points to first char of argument */
219
220 if ((iInvertOption = (*pszCmdLine == '!')) != 0) pszCmdLine++;
221
222 if (ArgCmp(pszCmdLine, "B:"))
223 {
224 pszCmdLine += 2;
225 InitComPort(strtol(pszCmdLine, &pszCmdLine, 0));
226 continue;
227 }
228
229 if (ArgCmp(pszCmdLine, "COM:"))
230 {
231 pszCmdLine += 4;
232 /* set COM port base address for debug messages */
233 D32g_ComBase = strtol(pszCmdLine, &pszCmdLine, 0);
234 #ifdef TESTVER
235 if (D32g_ComBase == 0) SetPsdPutc();
236 #endif
237 if (D32g_ComBase == 1) D32g_ComBase = 0x3f8;
238 if (D32g_ComBase == 2) D32g_ComBase = 0x2f8;
239 continue;
240 }
241
242 #ifdef DEBUG
243 if (ArgCmp(pszCmdLine, "DEBUG:"))
244 {
245 pszCmdLine += 6;
246 D32g_DbgLevel = strtol(pszCmdLine, &pszCmdLine, 0);
247 continue;
248 }
249 #endif
250
251 if (ArgCmp(pszCmdLine, "G:"))
252 {
253 u16 usVendor;
254 u16 usDevice;
255
256 pszCmdLine += 2;
257 /* add specfied PCI ID as a supported generic AHCI adapter */
258 usVendor = strtol(pszCmdLine, &pszCmdLine, 16);
259 if (*pszCmdLine != ':') break;
260 pszCmdLine++;
261 usDevice = strtol(pszCmdLine, &pszCmdLine, 16);
262 if (add_pci_id(usVendor, usDevice))
263 {
264 iprintf("OS2AHCI.ADD: failed to add PCI ID %04x:%04x", usVendor, usDevice);
265 iStatus = 1;
266 }
267 thorough_scan = 1;
268 continue;
269 }
270
271 if (ArgCmp(pszCmdLine, "T"))
272 {
273 pszCmdLine++;
274 /* perform thorough PCI scan (i.e. look for individual supported PCI IDs) */
275 thorough_scan = !iInvertOption;
276 continue;
277 }
278
279 if (ArgCmp(pszCmdLine, "R"))
280 {
281 pszCmdLine++;
282 /* reset ports during initialization */
283 init_reset = !iInvertOption;
284 continue;
285 }
286
287 if (ArgCmp(pszCmdLine, "F"))
288 {
289 pszCmdLine++;
290 /* force write cache regardless of IORB flags */
291 force_write_cache = 1;
292 continue;
293 }
294
295 if (ArgCmp(pszCmdLine, "A:"))
296 {
297 pszCmdLine += 2;
298 /* set adapter index for adapter and port-related options */
299 adapter_index = strtol(pszCmdLine, &pszCmdLine, 0);
300 if (adapter_index < 0 || adapter_index >= MAX_AD)
301 {
302 iprintf("OS2AHCI.ADD: invalid adapter index (%d)", adapter_index);
303 iStatus = 1;
304 }
305 continue;
306 }
307
308 if (ArgCmp(pszCmdLine, "P:"))
309 {
310 pszCmdLine += 2;
311 /* set port index for port-related options */
312 port_index = strtol(pszCmdLine, &pszCmdLine, 0);
313 if (port_index < 0 || port_index >= AHCI_MAX_PORTS)
314 {
315 iprintf("OS2AHCI.ADD: invalid port index (%d)", port_index);
316 iStatus = 1;
317 }
318 continue;
319 }
320
321 if (ArgCmp(pszCmdLine, "I"))
322 {
323 pszCmdLine++;
324 /* ignore current adapter index */
325 if (adapter_index >= 0)
326 {
327 if (port_index >= 0) port_ignore[adapter_index][port_index] = !iInvertOption;
328 else ad_ignore |= 1U << adapter_index;
329 }
330 continue;
331 }
332
333 if (ArgCmp(pszCmdLine, "S"))
334 {
335 pszCmdLine++;
336 /* enable SCSI emulation for ATAPI devices */
337 set_port_option(emulate_scsi, !iInvertOption);
338 continue;
339 }
340
341 if (ArgCmp(pszCmdLine, "N"))
342 {
343 pszCmdLine++;
344 /* enable NCQ */
345 set_port_option(enable_ncq, !iInvertOption);
346 continue;
347 }
348
349 if (ArgCmp(pszCmdLine, "LS:"))
350 {
351 int optval;
352
353 pszCmdLine += 3;
354 /* set link speed */
355 optval = strtol(pszCmdLine, &pszCmdLine, 0);
356 set_port_option(link_speed, optval);
357 /* need to reset the port in order to establish link settings */
358 init_reset = 1;
359 continue;
360 }
361
362 if (ArgCmp(pszCmdLine, "LP:"))
363 {
364 int optval;
365
366 pszCmdLine += 3;
367 /* set power management */
368 optval = strtol(pszCmdLine, &pszCmdLine, 0);
369 set_port_option(link_power, optval);
370 /* need to reset the port in order to establish link settings */
371 init_reset = 1;
372 continue;
373 }
374
375 if (ArgCmp(pszCmdLine, "4"))
376 {
377 pszCmdLine++;
378 /* enable 4K sector geometry enhancement (track size = 56) */
379 if (!iInvertOption) set_port_option(track_size, 56);
380 continue;
381 }
382
383 if (ArgCmp(pszCmdLine, "U"))
384 {
385 pszCmdLine++;
386 /* Specify to use the MBR test to ignore non-MBR disks.
387 * Default is on.
388 */
389 use_mbr_test = !iInvertOption;
390 continue;
391 }
392
393 if (ArgCmp(pszCmdLine, "V"))
394 {
395 pszCmdLine++;
396 iVerbose = 1;
397 continue;
398 }
399
400 if (ArgCmp(pszCmdLine, "W"))
401 {
402 pszCmdLine++;
403 /* Specify to allow the trace buffer to wrap when full. */
404 D32g_DbgBufWrap = !iInvertOption;
405 continue;
406 }
407
408 iprintf("OS2AHCI.ADD: Unrecognized switch: %s", pszCmdLine-1);
409 iStatus = 1; /* unrecognized argument */
410 }
411
412 if (iStatus) goto init_fail;
413
414 dprintf(0,"BldLevel: %s\n", BldLevel);
415 dprintf(0,"CmdLine: %s\n", cmd_line);
416 /*
417 if (sizeof(ADD_WORKSPACE) > ADD_WORKSPACE_SIZE)
418 {
419 dprintf(0,"ADD_WORKSPACE size is too big! %d>16\n", sizeof(ADD_WORKSPACE));
420 goto init_fail;
421 }
422 */
423
424 /* print initialization message */
425 if (iVerbose) iprintf("OS2AHCI.ADD: driver version %d.%02d", DMAJOR, DMINOR);
426
427 #if defined(TESTVER) && defined(DEBUG)
428 #include "testver.c"
429 #endif
430
431 /* scan PCI bus for supported devices */
432 scan_pci_bus();
433
434 if (ad_info_cnt > 0)
435 {
436 /* initialization succeeded and we found at least one AHCI adapter */
437
438 if (Dev32Help_RegisterDeviceClass(drv_name, add_entry, 0, 1, &add_handle))
439 {
440 iprintf("OS2AHCI.ADD: couldn't register device class");
441 goto init_fail;
442 }
443
444 Timer_InitTimer(TIMER_COUNT);
445
446 /* allocate context hooks */
447 KernAllocateContextHook(RestartCtxHook, 0, &RestartCtxHook_h);
448 KernAllocateContextHook(ResetCtxHook, 0, &ResetCtxHook_h);
449 KernAllocateContextHook(engine_ctxhook, 0, &engine_ctxhook_h);
450
451 /* register kernel exit routine for trap dumps */
452 Dev32Help_RegisterKrnlExit(shutdown_driver, FLAG_KRNL_EXIT_ADD, TYPE_KRNL_EXIT_INT13);
453
454 return(RPDONE);
455 }
456 else
457 {
458 /* no adapters found */
459 if (iVerbose) iprintf("OS2AHCI.ADD: No adapters found.");
460 }
461
462init_fail:
463 /* initialization failed; set segment sizes to 0 and return error */
464 init_drv_failed = 1;
465
466 if (rm_drvh != 0)
467 {
468 /* remove driver from resource manager */
469 RMDestroyDriver(rm_drvh);
470 }
471
472 if (iVerbose) iprintf("OS2AHCI.ADD: driver *not* installed");
473 return(RPDONE | RPERR_INITFAIL);
474}
475
476/******************************************************************************
477 * Generic IOCTL via character device driver. IOCTLs are used to control the
478 * driver operation and to execute native ATA and ATAPI (SCSI) commands from
479 * ring 3 applications. On top of that, some predefined IOCTLs (e.g. SMART
480 * commands for ATA disks) are implemented here.
481 */
482USHORT gen_ioctl(REQPACKET *ioctl)
483{
484 DPRINTF(DBG_FUNCBEG, DBG_PREFIX": IOCTL 0x%x/0x%x\n", ioctl->ioctl.bCategory, ioctl->ioctl.bFunction);
485
486 switch (ioctl->ioctl.bCategory)
487 {
488 case OS2AHCI_IOCTL_CATEGORY:
489 switch (ioctl->ioctl.bFunction)
490 {
491 case OS2AHCI_IOCTL_GET_DEVLIST:
492 return(ioctl_get_devlist(ioctl));
493
494 case OS2AHCI_IOCTL_PASSTHROUGH:
495 return(ioctl_passthrough(ioctl));
496 }
497 break;
498
499 case DSKSP_CAT_GENERIC:
500 return(ioctl_gen_dsk(ioctl));
501
502 case DSKSP_CAT_SMART:
503 return(ioctl_smart(ioctl));
504 }
505
506 return(RPDONE | RPERR_BADCOMMAND);
507}
508
509/******************************************************************************
510 * Read from character device. If tracing is on (internal ring buffer trace),
511 * we return data from the trace buffer; if not, we might return a device
512 * dump similar to IBM1S506.ADD/DANIS506.ADD (TODO).
513 */
514USHORT char_dev_input(REQPACKET *pPacket)
515{
516 void *LinAdr;
517
518 if (Dev32Help_PhysToLin(pPacket->io.ulAddress, pPacket->io.usCount, &LinAdr))
519 {
520 pPacket->io.usCount = 0;
521 return RPDONE | RPERR_GENERAL;
522 }
523
524 pPacket->io.usCount = dCopyToUser(LinAdr, pPacket->io.usCount);
525
526 return RPDONE;
527}
528
529/******************************************************************************
530 * Device driver exit handler. This handler is called when OS/2 shuts down and
531 * flushes the write caches of all attached devices. Since this is effectively
532 * the same we do when suspending, we'll call out to the corresponding suspend
533 * function.
534 *
535 * NOTE: Errors are ignored because there's no way we could stop the shutdown
536 * or do something about the error, unless retrying endlessly is
537 * considered an option.
538 */
539USHORT exit_drv(int func)
540{
541 DPRINTF(DBG_FUNCBEG|DBG_INIT, DBG_PREFIX": exit_drv(%d) called\n", func);
542
543 if (func == 0)
544 {
545 /* we're only interested in the second phase of the shutdown */
546 return(RPDONE);
547 }
548
549 suspend();
550 return(RPDONE);
551}
552
553/******************************************************************************
554 * Device driver suspend/resume handler. This handler is called when ACPI is
555 * executing a suspend or resume.
556 */
557USHORT sr_drv(int func)
558{
559 DPRINTF(DBG_FUNCBEG|DBG_INIT, DBG_PREFIX": sr_drv(%d) called\n", func);
560
561 if (func) resume();
562 else suspend();
563
564 return(RPDONE);
565}
566
567/******************************************************************************
568 * ADD entry point. This is the main entry point for all ADD requests. Due to
569 * the asynchronous nature of ADD drivers, this function primarily queues the
570 * IORB(s) to the corresponding adapter or port queues, then triggers the
571 * state machine to initiate processing queued IORBs.
572 *
573 * NOTE: In order to prevent race conditions or engine stalls, certain rules
574 * around locking, unlocking and IORB handling in general have been
575 * established. Refer to the comments in "trigger_engine()" for
576 * details.
577 */
578void add_entry(IORBH FAR16DATA *vFirstIorb)
579{
580 IORBH FAR16DATA *vIorb;
581 IORBH FAR16DATA *vNext = FAR16NULL;
582
583 spin_lock(drv_lock);
584
585 for (vIorb=vFirstIorb; vIorb!=FAR16NULL; vIorb=vNext)
586 {
587 IORBH *pIorb = Far16ToFlat(vIorb);
588
589 /* Queue this IORB. Queues primarily exist on port level but there are
590 * some requests which affect the whole driver, most notably
591 * IOCC_CONFIGURATION. In either case, adding the IORB to the driver or
592 * port queue will change the links, thus we need to save the original
593 * link in 'vNext'.
594 */
595 if (pIorb->RequestControl & IORB_CHAIN) vNext = pIorb->f16NxtIORB;
596 else vNext = (IORBH FAR16DATA *)0;
597
598 pIorb->Status = 0;
599 pIorb->ErrorCode = 0;
600 memset(&pIorb->ADDWorkSpace, 0x00, sizeof(ADD_WORKSPACE));
601
602 //#ifdef DEBUG
603 //DumpIorb(pIorb); /* DAZ TESTING */
604 //#endif
605
606 if (iorb_driver_level(pIorb))
607 {
608 /* driver-level IORB */
609 pIorb->UnitHandle = 0;
610 iorb_queue_add(&driver_queue, vIorb, pIorb);
611 }
612 else
613 {
614 /* port-level IORB */
615 int a = iorb_unit_adapter(pIorb);
616 int p = iorb_unit_port(pIorb);
617 int d = iorb_unit_device(pIorb);
618
619 if (a >= ad_info_cnt ||
620 p > ad_infos[a].port_max ||
621 d > ad_infos[a].ports[p].dev_max ||
622 (ad_infos[a].port_map & (1UL << p)) == 0)
623 {
624 /* unit handle outside of the allowed range */
625 dprintf(0, DBG_PREFIX": ERROR: IORB for %d.%d.%d is out of range\n", a, p, d);
626 pIorb->Status = IORB_ERROR;
627 pIorb->ErrorCode = IOERR_CMD_SYNTAX;
628 iorb_complete(vIorb, pIorb);
629 continue;
630 }
631
632 iorb_queue_add(&ad_infos[a].ports[p].iorb_queue, vIorb, pIorb);
633 }
634 }
635
636 /* trigger state machine */
637 trigger_engine();
638
639 spin_unlock(drv_lock);
640}
641
642/******************************************************************************
643 * Trigger IORB queue engine. This is a wrapper function for trigger_engine_1()
644 * which will try to get all IORBs sent on their way a couple of times. If
645 * there are still IORBs ready for processing after this, this function will
646 * hand off to a context hook which will continue to trigger the engine until
647 * all IORBs have been sent.
648 *
649 * NOTE: While initialization has not completed (or during suspend/resume
650 * operations), this function will loop indefinitely because we can't
651 * rely on interrupt handlers or context hooks and complex IORBs
652 * requiring multiple requeues would eventually hang and time out if
653 * we stopped triggering here.
654 */
655void trigger_engine(void)
656{
657 int i;
658
659 for (i = 0; i < 3 || !init_complete; i++)
660 {
661 if (trigger_engine_1() == 0)
662 {
663 /* done -- all IORBs have been sent on their way */
664 return;
665 }
666 }
667
668 /* Something keeps bouncing; hand off to the engine context hook which will
669 * keep trying in the background.
670 */
671 KernArmHook(engine_ctxhook_h, 0, 0);
672}
673
674/******************************************************************************
675 * Trigger IORB queue engine in order to send commands in the driver/port IORB
676 * queues to the AHCI hardware. This function will return the number of IORBs
677 * sent. Keep in mind that IORBs might "bounce" if the adapter/port is not in
678 * a state to accept the command, thus it might take quite a few calls to get
679 * all IORBs on their way. This is why there's a wrapper function which tries
680 * it a few times, then hands off to a context hook which will keep trying in
681 * the background.
682 *
683 * IORBs might complete before send_iorb() has returned, at any time during
684 * interrupt processing or on another CPU on SMP systems. IORB completion
685 * means modifications to the corresponding IORB queue (the completed IORB
686 * is removed from the queue) thus we need to protect the IORB queues from
687 * race conditions. The safest approach short of keeping the driver-level
688 * spinlock aquired permanently is to keep it throughout this function and
689 * release it temporarily in send_iorb().
690 *
691 * This implies that the handler functions are fully responsible for aquiring
692 * the driver-level spinlock when they need it, and for releasing it again.
693 *
694 * As a rule of thumb, get the driver-level spinlock whenever accessing
695 * volatile variables (IORB queues, values in ad_info[], ...).
696 *
697 * Additional Notes:
698 *
699 * - This function is expected to be called with the spinlock aquired
700 *
701 * - Adapters can be flagged as 'busy' which means no new IORBs are sent (they
702 * just remain in the queue). This can be used to release the driver-level
703 * spinlock while making sure no new IORBs are going to hit the hardware.
704 * In order to prevent engine stalls, all handlers using this functionality
705 * need to invoke trigger_engine() after resetting the busy flag.
706 *
707 * - Driver-level IORBs are not synchronized by adapter-level 'busy' flags.
708 * However, the driver-level queue is worked "one entry at a time" which
709 * means that no new IORBs will be queued on the driver-level queue until
710 * the head element has completed processing. This means that driver-
711 * level IORB handlers don't need to protect against each other. But they
712 * they do need to keep in mind interference with port-level IORBs:
713 *
714 * - Driver-level IORB handlers must obtain the spinlock and/or flag all
715 * adapters as 'busy' which are affected by the driver-level IORB
716 *
717 * - Driver-level IORB handlers must not access the hardware of a
718 * particular adapter if it's flagged as 'busy' by another IORB.
719 */
720int trigger_engine_1(void)
721{
722 IORBH FAR16DATA *vIorb;
723 IORBH *pIorb;
724 IORBH FAR16DATA *vNext;
725 int iorbs_sent = 0;
726 int a;
727 int p;
728
729 iorbs_sent = 0;
730
731 /* process driver-level IORBs */
732 if ((vIorb = driver_queue.vRoot) != FAR16NULL)
733 {
734 pIorb = Far16ToFlat(vIorb);
735
736 if (!add_workspace(pIorb)->processing)
737 {
738 send_iorb(vIorb, pIorb);
739 iorbs_sent++;
740 }
741 }
742
743 /* process port-level IORBs */
744 for (a = 0; a < ad_info_cnt; a++)
745 {
746 AD_INFO *ai = ad_infos + a;
747 if (ai->busy)
748 {
749 /* adapter is busy; don't process any IORBs */
750 continue;
751 }
752 for (p = 0; p <= ai->port_max; p++)
753 {
754 /* send all queued IORBs on this port */
755 vNext = FAR16NULL;
756 for (vIorb = ai->ports[p].iorb_queue.vRoot; vIorb != FAR16NULL; vIorb = vNext)
757 {
758 pIorb = Far16ToFlat(vIorb);
759
760 vNext = pIorb->f16NxtIORB;
761 if (!add_workspace(pIorb)->processing)
762 {
763 send_iorb(vIorb, pIorb);
764 iorbs_sent++;
765 }
766 }
767 }
768 }
769
770 return(iorbs_sent);
771}
772
773/******************************************************************************
774 * Send a single IORB to the corresponding AHCI adapter/port. This is just a
775 * switch board for calling the corresponding iocc_*() handler function.
776 *
777 * NOTE: This function is expected to be called with the driver-level spinlock
778 * aquired. It will release it before calling any of the handler
779 * functions and re-aquire it when done.
780 */
781void send_iorb(IORBH FAR16DATA *vIorb, IORBH *pIorb)
782{
783 /* Mark IORB as "processing" before doing anything else. Once the IORB is
784 * marked as "processing", we can release the spinlock because subsequent
785 * invocations of trigger_engine() (e.g. at interrupt time) will ignore this
786 * IORB.
787 */
788 add_workspace(pIorb)->processing = 1;
789 spin_unlock(drv_lock);
790
791 switch (pIorb->CommandCode)
792 {
793 case IOCC_CONFIGURATION:
794 iocc_configuration(vIorb, pIorb);
795 break;
796
797 case IOCC_DEVICE_CONTROL:
798 iocc_device_control(vIorb, pIorb);
799 break;
800
801 case IOCC_UNIT_CONTROL:
802 iocc_unit_control(vIorb, pIorb);
803 break;
804
805 case IOCC_GEOMETRY_64:
806 add_workspace(pIorb)->fIs64bit = 1;
807 /* fall thru */
808 case IOCC_GEOMETRY:
809 iocc_geometry(vIorb, pIorb);
810 break;
811
812 case IOCC_EXECUTE_IO_64:
813 add_workspace(pIorb)->fIs64bit = 1;
814 /* fall thru */
815 case IOCC_EXECUTE_IO:
816 iocc_execute_io(vIorb, pIorb);
817 break;
818
819 case IOCC_UNIT_STATUS:
820 iocc_unit_status(vIorb, pIorb);
821 break;
822
823 case IOCC_ADAPTER_PASSTHRU:
824 AhciStats.ulTestCount1++;
825 iocc_adapter_passthru(vIorb, pIorb);
826 break;
827
828 default:
829 /* unsupported call */
830 iorb_seterr(pIorb, IOERR_CMD_NOT_SUPPORTED);
831 iorb_done(vIorb, pIorb);
832 break;
833 }
834
835 /* re-aquire spinlock before returning to trigger_engine() */
836 spin_lock(drv_lock);
837}
838
839/******************************************************************************
840 * Handle IOCC_CONFIGURATION requests.
841 */
842void iocc_configuration(IORBH FAR16DATA *vIorb, IORBH *pIorb)
843{
844 int a;
845
846 switch (pIorb->CommandModifier)
847 {
848
849 case IOCM_COMPLETE_INIT:
850 /* Complete initialization. From now on, we won't have to restore the BIOS
851 * configuration after each command and we're fully operational (i.e. will
852 * use interrupts, timers and context hooks instead of polling).
853 */
854 if (!init_complete)
855 {
856 DPRINTF(DBG_INIT, DBG_PREFIX": leaving initialization mode\n");
857 for (a = 0; a < ad_info_cnt; a++)
858 {
859 lock_adapter(ad_infos + a);
860 ahci_complete_init(ad_infos + a);
861 }
862 init_complete = 1;
863
864 /* release all adapters */
865 for (a = 0; a < ad_info_cnt; a++)
866 {
867 unlock_adapter(ad_infos + a);
868 }
869 DPRINTF(DBG_INIT, DBG_PREFIX": leaving initialization mode 2\n");
870
871 #ifdef LEGACY_APM
872 /* register APM hook */
873 apm_init();
874 #endif
875 }
876 iorb_done(vIorb, pIorb);
877 break;
878
879 case IOCM_GET_DEVICE_TABLE:
880 /* construct a device table */
881 iocm_device_table(vIorb, pIorb);
882 break;
883
884 default:
885 iorb_seterr(pIorb, IOERR_CMD_NOT_SUPPORTED);
886 iorb_done(vIorb, pIorb);
887 break;
888 }
889}
890
891/******************************************************************************
892 * Handle IOCC_DEVICE_CONTROL requests.
893 */
894void iocc_device_control(IORBH FAR16DATA *vIorb, IORBH *pIorb)
895{
896 AD_INFO *ai = ad_infos + iorb_unit_adapter(pIorb);
897 IORBH FAR16DATA *vPtr;
898 IORBH FAR16DATA *vNext = FAR16NULL;
899 int p = iorb_unit_port(pIorb);
900 int d = iorb_unit_device(pIorb);
901
902 switch (pIorb->CommandModifier)
903 {
904 case IOCM_ABORT:
905 /* abort all pending commands on specified port and device */
906 spin_lock(drv_lock);
907 for (vPtr = ai->ports[p].iorb_queue.vRoot; vPtr != FAR16NULL; vPtr = vNext)
908 {
909 IORBH *pPtr = Far16ToFlat(vPtr);
910
911 vNext = pPtr->f16NxtIORB;
912 /* move all matching IORBs to the abort queue */
913 if (vPtr != vIorb && iorb_unit_device(pPtr) == d)
914 {
915 iorb_queue_del(&ai->ports[p].iorb_queue, vPtr);
916 iorb_queue_add(&abort_queue, vPtr, pPtr);
917 pPtr->ErrorCode = IOERR_CMD_ABORTED;
918 }
919 }
920 spin_unlock(drv_lock);
921
922 /* trigger reset context hook which will finish the abort processing */
923 SafeArmCtxHook(ResetCtxHook_h, 0);
924 break;
925
926 case IOCM_SUSPEND:
927 case IOCM_RESUME:
928 case IOCM_GET_QUEUE_STATUS:
929 /* Suspend/resume operations allow access to the hardware for other
930 * entities such as IBMIDECD.FLT. Since os2ahci implements both ATA
931 * and ATAPI in the same driver, this won't be required.
932 */
933 iorb_seterr(pIorb, IOERR_CMD_NOT_SUPPORTED);
934 break;
935
936 case IOCM_LOCK_MEDIA:
937 case IOCM_UNLOCK_MEDIA:
938 case IOCM_EJECT_MEDIA:
939 /* unit control commands to lock, unlock and eject media */
940 /* will be supported later... */
941 iorb_seterr(pIorb, IOERR_CMD_NOT_SUPPORTED);
942 break;
943
944 default:
945 iorb_seterr(pIorb, IOERR_CMD_NOT_SUPPORTED);
946 break;
947 }
948
949 iorb_done(vIorb, pIorb);
950}
951
952/******************************************************************************
953 * Handle IOCC_UNIT_CONTROL requests.
954 */
955void iocc_unit_control(IORBH FAR16DATA *vIorb, IORBH *pIorb)
956{
957 IORB_UNIT_CONTROL *pIorb_uc = (IORB_UNIT_CONTROL *)pIorb;
958 int a = iorb_unit_adapter(pIorb);
959 int p = iorb_unit_port(pIorb);
960 int d = iorb_unit_device(pIorb);
961
962 spin_lock(drv_lock);
963 switch (pIorb->CommandModifier)
964 {
965 case IOCM_ALLOCATE_UNIT:
966 /* allocate unit for exclusive access */
967 if (ad_infos[a].ports[p].devs[d].allocated)
968 {
969 iorb_seterr(pIorb, IOERR_UNIT_ALLOCATED);
970 }
971 else
972 {
973 ad_infos[a].ports[p].devs[d].allocated = 1;
974 }
975 break;
976
977 case IOCM_DEALLOCATE_UNIT:
978 /* deallocate exclusive access to unit */
979 if (!ad_infos[a].ports[p].devs[d].allocated)
980 {
981 iorb_seterr(pIorb, IOERR_UNIT_NOT_ALLOCATED);
982 }
983 else
984 {
985 ad_infos[a].ports[p].devs[d].allocated = 0;
986 }
987 break;
988
989 case IOCM_CHANGE_UNITINFO:
990 /* Change unit (device) information. One reason for this IOCM is the
991 * interface for filter device drivers: a filter device driver can
992 * either change existing UNITINFOs or permanently allocate units
993 * and fabricate new [logical] units; the former is the reason why we
994 * must store the pointer to the updated UNITNIFO for subsequent
995 * IOCC_CONFIGURATION/IOCM_GET_DEVICE_TABLE calls.
996 */
997 if (!ad_infos[a].ports[p].devs[d].allocated)
998 {
999 iorb_seterr(pIorb, IOERR_UNIT_NOT_ALLOCATED);
1000 break;
1001 }
1002 ad_infos[a].ports[p].devs[d].unit_info = pIorb_uc->f16UnitInfo;
1003 break;
1004
1005 default:
1006 iorb_seterr(pIorb, IOERR_CMD_NOT_SUPPORTED);
1007 break;
1008 }
1009
1010 spin_unlock(drv_lock);
1011 iorb_done(vIorb, pIorb);
1012}
1013
1014/******************************************************************************
1015 * Scan all ports for AHCI devices and construct a DASD device table.
1016 *
1017 * NOTES: This function may be called multiple times. Only the first
1018 * invocation will actually scan for devices; all subsequent calls will
1019 * merely return the results of the initial scan, potentially augmented
1020 * by modified unit infos after IOCC_CONFIGURATION/IOCM_CHANGE_UNITINFO
1021 * requests.
1022 *
1023 * In order to support applications that can't deal with ATAPI devices
1024 * (i.e. need a SCSI adapter) os2ahci will optionally report ATAPI
1025 * dvices as SCSI devices. The corresponding SCSI adapter doesn't
1026 * really exist and is only reported here for the IOCM_GET_DEVICETABLE
1027 * request. The units attached to this adapter will use the real HW
1028 * unit IDs, thus we'll never receive a command specific to the
1029 * emulated SCSI adapter and won't need to set up any sort of entity
1030 * for it; the only purpose of the emulated SCSI adapter is to pass the
1031 * bus type "AI_DEVBUS_SCSI_2" upstream, and the emulated units, of
1032 * course. The emulated SCSI target IDs are allocated as follows:
1033 *
1034 * 0 the virtual adapter
1035 * 1..n emulated devices; SCSI target ID increments sequentially
1036 */
1037void iocm_device_table(IORBH FAR16DATA *vIorb, IORBH *pIorb)
1038{
1039 IORB_CONFIGURATION *pIorb_conf;
1040 DEVICETABLE FAR16DATA *vDt;
1041 DEVICETABLE *pDt;
1042 char *pPos;
1043 int scsi_units = 0;
1044 int scsi_id = 1;
1045 int rc;
1046 int dta;
1047 int a;
1048 int p;
1049 int d;
1050
1051 pIorb_conf = (IORB_CONFIGURATION *)pIorb;
1052 vDt = pIorb_conf->f16DeviceTable;
1053 pDt = Far16ToFlat(vDt);
1054
1055 spin_lock(drv_lock);
1056
1057 /* initialize device table header */
1058 pDt->ADDLevelMajor = ADD_LEVEL_MAJOR;
1059 pDt->ADDLevelMinor = ADD_LEVEL_MINOR;
1060 pDt->ADDHandle = add_handle;
1061 pDt->TotalAdapters = ad_info_cnt + 1;
1062
1063 /* set start of adapter and device information tables */
1064 pPos = (char*)&pDt->pAdapter[pDt->TotalAdapters];
1065
1066 /* go through all adapters, including the virtual SCSI adapter */
1067 for (dta = 0; dta < pDt->TotalAdapters; dta++)
1068 {
1069 ADAPTERINFO *pPtr = (ADAPTERINFO *)pPos;
1070
1071 /* sanity check for sufficient space in device table */
1072 if ((u32)(pPtr + 1) - (u32)pDt > pIorb_conf->DeviceTableLen)
1073 {
1074 dprintf(0, DBG_PREFIX": error: device table provided by DASD too small\n");
1075 iorb_seterr(pIorb, IOERR_CMD_SW_RESOURCE);
1076 goto iocm_device_table_done;
1077 }
1078
1079 pDt->pAdapter[dta] = MakeNear16PtrFromDiff(pIorb_conf->f16DeviceTable, pDt, pPtr);
1080
1081 //DPRINTF(2,"iocm_device_table: ptr=%x dta=%x pAdapter[dta]=%x pDeviceTable=%x\n",
1082 // ptr, dta, dt->pAdapter[dta], iorb_conf->pDeviceTable);
1083 memset(pPtr, 0x00, sizeof(*pPtr));
1084
1085 pPtr->AdapterIOAccess = AI_IOACCESS_BUS_MASTER;
1086 pPtr->AdapterHostBus = AI_HOSTBUS_OTHER | AI_BUSWIDTH_32BIT;
1087 pPtr->AdapterFlags = AF_16M | AF_HW_SCATGAT;
1088 pPtr->MaxHWSGList = AHCI_MAX_SG / 2; /* AHCI S/G elements are 22 bits */
1089
1090 if (dta < ad_info_cnt)
1091 {
1092 /* this is a physical AHCI adapter */
1093 AD_INFO *ad_info = ad_infos + dta;
1094
1095 pPtr->AdapterDevBus = AI_DEVBUS_ST506 | AI_DEVBUS_32BIT;
1096 snprintf(pPtr->AdapterName, sizeof(pPtr->AdapterName), "AHCI_%d", dta);
1097
1098 if (!ad_info->port_scan_done)
1099 {
1100 /* first call; need to scan AHCI hardware for devices */
1101 if (ad_info->busy)
1102 {
1103 dprintf(0, DBG_PREFIX": error: port scan requested while adapter was busy\n");
1104 iorb_seterr(pIorb, IOERR_CMD_SW_RESOURCE);
1105 goto iocm_device_table_done;
1106 }
1107 ad_info->busy = 1;
1108 spin_unlock(drv_lock);
1109 rc = ahci_scan_ports(ad_info);
1110 spin_lock(drv_lock);
1111 ad_info->busy = 0;
1112
1113 if (rc != 0)
1114 {
1115 dprintf(0, DBG_PREFIX": error: port scan failed on adapter #%d\n", dta);
1116 iorb_seterr(pIorb, IOERR_CMD_SW_RESOURCE);
1117 goto iocm_device_table_done;
1118 }
1119 ad_info->port_scan_done = 1;
1120 }
1121
1122 /* insert physical (i.e. AHCI) devices into the device table */
1123 for (p = 0; p <= ad_info->port_max; p++)
1124 {
1125 for (d = 0; d <= ad_info->ports[p].dev_max; d++)
1126 {
1127 if (ad_info->ports[p].devs[d].present)
1128 {
1129 if (ad_info->ports[p].devs[d].atapi && emulate_scsi[dta][p])
1130 {
1131 /* report this unit as SCSI unit */
1132 scsi_units++;
1133 //continue;
1134 }
1135 if (add_unit_info(pIorb_conf, dta, dta, p, d, 0))
1136 {
1137 goto iocm_device_table_done;
1138 }
1139 }
1140 }
1141 }
1142 }
1143 else
1144 {
1145 /* this is the virtual SCSI adapter */
1146 if (scsi_units == 0)
1147 {
1148 /* not a single unit to be emulated via SCSI */
1149 pDt->TotalAdapters--;
1150 break;
1151 }
1152
1153 /* set adapter name and bus type to mimic a SCSI controller */
1154 pPtr->AdapterDevBus = AI_DEVBUS_SCSI_2 | AI_DEVBUS_16BIT;
1155 snprintf(pPtr->AdapterName, sizeof(pPtr->AdapterName), "AHCI_SCSI_0");
1156
1157 /* add all ATAPI units to be emulated by this virtual adaper */
1158 for (a = 0; a < ad_info_cnt; a++)
1159 {
1160 AD_INFO *ad_info = ad_infos + a;
1161
1162 for (p = 0; p <= ad_info->port_max; p++)
1163 {
1164 for (d = 0; d <= ad_info->ports[p].dev_max; d++)
1165 {
1166 if (ad_info->ports[p].devs[d].present && ad_info->ports[p].devs[d].atapi && emulate_scsi[a][p])
1167 {
1168 if (add_unit_info(pIorb_conf, dta, a, p, d, scsi_id++))
1169 {
1170 goto iocm_device_table_done;
1171 }
1172 }
1173 }
1174 }
1175 }
1176 }
1177
1178 /* calculate offset for next adapter */
1179 pPos = (char *)(pPtr->UnitInfo + pPtr->AdapterUnits);
1180 }
1181
1182iocm_device_table_done:
1183 spin_unlock(drv_lock);
1184 iorb_done(vIorb, pIorb);
1185}
1186
1187/******************************************************************************
1188 * Handle IOCC_GEOMETRY requests.
1189 */
1190void iocc_geometry(IORBH FAR16DATA *vIorb, IORBH *pIorb)
1191{
1192 switch (pIorb->CommandModifier)
1193 {
1194 case IOCM_GET_MEDIA_GEOMETRY:
1195 case IOCM_GET_DEVICE_GEOMETRY:
1196 add_workspace(pIorb)->idempotent = 1;
1197 ahci_get_geometry(vIorb, pIorb);
1198 break;
1199
1200 default:
1201 iorb_seterr(pIorb, IOERR_CMD_NOT_SUPPORTED);
1202 iorb_done(vIorb, pIorb);
1203 }
1204}
1205
1206/******************************************************************************
1207 * Handle IOCC_EXECUTE_IO requests.
1208 */
1209void iocc_execute_io(IORBH FAR16DATA *vIorb, IORBH *pIorb)
1210{
1211 switch (pIorb->CommandModifier)
1212 {
1213 case IOCM_READ:
1214 add_workspace(pIorb)->idempotent = 1;
1215 ahci_read(vIorb, pIorb);
1216 break;
1217
1218 case IOCM_READ_VERIFY:
1219 add_workspace(pIorb)->idempotent = 1;
1220 ahci_verify(vIorb, pIorb);
1221 break;
1222
1223 case IOCM_WRITE:
1224 case IOCM_WRITE_VERIFY:
1225 add_workspace(pIorb)->idempotent = 1;
1226 ahci_write(vIorb, pIorb);
1227 break;
1228
1229 default:
1230 iorb_seterr(pIorb, IOERR_CMD_NOT_SUPPORTED);
1231 iorb_done(vIorb, pIorb);
1232 }
1233}
1234
1235/******************************************************************************
1236 * Handle IOCC_UNIT_STATUS requests.
1237 */
1238void iocc_unit_status(IORBH FAR16DATA *vIorb, IORBH *pIorb)
1239{
1240 switch (pIorb->CommandModifier)
1241 {
1242 case IOCM_GET_UNIT_STATUS:
1243 add_workspace(pIorb)->idempotent = 1;
1244 ahci_unit_ready(vIorb, pIorb);
1245 break;
1246
1247 default:
1248 iorb_seterr(pIorb, IOERR_CMD_NOT_SUPPORTED);
1249 iorb_done(vIorb, pIorb);
1250 }
1251}
1252
1253/******************************************************************************
1254 * Handle IOCC_ADAPTER_PASSTHROUGH requests.
1255 */
1256void iocc_adapter_passthru(IORBH FAR16DATA *vIorb, IORBH *pIorb)
1257{
1258 switch (pIorb->CommandModifier)
1259 {
1260 case IOCM_EXECUTE_CDB:
1261 add_workspace(pIorb)->idempotent = 0;
1262 ahci_execute_cdb(vIorb, pIorb);
1263 break;
1264
1265 case IOCM_EXECUTE_ATA:
1266 add_workspace(pIorb)->idempotent = 0;
1267 ahci_execute_ata(vIorb, pIorb);
1268 break;
1269
1270 default:
1271 iorb_seterr(pIorb, IOERR_CMD_NOT_SUPPORTED);
1272 iorb_done(vIorb, pIorb);
1273 }
1274}
1275
1276/******************************************************************************
1277 * Add an IORB to the specified queue. This function must be called with the
1278 * adapter-level spinlock aquired.
1279 */
1280void iorb_queue_add(IORB_QUEUE *queue, IORBH FAR16DATA *vIorb, IORBH *pIorb)
1281{
1282 if (iorb_priority(pIorb)
1283 {
1284 /* priority IORB; insert at first position */
1285 pIorb->f16NxtIORB = queue->vRoot;
1286 queue->vRoot = vIorb;
1287 }
1288 else
1289 {
1290 /* append IORB to end of queue */
1291 pIorb->f16NxtIORB = FAR16NULL;
1292
1293 if (queue->vRoot == FAR16NULL)
1294 {
1295 queue->vRoot = vIorb;
1296 }
1297 else
1298 {
1299 ((IORBH *)Far16ToFlat(queue->vTail))->f16NxtIORB = vIorb;
1300 }
1301 queue->vTail = vIorb;
1302 }
1303
1304 #ifdef DEBUG
1305 {
1306 /* determine queue type (local, driver, abort or port) and minimum debug
1307 * level; otherwise, queue debug prints can become really confusing.
1308 */
1309 char *queue_type;
1310 int min_debug = DBG_DETAILED;
1311
1312 if ((u32)queue >> 16 == (u32)&queue >> 16) /* DAZ this is bogus */
1313 {
1314 /* this queue is on the stack */
1315 queue_type = "local";
1316 min_debug = DBG_DETAILED;
1317 }
1318 else if (queue == &driver_queue)
1319 {
1320 queue_type = "driver";
1321 }
1322 else if (queue == &abort_queue)
1323 {
1324 queue_type = "abort";
1325 min_debug = DBG_DETAILED;
1326 }
1327 else
1328 {
1329 queue_type = "port";
1330 }
1331
1332 dprintf(min_debug, DBG_PREFIX": IORB %x queued (cmd=%d/%d queue=%x [%s], timeout=%d)\n",
1333 vIorb, pIorb->CommandCode, pIorb->CommandModifier, queue, queue_type,
1334 pIorb->Timeout);
1335 }
1336 #endif
1337}
1338
1339/******************************************************************************
1340 * Remove an IORB from the specified queue. This function must be called with
1341 * the adapter-level spinlock aquired.
1342 */
1343int iorb_queue_del(IORB_QUEUE *queue, IORBH FAR16DATA *vIorb)
1344{
1345 IORBH FAR16DATA *_vIorb;
1346 IORBH FAR16DATA *_vPrev = FAR16NULL;
1347 int found = 0;
1348
1349 for (_vIorb = queue->vRoot; _vIorb != FAR16NULL; )
1350 {
1351 IORBH *_pIorb = Far16ToFlat(_vIorb);
1352 if (_vIorb == vIorb)
1353 {
1354 /* found the IORB to be removed */
1355 if (_vPrev != FAR16NULL)
1356 {
1357 ((IORBH*)Far16ToFlat(_vPrev))->f16NxtIORB = _pIorb->f16NxtIORB;
1358 }
1359 else
1360 {
1361 queue->vRoot = _pIorb->f16NxtIORB;
1362 }
1363 if (_vIorb == queue->vTail)
1364 {
1365 queue->vTail = _vPrev;
1366 }
1367 found = 1;
1368 break;
1369 }
1370 _vPrev = _vIorb;
1371 _vIorb = _pIorb->f16NxtIORB;
1372 }
1373
1374 #ifdef DEBUG
1375 if (found)
1376 {
1377 DPRINTF(DBG_DETAILED, DBG_PREFIX": IORB %x removed (queue = %x)\n", vIorb, queue);
1378 }
1379 else
1380 {
1381 DPRINTF(0, DBG_PREFIX": IORB %x not found in queue %x\n", vIorb, queue);
1382 }
1383 #endif
1384
1385 return(!found);
1386}
1387
1388/******************************************************************************
1389 * Set the error code in the specified IORB
1390 *
1391 * NOTE: This function does *not* call iorb_done(). It merely sets the IORB
1392 * status to the specified error code.
1393 */
1394void iorb_seterr(IORBH *pIorb, USHORT error_code)
1395{
1396 pIorb->ErrorCode = error_code;
1397 pIorb->Status |= IORB_ERROR;
1398}
1399
1400/******************************************************************************
1401 * Mark the specified IORB as done and notify the asynchronous post function,
1402 * if any. The IORB is also removed from the corresponding IORB queue.
1403 *
1404 * NOTES: This function does not clear the Status field; it merely adds the
1405 * IORB_DONE flag.
1406 *
1407 * This function is expected to be called *without* the corresponding
1408 * driver-level drv_lock aquired. It will aquire the spinlock before
1409 * updating the IORB queue and release it before notifying the upstream
1410 * code in order to prevent deadlocks.
1411 *
1412 * Due to this logic, this function is only good for simple task-time
1413 * completions. Functions working on lists of IORBs (such as interrupt
1414 * handlers or context hooks) should call iorb_complete() directly and
1415 * implement their own logic for removing the IORB from the port queue.
1416 * See abort_ctxhook() for an example.
1417 */
1418void iorb_done(IORBH FAR16DATA *vIorb, IORBH *pIorb)
1419{
1420 int a = iorb_unit_adapter(pIorb);
1421 int p = iorb_unit_port(pIorb);
1422
1423 /* remove IORB from corresponding queue */
1424 spin_lock(drv_lock);
1425 if (iorb_driver_level(pIorb))
1426 {
1427 iorb_queue_del(&driver_queue, vIorb);
1428 }
1429 else
1430 {
1431 iorb_queue_del(&ad_infos[a].ports[p].iorb_queue, vIorb);
1432 }
1433 aws_free(add_workspace(pIorb));
1434 spin_unlock(drv_lock);
1435
1436 iorb_complete(vIorb, pIorb);
1437}
1438
1439/******************************************************************************
1440 * Complete an IORB. This should be called without the adapter-level spinlock
1441 * to allow the IORB completion routine to perform whatever processing it
1442 * requires. This implies that the IORB should no longer be in any global
1443 * queue because the IORB completion routine may well reuse the IORB and send
1444 * the next request to us before even returning from this function.
1445 */
1446void iorb_complete(IORBH FAR16DATA *vIorb, IORBH *pIorb)
1447{
1448 pIorb->Status |= IORB_DONE;
1449
1450 DPRINTF(DBG_FUNCBEG, DBG_PREFIX": IORB %x complete status=0x%04x error=0x%04x\n",
1451 vIorb, pIorb->Status, pIorb->ErrorCode);
1452
1453 if (pIorb->RequestControl & IORB_ASYNC_POST)
1454 {
1455 Dev32Help_CallFar16((PFNFAR16)pIorb->f16NotifyAddress, vIorb);
1456 }
1457}
1458
1459/******************************************************************************
1460 * Requeue the specified IORB such that it will be sent downstream for
1461 * processing again. This includes freeing all resources currently allocated
1462 * (timer, buffer, ...) and resetting the flags to 0. The driver-level
1463 * spinlock must be aquired when calling this function.
1464 *
1465 * The following flags are preserved:
1466 * - no_ncq
1467 */
1468void iorb_requeue(IORBH *pIorb)
1469{
1470 ADD_WORKSPACE *aws = add_workspace(pIorb);
1471 u16 no_ncq = aws->no_ncq;
1472 u16 unaligned = aws->unaligned;
1473 u16 retries = aws->retries;
1474
1475 aws_free(aws);
1476 memset(aws, 0x00, sizeof(*aws));
1477
1478 aws->no_ncq = no_ncq;
1479 aws->unaligned = unaligned;
1480 aws->retries = retries;
1481}
1482
1483/******************************************************************************
1484 * Free resources in ADD workspace (timer, buffer, ...). This function should
1485 * be called with the spinlock held to prevent race conditions.
1486 */
1487void aws_free(ADD_WORKSPACE *aws)
1488{
1489 if (aws->timer != 0)
1490 {
1491 Timer_CancelTimer(aws->timer);
1492 aws->timer = 0;
1493 }
1494
1495 if (aws->buf != NULL)
1496 {
1497 MemFree(aws->buf);
1498 aws->buf = NULL;
1499 }
1500}
1501
1502/******************************************************************************
1503 * Lock the adapter, waiting for availability if necessary. This is expected
1504 * to be called at task/request time without the driver-level spinlock
1505 * aquired. Don't call at interrupt time.
1506 */
1507void lock_adapter(AD_INFO *ai)
1508{
1509 TIMER Timer;
1510
1511 spin_lock(drv_lock);
1512 while (ai->busy)
1513 {
1514 spin_unlock(drv_lock);
1515 TimerInit(&Timer, 250);
1516 while (!TimerCheckAndBlock(&Timer));
1517 spin_lock(drv_lock);
1518 }
1519 ai->busy = 1;
1520 spin_unlock(drv_lock);
1521}
1522
1523/******************************************************************************
1524 * Unlock adapter (i.e. reset busy flag)
1525 */
1526void unlock_adapter(AD_INFO *ai)
1527{
1528 ai->busy = 0;
1529}
1530
1531/******************************************************************************
1532 * Timeout handler for I/O commands. Since timeout handling can involve
1533 * lengthy operations like port resets, the main code is located in a
1534 * separate function which is invoked via a context hook.
1535 */
1536void __syscall timeout_callback(ULONG timer_handle, ULONG ulArg)
1537{
1538 IORBH FAR16DATA *vIorb = (IORBH FAR16DATA *)CastULONGToFar16(ulArg);
1539 IORBH *pIorb = Far16ToFlat(vIorb);
1540 int a = iorb_unit_adapter(pIorb);
1541 int p = iorb_unit_port(pIorb);
1542
1543 Timer_CancelTimer(timer_handle);
1544 dprintf(0, DBG_PREFIX": timeout for IORB %x port=%x.%x\n", ulArg, a, p);
1545
1546 /* Trigger abort processing function. We don't really care whether this
1547 * succeeds because the only reason why it would fail should be multiple
1548 * calls to DevHelp_ArmCtxHook() before the context hook had a chance to
1549 * start executing, which leaves two scenarios:
1550 *
1551 * - We succeded in arming the context hook. Fine.
1552 *
1553 * - We armed the context hook a second time before it had a chance to
1554 * start executing. In this case, the already scheduled context hook
1555 * will process our IORB as well.
1556 */
1557 SafeArmCtxHook(ResetCtxHook_h, ulArg);
1558}
1559
1560/******************************************************************************
1561 * Reset handler watchdog. If a timeout occurs, a context hook is armed which
1562 * will execute as soon as a kernel thread yields the CPU. However, some
1563 * kernel components won't yield the CPU during the early boot phase and the
1564 * only way to kick some sense into those components is to run the context
1565 * hook right inside this timer callback. Not exactly pretty, especially
1566 * considering the fact that context hooks were implemented to prevent running
1567 * lengthy operations like a port reset at interrupt time, but without this
1568 * watchdog mechanism we run the risk of getting completely stalled by device
1569 * problems during the early boot phase.
1570 */
1571void __syscall WatchdogTimer(ULONG timer_handle, ULONG ulArg)
1572{
1573 /* reset watchdog timer */
1574 Timer_CancelTimer(timer_handle);
1575 th_watchdog = 0;
1576 dprintf(0, DBG_PREFIX": BEG %s\n", ulArg==ResetCtxHook_h?"Reset":"Restart");
1577
1578 /* The ctxhook cannot be called directly because it does things
1579 * that are illegal in an interrupt handler.
1580 */
1581 SafeArmCtxHook(ulArg, 0);
1582}
1583
1584/******************************************************************************
1585 * Add unit info to ADAPTERINFO array (IOCC_GET_DEVICE_TABLE requests). The
1586 * adapter info array in the device table, dt->pAdapter[], is expected to be
1587 * initialized for the specified index (dt_ai).
1588 *
1589 * Please note that the device table adapter index, dta, is not always equal
1590 * to the physical adapter index, a: if SCSI emulation has been activated, the
1591 * last reported adapter is a virtual SCSI adapter and the physical adapter
1592 * indexes for those units are, of course, different from the device table
1593 * index of the virtual SCSI adapter.
1594 */
1595static int add_unit_info(IORB_CONFIGURATION *pIorb_conf, int dta,
1596 int a, int p, int d, int scsi_id)
1597{
1598 DEVICETABLE *pDt = Far16ToFlat(pIorb_conf->f16DeviceTable);
1599 ADAPTERINFO *pPtr;
1600 UNITINFO *pUi;
1601 AD_INFO *ai = ad_infos + a;
1602
1603 pPtr = (ADAPTERINFO *)MakeFlatFromNear16(pIorb_conf->f16DeviceTable, pDt->pAdapter[dta]);
1604 //DPRINTF(2,"add_unit_info: ptr=%x dta=%x pAdapter[dta]=%x pDeviceTable=%x\n",
1605 // ptr, dta, dt->pAdapter[dta], iorb_conf->pDeviceTable);
1606
1607 pUi = &pPtr->UnitInfo[pPtr->AdapterUnits];
1608
1609 if ((u32)(pUi + 1) - (u32)pDt > pIorb_conf->DeviceTableLen)
1610 {
1611 dprintf(0, DBG_PREFIX": error: device table provided by DASD too small\n");
1612 iorb_seterr(&pIorb_conf->iorbh, IOERR_CMD_SW_RESOURCE);
1613 return(-1);
1614 }
1615
1616 if (ai->ports[p].devs[d].unit_info == NULL)
1617 {
1618 /* provide original information about this device (unit) */
1619 memset(pUi, 0x00, sizeof(*pUi));
1620 pUi->AdapterIndex = dta; /* device table adapter index */
1621 pUi->UnitHandle = iorb_unit(a, p, d); /* physical adapter index */
1622 pUi->UnitIndex = pPtr->AdapterUnits;
1623 pUi->UnitType = ai->ports[p].devs[d].dev_type;
1624 pUi->QueuingCount = ai->ports[p].devs[d].ncq_max;
1625 if (ai->ports[p].devs[d].removable) pUi->UnitFlags |= UF_REMOVABLE;
1626 if (ai->ports[p].devs[d].ignored) pUi->UnitFlags |= UF_NODASD_SUPT;
1627 if (scsi_id > 0) pUi->UnitSCSITargetID = scsi_id; /* set fake SCSI ID for this unit */
1628 }
1629 else
1630 {
1631 /* copy updated device (unit) information (IOCM_CHANGE_UNITINFO) */
1632 memcpy(pUi, ai->ports[p].devs[d].unit_info, sizeof(*pUi));
1633 }
1634
1635 pPtr->AdapterUnits++;
1636 return(0);
1637}
1638
Note: See TracBrowser for help on using the repository browser.