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

Last change on this file since 80 was 80, checked in by chris, 14 years ago

Version 1.06
============

  • Finally came across a BIOS which accesses the ICH7/8 controller via SATA registers (i.e. not AHCI mode). This required a few changes to the code at boot time because it turned out that COMRESETs are required whenever switching to/from AHCI mode to allow the AHCI or SATA controller to re-discover the attached devices:
  • 'init_reset' will now be forced on when finding a controller in non-AHCI mode at boot time.
  • A COMRESET is initiated for each implemented port after turning off AHCI mode when restoring the BIOS configuration; this is done only for Intel controllers at this point because they map the AHCI port SCR MMIO registers even when not in AHCI mode.
  • apm_suspend() has been adjusted to restore the BIOS configuration to prevent needless timeouts when the BIOS takes over during suspend or power-off operations.
  • Small changes to the functions which save/restore BIOS/port settings to avoid pitfalls; among others, the port save/restore code now also saves and restores the port's engine status.
  • Improvements to debug logging around port resets.
  • Moved code to clear pending interrupts from ahci_reset_port() to ahci_stop_port() because both need it and resetting a port involves stopping it, first.
  • NCQ mode has found to cause problems on a Dell D630. This may be related to the hard disk used for the test but since I've never seen more than one queued command regardless of the I/O load (even during simulaneous xcopy operations), NCQ mode is now off by default and needs to be turned on via the /N switch (i.e. the the /N switch now has a reversed meaning).
  • Removed the code which attempts to establish another MMIO base address in case the one assigned by the BIOS can't be reserved via resource manager; if there's a conflict, it's extremely unlikely we would ever be able to restore the BIOS MMIO address at boot time without the BIOS clashing with whatever conflicts with the MMIO address, thus there's no point trying to do any of this.
  • Implemented a reset context hook watchdog; in the early boot phase, some components apparently don't yield the CPU so the context hook will never execute without the watchdog. Now we'll give the context hook 10 seconds to execute, otherwise the watchdog will expire and we'll call the context hook directly from the corresponding timer callback.
File size: 45.2 KB
Line 
1/******************************************************************************
2 * os2ahci.c - main file for os2ahci driver
3 *
4 * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
5 * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include "os2ahci.h"
23#include "bldday.h"
24
25#include "ioctl.h"
26
27/* -------------------------- macros and constants ------------------------- */
28
29/* parse integer command line parameter */
30#define drv_parm_int(s, value, type, radix) \
31 { \
32 char _far *_ep; \
33 if ((s)[1] != ':') { \
34 cprintf("missing colon (:) after /%c\n", *(s)); \
35 goto init_fail; \
36 } \
37 value = (type) strtol((s) + 2, \
38 (const char _far* _far*) &_ep, \
39 radix); \
40 s = _ep; \
41 }
42
43/* set two-dimensional array of port options */
44#define set_port_option(opt, val) \
45 if (adapter_index == -1) { \
46 /* set option for all adapters and ports */ \
47 memset(opt, val, sizeof(opt)); \
48 } else if (port_index == -1) { \
49 /* set option for all ports on current adapter */ \
50 memset(opt[adapter_index], val, sizeof(*opt)); \
51 } else { \
52 /* set option for specific port */ \
53 opt[adapter_index][port_index] = val; \
54 }
55
56/* ------------------------ typedefs and structures ------------------------ */
57
58/* -------------------------- function prototypes -------------------------- */
59
60void _cdecl small_code_ (void);
61
62static int add_unit_info (IORB_CONFIGURATION _far *iorb_conf, int dt_ai,
63 int a, int p, int d, int scsi_id);
64
65/* ------------------------ global/static variables ------------------------ */
66
67int debug = 0; /* if > 0, print debug messages to COM1 */
68int thorough_scan = 1; /* if != 0, perform thorough PCI scan */
69int init_reset; /* if != 0, reset ports during init */
70
71PFN Device_Help = 0; /* pointer to device helper entry point */
72ULONG RMFlags = 0; /* required by resource manager library */
73PFN RM_Help0 = NULL; /* required by resource manager library */
74PFN RM_Help3 = NULL; /* required by resource manager library */
75HDRIVER rm_drvh; /* resource manager driver handle */
76char rm_drvname[80]; /* driver name as returned by RM */
77USHORT add_handle; /* driver handle (RegisterDeviceClass) */
78UCHAR timer_pool[TIMER_POOL_SIZE]; /* timer pool */
79
80/* resource manager driver information structure */
81DRIVERSTRUCT rm_drvinfo = {
82 "OS2AHCI", /* driver name */
83 "AHCI SATA Driver", /* driver description */
84 "GNU", /* vendor name */
85 CMVERSION_MAJOR, /* RM interface version major */
86 CMVERSION_MINOR, /* RM interface version minor */
87 BLD_YEAR, BLD_MONTH, BLD_DAY, /* date */
88 0, /* driver flags */
89 DRT_ADDDM, /* driver type */
90 DRS_ADD, /* driver sub type */
91 NULL /* driver callback */
92};
93
94ULONG drv_lock; /* driver-level spinlock */
95IORB_QUEUE driver_queue; /* driver-level IORB queue */
96AD_INFO ad_infos[MAX_AD]; /* adapter information list */
97int ad_info_cnt; /* number of entries in ad_infos[] */
98u16 ad_ignore; /* bitmap with adapter indexes to ignore */
99int init_complete; /* if != 0, initialization has completed */
100
101/* apapter/port-specific options saved when parsing the command line */
102u8 emulate_scsi[MAX_AD][AHCI_MAX_PORTS];
103u8 enable_ncq[MAX_AD][AHCI_MAX_PORTS];
104u8 link_speed[MAX_AD][AHCI_MAX_PORTS];
105u8 link_power[MAX_AD][AHCI_MAX_PORTS];
106
107static char init_msg[] = "OS2AHCI driver version %d.%02d\n";
108static char exit_msg[] = "OS2AHCI driver *not* installed\n";
109static char eval_msg[] = ANSI_CLR_RED ANSI_CLR_BRIGHT "Evaluation version "
110 "- not licensed for production use.\n" ANSI_RESET;
111
112/* ----------------------------- start of code ----------------------------- */
113
114/******************************************************************************
115 * OS/2 device driver main strategy function. This function is only used
116 * for initialization purposes; all other calls go directly to the adapter
117 * device driver's strategy function.
118 */
119USHORT _cdecl c_strat(RPH _far *req)
120{
121 u16 rc;
122
123 switch (req->Cmd) {
124
125 case CMDInitBase:
126 rc = init_drv((RPINITIN _far *) req);
127 break;
128
129 case CMDShutdown:
130 rc = exit_drv(((RPSAVERESTORE _far *) req)->FuncCode);
131 break;
132
133 case CMDGenIOCTL:
134 rc = gen_ioctl((RP_GENIOCTL _far *) req);
135 break;
136
137 default:
138 rc = STDON | STATUS_ERR_UNKCMD;
139 break;
140 }
141
142 return(rc);
143}
144
145/******************************************************************************
146 * Intialize the os2ahci driver. This includes command line parsing, scanning
147 * the PCI bus for supported AHCI adapters, etc.
148 */
149USHORT init_drv(RPINITIN _far *req)
150{
151 RPINITOUT _far *rsp = (RPINITOUT _far *) req;
152 DDD_PARM_LIST _far *ddd_pl = (DDD_PARM_LIST _far *) req->InitArgs;
153 APIRET rmrc;
154 char _far *cmd_line;
155 char _far *s;
156 int adapter_index = -1;
157 int port_index = -1;
158 int invert_option;
159 int optval;
160 u16 vendor;
161 u16 device;
162
163 /* set device helper entry point */
164 Device_Help = req->DevHlpEP;
165
166 /* create driver-level spinlock */
167 DevHelp_CreateSpinLock(&drv_lock);
168
169 /* initialize libc code */
170 init_libc();
171
172 /* print initialization message */
173 cprintf(init_msg, VERSION / 100, VERSION % 100);
174 cprintf(eval_msg);
175
176 /* register driver with resource manager */
177 if ((rmrc = RMCreateDriver(&rm_drvinfo, &rm_drvh)) != RMRC_SUCCESS) {
178 cprintf("failed to register driver with resource manager (rc = %d)\n", rmrc);
179 goto init_fail;
180 }
181
182 /* parse command line parameters */
183 cmd_line = (char _far *) ((u32) ddd_pl & 0xffff0000l) + ddd_pl->cmd_line_args;
184
185 for (s = cmd_line; *s != 0; s++) {
186 if (*s == '/') {
187 if ((invert_option = (s[1] == '!')) != 0) {
188 s++;
189 }
190 s++;
191 switch (tolower(*s)) {
192
193 case '\0':
194 /* end of command line; can only happen if command line is incorrect */
195 cprintf("incomplete command line option\n");
196 goto init_fail;
197
198 case 'c':
199 /* set COM port base address for debug messages */
200 drv_parm_int(s, com_base, u16, 16);
201 break;
202
203 case 'd':
204 /* increase debug level */
205 if (debug++ == 0) {
206 init_com();
207 }
208 break;
209
210 case 'g':
211 /* add specfied PCI ID as a supported generic AHCI adapter */
212 drv_parm_int(s, vendor, u16, 16);
213 drv_parm_int(s, device, u16, 16);
214 if (add_pci_id(vendor, device)) {
215 cprintf("failed to add PCI ID %04x:%04x\n", vendor, device);
216 goto init_fail;
217 }
218 thorough_scan = 1;
219 break;
220
221 case 't':
222 /* perform thorough PCI scan (i.e. look for individual supported PCI IDs) */
223 thorough_scan = !invert_option;
224 break;
225
226 case 'r':
227 /* reset ports during initialization */
228 init_reset = 1;
229 break;
230
231 case 'a':
232 /* set adapter index for adapter and port-related options */
233 drv_parm_int(s, adapter_index, int, 10);
234 if (adapter_index < 0 || adapter_index >= MAX_AD) {
235 cprintf("invalid adapter index (%d)\n", adapter_index);
236 goto init_fail;
237 }
238 break;
239
240 case 'p':
241 /* set port index for port-related options */
242 drv_parm_int(s, port_index, int, 10);
243 if (port_index < 0 || port_index >= AHCI_MAX_PORTS) {
244 cprintf("invalid port index (%d)\n", port_index);
245 goto init_fail;
246 }
247 break;
248
249 case 'i':
250 /* ignore current adapter index */
251 if (adapter_index >= 0) {
252 ad_ignore |= 1U << adapter_index;
253 }
254 break;
255
256 case 's':
257 /* enable SCSI emulation for ATAPI devices */
258 set_port_option(emulate_scsi, !invert_option);
259 break;
260
261 case 'n':
262 /* disable NCQ */
263 set_port_option(enable_ncq, !invert_option);
264 break;
265
266 case 'l':
267 /* set link speed or power savings */
268 s++;
269 switch (tolower(*s)) {
270 case 's':
271 /* set link speed */
272 drv_parm_int(s, optval, int, 10);
273 set_port_option(link_speed, optval);
274 break;
275 case 'p':
276 /* set power management */
277 drv_parm_int(s, optval, int, 10);
278 set_port_option(link_power, optval);
279 break;
280 default:
281 cprintf("invalid link parameter (%c)\n", *s);
282 goto init_fail;
283 }
284 /* need to reset the port in order to establish link settings */
285 init_reset = 1;
286 break;
287
288 default:
289 cprintf("invalid option: /%c\n", *s);
290 goto init_fail;
291 }
292 }
293 }
294
295 /* scan PCI bus for supported devices */
296 scan_pci_bus();
297
298 if (ad_info_cnt > 0) {
299 /* initialization succeeded and we found at least one AHCI adapter */
300 ADD_InitTimer(timer_pool, sizeof(timer_pool));
301 mdelay_cal();
302
303 if (DevHelp_RegisterDeviceClass("OS2AHCI", (PFN) add_entry, 0, 1,
304 &add_handle)) {
305 cprintf("error: couldn't register device class\n");
306 goto init_fail;
307 }
308
309 /* allocate context hooks */
310 if (DevHelp_AllocateCtxHook(mk_NPFN(restart_hook), &restart_ctxhook_h) != 0 ||
311 DevHelp_AllocateCtxHook(mk_NPFN(reset_hook), &reset_ctxhook_h) != 0 ||
312 DevHelp_AllocateCtxHook(mk_NPFN(engine_hook), &engine_ctxhook_h)) {
313 cprintf("failed to allocate task-time context hooks\n");
314 goto init_fail;
315 }
316
317 rsp->CodeEnd = (u16) end_of_code;
318 rsp->DataEnd = (u16) &end_of_data;
319 return(STDON);
320
321 } else {
322 /* no adapters found */
323 cprintf(" No adapters found.\n");
324 }
325
326init_fail:
327 /* initialization failed; set segment sizes to 0 and return error */
328 rsp->CodeEnd = 0;
329 rsp->DataEnd = 0;
330
331 /* free context hooks */
332 if (engine_ctxhook_h != 0) DevHelp_FreeCtxHook(engine_ctxhook_h);
333 if (reset_ctxhook_h != 0) DevHelp_FreeCtxHook(reset_ctxhook_h);
334 if (restart_ctxhook_h != 0) DevHelp_FreeCtxHook(restart_ctxhook_h);
335
336 if (rm_drvh != 0) {
337 /* remove driver from resource manager */
338 RMDestroyDriver(rm_drvh);
339 }
340
341 cprintf(exit_msg);
342 return(STDON | ERROR_I24_QUIET_INIT_FAIL);
343}
344
345/******************************************************************************
346 * Generic IOCTL via character device driver. IOCTLs are used to control the
347 * driver operation and to execute native ATA and ATAPI (SCSI) commands from
348 * ring 3 applications.
349 */
350USHORT gen_ioctl(RP_GENIOCTL _far *ioctl)
351{
352 switch (ioctl->Category) {
353
354 case OS2AHCI_IOCTL_CATEGORY:
355
356 switch (ioctl->Function) {
357
358 case OS2AHCI_IOCTL_GET_DEVLIST:
359 return(ioctl_get_devlist(ioctl));
360
361 case OS2AHCI_IOCTL_PASSTHROUGH:
362 return(ioctl_passthrough(ioctl));
363
364 }
365 }
366 return(STDON | STATUS_ERR_UNKCMD);
367}
368
369/******************************************************************************
370 * Device driver exit handler. This handler is called when OS/2 shuts down and
371 * flushes the write caches of all attached devices. Since this is effectively
372 * the same we do when suspending, we'll call out to the corresponding APM
373 * function.
374 *
375 * NOTE: Errors are ignored because there's no way we could stop the shutdown
376 * or do something about the error, unless retrying endlessly is
377 * considered an option.
378 */
379USHORT exit_drv(int func)
380{
381 dprintf("exit_drv(%d) called\n", func);
382
383 if (func == 0) {
384 /* we're only interested in the second phase of the shutdown */
385 return(STDON);
386 }
387
388 apm_suspend();
389 return(STDON);
390}
391
392/******************************************************************************
393 * ADD entry point. This is the main entry point for all ADD requests. Due to
394 * the asynchronous nature of ADD drivers, this function primarily queues the
395 * IORB(s) to the corresponding adapter or port queues, then triggers the
396 * state machine to initiate processing queued IORBs.
397 *
398 * NOTE: In order to prevent race conditions or engine stalls, certain rules
399 * around locking, unlocking and IORB handling in general have been
400 * established. Refer to the comments in "trigger_engine()" for
401 * details.
402 */
403void _cdecl _far _loadds add_entry(IORBH _far *first_iorb)
404{
405 IORBH _far *iorb;
406 IORBH _far *next = NULL;
407
408 spin_lock(drv_lock);
409
410 for (iorb = first_iorb; iorb != NULL; iorb = next) {
411 /* Queue this IORB. Queues primarily exist on port level but there are
412 * some requests which affect the whole driver, most notably
413 * IOCC_CONFIGURATION. In either case, adding the IORB to the driver or
414 * port queue will change the links, thus we need to save the original
415 * link in 'next'.
416 */
417 next = (iorb->RequestControl | IORB_CHAIN) ? iorb->pNxtIORB : 0;
418
419 iorb->Status = 0;
420 iorb->ErrorCode = 0;
421 memset(&iorb->ADDWorkSpace, 0x00, sizeof(ADD_WORKSPACE));
422
423 if (iorb_driver_level(iorb)) {
424 /* adapter-level IORB */
425 iorb->UnitHandle = 0;
426 iorb_queue_add(&driver_queue, iorb);
427
428 } else {
429 /* port-level IORB */
430 int a = iorb_unit_adapter(iorb);
431 int p = iorb_unit_port(iorb);
432 int d = iorb_unit_device(iorb);
433
434 if (a >= ad_info_cnt ||
435 p > ad_infos[a].port_max ||
436 d > ad_infos[a].ports[p].dev_max ||
437 (ad_infos[a].port_map & (1UL << p)) == 0) {
438
439 /* unit handle outside of the allowed range */
440 dprintf("warning: IORB for %d.%d.%d out of range\n", a, p, d);
441 iorb->Status = IORB_ERROR;
442 iorb->ErrorCode = IOERR_CMD_SYNTAX;
443 iorb_complete(iorb);
444 continue;
445 }
446
447 iorb_queue_add(&ad_infos[a].ports[p].iorb_queue, iorb);
448 }
449 }
450
451 /* trigger state machine */
452 trigger_engine();
453
454 spin_unlock(drv_lock);
455}
456
457/******************************************************************************
458 * Trigger IORB queue engine. This is a wrapper function for trigger_engine_1()
459 * which will try to get all IORBs sent on their way a couple of times. If
460 * there are still IORBs ready for processing after this, this function will
461 * hand off to a context hook which will continue to trigger the engine until
462 * all IORBs have been sent.
463 */
464void trigger_engine(void)
465{
466 int i;
467
468 for (i = 0; i < 3; i++) {
469 if (trigger_engine_1() == 0) {
470 /* done -- all IORBs have been sent on their way */
471 return;
472 }
473 }
474
475 /* Something keeps bouncing; hand off to the engine context hook which will
476 * keep trying in the background.
477 */
478 DevHelp_ArmCtxHook(0, engine_ctxhook_h);
479}
480
481/******************************************************************************
482 * Trigger IORB queue engine in order to send commands in the driver/port IORB
483 * queues to the AHCI hardware. This function will return the number of IORBs
484 * sent. Keep in mind that IORBs might "bounce" if the adapter/port is not in
485 * a state to accept the command, thus it might take quite a few calls to get
486 * all IORBs on their way. This is why there's a wrapper function which tries
487 * it a few times, then hands off to a context hook which will keep trying in
488 * the background.
489 *
490 * IORBs might complete before send_iorb() has returned, at any time during
491 * interrupt processing or on another CPU on SMP systems. IORB completion
492 * means modifications to the corresponding IORB queue (the completed IORB
493 * is removed from the queue) thus we need to protect the IORB queues from
494 * race conditions. The safest approach short of keeping the driver-level
495 * spinlock aquired permanently is to keep it throughout this function and
496 * release it temporarily in send_iorb().
497 *
498 * This implies that the handler functions are fully responsible for aquiring
499 * the driver-level spinlock when they need it, and for releasing it again.
500 *
501 * As a rule of thumb, get the driver-level spinlock whenever accessing
502 * volatile variables (IORB queues, values in ad_info[], ...).
503 *
504 * Additional Notes:
505 *
506 * - This function is expected to be called with the spinlock aquired
507 *
508 * - Adapters can be flagged as 'busy' which means no new IORBs are sent (they
509 * just remain in the queue). This can be used to release the driver-level
510 * spinlock while making sure no new IORBs are going to hit the hardware.
511 * In order to prevent engine stalls, all handlers using this functionality
512 * need to invoke trigger_engine() after resetting the busy flag.
513 *
514 * - Driver-level IORBs are not synchronized by adapter-level 'busy' flags.
515 * However, the driver-level queue is worked "one entry at a time" which
516 * means that no new IORBs will be queued on the driver-level queue until
517 * the head element has completed processing. This means that driver-
518 * level IORB handlers don't need to protect against each other. But they
519 * they do need to keep in mind interference with port-level IORBs:
520 *
521 * - Driver-level IORB handlers must obtain the spinlock and/or flag all
522 * adapters as 'busy' which are affected by the driver-level IORB
523 *
524 * - Driver-level IORB handlers must not access the hardware of a
525 * particular adapter if it's flagged as 'busy'
526 */
527int trigger_engine_1(void)
528{
529 IORBH _far *iorb;
530 IORBH _far *next;
531 int iorbs_sent = 0;
532 int a;
533 int p;
534
535 iorbs_sent = 0;
536
537 /* process driver-level IORBs */
538 if ((iorb = driver_queue.root) != NULL && !add_workspace(iorb)->processing) {
539 send_iorb(iorb);
540 iorbs_sent++;
541 }
542
543 /* process port-level IORBs */
544 for (a = 0; a < ad_info_cnt; a++) {
545 AD_INFO *ai = ad_infos + a;
546 if (ai->busy) {
547 /* adapter is busy; don't process any IORBs */
548 continue;
549 }
550 for (p = 0; p <= ai->port_max; p++) {
551 /* send all queued IORBs on this port */
552 next = NULL;
553 for (iorb = ai->ports[p].iorb_queue.root; iorb != NULL; iorb = next) {
554 next = iorb->pNxtIORB;
555 if (!add_workspace(iorb)->processing) {
556 send_iorb(iorb);
557 iorbs_sent++;
558 }
559 }
560 }
561 }
562
563 return(iorbs_sent);
564}
565
566/******************************************************************************
567 * Send a single IORB to the corresponding AHCI adapter/port. This is just a
568 * switch board for calling the corresponding iocc_*() handler function.
569 *
570 * NOTE: This function is expected to be called with the driver-level spinlock
571 * aquired. It will release it before calling any of the handler
572 * functions and re-aquire it when done.
573 */
574void send_iorb(IORBH _far *iorb)
575{
576 /* Mark IORB as "processing" before doing anything else. Once the IORB is
577 * marked as "processing", we can release the spinlock because subsequent
578 * invocations of trigger_engine() (e.g. at interrupt time) will ignore this
579 * IORB.
580 */
581 add_workspace(iorb)->processing = 1;
582 spin_unlock(drv_lock);
583
584 switch (iorb->CommandCode) {
585
586 case IOCC_CONFIGURATION:
587 iocc_configuration(iorb);
588 break;
589
590 case IOCC_DEVICE_CONTROL:
591 iocc_device_control(iorb);
592 break;
593
594 case IOCC_UNIT_CONTROL:
595 iocc_unit_control(iorb);
596 break;
597
598 case IOCC_GEOMETRY:
599 iocc_geometry(iorb);
600 break;
601
602 case IOCC_EXECUTE_IO:
603 iocc_execute_io(iorb);
604 break;
605
606 case IOCC_UNIT_STATUS:
607 iocc_unit_status(iorb);
608 break;
609
610 case IOCC_ADAPTER_PASSTHRU:
611 iocc_adapter_passthru(iorb);
612 break;
613
614 default:
615 /* unsupported call */
616 iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
617 iorb_done(iorb);
618 break;
619 }
620
621 /* re-aquire spinlock before returning to trigger_engine() */
622 spin_lock(drv_lock);
623}
624
625/******************************************************************************
626 * Handle IOCC_CONFIGURATION requests.
627 */
628void iocc_configuration(IORBH _far *iorb)
629{
630 int a;
631
632 switch (iorb->CommandModifier) {
633
634 case IOCM_COMPLETE_INIT:
635 /* Complete initialization. From now on, we won't have to restore the BIOS
636 * configuration after each command and we're fully operational (i.e. will
637 * use interrupts, timers and context hooks instead of polling).
638 */
639 if (!init_complete) {
640 dprintf("leaving initialization mode\n");
641 for (a = 0; a < ad_info_cnt; a++) {
642 lock_adapter(ad_infos + a);
643 ahci_complete_init(ad_infos + a);
644 }
645 init_complete = 1;
646
647 /* release all adapters */
648 for (a = 0; a < ad_info_cnt; a++) {
649 unlock_adapter(ad_infos + a);
650 }
651
652 /* register APM hook */
653 apm_init();
654 }
655 iorb_done(iorb);
656 break;
657
658 case IOCM_GET_DEVICE_TABLE:
659 /* construct a device table */
660 iocm_device_table(iorb);
661 break;
662
663 default:
664 iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
665 iorb_done(iorb);
666 break;
667 }
668}
669
670/******************************************************************************
671 * Handle IOCC_DEVICE_CONTROL requests.
672 */
673void iocc_device_control(IORBH _far *iorb)
674{
675 AD_INFO *ai = ad_infos + iorb_unit_adapter(iorb);
676 IORBH _far *ptr;
677 IORBH _far *next = NULL;
678 int p = iorb_unit_port(iorb);
679 int d = iorb_unit_device(iorb);
680
681 switch (iorb->CommandModifier) {
682
683 case IOCM_ABORT:
684 /* abort all pending commands on specified port and device */
685 spin_lock(drv_lock);
686 for (ptr = ai->ports[p].iorb_queue.root; ptr != NULL; ptr = next) {
687 next = ptr->pNxtIORB;
688 /* move all matching IORBs to the abort queue */
689 if (ptr != iorb && iorb_unit_device(ptr) == d) {
690 iorb_queue_del(&ai->ports[p].iorb_queue, ptr);
691 iorb_queue_add(&abort_queue, ptr);
692 ptr->ErrorCode = IOERR_CMD_ABORTED;
693 }
694 }
695 spin_unlock(drv_lock);
696
697 /* trigger reset context hook which will finish the abort processing */
698 DevHelp_ArmCtxHook(0, reset_ctxhook_h);
699 break;
700
701 case IOCM_SUSPEND:
702 case IOCM_RESUME:
703 case IOCM_GET_QUEUE_STATUS:
704 /* Suspend/resume operations allow access to the hardware for other
705 * entities such as IBMIDECD.FLT. Since os2ahci implements both ATA
706 * and ATAPI in the same driver, this won't be required.
707 */
708 iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
709 break;
710
711 case IOCM_LOCK_MEDIA:
712 case IOCM_UNLOCK_MEDIA:
713 case IOCM_EJECT_MEDIA:
714 /* unit control commands to lock, unlock and eject media */
715 /* will be supported later... */
716 iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
717 break;
718
719 default:
720 iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
721 break;
722 }
723
724 iorb_done(iorb);
725}
726
727/******************************************************************************
728 * Handle IOCC_UNIT_CONTROL requests.
729 */
730void iocc_unit_control(IORBH _far *iorb)
731{
732 IORB_UNIT_CONTROL _far *iorb_uc = (IORB_UNIT_CONTROL _far *) iorb;
733 int a = iorb_unit_adapter(iorb);
734 int p = iorb_unit_port(iorb);
735 int d = iorb_unit_device(iorb);
736
737 spin_lock(drv_lock);
738 switch (iorb->CommandModifier) {
739
740 case IOCM_ALLOCATE_UNIT:
741 /* allocate unit for exclusive access */
742 if (ad_infos[a].ports[p].devs[d].allocated) {
743 iorb_seterr(iorb, IOERR_UNIT_ALLOCATED);
744 } else {
745 ad_infos[a].ports[p].devs[d].allocated = 1;
746 }
747 break;
748
749 case IOCM_DEALLOCATE_UNIT:
750 /* deallocate exclusive access to unit */
751 if (!ad_infos[a].ports[p].devs[d].allocated) {
752 iorb_seterr(iorb, IOERR_UNIT_NOT_ALLOCATED);
753 } else {
754 ad_infos[a].ports[p].devs[d].allocated = 0;
755 }
756 break;
757
758 case IOCM_CHANGE_UNITINFO:
759 /* Change unit (device) information. One reason for this IOCM is the
760 * interface for filter device drivers: a filter device driver can
761 * either change existing UNITINFOs or permanently allocate units
762 * and fabricate new [logical] units; the former is the reason why we
763 * must store the pointer to the updated UNITNIFO for subsequent
764 * IOCC_CONFIGURATION/IOCM_GET_DEVICE_TABLE calls.
765 */
766 if (!ad_infos[a].ports[p].devs[d].allocated) {
767 iorb_seterr(iorb, IOERR_UNIT_NOT_ALLOCATED);
768 break;
769 }
770 ad_infos[a].ports[p].devs[d].unit_info = iorb_uc->pUnitInfo;
771 break;
772
773 default:
774 iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
775 break;
776 }
777
778 spin_unlock(drv_lock);
779 iorb_done(iorb);
780}
781
782/******************************************************************************
783 * Scan all ports for AHCI devices and construct a DASD device table.
784 *
785 * NOTES: This function may be called multiple times. Only the first
786 * invocation will actually scan for devices; all subsequent calls will
787 * merely return the results of the initial scan, potentially augmented
788 * by modified unit infos after IOCC_CONFIGURATION/IOCM_CHANGE_UNITINFO
789 * requests.
790 *
791 * In order to support applications that can't deal with ATAPI devices
792 * (i.e. need a SCSI adapter) os2ahci will optionally report ATAPI
793 * dvices as SCSI devices. The corresponding SCSI adapter doesn't
794 * really exist and is only reported here for the IOCM_GET_DEVICETABLE
795 * request. The units attached to this adapter will use the real HW
796 * unit IDs, thus we'll never receive a command specific to the
797 * emulated SCSI adapter and won't need to set up any sort of entity
798 * for it; the only purpose of the emulated SCSI adapter is to pass the
799 * bus type "AI_DEVBUS_SCSI_2" upstream, and the emulated units, of
800 * course. The emulated SCSI target IDs are allocated as follows:
801 *
802 * 0 the virtual adapter
803 * 1..n emulated devices; SCSI target ID increments sequentially
804 */
805void iocm_device_table(IORBH _far *iorb)
806{
807 IORB_CONFIGURATION _far *iorb_conf;
808 DEVICETABLE _far *dt;
809 char _far *pos;
810 int scsi_units = 0;
811 int scsi_id = 1;
812 int rc;
813 int dta;
814 int a;
815 int p;
816 int d;
817
818 iorb_conf = (IORB_CONFIGURATION _far *) iorb;
819 dt = iorb_conf->pDeviceTable;
820
821 spin_lock(drv_lock);
822
823 /* initialize device table header */
824 dt->ADDLevelMajor = ADD_LEVEL_MAJOR;
825 dt->ADDLevelMinor = ADD_LEVEL_MINOR;
826 dt->ADDHandle = add_handle;
827 dt->TotalAdapters = ad_info_cnt + 1;
828
829 /* set start of adapter and device information tables */
830 pos = (char _far *) (dt->pAdapter + dt->TotalAdapters);
831
832 /* go through all adapters, including the virtual SCSI adapter */
833 for (dta = 0; dta < dt->TotalAdapters; dta++) {
834 ADAPTERINFO _far *ptr = (ADAPTERINFO _far *) pos;
835
836 /* sanity check for sufficient space in device table */
837 if ((u32) (ptr + 1) - (u32) dt > iorb_conf->DeviceTableLen) {
838 dprintf("error: device table provided by DASD too small\n");
839 iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
840 goto iocm_device_table_done;
841 }
842
843 dt->pAdapter[dta] = (ADAPTERINFO _near *) ((u32) ptr & 0xffff);
844 memset(ptr, 0x00, sizeof(*ptr));
845
846 ptr->AdapterIOAccess = AI_IOACCESS_BUS_MASTER;
847 ptr->AdapterHostBus = AI_HOSTBUS_OTHER | AI_BUSWIDTH_32BIT;
848 ptr->AdapterFlags = AF_16M | AF_HW_SCATGAT;
849 ptr->MaxHWSGList = AHCI_MAX_SG / 2; /* AHCI S/G elements are 22 bits */
850
851 if (dta < ad_info_cnt) {
852 /* this is a physical AHCI adapter */
853 AD_INFO *ad_info = ad_infos + dta;
854
855 ptr->AdapterDevBus = AI_DEVBUS_ST506 | AI_DEVBUS_32BIT;
856 sprintf(ptr->AdapterName, "AHCI_%d", dta);
857
858 if (!ad_info->port_scan_done) {
859 /* first call; need to scan AHCI hardware for devices */
860 if (ad_info->busy) {
861 dprintf("error: port scan requested while adapter was busy\n");
862 iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
863 goto iocm_device_table_done;
864 }
865 ad_info->busy = 1;
866 spin_unlock(drv_lock);
867 rc = ahci_scan_ports(ad_info);
868 spin_lock(drv_lock);
869 ad_info->busy = 0;
870
871 if (rc != 0) {
872 dprintf("error: port scan failed on adapter #%d\n", dta);
873 iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
874 goto iocm_device_table_done;
875 }
876 ad_info->port_scan_done = 1;
877 }
878
879 /* insert physical (i.e. AHCI) devices into the device table */
880 for (p = 0; p <= ad_info->port_max; p++) {
881 for (d = 0; d <= ad_info->ports[p].dev_max; d++) {
882 if (ad_info->ports[p].devs[d].present) {
883 if (ad_info->ports[p].devs[d].atapi && emulate_scsi[dta][p]) {
884 /* only report this unit as SCSI unit */
885 scsi_units++;
886 continue;
887 }
888 if (add_unit_info(iorb_conf, dta, dta, p, d, 0)) {
889 goto iocm_device_table_done;
890 }
891 }
892 }
893 }
894
895 } else {
896 /* this is the virtual SCSI adapter */
897 if (scsi_units == 0) {
898 /* not a single unit to be emulated via SCSI */
899 dt->TotalAdapters--;
900 break;
901 }
902
903 /* set adapter name and bus type to mimic a SCSI controller */
904 ptr->AdapterDevBus = AI_DEVBUS_SCSI_2 | AI_DEVBUS_16BIT;
905 sprintf(ptr->AdapterName, "AHCI_SCSI_0");
906
907 /* add all ATAPI units to be emulated by this virtual adaper */
908 for (a = 0; a < ad_info_cnt; a++) {
909 AD_INFO *ad_info = ad_infos + a;
910
911 for (p = 0; p <= ad_info->port_max; p++) {
912 for (d = 0; d <= ad_info->ports[p].dev_max; d++) {
913 if (ad_info->ports[p].devs[d].present &&
914 ad_info->ports[p].devs[d].atapi &&
915 emulate_scsi[a][p]) {
916 if (add_unit_info(iorb_conf, dta, a, p, d, scsi_id++)) {
917 goto iocm_device_table_done;
918 }
919 }
920 }
921 }
922 }
923 }
924
925 /* calculate offset for next adapter */
926 pos = (char _far *) (ptr->UnitInfo + ptr->AdapterUnits);
927 }
928
929iocm_device_table_done:
930 spin_unlock(drv_lock);
931 iorb_done(iorb);
932}
933
934/******************************************************************************
935 * Handle IOCC_GEOMETRY requests.
936 */
937void iocc_geometry(IORBH _far *iorb)
938{
939 switch (iorb->CommandModifier) {
940
941 case IOCM_GET_MEDIA_GEOMETRY:
942 case IOCM_GET_DEVICE_GEOMETRY:
943 add_workspace(iorb)->idempotent = 1;
944 ahci_get_geometry(iorb);
945 break;
946
947 default:
948 iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
949 iorb_done(iorb);
950 }
951}
952
953/******************************************************************************
954 * Handle IOCC_EXECUTE_IO requests.
955 */
956void iocc_execute_io(IORBH _far *iorb)
957{
958 switch (iorb->CommandModifier) {
959
960 case IOCM_READ:
961 add_workspace(iorb)->idempotent = 1;
962 ahci_read(iorb);
963 break;
964
965 case IOCM_READ_VERIFY:
966 add_workspace(iorb)->idempotent = 1;
967 ahci_verify(iorb);
968 break;
969
970 case IOCM_WRITE:
971 add_workspace(iorb)->idempotent = 1;
972 ahci_write(iorb);
973 break;
974
975 case IOCM_WRITE_VERIFY:
976 add_workspace(iorb)->idempotent = 1;
977 ahci_write(iorb);
978 break;
979
980 default:
981 iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
982 iorb_done(iorb);
983 }
984}
985
986/******************************************************************************
987 * Handle IOCC_UNIT_STATUS requests.
988 */
989void iocc_unit_status(IORBH _far *iorb)
990{
991 switch (iorb->CommandModifier) {
992
993 case IOCM_GET_UNIT_STATUS:
994 add_workspace(iorb)->idempotent = 1;
995 ahci_unit_ready(iorb);
996 break;
997
998 default:
999 iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
1000 iorb_done(iorb);
1001 }
1002}
1003
1004/******************************************************************************
1005 * Handle IOCC_ADAPTER_PASSTHROUGH requests.
1006 */
1007void iocc_adapter_passthru(IORBH _far *iorb)
1008{
1009 switch (iorb->CommandModifier) {
1010
1011 case IOCM_EXECUTE_CDB:
1012 add_workspace(iorb)->idempotent = 0;
1013 ahci_execute_cdb(iorb);
1014 break;
1015
1016 case IOCM_EXECUTE_ATA:
1017 add_workspace(iorb)->idempotent = 0;
1018 ahci_execute_ata(iorb);
1019 break;
1020
1021 default:
1022 iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
1023 iorb_done(iorb);
1024 }
1025}
1026
1027/******************************************************************************
1028 * Add an IORB to the specified queue. This function must be called with the
1029 * adapter-level spinlock aquired.
1030 */
1031void iorb_queue_add(IORB_QUEUE _far *queue, IORBH _far *iorb)
1032{
1033 if (iorb_priority(iorb) {
1034 /* priority IORB; insert at first position */
1035 iorb->pNxtIORB = queue->root;
1036 queue->root = iorb;
1037
1038 } else {
1039 /* append IORB to end of queue */
1040 iorb->pNxtIORB = NULL;
1041
1042 if (queue->root == NULL) {
1043 queue->root = iorb;
1044 } else {
1045 queue->tail->pNxtIORB = iorb;
1046 }
1047 queue->tail = iorb;
1048 }
1049
1050 if (debug) {
1051 /* determine queue type (local, driver, abort or port) and minimum debug
1052 * level; otherwise, queue debug prints can become really confusing.
1053 */
1054 char *queue_type;
1055 int min_debug = 1;
1056
1057 if ((u32) queue >> 16 == (u32) (void _far *) &queue >> 16) {
1058 /* this queue is on the stack */
1059 queue_type = "local";
1060 min_debug = 2;
1061
1062 } else if (queue == &driver_queue) {
1063 queue_type = "driver";
1064
1065 } else if (queue == &abort_queue) {
1066 queue_type = "abort";
1067 min_debug = 2;
1068
1069 } else {
1070 queue_type = "port";
1071 }
1072
1073 if (debug >= min_debug) {
1074 printf("IORB %Fp queued (cmd = %d/%d, queue = %Fp [%s], timeout = %ld)\n",
1075 iorb, iorb->CommandCode, iorb->CommandModifier, queue, queue_type,
1076 iorb->Timeout);
1077 }
1078 }
1079}
1080
1081/******************************************************************************
1082 * Remove an IORB from the specified queue. This function must be called with
1083 * the adapter-level spinlock aquired.
1084 */
1085int iorb_queue_del(IORB_QUEUE _far *queue, IORBH _far *iorb)
1086{
1087 IORBH _far *_iorb;
1088 IORBH _far *_prev = NULL;
1089 int found = 0;
1090
1091 for (_iorb = queue->root; _iorb != NULL; _iorb = _iorb->pNxtIORB) {
1092 if (_iorb == iorb) {
1093 /* found the IORB to be removed */
1094 if (_prev != NULL) {
1095 _prev->pNxtIORB = _iorb->pNxtIORB;
1096 } else {
1097 queue->root = _iorb->pNxtIORB;
1098 }
1099 if (_iorb == queue->tail) {
1100 queue->tail = _prev;
1101 }
1102 found = 1;
1103 break;
1104 }
1105 _prev = _iorb;
1106 }
1107
1108 if (found) {
1109 ddprintf("IORB %Fp removed (queue = %Fp)\n", iorb, queue);
1110 } else {
1111 dprintf("IORB %Fp not found in queue %Fp\n", iorb, queue);
1112 }
1113
1114 return(!found);
1115}
1116
1117/******************************************************************************
1118 * Set the error code in the specified IORB
1119 *
1120 * NOTE: This function does *not* call iorb_done(). It merely sets the IORB
1121 * status to the specified error code.
1122 */
1123void iorb_seterr(IORBH _far *iorb, USHORT error_code)
1124{
1125 iorb->ErrorCode = error_code;
1126 iorb->Status |= IORB_ERROR;
1127}
1128
1129/******************************************************************************
1130 * Mark the specified IORB as done and notify the asynchronous post function,
1131 * if any. The IORB is also removed from the corresponding IORB queue.
1132 *
1133 * NOTES: This function does not clear the Status field; it merely adds the
1134 * IORB_DONE flag.
1135 *
1136 * This function is expected to be called *without* the corresponding
1137 * driver-level drv_lock aquired. It will aquire the spinlock before
1138 * updating the IORB queue and release it before notifying the upstream
1139 * code in order to prevent deadlocks.
1140 *
1141 * Due to this logic, this function is only good for simple task-time
1142 * completions. Functions working on lists of IORBs (such as interrupt
1143 * handlers or context hooks) should call iorb_complete() directly and
1144 * implement their own logic for removing the IORB from the port queue.
1145 * See abort_ctxhook() for an example.
1146 */
1147void iorb_done(IORBH _far *iorb)
1148{
1149 int a = iorb_unit_adapter(iorb);
1150 int p = iorb_unit_port(iorb);
1151
1152 /* remove IORB from corresponding queue */
1153 spin_lock(drv_lock);
1154 if (iorb_driver_level(iorb)) {
1155 iorb_queue_del(&driver_queue, iorb);
1156 } else {
1157 iorb_queue_del(&ad_infos[a].ports[p].iorb_queue, iorb);
1158 }
1159 aws_free(add_workspace(iorb));
1160 spin_unlock(drv_lock);
1161
1162 iorb_complete(iorb);
1163}
1164
1165/******************************************************************************
1166 * Complete an IORB. This should be called without the adapter-level spinlock
1167 * to allow the IORB completion routine to perform whatever processing it
1168 * requires. This implies that the IORB should no longer be in any global
1169 * queue because the IORB completion routine may well reuse the IORB and send
1170 * the next request to us before even returning from this function.
1171 */
1172void iorb_complete(IORBH _far *iorb)
1173{
1174 iorb->Status |= IORB_DONE;
1175
1176 dprintf("IORB %Fp complete (status = 0x%04x, error = 0x%04x)\n",
1177 iorb, iorb->Status, iorb->ErrorCode);
1178
1179 if (iorb->RequestControl & IORB_ASYNC_POST) {
1180 iorb->NotifyAddress(iorb);
1181 }
1182}
1183
1184/******************************************************************************
1185 * Requeue the specified IORB such that it will be sent downstream for
1186 * processing again. This includes freeing all resources currently allocated
1187 * (timer, buffer, ...) and resetting the flags to 0. The driver-level
1188 * spinlock must be aquired when calling this function.
1189 *
1190 * The following flags are preserved:
1191 * - no_ncq
1192 */
1193void iorb_requeue(IORBH _far *iorb)
1194{
1195 ADD_WORKSPACE _far *aws = add_workspace(iorb);
1196 u16 no_ncq = aws->no_ncq;
1197
1198 aws_free(aws);
1199 memset(aws, 0x00, sizeof(*aws));
1200 aws->no_ncq = no_ncq;
1201}
1202
1203/******************************************************************************
1204 * Free resources in ADD workspace (timer, buffer, ...). This function should
1205 * be called with the spinlock held to prevent race conditions.
1206 */
1207void aws_free(ADD_WORKSPACE _far *aws)
1208{
1209 if (aws->timer != 0) {
1210 ADD_CancelTimer(aws->timer);
1211 aws->timer = 0;
1212 }
1213
1214 if (aws->buf != NULL) {
1215 free(aws->buf);
1216 aws->buf = NULL;
1217 }
1218}
1219
1220/******************************************************************************
1221 * Lock the adapter, waiting for availability if necessary. This is expected
1222 * to be called at task/request time without the driver-level spinlock
1223 * aquired. Don't call at interrupt time.
1224 */
1225void lock_adapter(AD_INFO *ai)
1226{
1227 spin_lock(drv_lock);
1228 while (ai->busy) {
1229 spin_unlock(drv_lock);
1230 msleep(250);
1231 spin_lock(drv_lock);
1232 }
1233 ai->busy = 1;
1234 spin_unlock(drv_lock);
1235}
1236
1237/******************************************************************************
1238 * Unlock adapter (i.e. reset busy flag)
1239 */
1240void unlock_adapter(AD_INFO *ai)
1241{
1242 ai->busy = 0;
1243}
1244
1245/******************************************************************************
1246 * Timeout handler for I/O commands. Since timeout handling can involve
1247 * lengthy operations like port resets, the main code is located in a
1248 * separate function which is invoked via a context hook.
1249 */
1250void _cdecl _far timeout_callback(ULONG timer_handle, ULONG p1,
1251 ULONG p2)
1252{
1253 IORBH _far *iorb = (IORBH _far *) p1;
1254 int a = iorb_unit_adapter(iorb);
1255 int p = iorb_unit_port(iorb);
1256
1257 ADD_CancelTimer(timer_handle);
1258 dprintf("timeout for IORB %Fp\n", iorb);
1259
1260 /* Move the timed-out IORB to the abort queue. Since it's possible that the
1261 * IORB has completed after the timeout has expired but before we got to
1262 * this line of code, we'll check the return code of iorb_queue_del(): If it
1263 * returns an error, the IORB must have completed a few microseconds ago and
1264 * there is no timeout.
1265 */
1266 spin_lock(drv_lock);
1267 if (iorb_queue_del(&ad_infos[a].ports[p].iorb_queue, iorb) == 0) {
1268 iorb_queue_add(&abort_queue, iorb);
1269 iorb->ErrorCode = IOERR_ADAPTER_TIMEOUT;
1270 }
1271 spin_unlock(drv_lock);
1272
1273 /* Trigger abort processing function. We don't really care whether this
1274 * succeeds because the only reason why it would fail should be multiple
1275 * calls to DevHelp_ArmCtxHook() before the context hook had a chance to
1276 * start executing, which leaves two scenarios:
1277 *
1278 * - We succeded in arming the context hook. Fine.
1279 *
1280 * - We armed the context hook a second time before it had a chance to
1281 * start executing. In this case, the already scheduled context hook
1282 * will process our IORB as well.
1283 */
1284 DevHelp_ArmCtxHook(0, reset_ctxhook_h);
1285
1286 /* Set up a watchdog timer which calls the context hook manually in case
1287 * some kernel thread is looping around the IORB_COMPLETE status bit
1288 * without yielding the CPU (kernel threads don't preempt). This shouldn't
1289 * happen per design because kernel threads are supposed to yield but it
1290 * does in the early boot phase.
1291 */
1292 ADD_StartTimerMS(&th_reset_watchdog, 5000, (PFN) reset_watchdog, 0, 0);
1293}
1294
1295/******************************************************************************
1296 * Reset handler watchdog. If a timeout occurs, a context hook is armed which
1297 * will execute as soon as a kernel thread yields the CPU. However, some
1298 * kernel components won't yield the CPU during the early boot phase and the
1299 * only way to kick some sense into those components is to run the context
1300 * hook right inside this timer callback. Not exactly pretty, especially
1301 * considering the fact that context hooks were implemented to prevent running
1302 * lengthy operations like a port reset at task time, but without this
1303 * watchdog mechanism we run the risk of getting completely stalled by device
1304 * problems during the early boot phase.
1305 */
1306void _cdecl _far reset_watchdog(ULONG timer_handle, ULONG p1,
1307 ULONG p2)
1308{
1309 /* reset watchdog timer */
1310 ADD_CancelTimer(timer_handle);
1311 dprintf("reset watchdog invoked\n");
1312
1313 /* call context hook manually */
1314 reset_ctxhook(0);
1315}
1316
1317/******************************************************************************
1318 * small_code_ - this dummy func resolves the undefined reference linker
1319 * error that occurrs when linking WATCOM objects with DDK's link.exe
1320 */
1321void _cdecl small_code_(void)
1322{
1323}
1324
1325/******************************************************************************
1326 * Add unit info to ADAPTERINFO array (IOCC_GET_DEVICE_TABLE requests). The
1327 * adapter info array in the device table, dt->pAdapter[], is expected to be
1328 * initialized for the specified index (dt_ai).
1329 *
1330 * Please note that the device table adapter index, dta, is not always equal
1331 * to the physical adapter index, a: if SCSI emulation has been activated, the
1332 * last reported adapter is a virtual SCSI adapter and the physical adapter
1333 * indexes for those units are, of course, different from the device table
1334 * index of the virtual SCSI adapter.
1335 */
1336static int add_unit_info(IORB_CONFIGURATION _far *iorb_conf, int dta,
1337 int a, int p, int d, int scsi_id)
1338{
1339 DEVICETABLE _far *dt = iorb_conf->pDeviceTable;
1340 ADAPTERINFO _far *ptr = (ADAPTERINFO _far *) (((u32) dt & 0xffff0000U) +
1341 (u16) dt->pAdapter[dta]);
1342 UNITINFO _far *ui = ptr->UnitInfo + ptr->AdapterUnits;
1343 AD_INFO *ai = ad_infos + a;
1344
1345 if ((u32) (ui + 1) - (u32) dt > iorb_conf->DeviceTableLen) {
1346 dprintf("error: device table provided by DASD too small\n");
1347 iorb_seterr(&iorb_conf->iorbh, IOERR_CMD_SW_RESOURCE);
1348 return(-1);
1349 }
1350
1351 if (ai->ports[p].devs[d].unit_info == NULL) {
1352 /* provide original information about this device (unit) */
1353 memset(ui, 0x00, sizeof(*ui));
1354 ui->AdapterIndex = dta; /* device table adapter index */
1355 ui->UnitHandle = iorb_unit(a, p, d); /* physical adapter index */
1356 ui->UnitIndex = ptr->AdapterUnits;
1357 ui->UnitType = ai->ports[p].devs[d].dev_type;
1358 ui->QueuingCount = ai->ports[p].devs[d].ncq_max;;
1359 if (ai->ports[p].devs[d].removable) {
1360 ui->UnitFlags |= UF_REMOVABLE;
1361 }
1362 if (scsi_id > 0) {
1363 /* set fake SCSI ID for this unit */
1364 ui->UnitSCSITargetID = scsi_id;
1365 }
1366 } else {
1367 /* copy updated device (unit) information (IOCM_CHANGE_UNITINFO) */
1368 memcpy(ui, ai->ports[p].devs[d].unit_info, sizeof(*ui));
1369 }
1370
1371 ptr->AdapterUnits++;
1372 return(0);
1373}
1374
Note: See TracBrowser for help on using the repository browser.