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

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

changed serial port setup (still not working ok), prolonged timeout during port reset

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