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

Last change on this file since 213 was 213, checked in by David Azarewicz, 12 hours ago

Update version

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 if (iInvertOption) D32g_DbgFlags &= ~D32DBGF_WRAP;
405 else D32g_DbgFlags |= D32DBGF_WRAP;
406 continue;
407 }
408
409 iprintf("OS2AHCI.ADD: Unrecognized switch: %s", pszCmdLine-1);
410 iStatus = 1; /* unrecognized argument */
411 }
412
413 if (iStatus) goto init_fail;
414
415 dprintf(0,"BldLevel: %s\n", BldLevel);
416 dprintf(0,"CmdLine: %s\n", cmd_line);
417 /*
418 if (sizeof(ADD_WORKSPACE) > ADD_WORKSPACE_SIZE)
419 {
420 dprintf(0,"ADD_WORKSPACE size is too big! %d>16\n", sizeof(ADD_WORKSPACE));
421 goto init_fail;
422 }
423 */
424
425 /* print initialization message */
426 if (iVerbose) iprintf("OS2AHCI.ADD: driver version %d.%02d", DMAJOR, DMINOR);
427
428 #if defined(TESTVER) && defined(DEBUG)
429 #include "testver.c"
430 #endif
431
432 /* scan PCI bus for supported devices */
433 scan_pci_bus();
434
435 if (ad_info_cnt > 0)
436 {
437 /* initialization succeeded and we found at least one AHCI adapter */
438
439 if (Dev32Help_RegisterDeviceClass(drv_name, add_entry, 0, 1, &add_handle))
440 {
441 iprintf("OS2AHCI.ADD: couldn't register device class");
442 goto init_fail;
443 }
444
445 Timer_InitTimer(TIMER_COUNT);
446
447 /* allocate context hooks */
448 KernAllocateContextHook(RestartCtxHook, 0, &RestartCtxHook_h);
449 KernAllocateContextHook(ResetCtxHook, 0, &ResetCtxHook_h);
450 KernAllocateContextHook(engine_ctxhook, 0, &engine_ctxhook_h);
451
452 /* register kernel exit routine for trap dumps */
453 Dev32Help_RegisterKrnlExit(shutdown_driver, FLAG_KRNL_EXIT_ADD, TYPE_KRNL_EXIT_INT13);
454
455 return(RPDONE);
456 }
457 else
458 {
459 /* no adapters found */
460 if (iVerbose) iprintf("OS2AHCI.ADD: No adapters found.");
461 }
462
463init_fail:
464 /* initialization failed; set segment sizes to 0 and return error */
465 init_drv_failed = 1;
466
467 if (rm_drvh != 0)
468 {
469 /* remove driver from resource manager */
470 RMDestroyDriver(rm_drvh);
471 }
472
473 if (iVerbose) iprintf("OS2AHCI.ADD: driver *not* installed");
474 return(RPDONE | RPERR_INITFAIL);
475}
476
477/******************************************************************************
478 * Generic IOCTL via character device driver. IOCTLs are used to control the
479 * driver operation and to execute native ATA and ATAPI (SCSI) commands from
480 * ring 3 applications. On top of that, some predefined IOCTLs (e.g. SMART
481 * commands for ATA disks) are implemented here.
482 */
483USHORT gen_ioctl(REQPACKET *ioctl)
484{
485 DPRINTF(DBG_FUNCBEG, DBG_PREFIX": IOCTL 0x%x/0x%x\n", ioctl->ioctl.bCategory, ioctl->ioctl.bFunction);
486
487 switch (ioctl->ioctl.bCategory)
488 {
489 case OS2AHCI_IOCTL_CATEGORY:
490 switch (ioctl->ioctl.bFunction)
491 {
492 case OS2AHCI_IOCTL_GET_DEVLIST:
493 return(ioctl_get_devlist(ioctl));
494
495 case OS2AHCI_IOCTL_PASSTHROUGH:
496 return(ioctl_passthrough(ioctl));
497 }
498 break;
499
500 case DSKSP_CAT_GENERIC:
501 return(ioctl_gen_dsk(ioctl));
502
503 case DSKSP_CAT_SMART:
504 return(ioctl_smart(ioctl));
505 }
506
507 return(RPDONE | RPERR_BADCOMMAND);
508}
509
510/******************************************************************************
511 * Read from character device. If tracing is on (internal ring buffer trace),
512 * we return data from the trace buffer; if not, we might return a device
513 * dump similar to IBM1S506.ADD/DANIS506.ADD (TODO).
514 */
515USHORT char_dev_input(REQPACKET *pPacket)
516{
517 void *LinAdr;
518
519 if (Dev32Help_PhysToLin(pPacket->io.ulAddress, pPacket->io.usCount, &LinAdr))
520 {
521 pPacket->io.usCount = 0;
522 return RPDONE | RPERR_GENERAL;
523 }
524
525 pPacket->io.usCount = dCopyToUser(LinAdr, pPacket->io.usCount);
526
527 return RPDONE;
528}
529
530/******************************************************************************
531 * Device driver exit handler. This handler is called when OS/2 shuts down and
532 * flushes the write caches of all attached devices. Since this is effectively
533 * the same we do when suspending, we'll call out to the corresponding suspend
534 * function.
535 *
536 * NOTE: Errors are ignored because there's no way we could stop the shutdown
537 * or do something about the error, unless retrying endlessly is
538 * considered an option.
539 */
540USHORT exit_drv(int func)
541{
542 DPRINTF(DBG_FUNCBEG|DBG_INIT, DBG_PREFIX": exit_drv(%d) called\n", func);
543
544 if (func == 0)
545 {
546 /* we're only interested in the second phase of the shutdown */
547 return(RPDONE);
548 }
549
550 suspend();
551 return(RPDONE);
552}
553
554/******************************************************************************
555 * Device driver suspend/resume handler. This handler is called when ACPI is
556 * executing a suspend or resume.
557 */
558USHORT sr_drv(int func)
559{
560 DPRINTF(DBG_FUNCBEG|DBG_INIT, DBG_PREFIX": sr_drv(%d) called\n", func);
561
562 if (func) resume();
563 else suspend();
564
565 return(RPDONE);
566}
567
568/******************************************************************************
569 * ADD entry point. This is the main entry point for all ADD requests. Due to
570 * the asynchronous nature of ADD drivers, this function primarily queues the
571 * IORB(s) to the corresponding adapter or port queues, then triggers the
572 * state machine to initiate processing queued IORBs.
573 *
574 * NOTE: In order to prevent race conditions or engine stalls, certain rules
575 * around locking, unlocking and IORB handling in general have been
576 * established. Refer to the comments in "trigger_engine()" for
577 * details.
578 */
579void add_entry(IORBH FAR16DATA *vFirstIorb)
580{
581 IORBH FAR16DATA *vIorb;
582 IORBH FAR16DATA *vNext = FAR16NULL;
583
584 spin_lock(drv_lock);
585
586 for (vIorb=vFirstIorb; vIorb!=FAR16NULL; vIorb=vNext)
587 {
588 IORBH *pIorb = Far16ToFlat(vIorb);
589
590 /* Queue this IORB. Queues primarily exist on port level but there are
591 * some requests which affect the whole driver, most notably
592 * IOCC_CONFIGURATION. In either case, adding the IORB to the driver or
593 * port queue will change the links, thus we need to save the original
594 * link in 'vNext'.
595 */
596 if (pIorb->RequestControl & IORB_CHAIN) vNext = pIorb->f16NxtIORB;
597 else vNext = (IORBH FAR16DATA *)0;
598
599 pIorb->Status = 0;
600 pIorb->ErrorCode = 0;
601 memset(&pIorb->ADDWorkSpace, 0x00, sizeof(ADD_WORKSPACE));
602
603 //#ifdef DEBUG
604 //DumpIorb(pIorb); /* DAZ TESTING */
605 //#endif
606
607 if (iorb_driver_level(pIorb))
608 {
609 /* driver-level IORB */
610 pIorb->UnitHandle = 0;
611 iorb_queue_add(&driver_queue, vIorb, pIorb);
612 }
613 else
614 {
615 /* port-level IORB */
616 int a = iorb_unit_adapter(pIorb);
617 int p = iorb_unit_port(pIorb);
618 int d = iorb_unit_device(pIorb);
619
620 if (a >= ad_info_cnt ||
621 p > ad_infos[a].port_max ||
622 d > ad_infos[a].ports[p].dev_max ||
623 (ad_infos[a].port_map & (1UL << p)) == 0)
624 {
625 /* unit handle outside of the allowed range */
626 dprintf(0, DBG_PREFIX": ERROR: IORB for %d.%d.%d is out of range\n", a, p, d);
627 pIorb->Status = IORB_ERROR;
628 pIorb->ErrorCode = IOERR_CMD_SYNTAX;
629 iorb_complete(vIorb, pIorb);
630 continue;
631 }
632
633 iorb_queue_add(&ad_infos[a].ports[p].iorb_queue, vIorb, pIorb);
634 }
635 }
636
637 /* trigger state machine */
638 trigger_engine();
639
640 spin_unlock(drv_lock);
641}
642
643/******************************************************************************
644 * Trigger IORB queue engine. This is a wrapper function for trigger_engine_1()
645 * which will try to get all IORBs sent on their way a couple of times. If
646 * there are still IORBs ready for processing after this, this function will
647 * hand off to a context hook which will continue to trigger the engine until
648 * all IORBs have been sent.
649 *
650 * NOTE: While initialization has not completed (or during suspend/resume
651 * operations), this function will loop indefinitely because we can't
652 * rely on interrupt handlers or context hooks and complex IORBs
653 * requiring multiple requeues would eventually hang and time out if
654 * we stopped triggering here.
655 */
656void trigger_engine(void)
657{
658 int i;
659
660 for (i = 0; i < 3 || !init_complete; i++)
661 {
662 if (trigger_engine_1() == 0)
663 {
664 /* done -- all IORBs have been sent on their way */
665 return;
666 }
667 }
668
669 /* Something keeps bouncing; hand off to the engine context hook which will
670 * keep trying in the background.
671 */
672 KernArmHook(engine_ctxhook_h, 0, 0);
673}
674
675/******************************************************************************
676 * Trigger IORB queue engine in order to send commands in the driver/port IORB
677 * queues to the AHCI hardware. This function will return the number of IORBs
678 * sent. Keep in mind that IORBs might "bounce" if the adapter/port is not in
679 * a state to accept the command, thus it might take quite a few calls to get
680 * all IORBs on their way. This is why there's a wrapper function which tries
681 * it a few times, then hands off to a context hook which will keep trying in
682 * the background.
683 *
684 * IORBs might complete before send_iorb() has returned, at any time during
685 * interrupt processing or on another CPU on SMP systems. IORB completion
686 * means modifications to the corresponding IORB queue (the completed IORB
687 * is removed from the queue) thus we need to protect the IORB queues from
688 * race conditions. The safest approach short of keeping the driver-level
689 * spinlock aquired permanently is to keep it throughout this function and
690 * release it temporarily in send_iorb().
691 *
692 * This implies that the handler functions are fully responsible for aquiring
693 * the driver-level spinlock when they need it, and for releasing it again.
694 *
695 * As a rule of thumb, get the driver-level spinlock whenever accessing
696 * volatile variables (IORB queues, values in ad_info[], ...).
697 *
698 * Additional Notes:
699 *
700 * - This function is expected to be called with the spinlock aquired
701 *
702 * - Adapters can be flagged as 'busy' which means no new IORBs are sent (they
703 * just remain in the queue). This can be used to release the driver-level
704 * spinlock while making sure no new IORBs are going to hit the hardware.
705 * In order to prevent engine stalls, all handlers using this functionality
706 * need to invoke trigger_engine() after resetting the busy flag.
707 *
708 * - Driver-level IORBs are not synchronized by adapter-level 'busy' flags.
709 * However, the driver-level queue is worked "one entry at a time" which
710 * means that no new IORBs will be queued on the driver-level queue until
711 * the head element has completed processing. This means that driver-
712 * level IORB handlers don't need to protect against each other. But they
713 * they do need to keep in mind interference with port-level IORBs:
714 *
715 * - Driver-level IORB handlers must obtain the spinlock and/or flag all
716 * adapters as 'busy' which are affected by the driver-level IORB
717 *
718 * - Driver-level IORB handlers must not access the hardware of a
719 * particular adapter if it's flagged as 'busy' by another IORB.
720 */
721int trigger_engine_1(void)
722{
723 IORBH FAR16DATA *vIorb;
724 IORBH *pIorb;
725 IORBH FAR16DATA *vNext;
726 int iorbs_sent = 0;
727 int a;
728 int p;
729
730 iorbs_sent = 0;
731
732 /* process driver-level IORBs */
733 if ((vIorb = driver_queue.vRoot) != FAR16NULL)
734 {
735 pIorb = Far16ToFlat(vIorb);
736
737 if (!add_workspace(pIorb)->processing)
738 {
739 send_iorb(vIorb, pIorb);
740 iorbs_sent++;
741 }
742 }
743
744 /* process port-level IORBs */
745 for (a = 0; a < ad_info_cnt; a++)
746 {
747 AD_INFO *ai = ad_infos + a;
748 if (ai->busy)
749 {
750 /* adapter is busy; don't process any IORBs */
751 continue;
752 }
753 for (p = 0; p <= ai->port_max; p++)
754 {
755 /* send all queued IORBs on this port */
756 vNext = FAR16NULL;
757 for (vIorb = ai->ports[p].iorb_queue.vRoot; vIorb != FAR16NULL; vIorb = vNext)
758 {
759 pIorb = Far16ToFlat(vIorb);
760
761 vNext = pIorb->f16NxtIORB;
762 if (!add_workspace(pIorb)->processing)
763 {
764 send_iorb(vIorb, pIorb);
765 iorbs_sent++;
766 }
767 }
768 }
769 }
770
771 return(iorbs_sent);
772}
773
774/******************************************************************************
775 * Send a single IORB to the corresponding AHCI adapter/port. This is just a
776 * switch board for calling the corresponding iocc_*() handler function.
777 *
778 * NOTE: This function is expected to be called with the driver-level spinlock
779 * aquired. It will release it before calling any of the handler
780 * functions and re-aquire it when done.
781 */
782void send_iorb(IORBH FAR16DATA *vIorb, IORBH *pIorb)
783{
784 /* Mark IORB as "processing" before doing anything else. Once the IORB is
785 * marked as "processing", we can release the spinlock because subsequent
786 * invocations of trigger_engine() (e.g. at interrupt time) will ignore this
787 * IORB.
788 */
789 add_workspace(pIorb)->processing = 1;
790 spin_unlock(drv_lock);
791
792 switch (pIorb->CommandCode)
793 {
794 case IOCC_CONFIGURATION:
795 iocc_configuration(vIorb, pIorb);
796 break;
797
798 case IOCC_DEVICE_CONTROL:
799 iocc_device_control(vIorb, pIorb);
800 break;
801
802 case IOCC_UNIT_CONTROL:
803 iocc_unit_control(vIorb, pIorb);
804 break;
805
806 case IOCC_GEOMETRY_64:
807 add_workspace(pIorb)->fIs64bit = 1;
808 /* fall thru */
809 case IOCC_GEOMETRY:
810 iocc_geometry(vIorb, pIorb);
811 break;
812
813 case IOCC_EXECUTE_IO_64:
814 add_workspace(pIorb)->fIs64bit = 1;
815 /* fall thru */
816 case IOCC_EXECUTE_IO:
817 iocc_execute_io(vIorb, pIorb);
818 break;
819
820 case IOCC_UNIT_STATUS:
821 iocc_unit_status(vIorb, pIorb);
822 break;
823
824 case IOCC_ADAPTER_PASSTHRU:
825 AhciStats.ulTestCount1++;
826 iocc_adapter_passthru(vIorb, pIorb);
827 break;
828
829 default:
830 /* unsupported call */
831 iorb_seterr(pIorb, IOERR_CMD_NOT_SUPPORTED);
832 iorb_done(vIorb, pIorb);
833 break;
834 }
835
836 /* re-aquire spinlock before returning to trigger_engine() */
837 spin_lock(drv_lock);
838}
839
840/******************************************************************************
841 * Handle IOCC_CONFIGURATION requests.
842 */
843void iocc_configuration(IORBH FAR16DATA *vIorb, IORBH *pIorb)
844{
845 int a;
846
847 switch (pIorb->CommandModifier)
848 {
849
850 case IOCM_COMPLETE_INIT:
851 /* Complete initialization. From now on, we won't have to restore the BIOS
852 * configuration after each command and we're fully operational (i.e. will
853 * use interrupts, timers and context hooks instead of polling).
854 */
855 if (!init_complete)
856 {
857 DPRINTF(DBG_INIT, DBG_PREFIX": leaving initialization mode\n");
858 for (a = 0; a < ad_info_cnt; a++)
859 {
860 lock_adapter(ad_infos + a);
861 ahci_complete_init(ad_infos + a);
862 }
863 init_complete = 1;
864
865 /* release all adapters */
866 for (a = 0; a < ad_info_cnt; a++)
867 {
868 unlock_adapter(ad_infos + a);
869 }
870 DPRINTF(DBG_INIT, DBG_PREFIX": leaving initialization mode 2\n");
871
872 #ifdef LEGACY_APM
873 /* register APM hook */
874 apm_init();
875 #endif
876 }
877 iorb_done(vIorb, pIorb);
878 break;
879
880 case IOCM_GET_DEVICE_TABLE:
881 /* construct a device table */
882 iocm_device_table(vIorb, pIorb);
883 break;
884
885 default:
886 iorb_seterr(pIorb, IOERR_CMD_NOT_SUPPORTED);
887 iorb_done(vIorb, pIorb);
888 break;
889 }
890}
891
892/******************************************************************************
893 * Handle IOCC_DEVICE_CONTROL requests.
894 */
895void iocc_device_control(IORBH FAR16DATA *vIorb, IORBH *pIorb)
896{
897 AD_INFO *ai = ad_infos + iorb_unit_adapter(pIorb);
898 IORBH FAR16DATA *vPtr;
899 IORBH FAR16DATA *vNext = FAR16NULL;
900 int p = iorb_unit_port(pIorb);
901 int d = iorb_unit_device(pIorb);
902
903 switch (pIorb->CommandModifier)
904 {
905 case IOCM_ABORT:
906 /* abort all pending commands on specified port and device */
907 spin_lock(drv_lock);
908 for (vPtr = ai->ports[p].iorb_queue.vRoot; vPtr != FAR16NULL; vPtr = vNext)
909 {
910 IORBH *pPtr = Far16ToFlat(vPtr);
911
912 vNext = pPtr->f16NxtIORB;
913 /* move all matching IORBs to the abort queue */
914 if (vPtr != vIorb && iorb_unit_device(pPtr) == d)
915 {
916 iorb_queue_del(&ai->ports[p].iorb_queue, vPtr);
917 iorb_queue_add(&abort_queue, vPtr, pPtr);
918 pPtr->ErrorCode = IOERR_CMD_ABORTED;
919 }
920 }
921 spin_unlock(drv_lock);
922
923 /* trigger reset context hook which will finish the abort processing */
924 SafeArmCtxHook(ResetCtxHook_h, 0);
925 break;
926
927 case IOCM_SUSPEND:
928 case IOCM_RESUME:
929 case IOCM_GET_QUEUE_STATUS:
930 /* Suspend/resume operations allow access to the hardware for other
931 * entities such as IBMIDECD.FLT. Since os2ahci implements both ATA
932 * and ATAPI in the same driver, this won't be required.
933 */
934 iorb_seterr(pIorb, IOERR_CMD_NOT_SUPPORTED);
935 break;
936
937 case IOCM_LOCK_MEDIA:
938 case IOCM_UNLOCK_MEDIA:
939 case IOCM_EJECT_MEDIA:
940 /* unit control commands to lock, unlock and eject media */
941 /* will be supported later... */
942 iorb_seterr(pIorb, IOERR_CMD_NOT_SUPPORTED);
943 break;
944
945 default:
946 iorb_seterr(pIorb, IOERR_CMD_NOT_SUPPORTED);
947 break;
948 }
949
950 iorb_done(vIorb, pIorb);
951}
952
953/******************************************************************************
954 * Handle IOCC_UNIT_CONTROL requests.
955 */
956void iocc_unit_control(IORBH FAR16DATA *vIorb, IORBH *pIorb)
957{
958 IORB_UNIT_CONTROL *pIorb_uc = (IORB_UNIT_CONTROL *)pIorb;
959 int a = iorb_unit_adapter(pIorb);
960 int p = iorb_unit_port(pIorb);
961 int d = iorb_unit_device(pIorb);
962
963 spin_lock(drv_lock);
964 switch (pIorb->CommandModifier)
965 {
966 case IOCM_ALLOCATE_UNIT:
967 /* allocate unit for exclusive access */
968 if (ad_infos[a].ports[p].devs[d].allocated)
969 {
970 iorb_seterr(pIorb, IOERR_UNIT_ALLOCATED);
971 }
972 else
973 {
974 ad_infos[a].ports[p].devs[d].allocated = 1;
975 }
976 break;
977
978 case IOCM_DEALLOCATE_UNIT:
979 /* deallocate exclusive access to unit */
980 if (!ad_infos[a].ports[p].devs[d].allocated)
981 {
982 iorb_seterr(pIorb, IOERR_UNIT_NOT_ALLOCATED);
983 }
984 else
985 {
986 ad_infos[a].ports[p].devs[d].allocated = 0;
987 }
988 break;
989
990 case IOCM_CHANGE_UNITINFO:
991 /* Change unit (device) information. One reason for this IOCM is the
992 * interface for filter device drivers: a filter device driver can
993 * either change existing UNITINFOs or permanently allocate units
994 * and fabricate new [logical] units; the former is the reason why we
995 * must store the pointer to the updated UNITNIFO for subsequent
996 * IOCC_CONFIGURATION/IOCM_GET_DEVICE_TABLE calls.
997 */
998 if (!ad_infos[a].ports[p].devs[d].allocated)
999 {
1000 iorb_seterr(pIorb, IOERR_UNIT_NOT_ALLOCATED);
1001 break;
1002 }
1003 ad_infos[a].ports[p].devs[d].unit_info = pIorb_uc->f16UnitInfo;
1004 break;
1005
1006 default:
1007 iorb_seterr(pIorb, IOERR_CMD_NOT_SUPPORTED);
1008 break;
1009 }
1010
1011 spin_unlock(drv_lock);
1012 iorb_done(vIorb, pIorb);
1013}
1014
1015/******************************************************************************
1016 * Scan all ports for AHCI devices and construct a DASD device table.
1017 *
1018 * NOTES: This function may be called multiple times. Only the first
1019 * invocation will actually scan for devices; all subsequent calls will
1020 * merely return the results of the initial scan, potentially augmented
1021 * by modified unit infos after IOCC_CONFIGURATION/IOCM_CHANGE_UNITINFO
1022 * requests.
1023 *
1024 * In order to support applications that can't deal with ATAPI devices
1025 * (i.e. need a SCSI adapter) os2ahci will optionally report ATAPI
1026 * dvices as SCSI devices. The corresponding SCSI adapter doesn't
1027 * really exist and is only reported here for the IOCM_GET_DEVICETABLE
1028 * request. The units attached to this adapter will use the real HW
1029 * unit IDs, thus we'll never receive a command specific to the
1030 * emulated SCSI adapter and won't need to set up any sort of entity
1031 * for it; the only purpose of the emulated SCSI adapter is to pass the
1032 * bus type "AI_DEVBUS_SCSI_2" upstream, and the emulated units, of
1033 * course. The emulated SCSI target IDs are allocated as follows:
1034 *
1035 * 0 the virtual adapter
1036 * 1..n emulated devices; SCSI target ID increments sequentially
1037 */
1038void iocm_device_table(IORBH FAR16DATA *vIorb, IORBH *pIorb)
1039{
1040 IORB_CONFIGURATION *pIorb_conf;
1041 DEVICETABLE FAR16DATA *vDt;
1042 DEVICETABLE *pDt;
1043 char *pPos;
1044 int scsi_units = 0;
1045 int scsi_id = 1;
1046 int rc;
1047 int dta;
1048 int a;
1049 int p;
1050 int d;
1051
1052 pIorb_conf = (IORB_CONFIGURATION *)pIorb;
1053 vDt = pIorb_conf->f16DeviceTable;
1054 pDt = Far16ToFlat(vDt);
1055
1056 spin_lock(drv_lock);
1057
1058 /* initialize device table header */
1059 pDt->ADDLevelMajor = ADD_LEVEL_MAJOR;
1060 pDt->ADDLevelMinor = ADD_LEVEL_MINOR;
1061 pDt->ADDHandle = add_handle;
1062 pDt->TotalAdapters = ad_info_cnt + 1;
1063
1064 /* set start of adapter and device information tables */
1065 pPos = (char*)&pDt->pAdapter[pDt->TotalAdapters];
1066
1067 /* go through all adapters, including the virtual SCSI adapter */
1068 for (dta = 0; dta < pDt->TotalAdapters; dta++)
1069 {
1070 ADAPTERINFO *pPtr = (ADAPTERINFO *)pPos;
1071
1072 /* sanity check for sufficient space in device table */
1073 if ((u32)(pPtr + 1) - (u32)pDt > pIorb_conf->DeviceTableLen)
1074 {
1075 dprintf(0, DBG_PREFIX": error: device table provided by DASD too small\n");
1076 iorb_seterr(pIorb, IOERR_CMD_SW_RESOURCE);
1077 goto iocm_device_table_done;
1078 }
1079
1080 pDt->pAdapter[dta] = MakeNear16PtrFromDiff(pIorb_conf->f16DeviceTable, pDt, pPtr);
1081
1082 //DPRINTF(2,"iocm_device_table: ptr=%x dta=%x pAdapter[dta]=%x pDeviceTable=%x\n",
1083 // ptr, dta, dt->pAdapter[dta], iorb_conf->pDeviceTable);
1084 memset(pPtr, 0x00, sizeof(*pPtr));
1085
1086 pPtr->AdapterIOAccess = AI_IOACCESS_BUS_MASTER;
1087 pPtr->AdapterHostBus = AI_HOSTBUS_OTHER | AI_BUSWIDTH_32BIT;
1088 pPtr->AdapterFlags = AF_16M | AF_HW_SCATGAT;
1089 pPtr->MaxHWSGList = AHCI_MAX_SG / 2; /* AHCI S/G elements are 22 bits */
1090
1091 if (dta < ad_info_cnt)
1092 {
1093 /* this is a physical AHCI adapter */
1094 AD_INFO *ad_info = ad_infos + dta;
1095
1096 pPtr->AdapterDevBus = AI_DEVBUS_ST506 | AI_DEVBUS_32BIT;
1097 snprintf(pPtr->AdapterName, sizeof(pPtr->AdapterName), "AHCI_%d", dta);
1098
1099 if (!ad_info->port_scan_done)
1100 {
1101 /* first call; need to scan AHCI hardware for devices */
1102 if (ad_info->busy)
1103 {
1104 dprintf(0, DBG_PREFIX": error: port scan requested while adapter was busy\n");
1105 iorb_seterr(pIorb, IOERR_CMD_SW_RESOURCE);
1106 goto iocm_device_table_done;
1107 }
1108 ad_info->busy = 1;
1109 spin_unlock(drv_lock);
1110 rc = ahci_scan_ports(ad_info);
1111 spin_lock(drv_lock);
1112 ad_info->busy = 0;
1113
1114 if (rc != 0)
1115 {
1116 dprintf(0, DBG_PREFIX": error: port scan failed on adapter #%d\n", dta);
1117 iorb_seterr(pIorb, IOERR_CMD_SW_RESOURCE);
1118 goto iocm_device_table_done;
1119 }
1120 ad_info->port_scan_done = 1;
1121 }
1122
1123 /* insert physical (i.e. AHCI) devices into the device table */
1124 for (p = 0; p <= ad_info->port_max; p++)
1125 {
1126 for (d = 0; d <= ad_info->ports[p].dev_max; d++)
1127 {
1128 if (ad_info->ports[p].devs[d].present)
1129 {
1130 if (ad_info->ports[p].devs[d].atapi && emulate_scsi[dta][p])
1131 {
1132 /* report this unit as SCSI unit */
1133 scsi_units++;
1134 //continue;
1135 }
1136 if (add_unit_info(pIorb_conf, dta, dta, p, d, 0))
1137 {
1138 goto iocm_device_table_done;
1139 }
1140 }
1141 }
1142 }
1143 }
1144 else
1145 {
1146 /* this is the virtual SCSI adapter */
1147 if (scsi_units == 0)
1148 {
1149 /* not a single unit to be emulated via SCSI */
1150 pDt->TotalAdapters--;
1151 break;
1152 }
1153
1154 /* set adapter name and bus type to mimic a SCSI controller */
1155 pPtr->AdapterDevBus = AI_DEVBUS_SCSI_2 | AI_DEVBUS_16BIT;
1156 snprintf(pPtr->AdapterName, sizeof(pPtr->AdapterName), "AHCI_SCSI_0");
1157
1158 /* add all ATAPI units to be emulated by this virtual adaper */
1159 for (a = 0; a < ad_info_cnt; a++)
1160 {
1161 AD_INFO *ad_info = ad_infos + a;
1162
1163 for (p = 0; p <= ad_info->port_max; p++)
1164 {
1165 for (d = 0; d <= ad_info->ports[p].dev_max; d++)
1166 {
1167 if (ad_info->ports[p].devs[d].present && ad_info->ports[p].devs[d].atapi && emulate_scsi[a][p])
1168 {
1169 if (add_unit_info(pIorb_conf, dta, a, p, d, scsi_id++))
1170 {
1171 goto iocm_device_table_done;
1172 }
1173 }
1174 }
1175 }
1176 }
1177 }
1178
1179 /* calculate offset for next adapter */
1180 pPos = (char *)(pPtr->UnitInfo + pPtr->AdapterUnits);
1181 }
1182
1183iocm_device_table_done:
1184 spin_unlock(drv_lock);
1185 iorb_done(vIorb, pIorb);
1186}
1187
1188/******************************************************************************
1189 * Handle IOCC_GEOMETRY requests.
1190 */
1191void iocc_geometry(IORBH FAR16DATA *vIorb, IORBH *pIorb)
1192{
1193 switch (pIorb->CommandModifier)
1194 {
1195 case IOCM_GET_MEDIA_GEOMETRY:
1196 case IOCM_GET_DEVICE_GEOMETRY:
1197 add_workspace(pIorb)->idempotent = 1;
1198 ahci_get_geometry(vIorb, pIorb);
1199 break;
1200
1201 default:
1202 iorb_seterr(pIorb, IOERR_CMD_NOT_SUPPORTED);
1203 iorb_done(vIorb, pIorb);
1204 }
1205}
1206
1207/******************************************************************************
1208 * Handle IOCC_EXECUTE_IO requests.
1209 */
1210void iocc_execute_io(IORBH FAR16DATA *vIorb, IORBH *pIorb)
1211{
1212 switch (pIorb->CommandModifier)
1213 {
1214 case IOCM_READ:
1215 add_workspace(pIorb)->idempotent = 1;
1216 ahci_read(vIorb, pIorb);
1217 break;
1218
1219 case IOCM_READ_VERIFY:
1220 add_workspace(pIorb)->idempotent = 1;
1221 ahci_verify(vIorb, pIorb);
1222 break;
1223
1224 case IOCM_WRITE:
1225 case IOCM_WRITE_VERIFY:
1226 add_workspace(pIorb)->idempotent = 1;
1227 ahci_write(vIorb, pIorb);
1228 break;
1229
1230 default:
1231 iorb_seterr(pIorb, IOERR_CMD_NOT_SUPPORTED);
1232 iorb_done(vIorb, pIorb);
1233 }
1234}
1235
1236/******************************************************************************
1237 * Handle IOCC_UNIT_STATUS requests.
1238 */
1239void iocc_unit_status(IORBH FAR16DATA *vIorb, IORBH *pIorb)
1240{
1241 switch (pIorb->CommandModifier)
1242 {
1243 case IOCM_GET_UNIT_STATUS:
1244 add_workspace(pIorb)->idempotent = 1;
1245 ahci_unit_ready(vIorb, pIorb);
1246 break;
1247
1248 default:
1249 iorb_seterr(pIorb, IOERR_CMD_NOT_SUPPORTED);
1250 iorb_done(vIorb, pIorb);
1251 }
1252}
1253
1254/******************************************************************************
1255 * Handle IOCC_ADAPTER_PASSTHROUGH requests.
1256 */
1257void iocc_adapter_passthru(IORBH FAR16DATA *vIorb, IORBH *pIorb)
1258{
1259 switch (pIorb->CommandModifier)
1260 {
1261 case IOCM_EXECUTE_CDB:
1262 add_workspace(pIorb)->idempotent = 0;
1263 ahci_execute_cdb(vIorb, pIorb);
1264 break;
1265
1266 case IOCM_EXECUTE_ATA:
1267 add_workspace(pIorb)->idempotent = 0;
1268 ahci_execute_ata(vIorb, pIorb);
1269 break;
1270
1271 default:
1272 iorb_seterr(pIorb, IOERR_CMD_NOT_SUPPORTED);
1273 iorb_done(vIorb, pIorb);
1274 }
1275}
1276
1277/******************************************************************************
1278 * Add an IORB to the specified queue. This function must be called with the
1279 * adapter-level spinlock aquired.
1280 */
1281void iorb_queue_add(IORB_QUEUE *queue, IORBH FAR16DATA *vIorb, IORBH *pIorb)
1282{
1283 if (iorb_priority(pIorb)
1284 {
1285 /* priority IORB; insert at first position */
1286 pIorb->f16NxtIORB = queue->vRoot;
1287 queue->vRoot = vIorb;
1288 }
1289 else
1290 {
1291 /* append IORB to end of queue */
1292 pIorb->f16NxtIORB = FAR16NULL;
1293
1294 if (queue->vRoot == FAR16NULL)
1295 {
1296 queue->vRoot = vIorb;
1297 }
1298 else
1299 {
1300 ((IORBH *)Far16ToFlat(queue->vTail))->f16NxtIORB = vIorb;
1301 }
1302 queue->vTail = vIorb;
1303 }
1304
1305 #ifdef DEBUG
1306 {
1307 /* determine queue type (local, driver, abort or port) and minimum debug
1308 * level; otherwise, queue debug prints can become really confusing.
1309 */
1310 char *queue_type;
1311 int min_debug = DBG_DETAILED;
1312
1313 if ((u32)queue >> 16 == (u32)&queue >> 16) /* DAZ this is bogus */
1314 {
1315 /* this queue is on the stack */
1316 queue_type = "local";
1317 min_debug = DBG_DETAILED;
1318 }
1319 else if (queue == &driver_queue)
1320 {
1321 queue_type = "driver";
1322 }
1323 else if (queue == &abort_queue)
1324 {
1325 queue_type = "abort";
1326 min_debug = DBG_DETAILED;
1327 }
1328 else
1329 {
1330 queue_type = "port";
1331 }
1332
1333 dprintf(min_debug, DBG_PREFIX": IORB %x queued (cmd=%d/%d queue=%x [%s], timeout=%d)\n",
1334 vIorb, pIorb->CommandCode, pIorb->CommandModifier, queue, queue_type,
1335 pIorb->Timeout);
1336 }
1337 #endif
1338}
1339
1340/******************************************************************************
1341 * Remove an IORB from the specified queue. This function must be called with
1342 * the adapter-level spinlock aquired.
1343 */
1344int iorb_queue_del(IORB_QUEUE *queue, IORBH FAR16DATA *vIorb)
1345{
1346 IORBH FAR16DATA *_vIorb;
1347 IORBH FAR16DATA *_vPrev = FAR16NULL;
1348 int found = 0;
1349
1350 for (_vIorb = queue->vRoot; _vIorb != FAR16NULL; )
1351 {
1352 IORBH *_pIorb = Far16ToFlat(_vIorb);
1353 if (_vIorb == vIorb)
1354 {
1355 /* found the IORB to be removed */
1356 if (_vPrev != FAR16NULL)
1357 {
1358 ((IORBH*)Far16ToFlat(_vPrev))->f16NxtIORB = _pIorb->f16NxtIORB;
1359 }
1360 else
1361 {
1362 queue->vRoot = _pIorb->f16NxtIORB;
1363 }
1364 if (_vIorb == queue->vTail)
1365 {
1366 queue->vTail = _vPrev;
1367 }
1368 found = 1;
1369 break;
1370 }
1371 _vPrev = _vIorb;
1372 _vIorb = _pIorb->f16NxtIORB;
1373 }
1374
1375 #ifdef DEBUG
1376 if (found)
1377 {
1378 DPRINTF(DBG_DETAILED, DBG_PREFIX": IORB %x removed (queue = %x)\n", vIorb, queue);
1379 }
1380 else
1381 {
1382 DPRINTF(0, DBG_PREFIX": IORB %x not found in queue %x\n", vIorb, queue);
1383 }
1384 #endif
1385
1386 return(!found);
1387}
1388
1389/******************************************************************************
1390 * Set the error code in the specified IORB
1391 *
1392 * NOTE: This function does *not* call iorb_done(). It merely sets the IORB
1393 * status to the specified error code.
1394 */
1395void iorb_seterr(IORBH *pIorb, USHORT error_code)
1396{
1397 pIorb->ErrorCode = error_code;
1398 pIorb->Status |= IORB_ERROR;
1399}
1400
1401/******************************************************************************
1402 * Mark the specified IORB as done and notify the asynchronous post function,
1403 * if any. The IORB is also removed from the corresponding IORB queue.
1404 *
1405 * NOTES: This function does not clear the Status field; it merely adds the
1406 * IORB_DONE flag.
1407 *
1408 * This function is expected to be called *without* the corresponding
1409 * driver-level drv_lock aquired. It will aquire the spinlock before
1410 * updating the IORB queue and release it before notifying the upstream
1411 * code in order to prevent deadlocks.
1412 *
1413 * Due to this logic, this function is only good for simple task-time
1414 * completions. Functions working on lists of IORBs (such as interrupt
1415 * handlers or context hooks) should call iorb_complete() directly and
1416 * implement their own logic for removing the IORB from the port queue.
1417 * See abort_ctxhook() for an example.
1418 */
1419void iorb_done(IORBH FAR16DATA *vIorb, IORBH *pIorb)
1420{
1421 int a = iorb_unit_adapter(pIorb);
1422 int p = iorb_unit_port(pIorb);
1423
1424 /* remove IORB from corresponding queue */
1425 spin_lock(drv_lock);
1426 if (iorb_driver_level(pIorb))
1427 {
1428 iorb_queue_del(&driver_queue, vIorb);
1429 }
1430 else
1431 {
1432 iorb_queue_del(&ad_infos[a].ports[p].iorb_queue, vIorb);
1433 }
1434 aws_free(add_workspace(pIorb));
1435 spin_unlock(drv_lock);
1436
1437 iorb_complete(vIorb, pIorb);
1438}
1439
1440/******************************************************************************
1441 * Complete an IORB. This should be called without the adapter-level spinlock
1442 * to allow the IORB completion routine to perform whatever processing it
1443 * requires. This implies that the IORB should no longer be in any global
1444 * queue because the IORB completion routine may well reuse the IORB and send
1445 * the next request to us before even returning from this function.
1446 */
1447void iorb_complete(IORBH FAR16DATA *vIorb, IORBH *pIorb)
1448{
1449 pIorb->Status |= IORB_DONE;
1450
1451 DPRINTF(DBG_FUNCBEG, DBG_PREFIX": IORB %x complete status=0x%04x error=0x%04x\n",
1452 vIorb, pIorb->Status, pIorb->ErrorCode);
1453
1454 if (pIorb->RequestControl & IORB_ASYNC_POST)
1455 {
1456 Dev32Help_CallFar16((PFNFAR16)pIorb->f16NotifyAddress, vIorb);
1457 }
1458}
1459
1460/******************************************************************************
1461 * Requeue the specified IORB such that it will be sent downstream for
1462 * processing again. This includes freeing all resources currently allocated
1463 * (timer, buffer, ...) and resetting the flags to 0. The driver-level
1464 * spinlock must be aquired when calling this function.
1465 *
1466 * The following flags are preserved:
1467 * - no_ncq
1468 */
1469void iorb_requeue(IORBH *pIorb)
1470{
1471 ADD_WORKSPACE *aws = add_workspace(pIorb);
1472 u16 no_ncq = aws->no_ncq;
1473 u16 unaligned = aws->unaligned;
1474 u16 retries = aws->retries;
1475
1476 aws_free(aws);
1477 memset(aws, 0x00, sizeof(*aws));
1478
1479 aws->no_ncq = no_ncq;
1480 aws->unaligned = unaligned;
1481 aws->retries = retries;
1482}
1483
1484/******************************************************************************
1485 * Free resources in ADD workspace (timer, buffer, ...). This function should
1486 * be called with the spinlock held to prevent race conditions.
1487 */
1488void aws_free(ADD_WORKSPACE *aws)
1489{
1490 if (aws->timer != 0)
1491 {
1492 Timer_CancelTimer(aws->timer);
1493 aws->timer = 0;
1494 }
1495
1496 if (aws->buf != NULL)
1497 {
1498 MemFree(aws->buf);
1499 aws->buf = NULL;
1500 }
1501}
1502
1503/******************************************************************************
1504 * Lock the adapter, waiting for availability if necessary. This is expected
1505 * to be called at task/request time without the driver-level spinlock
1506 * aquired. Don't call at interrupt time.
1507 */
1508void lock_adapter(AD_INFO *ai)
1509{
1510 TIMER Timer;
1511
1512 spin_lock(drv_lock);
1513 while (ai->busy)
1514 {
1515 spin_unlock(drv_lock);
1516 TimerInit(&Timer, 250);
1517 while (!TimerCheckAndBlock(&Timer));
1518 spin_lock(drv_lock);
1519 }
1520 ai->busy = 1;
1521 spin_unlock(drv_lock);
1522}
1523
1524/******************************************************************************
1525 * Unlock adapter (i.e. reset busy flag)
1526 */
1527void unlock_adapter(AD_INFO *ai)
1528{
1529 ai->busy = 0;
1530}
1531
1532/******************************************************************************
1533 * Timeout handler for I/O commands. Since timeout handling can involve
1534 * lengthy operations like port resets, the main code is located in a
1535 * separate function which is invoked via a context hook.
1536 */
1537void __syscall timeout_callback(ULONG timer_handle, ULONG ulArg)
1538{
1539 IORBH FAR16DATA *vIorb = (IORBH FAR16DATA *)CastULONGToFar16(ulArg);
1540 IORBH *pIorb = Far16ToFlat(vIorb);
1541 int a = iorb_unit_adapter(pIorb);
1542 int p = iorb_unit_port(pIorb);
1543
1544 Timer_CancelTimer(timer_handle);
1545 dprintf(0, DBG_PREFIX": timeout for IORB %x port=%x.%x\n", ulArg, a, p);
1546
1547 /* Trigger abort processing function. We don't really care whether this
1548 * succeeds because the only reason why it would fail should be multiple
1549 * calls to DevHelp_ArmCtxHook() before the context hook had a chance to
1550 * start executing, which leaves two scenarios:
1551 *
1552 * - We succeded in arming the context hook. Fine.
1553 *
1554 * - We armed the context hook a second time before it had a chance to
1555 * start executing. In this case, the already scheduled context hook
1556 * will process our IORB as well.
1557 */
1558 SafeArmCtxHook(ResetCtxHook_h, ulArg);
1559}
1560
1561/******************************************************************************
1562 * Reset handler watchdog. If a timeout occurs, a context hook is armed which
1563 * will execute as soon as a kernel thread yields the CPU. However, some
1564 * kernel components won't yield the CPU during the early boot phase and the
1565 * only way to kick some sense into those components is to run the context
1566 * hook right inside this timer callback. Not exactly pretty, especially
1567 * considering the fact that context hooks were implemented to prevent running
1568 * lengthy operations like a port reset at interrupt time, but without this
1569 * watchdog mechanism we run the risk of getting completely stalled by device
1570 * problems during the early boot phase.
1571 */
1572void __syscall WatchdogTimer(ULONG timer_handle, ULONG ulArg)
1573{
1574 /* reset watchdog timer */
1575 Timer_CancelTimer(timer_handle);
1576 th_watchdog = 0;
1577 dprintf(0, DBG_PREFIX": BEG %s\n", ulArg==ResetCtxHook_h?"Reset":"Restart");
1578
1579 /* The ctxhook cannot be called directly because it does things
1580 * that are illegal in an interrupt handler.
1581 */
1582 SafeArmCtxHook(ulArg, 0);
1583}
1584
1585/******************************************************************************
1586 * Add unit info to ADAPTERINFO array (IOCC_GET_DEVICE_TABLE requests). The
1587 * adapter info array in the device table, dt->pAdapter[], is expected to be
1588 * initialized for the specified index (dt_ai).
1589 *
1590 * Please note that the device table adapter index, dta, is not always equal
1591 * to the physical adapter index, a: if SCSI emulation has been activated, the
1592 * last reported adapter is a virtual SCSI adapter and the physical adapter
1593 * indexes for those units are, of course, different from the device table
1594 * index of the virtual SCSI adapter.
1595 */
1596static int add_unit_info(IORB_CONFIGURATION *pIorb_conf, int dta,
1597 int a, int p, int d, int scsi_id)
1598{
1599 DEVICETABLE *pDt = Far16ToFlat(pIorb_conf->f16DeviceTable);
1600 ADAPTERINFO *pPtr;
1601 UNITINFO *pUi;
1602 AD_INFO *ai = ad_infos + a;
1603
1604 pPtr = (ADAPTERINFO *)MakeFlatFromNear16(pIorb_conf->f16DeviceTable, pDt->pAdapter[dta]);
1605 //DPRINTF(2,"add_unit_info: ptr=%x dta=%x pAdapter[dta]=%x pDeviceTable=%x\n",
1606 // ptr, dta, dt->pAdapter[dta], iorb_conf->pDeviceTable);
1607
1608 pUi = &pPtr->UnitInfo[pPtr->AdapterUnits];
1609
1610 if ((u32)(pUi + 1) - (u32)pDt > pIorb_conf->DeviceTableLen)
1611 {
1612 dprintf(0, DBG_PREFIX": error: device table provided by DASD too small\n");
1613 iorb_seterr(&pIorb_conf->iorbh, IOERR_CMD_SW_RESOURCE);
1614 return(-1);
1615 }
1616
1617 if (ai->ports[p].devs[d].unit_info == NULL)
1618 {
1619 /* provide original information about this device (unit) */
1620 memset(pUi, 0x00, sizeof(*pUi));
1621 pUi->AdapterIndex = dta; /* device table adapter index */
1622 pUi->UnitHandle = iorb_unit(a, p, d); /* physical adapter index */
1623 pUi->UnitIndex = pPtr->AdapterUnits;
1624 pUi->UnitType = ai->ports[p].devs[d].dev_type;
1625 pUi->QueuingCount = ai->ports[p].devs[d].ncq_max;
1626 if (ai->ports[p].devs[d].removable) pUi->UnitFlags |= UF_REMOVABLE;
1627 if (ai->ports[p].devs[d].ignored) pUi->UnitFlags |= UF_NODASD_SUPT;
1628 if (scsi_id > 0) pUi->UnitSCSITargetID = scsi_id; /* set fake SCSI ID for this unit */
1629 }
1630 else
1631 {
1632 /* copy updated device (unit) information (IOCM_CHANGE_UNITINFO) */
1633 memcpy(pUi, ai->ports[p].devs[d].unit_info, sizeof(*pUi));
1634 }
1635
1636 pPtr->AdapterUnits++;
1637 return(0);
1638}
1639
Note: See TracBrowser for help on using the repository browser.