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

Last change on this file since 121 was 121, checked in by cjm, 14 years ago

OS2AHCI Version 1.19
====================

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