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

Last change on this file since 16 was 16, checked in by markus, 15 years ago

Changed wmakefile to use link.exe and alp.exe (Assembler) from DDK, now it compiles.

File size: 33.2 KB
Line 
1/******************************************************************************
2 * os2ahci.c - main file for os2ahci driver
3 *
4 * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
5 * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include "os2ahci.h"
23
24/* -------------------------- macros and constants ------------------------- */
25
26/* parse integer command line parameter */
27#define drv_parm_int(s, value, type, radix) \
28 { \
29 char _far *_ep; \
30 if ((s)[1] != ':') { \
31 cprintf("missing colon (:) after /%c\n", *(s)); \
32 goto init_fail; \
33 } \
34 value = (type) strtol((s) + 2, \
35 (const char _far* _far*) &_ep, \
36 radix); \
37 s = _ep; \
38 }
39
40/* ------------------------ typedefs and structures ------------------------ */
41
42/* -------------------------- function prototypes -------------------------- */
43
44 void small_code_ (void);
45
46/* ------------------------ global/static variables ------------------------ */
47
48int debug = 0; /* if > 0, print debug messages to COM1 */
49int thorough_scan; /* if != 0, perform thorough PCI scan */
50int init_reset; /* if != 0, reset ports during init */
51
52PFN Device_Help = 0; /* pointer to device helper entry point */
53ULONG RMFlags = 0; /* required by resource manager library */
54PFN RM_Help0 = NULL; /* required by resource manager library */
55PFN RM_Help3 = NULL; /* required by resource manager library */
56HDRIVER rm_drvh; /* resource manager driver handle */
57char rm_drvname[80]; /* driver name as returned by RM */
58USHORT add_handle; /* driver handle (RegisterDeviceClass) */
59UCHAR timer_pool[TIMER_POOL_SIZE]; /* timer pool */
60
61/* resource manager driver information structure */
62DRIVERSTRUCT rm_drvinfo = {
63 "OS2AHCI", /* driver name */
64 "AHCI SATA Driver", /* driver description */
65 "GNU", /* vendor name */
66 CMVERSION_MAJOR, /* RM interface version major */
67 CMVERSION_MINOR, /* RM interface version minor */
68 2010, 4, 27, /* date */
69 0, /* driver flags */
70 DRT_ADDDM, /* driver type */
71 DRS_ADD, /* driver sub type */
72 NULL /* driver callback */
73};
74
75ULONG drv_lock; /* driver-level spinlock */
76IORB_QUEUE driver_queue; /* driver-level IORB queue */
77AD_INFO ad_infos[MAX_AD]; /* adapter information list */
78int ad_info_cnt; /* number of entries in ad_infos[] */
79int init_complete; /* if != 0, initialization has completed */
80
81/* apapter/port-specific options saved when parsing the command line */
82u8 link_speed[MAX_AD][AHCI_MAX_PORTS];
83u8 disable_ncq[MAX_AD][AHCI_MAX_PORTS];
84
85static char init_msg[] = "OS2AHCI driver version %d.%02d\n";
86static char exit_msg[] = "OS2AHCI driver *not* installed\n";
87
88/* ----------------------------- start of code ----------------------------- */
89
90/******************************************************************************
91 * OS/2 device driver main strategy function. This function is only used
92 * for initialization purposes; all other calls go directly to the adapter
93 * device driver's strategy function.
94 */
95USHORT c_strat(RPH _far *req)
96{
97 u16 rc;
98
99 switch (req->Cmd) {
100
101 case CMDInitBase:
102 rc = init_drv((RPINITIN _far *) req);
103 break;
104
105 default:
106 rc = STDON | STATUS_ERR_UNKCMD;
107 break;
108 }
109
110 return(rc);
111}
112
113/******************************************************************************
114 * Intialize the os2ahci driver. This includes command line parsing, scanning
115 * the PCI bus for supported AHCI adapters, etc.
116 */
117USHORT init_drv(RPINITIN _far *req)
118{
119 RPINITOUT _far *rsp = (RPINITOUT _far *) req;
120 DDD_PARM_LIST _far *ddd_pl = (DDD_PARM_LIST _far *) req->InitArgs;
121 APIRET rmrc;
122 char _far *cmd_line;
123 char _far *s;
124 int adapter_index;
125 int port_index;
126 u16 vendor;
127 u16 device;
128
129 /* set device helper entry point */
130 Device_Help = req->DevHlpEP;
131
132 /* create driver-level spinlock */
133 DevHelp_CreateSpinLock(&drv_lock);
134
135 if (debug) {
136 /* initialize debug interface (COM1) */
137 init_com1();
138 }
139
140 /* print initialization message */
141 cprintf(init_msg, VERSION / 100, VERSION % 100);
142
143 /* register driver with resource manager */
144 if ((rmrc = RMCreateDriver(&rm_drvinfo, &rm_drvh)) != RMRC_SUCCESS) {
145 cprintf("failed to register driver with resource manager (rc = %d)\n", rmrc);
146 goto init_fail;
147 }
148
149 /* parse command line parameters */
150 cmd_line = (char _far *) ((u32) ddd_pl & 0xffff0000l) + ddd_pl->cmd_line_args;
151 adapter_index = 0;
152 port_index = 0;
153
154 for (s = cmd_line; *s != 0; s++) {
155 if (*s == '/' && s[1] != '\0') {
156 s++;
157 switch(tolower(*s)) {
158
159 case 'c':
160 /* set COM port base address for debug messages */
161 drv_parm_int(s, com_base, u16, 16);
162 break;
163
164 case 'd':
165 /* increase debug level */
166 debug++;
167 break;
168
169 case 'i':
170 /* add specfied PCI ID as a supported generic AHCI adapter */
171 drv_parm_int(s, vendor, u16, 16);
172 drv_parm_int(s, device, u16, 16);
173 if (add_pci_id(vendor, device)) {
174 cprintf("failed to add PCI ID %04x:%04x\n", vendor, device);
175 goto init_fail;
176 }
177 thorough_scan = 1;
178 break;
179
180 case 't':
181 /* perform thorough PCI scan (i.e. look for individual supported PCI IDs) */
182 thorough_scan = 1;
183 break;
184
185 case 'r':
186 /* reset ports during initialization */
187 init_reset = 1;
188 break;
189
190 case 'a':
191 /* set adapter index for adapter and port-related options */
192 drv_parm_int(s, adapter_index, int, 10);
193 if (adapter_index < 0 || adapter_index >= MAX_AD) {
194 cprintf("invalid adapter index (%d)\n", adapter_index);
195 goto init_fail;
196 }
197 break;
198
199 case 'p':
200 /* set port index for port-related options */
201 drv_parm_int(s, port_index, int, 10);
202 if (port_index < 0 || port_index >= AHCI_MAX_PORTS) {
203 cprintf("invalid port index (%d)\n", port_index);
204 goto init_fail;
205 }
206 break;
207
208 case 's':
209 /* set link speed of current port on current adapter */
210 drv_parm_int(s, link_speed[adapter_index][port_index], u8, 10);
211 init_reset = 1;
212 break;
213
214 case 'n':
215 /* disable NCQ on this port */
216 drv_parm_int(s, disable_ncq[adapter_index][port_index], u8, 10);
217 break;
218
219 default:
220 cprintf("invalid option: /%c\n", *s);
221 goto init_fail;
222 }
223 }
224 }
225
226 /* scan PCI bus for supported devices */
227 scan_pci_bus();
228
229 if (ad_info_cnt > 0) {
230 /* initialization succeeded and we found at least one AHCI adapter */
231 ADD_InitTimer(timer_pool, sizeof(timer_pool));
232 mdelay_cal();
233
234 if (DevHelp_RegisterDeviceClass("OS2AHCI", (PFN) add_entry, 0, 1,
235 &add_handle)) {
236 cprintf("error: couldn't register device class\n");
237 goto init_fail;
238 }
239
240 /* allocate context hooks */
241 if (DevHelp_AllocateCtxHook(mk_NPFN(restart_hook), &restart_ctxhook_h) != 0 ||
242 DevHelp_AllocateCtxHook(mk_NPFN(reset_hook), &reset_ctxhook_h) != 0 ||
243 DevHelp_AllocateCtxHook(mk_NPFN(engine_hook), &engine_ctxhook_h)) {
244 cprintf("failed to allocate task-time context hooks\n");
245 goto init_fail;
246 }
247
248 rsp->CodeEnd = (u16) end_of_code;
249 rsp->DataEnd = (u16) &end_of_data;
250 return(STDON);
251 }
252
253init_fail:
254 /* initialization failed; set segment sizes to 0 and return error */
255 cprintf(exit_msg);
256 rsp->CodeEnd = 0;
257 rsp->DataEnd = 0;
258
259 /* free context hooks */
260 if (engine_ctxhook_h != 0) DevHelp_FreeCtxHook(engine_ctxhook_h);
261 if (reset_ctxhook_h != 0) DevHelp_FreeCtxHook(reset_ctxhook_h);
262 if (restart_ctxhook_h != 0) DevHelp_FreeCtxHook(restart_ctxhook_h);
263
264 if (rm_drvh != 0) {
265 /* remove driver from resource manager */
266 RMDestroyDriver(rm_drvh);
267 }
268
269 cprintf(exit_msg);
270 return(STDON | ERROR_I24_QUIET_INIT_FAIL);
271}
272
273/******************************************************************************
274 * ADD entry point. This is the main entry point for all ADD requests. Due to
275 * the asynchronous nature of ADD drivers, this function primarily queues the
276 * IORB(s) to the corresponding adapter or port queues, then triggers the
277 * state machine to initiate processing queued IORBs.
278 *
279 * NOTE: In order to prevent race conditions or engine stalls, certain rules
280 * around locking, unlocking and IORB handling in general have been
281 * established. Refer to the comments in "trigger_engine()" for
282 * details.
283 */
284void _far _loadds add_entry(IORBH _far *first_iorb)
285{
286 IORBH _far *iorb;
287 IORBH _far *next = NULL;
288
289 spin_lock(drv_lock);
290
291 for (iorb = first_iorb; iorb != NULL; iorb = next) {
292 /* Queue this IORB. Queues primarily exist on port level but there are
293 * some requests which affect the whole driver, most notably
294 * IOCC_CONFIGURATION. In either case, adding the IORB to the driver or
295 * port queue will change the links, thus we need to save the original
296 * link in 'next'.
297 */
298 next = (iorb->RequestControl | IORB_CHAIN) ? iorb->pNxtIORB : 0;
299
300 iorb->Status = 0;
301 iorb->ErrorCode = 0;
302 memset(&iorb->ADDWorkSpace, 0x00, sizeof(ADD_WORKSPACE));
303
304 if (iorb_driver_level(iorb)) {
305 /* adapter-level IORB */
306 iorb->UnitHandle = 0;
307 iorb_queue_add(&driver_queue, iorb);
308
309 } else {
310 /* port-level IORB */
311 int a = iorb_unit_adapter(iorb);
312 int p = iorb_unit_port(iorb);
313 int d = iorb_unit_device(iorb);
314
315 if (a >= ad_info_cnt ||
316 p > ad_infos[a].port_max ||
317 d > ad_infos[a].ports[p].dev_max ||
318 (ad_infos[a].port_map & (1UL << p)) == 0) {
319
320 /* unit handle outside of the allowed range */
321 dprintf("warning: IORB for %d.%d.%d out of range\n", a, p, d);
322 iorb->Status = IORB_ERROR | IORB_DONE;
323 iorb->ErrorCode = IOERR_CMD_SYNTAX;
324 if (iorb->RequestControl & IORB_ASYNC_POST) {
325 iorb->NotifyAddress(iorb);
326 }
327 continue;
328 }
329
330 iorb_queue_add(&ad_infos[a].ports[p].iorb_queue, iorb);
331 }
332 }
333
334 /* trigger state machine */
335 trigger_engine();
336
337 spin_unlock(drv_lock);
338}
339
340/******************************************************************************
341 * Trigger IORB queue engine. This is a wrapper function for trigger_engine_1()
342 * which will try to get all IORBs sent on their way a couple of times. If
343 * there are still IORBs ready for processing after this, this function will
344 * hand off to a context hook which will continue to trigger the engine until
345 * all IORBs have been sent.
346 */
347void trigger_engine(void)
348{
349 int i;
350
351 for (i = 0; i < 3; i++) {
352 if (trigger_engine_1() == 0) {
353 /* done -- all IORBs have been sent on their way */
354 return;
355 }
356 }
357
358 /* Something keeps bouncing; hand off to the engine context hook which will
359 * keep trying in the background.
360 */
361 DevHelp_ArmCtxHook(0, engine_ctxhook_h);
362}
363
364/******************************************************************************
365 * Trigger IORB queue engine in order to send commands in the driver/port IORB
366 * queues to the AHCI hardware. This function will return the number of IORBs
367 * sent. Keep in mind that IORBs might "bounce" if the adapter/port is not in
368 * a state to accept the command, thus it might take quite a few calls to get
369 * all IORBs on their way. This is why there's a wrapper function which tries
370 * it a few times, then hands off to a context hook which will keep trying in
371 * the background.
372 *
373 * IORBs might complete before send_iorb() has returned, at any time during
374 * interrupt processing or on another CPU on SMP systems. IORB completion
375 * means modifications to the corresponding IORB queue (the completed IORB
376 * is removed from the queue) thus we need to protect the IORB queues from
377 * race conditions. The safest approach short of keeping the driver-level
378 * spinlock aquired permanently is to keep it throughout this function and
379 * release it temporarily in send_iorb().
380 *
381 * This implies that the handler functions are fully responsible for aquiring
382 * the driver-level spinlock when they need it, and for releasing it again.
383 *
384 * As a rule of thumb, get the driver-level spinlock whenever accessing
385 * volatile variables (IORB queues, values in ad_info[], ...).
386 *
387 * Additional Notes:
388 *
389 * - This function is expected to be called with the spinlock aquired
390 *
391 * - Adapters can be flagged as 'busy' which means no new IORBs are sent (they
392 * just remain in the queue). This can be used to release the driver-level
393 * spinlock while making sure no new IORBs are going to hit the hardware.
394 * In order to prevent engine stalls, all handlers using this functionality
395 * need to invoke trigger_engine() after resetting the busy flag.
396 *
397 * - Driver-level IORBs are not synchronized by adapter-level 'busy' flags.
398 * However, the driver-level queue is worked "one entry at a time" which
399 * means that no new IORBs will be queued on the driver-level queue until
400 * the head element has completed processing. This means that driver-
401 * level IORB handlers don't need to protect against each other. But they
402 * they do need to keep in mind interference with port-level IORBs:
403 *
404 * - Driver-level IORB handlers must obtain the spinlock and/or flag all
405 * adapters as 'busy' which are affected by the driver-level IORB
406 *
407 * - Driver-level IORB handlers must not access the hardware of a
408 * particular adapter if it's flagged as 'busy'
409 */
410int trigger_engine_1(void)
411{
412 IORBH _far *iorb;
413 IORBH _far *next;
414 int iorbs_sent = 0;
415 int a;
416 int p;
417
418 iorbs_sent = 0;
419
420 /* process driver-level IORBs */
421 if ((iorb = driver_queue.root) != NULL && !add_workspace(iorb)->processing) {
422 send_iorb(iorb);
423 iorbs_sent++;
424 }
425
426 /* process port-level IORBs */
427 for (a = 0; a < ad_info_cnt; a++) {
428 AD_INFO *ai = ad_infos + a;
429 if (ai->busy) {
430 /* adapter is busy; don't process any IORBs */
431 continue;
432 }
433 for (p = 0; p <= ai->port_max; p++) {
434 /* send all queued IORBs on this port */
435 next = NULL;
436 for (iorb = ai->ports[p].iorb_queue.root; iorb != NULL; iorb = next) {
437 next = iorb->pNxtIORB;
438 if (!add_workspace(iorb)->processing) {
439 send_iorb(iorb);
440 iorbs_sent++;
441 }
442 }
443 }
444 }
445
446 return(iorbs_sent);
447}
448
449/******************************************************************************
450 * Send a single IORB to the corresponding AHCI adapter/port. This is just a
451 * switch board for calling the corresponding iocc_*() handler function.
452 *
453 * NOTE: This function is expected to be called with the driver-level spinlock
454 * aquired. It will release it before calling any of the handler
455 * functions and re-aquire it when done.
456 */
457void send_iorb(IORBH _far *iorb)
458{
459 /* Mark IORB as "processing" before doing anything else. Once the IORB is
460 * marked as "processing", we can release the spinlock because subsequent
461 * invocations of trigger_engine() (e.g. at interrupt time) will ignore this
462 * IORB.
463 */
464 add_workspace(iorb)->processing = 1;
465 spin_unlock(drv_lock);
466
467 switch (iorb->CommandCode) {
468
469 case IOCC_CONFIGURATION:
470 iocc_configuration(iorb);
471 break;
472
473 case IOCC_DEVICE_CONTROL:
474 iocc_device_control(iorb);
475 break;
476
477 case IOCC_UNIT_CONTROL:
478 iocc_unit_control(iorb);
479 break;
480
481 case IOCC_GEOMETRY:
482 iocc_geometry(iorb);
483 break;
484
485 case IOCC_EXECUTE_IO:
486 iocc_execute_io(iorb);
487 break;
488
489 case IOCC_UNIT_STATUS:
490 iocc_unit_status(iorb);
491 break;
492
493 case IOCC_ADAPTER_PASSTHRU:
494 iocc_adapter_passthru(iorb);
495 break;
496
497 default:
498 /* unsupported call */
499 iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
500 iorb_done(iorb);
501 break;
502 }
503
504 /* re-aquire spinlock before returning to trigger_engine() */
505 spin_lock(drv_lock);
506}
507
508/******************************************************************************
509 * Handle IOCC_CONFIGURATION requests.
510 */
511void iocc_configuration(IORBH _far *iorb)
512{
513 int a;
514
515 switch (iorb->CommandModifier) {
516
517 case IOCM_COMPLETE_INIT:
518 /* Complete initialization. From now on, we won't have to restore the BIOS
519 * configuration after each command and we're fully operational (i.e. will
520 * use interrupts, timers and context hooks instead of polling).
521 */
522 if (!init_complete) {
523 dprintf("leaving initialization mode\n");
524 spin_lock(drv_lock);
525 for (a = 0; a < ad_info_cnt; a++) {
526 ahci_complete_init(ad_infos + a);
527 }
528 init_complete = 1;
529 spin_unlock(drv_lock);
530 }
531 iorb_done(iorb);
532 break;
533
534 case IOCM_GET_DEVICE_TABLE:
535 /* construct a device table */
536 iocm_device_table(iorb);
537 break;
538
539 default:
540 iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
541 iorb_done(iorb);
542 break;
543 }
544}
545
546/******************************************************************************
547 * Handle IOCC_DEVICE_CONTROL requests.
548 */
549void iocc_device_control(IORBH _far *iorb)
550{
551 AD_INFO *ai = ad_infos + iorb_unit_adapter(iorb);
552 IORBH _far *ptr;
553 IORBH _far *next = NULL;
554 int p = iorb_unit_port(iorb);
555 int d = iorb_unit_device(iorb);
556
557 switch (iorb->CommandModifier) {
558
559 case IOCM_ABORT:
560 /* abort all pending commands on specified port and device */
561 spin_lock(drv_lock);
562 for (ptr = ai->ports[p].iorb_queue.root; ptr != NULL; ptr = next) {
563 next = ptr->pNxtIORB;
564 /* move all matching IORBs to the abort queue */
565 if (ptr != iorb && iorb_unit_device(ptr) == d) {
566 iorb_queue_del(&ai->ports[p].iorb_queue, ptr);
567 iorb_queue_add(&abort_queue, ptr);
568 ptr->ErrorCode = IOERR_CMD_ABORTED;
569 }
570 }
571 spin_unlock(drv_lock);
572
573 /* trigger reset context hook which will finish the abort processing */
574 DevHelp_ArmCtxHook(0, reset_ctxhook_h);
575 break;
576
577 case IOCM_SUSPEND:
578 case IOCM_RESUME:
579 case IOCM_GET_QUEUE_STATUS:
580 /* Suspend/resume operations allow access to the hardware for other
581 * entities such as IBMIDECD.FLT. Since os2ahci implements both ATA
582 * and ATAPI in the same driver, this won't be required.
583 */
584 iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
585 break;
586
587 case IOCM_LOCK_MEDIA:
588 case IOCM_UNLOCK_MEDIA:
589 case IOCM_EJECT_MEDIA:
590 /* unit control commands to lock, unlock and eject media */
591 /* will be supported later... */
592 iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
593 break;
594
595 default:
596 iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
597 break;
598 }
599
600 iorb_done(iorb);
601}
602
603/******************************************************************************
604 * Handle IOCC_UNIT_CONTROL requests.
605 */
606void iocc_unit_control(IORBH _far *iorb)
607{
608 IORB_UNIT_CONTROL _far *iorb_uc = (IORB_UNIT_CONTROL _far *) iorb;
609 int a = iorb_unit_adapter(iorb);
610 int p = iorb_unit_port(iorb);
611 int d = iorb_unit_device(iorb);
612
613 spin_lock(drv_lock);
614 switch (iorb->CommandModifier) {
615
616 case IOCM_ALLOCATE_UNIT:
617 /* allocate unit for exclusive access */
618 if (ad_infos[a].ports[p].devs[d].allocated) {
619 iorb_seterr(iorb, IOERR_UNIT_ALLOCATED);
620 } else {
621 ad_infos[a].ports[p].devs[d].allocated = 1;
622 }
623 break;
624
625 case IOCM_DEALLOCATE_UNIT:
626 /* deallocate exclusive access to unit */
627 if (!ad_infos[a].ports[p].devs[d].allocated) {
628 iorb_seterr(iorb, IOERR_UNIT_NOT_ALLOCATED);
629 } else {
630 ad_infos[a].ports[p].devs[d].allocated = 0;
631 }
632 break;
633
634 case IOCM_CHANGE_UNITINFO:
635 /* Change unit (device) information. One reason for this IOCM is the
636 * interface for filter device drivers: a filter device driver can
637 * either change existing UNITINFOs or permanently allocate units
638 * and fabricate new [logical] units; the former is the reason why we
639 * must store the pointer to the updated UNITNIFO for subsequent
640 * IOCC_CONFIGURATION/IOCM_GET_DEVICE_TABLE calls.
641 */
642 if (!ad_infos[a].ports[p].devs[d].allocated) {
643 iorb_seterr(iorb, IOERR_UNIT_NOT_ALLOCATED);
644 break;
645 }
646 ad_infos[a].ports[p].devs[d].unit_info = iorb_uc->pUnitInfo;
647 break;
648
649 default:
650 iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
651 break;
652 }
653
654 spin_unlock(drv_lock);
655 iorb_done(iorb);
656}
657
658/******************************************************************************
659 * Scan all ports for AHCI devices and construct a DASD device table.
660 *
661 * NOTE: This function may be called multiple times. Only the first invocation
662 * will actually scan for devices; all subsequent calls will merely
663 * return the results of the initial scan, potentially augmented by
664 * modified unit infos after IOCC_CONFIGURATION/IOCM_CHANGE_UNITINFO
665 * requests.
666 */
667void iocm_device_table(IORBH _far *iorb)
668{
669 IORB_CONFIGURATION _far *iorb_conf;
670 DEVICETABLE _far *dt;
671 char _far *pos;
672 int rc;
673 int a;
674 int p;
675 int d;
676
677 iorb_conf = (IORB_CONFIGURATION _far *) iorb;
678 dt = iorb_conf->pDeviceTable;
679
680 spin_lock(drv_lock);
681
682 /* initialize device table header */
683 dt->ADDLevelMajor = ADD_LEVEL_MAJOR;
684 dt->ADDLevelMinor = ADD_LEVEL_MINOR;
685 dt->ADDHandle = add_handle;
686 dt->TotalAdapters = ad_info_cnt;
687
688 /* Initial position of dynamic portion of device table (i.e. behind the
689 * array of ADAPTERINFO pointers, pAdapter, in the device table)
690 */
691 pos = (char _far *) (dt->pAdapter + ad_info_cnt);
692
693 for (a = 0; a < ad_info_cnt; a++) {
694 ADAPTERINFO _far *ptr = (ADAPTERINFO _far *) pos;
695 AD_INFO *ad_info = ad_infos + a;
696 int units = 0;
697
698 /* sanity check for sufficient space in device table */
699 if ((u32) (ptr + 1) - (u32) dt > iorb_conf->DeviceTableLen) {
700 dprintf("error: device table provided by DASD too small\n");
701 iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
702 goto iocm_device_table_done;
703 }
704
705 /* set ADAPTERINFO offset in device table */
706 dt->pAdapter[a] = (ADAPTERINFO _near *) ((u32) ptr & 0xffff);
707
708 /* fill in adapter information structure in device table */
709 memset(ptr, 0x00, sizeof(*ptr));
710 sprintf(ptr->AdapterName, "AHCI_%d", a);
711 ptr->AdapterDevBus = AI_DEVBUS_ST506 | AI_DEVBUS_32BIT;
712 ptr->AdapterIOAccess = AI_IOACCESS_BUS_MASTER;
713 ptr->AdapterHostBus = AI_HOSTBUS_OTHER | AI_BUSWIDTH_32BIT;
714 ptr->AdapterFlags = AF_16M | AF_HW_SCATGAT;
715
716 /* AHCI limits S/G elements to 22 bits, thus we'll report only half of
717 * our S/G list buffers to reduce complexity. The command preparation code
718 * will always try to map as many S/G elements as possible so the physical
719 * S/G list capacity is not really wasted except in rare conditions where
720 * we need to split commands with long S/G lists without any suitable split
721 * points except those at the reported MaxHWSGList.
722 */
723 ptr->MaxHWSGList = AHCI_MAX_SG / 2;
724
725 if (!ad_info->port_scan_done) {
726 /* First call; need to scan AHCI hardware for devices. Since this might
727 * be a lengthy operation, especially when init_reset is set, we'll mark
728 * the adapter as busy (new IORBs will only be queued but not executed)
729 * and release the spinlock while scanning the ports so interrupts will
730 * be processed.
731 */
732 if (ad_info->busy) {
733 dprintf("error: port scan requested while adapter was busy\n");
734 iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
735 goto iocm_device_table_done;
736 }
737 ad_info->busy = 1;
738 spin_unlock(drv_lock);
739 rc = ahci_scan_ports(ad_info);
740 spin_lock(drv_lock);
741 ad_info->busy = 0;
742
743 if (rc != 0) {
744 dprintf("error: port scan failed on adapter #%d\n", a);
745 iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
746 goto iocm_device_table_done;
747 }
748 ad_info->port_scan_done = 1;
749 }
750
751 /* insert devices (units) into the device table */
752 for (p = 0; p <= ad_info->port_max; p++) {
753 for (d = 0; d <= ad_info->ports[p].dev_max; d++) {
754 if (ad_info->ports[p].devs[d].present) {
755 UNITINFO _far *ui = ptr->UnitInfo + units;
756
757 /* sanity check for sufficient space in device table */
758 if ((u32) (ui + 1) - (u32) dt > iorb_conf->DeviceTableLen) {
759 dprintf("error: device table provided by DASD too small\n");
760 iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
761 goto iocm_device_table_done;
762 }
763
764 if (ad_info->ports[p].devs[d].unit_info == NULL) {
765 /* provide initial information about this device (unit) */
766 memset(ui, 0x00, sizeof(*ui));
767 ui->AdapterIndex = a;
768 ui->UnitIndex = units;
769 ui->UnitHandle = iorb_unit(a, p, d);
770 ui->UnitType = ad_info->ports[p].devs[d].dev_type;
771 ui->QueuingCount = ad_info->ports[p].devs[d].ncq_max;;
772 if (ad_info->ports[p].devs[d].removable) {
773 ui->UnitFlags |= UF_REMOVABLE;
774 }
775 } else {
776 /* copy updated device (unit) information (IOCM_CHANGE_UNITINFO) */
777 memcpy(ui, ad_info->ports[p].devs[d].unit_info, sizeof(*ui));
778 }
779 units++;
780 }
781 }
782 }
783
784 /* set total device (unit) count for this adapter */
785 ptr->AdapterUnits = units;
786
787 /* calculate offset for next adapter */
788 pos = (char _far *) (ptr->UnitInfo + units);
789 }
790
791iocm_device_table_done:
792 spin_unlock(drv_lock);
793 iorb_done(iorb);
794}
795
796/******************************************************************************
797 * Handle IOCC_GEOMETRY requests.
798 */
799void iocc_geometry(IORBH _far *iorb)
800{
801 switch (iorb->CommandModifier) {
802
803 case IOCM_GET_MEDIA_GEOMETRY:
804 case IOCM_GET_DEVICE_GEOMETRY:
805 add_workspace(iorb)->idempotent = 1;
806 ahci_get_geometry(iorb);
807 break;
808
809 default:
810 iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
811 iorb_done(iorb);
812 }
813}
814
815/******************************************************************************
816 * Handle IOCC_EXECUTE_IO requests.
817 */
818void iocc_execute_io(IORBH _far *iorb)
819{
820 switch (iorb->CommandModifier) {
821
822 case IOCM_READ:
823 add_workspace(iorb)->idempotent = 1;
824 ahci_read(iorb);
825 break;
826
827 case IOCM_READ_VERIFY:
828 add_workspace(iorb)->idempotent = 1;
829 ahci_verify(iorb);
830 break;
831
832 case IOCM_WRITE:
833 add_workspace(iorb)->idempotent = 1;
834 ahci_write(iorb);
835 break;
836
837 case IOCM_WRITE_VERIFY:
838 add_workspace(iorb)->idempotent = 1;
839 ahci_write(iorb);
840 break;
841
842 default:
843 iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
844 iorb_done(iorb);
845 }
846}
847
848/******************************************************************************
849 * Handle IOCC_UNIT_STATUS requests.
850 */
851void iocc_unit_status(IORBH _far *iorb)
852{
853 switch (iorb->CommandModifier) {
854
855 case IOCM_GET_UNIT_STATUS:
856 add_workspace(iorb)->idempotent = 1;
857 ahci_unit_ready(iorb);
858 break;
859
860 default:
861 iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
862 iorb_done(iorb);
863 }
864}
865
866/******************************************************************************
867 * Handle IOCC_ADAPTER_PASSTHROUGH requests.
868 */
869void iocc_adapter_passthru(IORBH _far *iorb)
870{
871 switch (iorb->CommandModifier) {
872
873 case IOCM_EXECUTE_CDB:
874 add_workspace(iorb)->idempotent = 0;
875 ahci_execute_cdb(iorb);
876 break;
877
878 case IOCM_EXECUTE_ATA:
879 add_workspace(iorb)->idempotent = 0;
880 ahci_execute_ata(iorb);
881 break;
882
883 default:
884 iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
885 iorb_done(iorb);
886 }
887}
888
889/******************************************************************************
890 * Add an IORB to the specified queue.
891 */
892void iorb_queue_add(IORB_QUEUE _far *queue, IORBH _far *iorb)
893{
894 if (iorb_priority(iorb) {
895 /* priority IORB; insert at first position */
896 iorb->pNxtIORB = queue->root;
897 queue->root = iorb;
898
899 } else {
900 /* append IORB to end of queue */
901 iorb->pNxtIORB = NULL;
902
903 if (queue->root == NULL) {
904 queue->root = iorb;
905 } else {
906 queue->tail->pNxtIORB = iorb;
907 }
908 queue->tail = iorb;
909 }
910
911 dprintf("IORB queued: %d/%d (queue = %Fp, IORB = %Fp)\n",
912 iorb->CommandCode, iorb->CommandModifier, queue, iorb);
913}
914
915/******************************************************************************
916 * Remove an IORB from the specified queue.
917 */
918int iorb_queue_del(IORB_QUEUE _far *queue, IORBH _far *iorb)
919{
920 IORBH _far *_iorb;
921 IORBH _far *_prev = NULL;
922 int found = 0;
923
924 for (_iorb = queue->root; _iorb != NULL; _iorb = _iorb->pNxtIORB) {
925 if (_iorb == iorb) {
926 /* found the IORB to be removed */
927 if (_prev != NULL) {
928 _prev->pNxtIORB = _iorb->pNxtIORB;
929 } else {
930 queue->root = _iorb->pNxtIORB;
931 }
932 if (_iorb == queue->tail) {
933 queue->tail = _prev;
934 }
935 found = 1;
936 break;
937 }
938 _prev = _iorb;
939 }
940
941 if (found) {
942 dprintf("IORB removed: %d/%d (queue = %Fp, IORB = %Fp) - %04x/%04x\n",
943 iorb->CommandCode, iorb->CommandModifier, queue, iorb,
944 iorb->Status, iorb->ErrorCode);
945 } else {
946 dprintf("IORB %Fp not found in queue %Fp\n", iorb, queue);
947 }
948
949 return(!found);
950}
951
952/******************************************************************************
953 * Set the error code in the specified IORB
954 *
955 * NOTE: This function does *not* call iorb_done(). It merely sets the IORB
956 * status to the specified error code.
957 */
958void iorb_seterr(IORBH _far *iorb, USHORT error_code)
959{
960 iorb->ErrorCode = error_code;
961 iorb->Status = IORB_ERROR;
962}
963
964/******************************************************************************
965 * Mark the specified IORB as done and notify the asynchronous post function,
966 * if any. The IORB is also removed from the corresponding IORB queue.
967 *
968 * NOTES: This function does not clear the Status field; it merely adds the
969 * IORB_DONE flag.
970 *
971 * This function is expected to be called *without* the corresponding
972 * driver-level drv_lock aquired. It will aquire the spinlock before
973 * updating the IORB queue and release it before notifying the upstream
974 * code in order to prevent deadlocks.
975 *
976 * Due to this logic, this function is only good for simple task-time
977 * completions. Functions working on lists of IORBs (such as interrupt
978 * handlers or context hooks) should implement their own logic. See
979 * abort_ctxhook() for an example.
980 */
981void iorb_done(IORBH _far *iorb)
982{
983 int a = iorb_unit_adapter(iorb);
984 int p = iorb_unit_port(iorb);
985
986 /* remove IORB from corresponding queue */
987 spin_lock(drv_lock);
988 if (iorb_driver_level(iorb)) {
989 iorb_queue_del(&driver_queue, iorb);
990 } else {
991 iorb_queue_del(&ad_infos[a].ports[p].iorb_queue, iorb);
992 }
993 aws_free(add_workspace(iorb));
994 spin_unlock(drv_lock);
995
996 /* notify caller, if requested */
997 iorb->Status |= IORB_DONE;
998 if (iorb->RequestControl & IORB_ASYNC_POST) {
999 iorb->NotifyAddress(iorb);
1000 }
1001}
1002
1003/******************************************************************************
1004 * Requeue the specified IORB such that it will be sent downstream for
1005 * processing again. This includes freeing all resources currently allocated
1006 * (timer, buffer, ...) and resetting the flags to 0.
1007 *
1008 * The following flags are preserved:
1009 * - no_ncq
1010 */
1011void iorb_requeue(IORBH _far *iorb)
1012{
1013 ADD_WORKSPACE _far *aws = add_workspace(iorb);
1014 u16 no_ncq = aws->no_ncq;
1015
1016 aws_free(aws);
1017 memset(aws, 0x00, sizeof(*aws));
1018 aws->no_ncq = no_ncq;
1019}
1020
1021/******************************************************************************
1022 * small_code_ - this dummy func resolves the undefined reference linker
1023 * error that occurrs when linking WATCOM objects with DDK's link.exe
1024 */
1025void small_code_(void)
1026{
1027}
Note: See TracBrowser for help on using the repository browser.