source: trunk/src/os2ahci/libc.c@ 71

Last change on this file since 71 was 71, checked in by chris, 15 years ago
  • Added minimum NCQ depth for SATA devices which don't support NCQ at all
  • Removed ATAPI devices from the flush cache queue as they caused timeouts with a DVD drive in an HP test box
  • Various cosmetic changes to debug levels, etc.
File size: 21.8 KB
Line 
1/******************************************************************************
2 * libc.c - minimal subset of C runtime library for os2ahci
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#define MSG_REPLACEMENT_STRING 1178 /* empty message with a single %; used
27 * for printing custom messages via
28 * DevHelp_Save_Message() */
29
30/* heap management constants */
31#define HEAP_SIZE 8192
32#define HEAP_UNIT 128
33#define HEAP_UNIT_CNT (HEAP_SIZE / HEAP_UNIT)
34
35/* ------------------------ typedefs and structures ------------------------ */
36
37/* mdelay() calibration status */
38typedef enum {
39 MD_NOT_CALIBRATED, /* delay loop not calibrated */
40 MD_CALIBRATION_START, /* calibration run started */
41 MD_CALIBRATION_END, /* calibration run ended */
42 MD_CALIBRATION_DONE /* calibration complete */
43} MDCAL;
44
45/* -------------------------- function prototypes -------------------------- */
46
47static void long_to_asc (long val,
48 char _far *buf,
49 int base,
50 int zero, int flen);
51static void _cdecl _far mdelay_timer_callback (ULONG timer_handle,
52 ULONG parm1,
53 ULONG parm2);
54static int mdelay_cal_end (void);
55
56/* ------------------------ global/static variables ------------------------ */
57
58/* debug COM port base address */
59u16 com_base = 0x03f8;
60
61static char hex_digits[] = "0123456789abcdef";
62static ULONG com_lock;
63
64/* message table for DosHelp_Save_Message() which prints the first string */
65static MSGTABLE init_msgtbl = {
66 MSG_REPLACEMENT_STRING,
67 1,
68 0
69};
70
71/* COM port initialization sequence */
72static struct {
73 int reg;
74 u8 data;
75} com_init_sequence[] = {
76 3, 0x80, /* overlay divisor latch register at 0x3f8 and 0x3f9 */
77 0, 0x01, /* set low byte of divisor to 1 (115200 baud) */
78 1, 0x00, /* set high byte of divisor to 0 */
79 3, 0x03, /* reset divisor latch register overlay and set 8,n,1 */
80 1, 0x00, /* disable interrupts */
81 4, 0x0f, /* modem control register */
82 -1, 0x00
83};
84
85/* delay loop calibration data */
86volatile MDCAL mdelay_cal_status = 0; /* delay loop calibration status */
87volatile u32 mdelay_loops_per_ms = 0; /* delay loop counter */
88
89/* very small heap for dynamic memory management */
90static u8 heap_buf[HEAP_SIZE];
91static u8 heap_units[HEAP_UNIT_CNT];
92
93/* ----------------------------- start of code ----------------------------- */
94
95/******************************************************************************
96 * Initialize COM1 to 115200,n,8,1
97 *
98 * NOTE: Something is wrong with this code, or the init sequence, but we never
99 * got around to fixing it because it works fine on Virtualbox, and on
100 * physical machines we tend to have the kernel debugger running on the
101 * same port simply because serial ports are not that plenty on PCs
102 * these days, thus KDB will set port parameters for us. This is going
103 * to be fixed eventually...
104 */
105void init_com1(void)
106{
107 int i;
108
109 spin_lock(com_lock);
110
111 for (i = 0; com_init_sequence[i].reg != -1; i++) {
112 u16 port = com_base + com_init_sequence[i].reg;
113 u8 data = com_init_sequence[i].data;
114 _asm {
115 mov dx, port;
116 mov al, data;
117 out dx, al;
118 }
119 }
120
121 spin_unlock(com_lock);
122}
123
124/******************************************************************************
125 * Print a formatted message into a string buffer. This is very basic,
126 * supporting only strings and integers (16 and 32 bits (l), decimal (d)
127 * and hex (x)). Formatting length modifiers are only supported with a single
128 * digit -- 32-bit numbers don't need more than 9 characters -- and an
129 * optional '0' in front.
130 */
131int vsprintf(char _far *buf, const char *fmt, va_list va)
132{
133 char _far *orig = buf;
134 char _far *s;
135 int lmod;
136 int fptr;
137 int zero;
138 int flen;
139
140 for (; *fmt != '\0'; fmt++) {
141 switch (*fmt) {
142
143 case '%':
144 fmt++;
145 zero = flen = 0;
146 if (*fmt >= '0' && *fmt <= '9') {
147 /* formatting length modifiers */
148 zero = (*fmt == '0') ? 1 : 0;
149 fmt += zero;
150 if ((flen = *fmt - '0') >= 1 && flen <= 9) {
151 fmt++;
152 }
153 }
154
155 /* data type modifiers */
156 lmod = (*fmt == 'l') ? 1 : 0;
157 fptr = (*fmt == 'F') ? 1 : 0;
158 fmt += lmod + fptr;
159
160 switch (*fmt) {
161
162 case 's':
163 if (fptr) {
164 char _far *p = va_arg(va, char _far *);
165 s = (p == 0) ? "[null]" : p;
166 } else {
167 char *p = va_arg(va, char *);
168 s = (p == 0) ? "[null]" : p;
169 }
170 while ((*buf = *(s++)) != '\0')
171 buf++;
172 break;
173
174 case 'c':
175 *(buf++) = (char) va_arg(va, int);
176 break;
177
178 case 'd':
179 long_to_asc((lmod) ? va_arg(va, long)
180 : va_arg(va, int), buf, 10, zero, flen);
181 buf += strlen(buf);
182 break;
183
184 case 'x':
185 long_to_asc((lmod) ? va_arg(va, u32)
186 : va_arg(va, u16), buf, 16, zero, flen);
187 buf += strlen(buf);
188 break;
189
190 case 'p':
191 if (fptr || lmod) {
192 u16 off = va_arg(va, u16);
193 u16 seg = va_arg(va, u16);
194 long_to_asc(seg, buf, 16, 1, 4);
195 buf += strlen(buf);
196 *(buf++) = ':';
197 long_to_asc(off, buf, 16, 1, 4);
198 buf += strlen(buf);
199 } else {
200 long_to_asc(va_arg(va, u16), buf, 16, 1, 4);
201 buf += strlen(buf);
202 }
203 break;
204
205 default:
206 *(buf++) = *fmt;
207 break;
208 }
209 break;
210
211 case '\n':
212 *(buf++) = '\r';
213 *(buf++) = '\n';
214 break;
215
216 default:
217 *(buf++) = *fmt;
218 break;
219
220 }
221 }
222
223 *buf = '\0';
224 return((int) (buf - orig));
225}
226
227/*******************************************************************************
228 * Print a formatted message into a string buffer. Relies on vsprintf()
229 */
230int sprintf(char _far *buf, const char *fmt, ...)
231{
232 va_list va;
233
234 va_start(va, fmt);
235 return(vsprintf(buf, fmt, va));
236}
237
238/******************************************************************************
239 * Print messages to COM
240 *
241 * NOTES: This function uses a 1K buffer for the resulting message. Thus,
242 * messages should never exceed 1024 bytes.
243 */
244void vprintf(const char *fmt, va_list va)
245{
246 static char buf[1024];
247 char *s;
248
249 spin_lock(com_lock);
250
251 vsprintf(buf, fmt, va);
252
253 /* write debug message to COM1 */
254 for (s = buf; *s != '\0'; s++) {
255
256 /* inp() and outp() are redefined by the DDK in an incompatible
257 * way (only words). Instead of messing around with those
258 * definitions, it's safer and easier to put the whole thing
259 * into an _asm block.
260 *
261 * The C equivalent would look like this:
262 *
263 * while (!(inp(com_base + 5) & 0x20));
264 * outp(com_base, *s);
265 */
266
267 _asm {
268 /* wait until COM transmitter is idle */
269 mov dx, com_base;
270 add dx, 5;
271 transmitter_not_idle:
272 in al, dx;
273 and al, 0x20;
274 jz transmitter_not_idle;
275
276 /* output character to be sent */
277 mov dx, com_base;
278 mov bx, s;
279 mov al, [bx];
280 out dx, al;
281 };
282 }
283
284 spin_unlock(com_lock);
285}
286
287/******************************************************************************
288 * Print messages to COM port
289 */
290void printf(const char *fmt, ...)
291{
292 va_list va;
293
294 va_start(va, fmt);
295 vprintf(fmt, va);
296}
297
298/******************************************************************************
299 * Print a message to the system console. This works only during device driver
300 * initialization.
301 *
302 * NOTE: This function uses a 1K buffer for the resulting message. Thus,
303 * messages should never exceed 1024 bytes...
304 */
305void cprintf(const char *fmt, ...)
306{
307 static char buf[1024];
308 va_list va;
309 size_t len;
310
311 va_start(va, fmt);
312 vsprintf(buf, fmt, va);
313
314 if (debug) {
315 /* print the same message to COM1 as well */
316 printf("%s", buf);
317 }
318
319 /* remove trailing CR/LF (DevHelp_Save_Message() will add it again) */
320 if ((len = strlen(buf)) >= 2 && buf[len-1] == '\n' && buf[len-2] == '\r') {
321 buf[len-2] = '\0';
322 }
323
324 init_msgtbl.MsgStrings[0] = buf;
325 DevHelp_Save_Message((NPBYTE) &init_msgtbl);
326}
327
328/******************************************************************************
329 * Print hex buffer to COM port.
330 */
331void phex(const void _far *p, int len, const char *fmt, ...)
332{
333 va_list va;
334 const unsigned char _far *buf = p;
335 long pos = 0;
336 int i;
337
338 if (!debug) {
339 return;
340 }
341
342 /* print header */
343 va_start(va, fmt);
344 vprintf(fmt, va);
345
346 /* print hex block */
347 while (len > 0) {
348 printf("%Fp ", buf);
349
350 /* print hex block */
351 for (i = 0; i < 16; i++) {
352 if (i < len) {
353 printf("%c%02x", ((i == 8) ? '-' : ' '), buf[i]);
354 } else {
355 printf(" ");
356 }
357 }
358
359 /* print ASCII block */
360 printf(" ");
361 for (i = 0; i < ((len > 16) ? 16 : len); i++) {
362 printf("%c", (buf[i] >= 32 && buf[i] < 128) ? buf[i] : '.');
363 }
364 printf("\n");
365
366 pos += 16;
367 buf += 16;
368 len -= 16;
369 }
370}
371
372/******************************************************************************
373 * Return length of zero-terminated string
374 */
375size_t strlen(const char _far *s)
376{
377 int len = 0;
378
379 while (*(s++) != '\0') {
380 len++;
381 }
382 return(len);
383}
384
385/******************************************************************************
386 * Copy zero-terminated string
387 */
388char _far *strcpy(char _far *dst, const char _far *src)
389{
390 char _far *orig = dst;
391
392 while ((*(dst++) = *(src++)) != '\0');
393 return(orig);
394}
395
396/******************************************************************************
397 * Compare blocks of memory
398 */
399int memcmp(void _far *p1, void _far *p2, size_t len)
400{
401 register char _far *s1 = p1;
402 register char _far *s2 = p2;
403 int n = 0;
404
405 while (len > 0) {
406 if ((n = *(s1++) - *(s2++)) != 0) {
407 return(n);
408 }
409 len--;
410 }
411 return(0);
412}
413
414/******************************************************************************
415 * Convert a string to a long value using the specified base
416 */
417long strtol(const char _far *buf, const char _far * _far *ep, int base)
418{
419 register const char _far *s = buf;
420 long val = 0;
421 int negative = 0;
422
423 /* skip leading whitespace */
424 while (*s == ' ' || *s == '\t') {
425 s++;
426 }
427
428 /* positive or negative */
429 if (*s == '-') {
430 negative = 1;
431 s++;
432 } else if (*s == '+') {
433 s++;
434 }
435
436 /* convert string to long integer */
437 for (;; s++) {
438 int digit = (*s <= '9') ? (*s - '0') : (tolower(*s) - 'a' + 10);
439 if (digit < 0 || digit >= base) {
440 break;
441 }
442 val *= base;
443 val += digit;
444 }
445
446 if (ep != NULL) {
447 *ep = s;
448 }
449 if (negative) {
450 val = -val;
451 }
452 return(val);
453}
454
455/******************************************************************************
456 * Extremely simple and stupid implementation of malloc(). The heap is very
457 * small, only 8K at the moment, and the memory blocks are managed using a
458 * simple array of "number of heap units allocated", zero meaning this unit is
459 * available. Each heap unit is currently 128 bytes.
460 *
461 * Dynamic memory is primarily used for things like ATA identify, ATAPI
462 * sense buffers, etc. and should be freed as soon as possible, otherwise
463 * we'll quickly run out of memory.
464 *
465 * NOTE: This function is not reentrant, thus must be called with the driver-
466 * level spinlock held. The main reason for this design is that most
467 * functions that need dynamic memory are already holding the spinlock.
468 */
469void *malloc(size_t len)
470{
471 u16 units = (len + HEAP_UNIT - 1) / HEAP_UNIT;
472 u16 i;
473 u16 n;
474
475 /* find a sequence of free heap units big enough for the requested length */
476 for (i = 0; i < HEAP_UNIT_CNT; i++) {
477 if (heap_units[i] == 0) {
478 for (n = i; n < i + units && n < HEAP_UNIT_CNT; n++) {
479 if (heap_units[n] != 0) {
480 break;
481 }
482 }
483 if (n == i + units) {
484 /* found a chunk large enough; update 'heap_units[]' */
485 for (; i < n; i++) {
486 heap_units[i] = (u8) (n - i);
487 }
488 return(heap_buf + (n - units) * HEAP_UNIT);
489 }
490
491 /* keep searching... */
492 i = n;
493 } else {
494 /* skip occupied heap units */
495 i += heap_units[i] - 1;
496 }
497 }
498
499 /* out of memory */
500 dprintf("malloc(%d): out of memory\n", len);
501 return(NULL);
502}
503
504/******************************************************************************
505 * Free block of memory allocted by malloc().
506 *
507 * NOTE: This function is not reentrant, thus must be called with the driver-
508 * level spinlock held. The main reason for this design is that most
509 * functions that need dynamic memory are already holding the spinlock.
510 */
511void free(void *ptr)
512{
513 u8 *p = (u8 *) ptr;
514 u16 first_unit;
515 u16 units;
516 u16 i;
517
518 if (p < heap_buf || p >= heap_buf + sizeof(heap_buf) ||
519 (u16) (p - heap_buf) % HEAP_UNIT != 0) {
520 dprintf("free(0x%p): invalid pointer (heap_buf = 0x%p)\n",
521 (u16) p, (u16) heap_buf);
522 return;
523 }
524
525 /* clear unit allocation counters in heap_units[] */
526 first_unit = (u16) (p - heap_buf) / HEAP_UNIT;
527 units = heap_units[first_unit];
528 for (i = first_unit; i < first_unit + units; i++) {
529 heap_units[i] = 0;
530 }
531}
532
533/******************************************************************************
534 * Calibrate 'mdelay()' loop. This is done by setting up a 1 second timer
535 * with a callback that sets 'mdelay_done' to MD_CALIBRATION_END. Then it
536 * calls mdelay() with a large milliseond value an initial delay loop counter
537 * of 1,000,000. When the timer triggers, 'mdelay()' will stop and update
538 * the delay loop counter.
539 *
540 * This function needs to be called at device driver init time. Since it uses
541 * ADD timers, it must be called with interrupts enabled. All this is not very
542 * precise (we should wait for a clock tick before starting, ...) but we don't
543 * really need precise timers.
544 */
545void mdelay_cal(void)
546{
547 ULONG timer_handle;
548
549 dprintf("calibrating delay loop... ");
550
551 mdelay_loops_per_ms = 100000;
552 mdelay_cal_status = MD_CALIBRATION_START;
553
554 ADD_StartTimerMS(&timer_handle, 1000, (PFN) mdelay_timer_callback, 0, 0);
555 mdelay(999999999);
556 ADD_CancelTimer(timer_handle);
557
558 dprintf("done (loops per ms = %ld)\n", mdelay_loops_per_ms);
559}
560
561/******************************************************************************
562 * Wait specified number of milliseconds. This is implemented using a busy
563 * loop and is only good for delays in the millisecond range but never for more
564 * than a few milliseconds and only in situations where a proper timer won't do.
565 * As a rule of thumb, don't call this function and use ADD timers, instead.
566 *
567 * NOTES:
568 *
569 * - Timers are problematic on x86 platforms because there's no reliable
570 * hardware timer on all architectures and the CPU clock speed may change
571 * while executing delay loops (AMD Cool&Quiet and Intel SpeedStep), thus
572 * calibration routines won't really be sufficient. But this usually only
573 * extends the delay and we don't really need a high precision timer. The
574 * exception are things like notebooks that are clocked slower when on
575 * battery and which got booted while on battery. Should still be OK,
576 * though, because our requirements are not that strict.
577 *
578 * - The code in this function is inefficient by design to make sure it
579 * will work with future CPUs which might otherwise be too fast for
580 * our loop counters. Part of this design is using volatile variables to
581 * force memory operations.
582 *
583 * - Before using this function, call mdelay_calibrate() to determine the
584 * number of inner loops required per millisecond.
585 */
586void mdelay(u32 millies)
587{
588 volatile u32 i;
589 volatile u32 n;
590
591 for (i = 0; i < millies; i++) {
592 for (n = 0; n < mdelay_loops_per_ms; n++) {
593 if (mdelay_cal_end()) {
594 /* this is a calibration run that just ended */
595 goto complete_calibration;
596 }
597 }
598 }
599 return;
600
601complete_calibration:
602 /* complete calibration cycle */
603 if (i < 1000) {
604 /* Initial value for delay loop was too high; interpolate results for
605 * an assumed initial delay loop divided by 1000.
606 */
607 i = i * 1000 + mdelay_loops_per_ms % 1000;
608 mdelay_loops_per_ms /= 1000;
609 }
610 mdelay_loops_per_ms = (mdelay_loops_per_ms * i) / 1000;
611 mdelay_cal_status = MD_CALIBRATION_DONE;
612}
613
614/******************************************************************************
615 * Sleep specified number of milliseonds. This is implemented by yielding the
616 * CPU until the system timer value indicates we're done. This function can
617 * only be called at task time, or from a context hook.
618 *
619 * NOTE: The accuracy is limited by the OS/2 timer interrupt frequency which
620 * can lead to intervals up to 55ms (18.2 timer interrupts per second).
621 */
622void msleep(u32 millies)
623{
624 volatile PGINFOSEG gis;
625 ULONG start;
626 ULONG end;
627 PSEL p;
628
629 if (DevHelp_GetDOSVar(DHGETDOSV_SYSINFOSEG, 0, (PPVOID) &p)) {
630 /* no global info segment; use mdelay() */
631 mdelay(millies);
632 return;
633 }
634 gis = (PGINFOSEG) ((u32) *p << 16);
635 start = gis->msecs;
636 end = start + millies;
637
638 if (end < start) {
639 /* wrap-around; wait until 'msecs' has wrapped, too */
640 while (gis->msecs >= start) {
641 DevHelp_Yield();
642 }
643 }
644
645 while (gis->msecs <= end) {
646 DevHelp_Yield();
647 }
648}
649
650/******************************************************************************
651 * Halt processing by submitting an internal error. This is a last resort and
652 * should only be called when the system state is corrupt.
653 */
654void panic(char *msg)
655{
656 DevHelp_InternalError(msg, strlen(msg));
657}
658
659/******************************************************************************
660 * Disable interrupts. The reason for using a separate function for this is
661 * that the presence of _asm statements will disable compiler optimizations.
662 * In order to support nested calls, this function will return 0 if the
663 * interrupts were already disabled or != 0, if not.
664 *
665 * NOTE: SMP systems must use spinlocks, thus this function will only be
666 * compiled on non-SMP builds.
667 */
668#ifndef OS2AHCI_SMP
669int disable(void)
670{
671 int rc = 0;
672
673 _asm {
674 pushf
675 pop ax
676 and ax, 0x0200; /* "interrupts enabled" bit */
677 mov rc, ax;
678 cli
679 }
680
681 return(rc);
682}
683#endif
684
685/******************************************************************************
686 * Enable interrupts. The reason for using a separate function for this is
687 * that the presence of _asm statements will disable compiler optimizations.
688 *
689 * NOTE: SMP systems must use spinlocks, thus this function will only be
690 * compiled on non-SMP builds.
691 */
692#ifndef OS2AHCI_SMP
693void enable(void)
694{
695 _asm sti;
696}
697#endif
698
699/******************************************************************************
700 * Convert 'long' to ASCII with the specified base
701 */
702static void long_to_asc(long val, char _far *buf, int base, int zero, int flen)
703{
704 register unsigned long abs_val = (unsigned long) val;
705 char tmp[80];
706 char _far *ptmp = tmp;
707 char _far *s;
708
709 if (base > 16) {
710 sprintf(buf, "[EVAL]");
711 return;
712 }
713
714 tmp[sizeof(tmp) - 1] = '\0';
715 for (s = ptmp + sizeof(tmp) - 2; s > ptmp; s--) {
716 *s = hex_digits[abs_val % base];
717 flen--;
718 if ((abs_val /= base) == 0) {
719 break;
720 }
721 }
722
723 /* prepend minus sign if val was negative and base is decimal or less */
724 if (val < 0 && base <= 0) {
725 *(--s) = '-';
726 flen--;
727 }
728
729 /* left-pad the resulting number with zeros or spaces up to 'flen' */
730 while (flen > 0) {
731 *(--s) = (zero) ? '0' : ' ';
732 flen--;
733 }
734
735 strcpy(buf, s);
736}
737
738/******************************************************************************
739 * Timer callback handler for 'mdelay_calibrate()'
740 */
741static void _cdecl _far mdelay_timer_callback(ULONG timer_handle,
742 ULONG parm1,
743 ULONG parm2)
744{
745 mdelay_cal_status = MD_CALIBRATION_END;
746}
747
748/******************************************************************************
749 * Determine whether an mdelay calibration run has just ended. This is in a
750 * function to prevent overzealous optimizers from removing the whole delay
751 * loop in mdelay().
752 */
753static int mdelay_cal_end(void)
754{
755 return(mdelay_cal_status == MD_CALIBRATION_END);
756}
Note: See TracBrowser for help on using the repository browser.