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

Last change on this file since 209 was 209, checked in by David Azarewicz, 4 years ago

Debugging support changes.

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