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

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

Version 1.09
============

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