diff options
Diffstat (limited to 'clockB/directio.c')
-rw-r--r-- | clockB/directio.c | 727 |
1 files changed, 0 insertions, 727 deletions
diff --git a/clockB/directio.c b/clockB/directio.c deleted file mode 100644 index d3a77eb19..000000000 --- a/clockB/directio.c +++ /dev/null @@ -1,727 +0,0 @@ -/************************************************************************** - - This is a component of the hwclock program. - - This file contains the code for accessing the hardware clock via - direct I/O (kernel-style input and output operations) as opposed - to via a device driver. - - - MAINTENANCE NOTES - - Here is some information on how the Hardware Clock works, from - unknown source and authority. In theory, the specification for this - stuff is the specification of Motorola's MC146818A clock chip, used - in the early ISA machines. Subsequent machines should have copied - its function exactly. In reality, though, the copies are inexact - and the MC146818A itself may fail to implement its specifications, - and we have just have to work with whatever is there (actually, - anything that Windows works with, because that's what determines - whether broken hardware has to be fixed!). - - i386 CMOS starts out with 14 bytes clock data - alpha has something similar, but with details - depending on the machine type. - - byte 0: seconds (0-59) - byte 2: minutes (0-59) - byte 4: hours (0-23 in 24hr mode, - 1-12 in 12hr mode, with high bit unset/set if am/pm) - byte 6: weekday (1-7, Sunday=1) - byte 7: day of the month (1-31) - byte 8: month (1-12) - byte 9: year (0-99) - - - Numbers are stored in BCD/binary if bit 2 of byte 11 is unset/set - The clock is in 12hr/24hr mode if bit 1 of byte 11 is unset/set - The clock is undefined (being updated) if bit 7 of byte 10 is set. - The clock is frozen (to be updated) by setting bit 7 of byte 11 - Bit 7 of byte 14 indicates whether the CMOS clock is reliable: - it is 1 if RTC power has been good since this bit was last read; - it is 0 when the battery is dead and system power has been off. - - The century situation is messy: - - Usually byte 50 (0x32) gives the century (in BCD, so 0x19 or 0x20 in - pure binary), but IBM PS/2 has (part of) a checksum there and uses - byte 55 (0x37). Sometimes byte 127 (0x7f) or Bank 1, byte 0x48 - gives the century. The original RTC will not access any century - byte; some modern versions will. If a modern RTC or BIOS increments - the century byte it may go from 0x19 to 0x20, but in some buggy - cases 0x1a is produced. - - CMOS byte 10 (clock status register A) has 3 bitfields: - bit 7: 1 if data invalid, update in progress (read-only bit) - (this is raised 224 us before the actual update starts) - 6-4 select base frequency - 010: 32768 Hz time base (default) - 111: reset - all other combinations are manufacturer-dependent - (e.g.: DS1287: 010 = start oscillator, anything else = stop) - 3-0 rate selection bits for interrupt - 0000 none - 0001, 0010 give same frequency as 1000, 1001 - 0011 122 microseconds (minimum, 8192 Hz) - .... each increase by 1 halves the frequency, doubles the period - 1111 500 milliseconds (maximum, 2 Hz) - 0110 976.562 microseconds (default 1024 Hz) - - Avoid setting the RTC clock within 2 seconds of the day rollover - that starts a new month or enters daylight saving time. - -****************************************************************************/ - -#include <stdio.h> -#include <fcntl.h> -#include <unistd.h> -#include <stdlib.h> -#include <errno.h> -#include <string.h> - -#if defined(__i386__) || defined(__alpha__) -#include <asm/io.h> /* for inb, outb */ -#else -void outb(int a, int b){} -int inb(int c){ return 0; } -#endif - -#include "hwclock.h" - -#define BCD_TO_BIN(val) (((val)&0x0f) + ((val)>>4)*10) -#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10) - - -/*---------------------------------------------------------------------------- - ATOMIC_TOP and ATOMIC_BOTTOM are wierd macros that help us to do - atomic operations when we do ugly low level I/O. - - You put ATOMIC_TOP above some code and ATOMIC_BOTTOM below it and - it makes sure all the enclosed code executes without interruption - by some other process (and, in some cases, even the kernel). - - These work fundamentally differently depending on the machine - architecture. In the case of a x86, it simply turns interrupts off - at the top and turns them back on at the bottom. - - For Alpha, we can't mess with interrupts (we shouldn't for x86 - either, but at least it tends to work!), so instead we start a loop - at the top and close it at the bottom. This loop repeats the - enclosed code until the upper 32 bits of the cycle counter are the - same before and after. That means there was no context change - while the enclosed code was executing. - - For other architectures, we do nothing, and the atomicity is only - feigned. - ------------------------------------------------------------------------------*/ - -#if defined(__i386__) -#define ATOMIC_TOP \ - { \ - const bool interrupts_were_enabled = interrupts_enabled; \ - __asm__ volatile ("cli"); \ - interrupts_enabled = FALSE; - -#define ATOMIC_BOTTOM \ - if (interrupts_were_enabled) { \ - __asm__ volatile ("sti"); \ - interrupts_enabled = TRUE; \ - } \ - } -#elif defined(__alpha__) -#define ATOMIC_TOP \ - { \ - unsigned long ts1, ts2, n; \ - n = 0; \ - do { \ - asm volatile ("rpcc %0" : "r="(ts1)); - -#define ATOMIC_BOTTOM \ - asm volatile ("rpcc %0" : "r="(ts2)); \ - n++; \ - } while ((ts1 ^ ts2) >> 32 != 0); \ - } -#else -#define ATOMIC_BOTTOM -#define ATOMIC_TOP -#endif - - -#if defined(__i386__) || defined(__alpha__) -/* The following are just constants. Oddly, this program will not - compile if the inb() and outb() functions use something even - slightly different from these variables. This is probably at least - partially related to the fact that __builtin_constant_p() doesn't - work (is never true) in an inline function. See comment to this - effect in asm/io.h. -*/ -static unsigned short clock_ctl_addr = 0x70; -static unsigned short clock_data_addr = 0x71; -#endif - - -static bool interrupts_enabled; - /* Interrupts are enabled as normal. We, unfortunately, turn interrupts - on the machine off in some places where we do the direct ISA accesses - to the Hardware Clock. It is in extremely poor form for a user space - program to do this, but that's the price we have to pay to run on an - ISA machine without the rtc driver in the kernel. - - Code which turns interrupts off uses this value to determine if they - need to be turned back on. - */ - - -void -assume_interrupts_enabled(void) { - interrupts_enabled = TRUE; -} - - - -static int -i386_iopl(const int level) { -/*---------------------------------------------------------------------------- - When compiled for an Intel target, this is just the iopl() kernel call. - When compiled for any other target, this is a dummy function. - - We do it this way in order to keep the conditional compilation stuff - out of the way so it doesn't mess up readability of the code. ------------------------------------------------------------------------------*/ -#ifdef __i386__ - extern int iopl(int level); - return iopl(level); -#else - return -1; -#endif -} - - - -bool -uf_bit_needed(const bool user_wants_uf) { -/*---------------------------------------------------------------------------- - Return true iff the UIP bit doesn't work on this hardware clock, so - we will need to use the UF bit to synchronize with the clock (if in - fact we synchronize using direct I/O to the clock). - - To wit, we need to use the UF bit on a DEC Alpha PC164/LX164/SX164. - Or, of course, if the user told us to. ------------------------------------------------------------------------------*/ - bool retval; - - if (user_wants_uf) retval = TRUE; - else { - if (alpha_machine && ( - is_in_cpuinfo("system variation", "PC164") || - is_in_cpuinfo("system variation", "LX164") || - is_in_cpuinfo("system variation", "SX164"))) - retval = TRUE; - else retval = FALSE; - } - if (debug && retval) - printf("We will be using the UF bit instead of the usual " - "UIP bit to synchronize with the clock, as required on " - "certain models of DEC Alpha.\n"); - - return retval; -} - - - -int -zero_year(const bool arc_opt, const bool srm_opt) { -/*---------------------------------------------------------------------------- - Return the year of the century (e.g. 0) to which a zero value in - the year register of the hardware clock applies (or at least what - we are to assume -- nobody can say for sure!) - - 'arc_opt' and 'srm_opt' are the true iff the user specified the - corresponding invocation option to instruct us that the machine is an - Alpha with ARC or SRM console time. - - A note about hardware clocks: - - ISA machines are simple: the year register is a year-of-century - register, so the zero year is zero. On Alphas, we may see 1980 or - 1952 (Digital Unix?) or 1958 (ALPHA_PRE_V1_2_SRM_CONSOLE) ------------------------------------------------------------------------------*/ - int retval; /* our return value */ - - if (arc_opt || srm_opt) { - /* User is telling us what epoch his machine uses. Believe it. */ - if (arc_opt) retval = 0; - else retval = 0; - } else { - unsigned long kernel_epoch; - char *reason; /* malloc'ed */ - - get_epoch(&kernel_epoch, &reason); - if (reason == NULL) retval = kernel_epoch; - else { - /* OK, the user doesn't know and the kernel doesn't know; - figure it out from the machine model - */ - free(reason); /* Don't care about kernel's excuses */ - /* See whether we are dealing with SRM or MILO, as they have - different "epoch" ideas. */ - if (is_in_cpuinfo("system serial number", "MILO")) { - if (debug) printf("booted from MILO\n"); - /* See whether we are dealing with a RUFFIAN aka UX, as they - have REALLY different TOY (TimeOfYear) format: BCD, and not - an ARC-style epoch. BCD is detected dynamically, but we - must NOT adjust like ARC. - */ - if (is_in_cpuinfo("system type", "Ruffian")) { - if (debug) printf("Ruffian BCD clock\n"); - retval = 0; - } else { - if (debug) printf("Not Ruffian BCD clock\n"); - retval = 80; - } - } else { - if (debug) printf("Not booted from MILO\n"); - retval = 0; - } - } - } - return retval; -} - - - -static inline unsigned char -hclock_read(const unsigned char reg, const int dev_port) { -/*--------------------------------------------------------------------------- - Relative byte 'reg' of the Hardware Clock value. - - Get this with direct CPU I/O instructions. If 'dev_port' is not -1, - use the /dev/port device driver (via the 'dev_port' file descriptor) - to do this I/O. Otherwise, use the kernel's inb()/outb() facility. - - On a system without the inb()/outb() facility, if 'dev_port' is -1, - just return 0. - - Results undefined if 'reg' is out of range. ----------------------------------------------------------------------------*/ - unsigned char ret; - - ATOMIC_TOP - if (dev_port >= 0) { - const unsigned char v = reg | 0x80; - lseek(dev_port, 0x170, 0); - write(dev_port, &v, 1); - lseek(dev_port, 0x171, 0); - read(dev_port, &ret, 1); - } else { -#if defined(__i386__) || defined(__alpha__) - /* & 0x7f ensures that we are not disabling NMI while we read. - Setting on Bit 7 here would disable NMI - - Various docs suggest that one should disable NMI while - reading/writing CMOS data, and enable it again afterwards. - This would yield the sequence - outb (reg | 0x80, 0x70); - val = inb(0x71); - outb (0x0d, 0x70); // 0x0d: random read-only location - Other docs state that "any write to 0x70 should be followed - by an action to 0x71 or the RTC wil be left in an unknown state". - Most docs say that it doesn't matter at all what one does. - */ - outb(reg & 0x7f, clock_ctl_addr); - ret = inb(clock_data_addr); -#else - ret = 0; -#endif - } - ATOMIC_BOTTOM - return ret; -} - - - -static inline void -hclock_write(unsigned char reg, unsigned char val, const int dev_port) { -/*---------------------------------------------------------------------------- - Set relative byte 'reg' of the Hardware Clock value to 'val'. - Do this with the kernel's outb() function if 'dev_port' is -1, but - if not, use the /dev/port device (via the 'dev_port' file descriptor), - which is almost the same thing. - - On a non-ISA, non-Alpha machine, if 'dev_port' is -1, do nothing. -----------------------------------------------------------------------------*/ - if (dev_port >= 0) { - unsigned char v; - v = reg | 0x80; - lseek(dev_port, 0x170, 0); - write(dev_port, &v, 1); - v = (val & 0xff); - lseek(dev_port, 0x171, 0); - write(dev_port, &v, 1); - } else { -#if defined(__i386__) || defined(__alpha__) - /* & 0x7f ensures that we are not disabling NMI while we read. - Setting on Bit 7 here would disable NMI - */ - outb(reg & 0x7f, clock_ctl_addr); - outb(val, clock_data_addr); -#endif - } -} - - - -static inline int -hardware_clock_busy(const int dev_port, const bool use_uf_bit) { -/*---------------------------------------------------------------------------- - Return whether the hardware clock is in the middle of an update - (which happens once each second). - - Use the clock's UIP bit (bit 7 of Control Register A) to tell - unless 'use_uf_bit' is true, in which case use the UF bit (bit 4 of - Control Register C). ------------------------------------------------------------------------------*/ - return - use_uf_bit ? (hclock_read(12, dev_port) & 0x10) : - (hclock_read(10, dev_port) & 0x80); -} - - - -void -synchronize_to_clock_tick_ISA(int *retcode_p, const int dev_port, - const bool use_uf_bit) { -/*---------------------------------------------------------------------------- - Same as synchronize_to_clock_tick(), but just for ISA. ------------------------------------------------------------------------------*/ - int i; /* local loop index */ - - /* Wait for rise. Should be within a second, but in case something - weird happens, we have a limit on this loop to reduce the impact - of this failure. - */ - for (i = 0; - !hardware_clock_busy(dev_port, use_uf_bit) && (i < 10000000); - i++); - if (i >= 10000000) *retcode_p = 1; - else { - /* Wait for fall. Should be within 2.228 ms. */ - for (i = 0; - hardware_clock_busy(dev_port, use_uf_bit) && (i < 1000000); - i++); - if (i >= 10000000) *retcode_p = 1; - else *retcode_p = 0; - } -} - - - -void -read_hardware_clock_isa(struct tm *tm, const int dev_port, - int hc_zero_year) { -/*---------------------------------------------------------------------------- - Read the hardware clock and return the current time via <tm> argument. - Assume we have an ISA machine and read the clock directly with CPU I/O - instructions. If 'dev_port' isn't -1, use the /dev/port facility to - do this I/O. Otherwise, use the kernel's inb()/outb() service. - - This function is not totally reliable. It takes a finite and - unpredictable amount of time to execute the code below. During that - time, the clock may change and we may even read an invalid value in - the middle of an update. We do a few checks to minimize this - possibility, but only the kernel can actually read the clock - properly, since it can execute code in a short and predictable - amount of time (by turning off interrupts). - - In practice, the chance of this function returning the wrong time is - extremely remote. - ------------------------------------------------------------------------------*/ - bool got_time; - /* We've successfully read a time from the Hardware Clock */ - int attempts; - /* Number of times we've tried to read the clock. This only - matters because we will give up (and proceed with garbage in - variables) rather than hang if something is broken and we are - never able to read the clock - */ - int hclock_sec = 0, hclock_min = 0, hclock_hour = 0, hclock_wday = 0, - hclock_mon = 0, hclock_mday = 0, hclock_year = 0; - /* The values we got from the Hardware Clock's registers, assuming - they are in pure binary. - */ - - int status = 0; /* Hardware Clock status register, as if pure binary */ - int adjusted_year; - int ampmhour; - int pmbit; - - got_time = FALSE; - attempts = 0; /* initial value */ - while (!got_time && attempts++ < 1000000) { - /* Bit 7 of Byte 10 of the Hardware Clock value is the Update In Progress - (UIP) bit, which is on while and 244 uS before the Hardware Clock - updates itself. It updates the counters individually, so reading - them during an update would produce garbage. The update takes 2mS, - so we could be spinning here that long waiting for this bit to turn - off. - - Furthermore, it is pathologically possible for us to be in this - code so long that even if the UIP bit is not on at first, the - clock has changed while we were running. We check for that too, - and if it happens, we start over. - */ - - if ((hclock_read(10, dev_port) & 0x80) == 0) { - /* No clock update in progress, go ahead and read */ - - status = hclock_read(11, dev_port); - - hclock_sec = hclock_read(0, dev_port); - hclock_min = hclock_read(2, dev_port); - hclock_hour = hclock_read(4, dev_port); - hclock_wday = hclock_read(6, dev_port); - hclock_mday = hclock_read(7, dev_port); - hclock_mon = hclock_read(8, dev_port); - hclock_year = hclock_read(9, dev_port); - /* Unless the clock changed while we were reading, consider this - a good clock read . - */ - if (hclock_sec == hclock_read(0, dev_port)) got_time = TRUE; - /* Yes, in theory we could have been running for 60 seconds and - the above test wouldn't work! - */ - } - } - - if (!(status & 0x04)) { - /* The hardware clock is in BCD mode. This is normal. */ - tm->tm_sec = BCD_TO_BIN(hclock_sec); - tm->tm_min = BCD_TO_BIN(hclock_min); - ampmhour = BCD_TO_BIN(hclock_hour & 0x7f); - pmbit = hclock_hour & 0x80; - tm->tm_wday = BCD_TO_BIN(hclock_wday) - 1; /* Used to be 3. Why?? */ - tm->tm_mday = BCD_TO_BIN(hclock_mday); - tm->tm_mon = BCD_TO_BIN(hclock_mon) - 1; - adjusted_year = BCD_TO_BIN(hclock_year); - } else { - /* The hardware clock registers are in pure binary format. */ - tm->tm_sec = hclock_sec; - tm->tm_min = hclock_min; - ampmhour = hclock_hour & 0x7f; - pmbit = hclock_hour & 0x80; - tm->tm_wday = hclock_wday - 1; /* Used to be 3. Why?? */ - tm->tm_mday = hclock_mday; - tm->tm_mon = hclock_mon - 1; - adjusted_year = hclock_year; - } - - if (!(status & 0x02)) { - /* Clock is in 12 hour (am/pm) mode. This is unusual. */ - if (pmbit == 0x80) { - if (ampmhour == 12) tm->tm_hour = 12; - else tm->tm_hour = 12 + ampmhour; - } else { - if (ampmhour ==12) tm->tm_hour = 0; - else tm->tm_hour = ampmhour; - } - } else { - /* Clock is in 24 hour mode. This is normal. */ - tm->tm_hour = ampmhour; - } - /* We don't use the century byte (Byte 50) of the Hardware Clock. - Here's why: It didn't exist in the original ISA specification, - so old machines don't have it, and even some new ones don't. - Some machines, including the IBM Valuepoint 6387-X93, use that - byte for something else. Some machines have the century in - Byte 55. - - Furthermore, the Linux standard time data structure doesn't - allow for times beyond about 2037 and no Linux systems were - running before 1937. Therefore, all the century byte could tell - us is that the clock is wrong or this whole program is obsolete! - - So we just say if the year of century is less than 37, it's the - 2000's, otherwise it's the 1900's. - - Alpha machines (some, anyway) don't have this ambiguity - because they do not have a year-of-century register. We - pretend they do anyway, for simplicity and to avoid - recognizing times that can't be represented in Linux standard - time. So even though we already have enough information to - know that the clock says 2050, we will render it as 1950. - */ - { - const int year_of_century = (adjusted_year + hc_zero_year) % 100; - if (year_of_century >= 37) tm->tm_year = year_of_century; - else tm->tm_year = year_of_century + 100; - } - tm->tm_isdst = -1; /* don't know whether it's daylight */ -} - - - -void -set_hardware_clock_isa(const struct tm new_tm, - const int hc_zero_year, - const int dev_port, - const bool testing) { -/*---------------------------------------------------------------------------- - Set the Hardware Clock to the time (in broken down format) - new_tm. Use direct I/O instructions to what we assume is - an ISA Hardware Clock. - - Iff 'dev_port' is -1, use the kernel inb()/outb() service, otherwise - use the /dev/port device (via file descriptor 'dev_port') - to do those I/O instructions. -----------------------------------------------------------------------------*/ - unsigned char save_control, save_freq_select; - - if (testing) - printf("Not setting Hardware Clock because running in test mode.\n"); - else { - int ampmhour; - /* The hour number that goes into the hardware clock, taking into - consideration whether the clock is in 12 or 24 hour mode - */ - int pmbit; - /* Value to OR into the hour register as the am/pm bit */ - const int adjusted_year = - (new_tm.tm_year - hc_zero_year)%100; - /* The number that goes in the hardware clock's year register */ - - int hclock_sec, hclock_min, hclock_hour, hclock_wday, hclock_mon, - hclock_mday, hclock_year; - /* The values we will put, in pure binary, in the Hardware Clock's - registers. - */ - - ATOMIC_TOP - - save_control = hclock_read(11, dev_port); - /* tell the clock it's being set */ - hclock_write(11, (save_control | 0x80), dev_port); - save_freq_select = hclock_read(10, dev_port); - /* stop and reset prescaler */ - hclock_write (10, (save_freq_select | 0x70), dev_port); - - - if (!(save_control & 0x02)) { - /* Clock is in 12 hour (am/pm) mode. This is unusual. */ - if (new_tm.tm_hour == 0) { - ampmhour = 12; - pmbit = 0x00; - } else if (new_tm.tm_hour < 12) { - ampmhour = new_tm.tm_hour; - pmbit = 0x00; - } else if (new_tm.tm_hour == 12) { - ampmhour = 12; - pmbit = 0x80; - } else { - ampmhour = new_tm.tm_hour - 12; - pmbit = 0x80; - } - } else { - /* Clock is in 24 hour mode. This is normal. */ - ampmhour = new_tm.tm_hour; - pmbit = 0x00; - } - - - if (!(save_control & 0x04)) { - /* Clock's registers are in BCD. This is normal. */ - hclock_sec = BIN_TO_BCD(new_tm.tm_sec); - hclock_min = BIN_TO_BCD(new_tm.tm_min); - hclock_hour = pmbit | BIN_TO_BCD(ampmhour); - hclock_wday = BIN_TO_BCD(new_tm.tm_wday + 1); /* Used to be 3. Why??*/ - hclock_mday = BIN_TO_BCD(new_tm.tm_mday); - hclock_mon = BIN_TO_BCD(new_tm.tm_mon + 1); - hclock_year = BIN_TO_BCD(adjusted_year); - } else { - /* Clock's registers are in pure binary. This is unusual. */ - hclock_sec = new_tm.tm_sec; - hclock_min = new_tm.tm_min; - hclock_hour = pmbit | ampmhour; - hclock_wday = new_tm.tm_wday + 1; /* Used to be 3. Why?? */ - hclock_mday = new_tm.tm_mday; - hclock_mon = new_tm.tm_mon + 1; - hclock_year = adjusted_year; - } - - hclock_write(0, hclock_sec, dev_port); - hclock_write(2, hclock_min, dev_port); - hclock_write(4, hclock_hour, dev_port); - hclock_write(6, hclock_wday, dev_port); - hclock_write(7, hclock_mday, dev_port); - hclock_write(8, hclock_mon, dev_port); - hclock_write(9, hclock_year, dev_port); - - /* We don't set the century byte (usually Byte 50) because it isn't - always there. (see further comments in read_hardware_clock_isa). - In previous releases, we did. - */ - - /* The kernel sources, linux/arch/i386/kernel/time.c, have the - following comment: - - The following flags have to be released exactly in this order, - otherwise the DS12887 (popular MC146818A clone with integrated - battery and quartz) will not reset the oscillator and will not - update precisely 500 ms later. You won't find this mentioned - in the Dallas Semiconductor data sheets, but who believes data - sheets anyway ... -- Markus Kuhn - - Hence, they will also be done in this order here. - faith@cs.unc.edu, Thu Nov 9 08:26:37 1995 - */ - - hclock_write (11, save_control, dev_port); - hclock_write (10, save_freq_select, dev_port); - - ATOMIC_BOTTOM - } -} - - - -void -get_inb_outb_privilege(const enum clock_access_method clock_access, - bool * const no_auth_p) { - - if (clock_access == ISA) { - const int rc = i386_iopl(3); - if (rc != 0) { - fprintf(stderr, MYNAME " is unable to get I/O port access. " - "I.e. iopl(3) returned nonzero return code %d.\n" - "This is often because the program isn't running " - "with superuser privilege, which it needs.\n", - rc); - *no_auth_p = TRUE; - } else *no_auth_p = FALSE; - } else *no_auth_p = FALSE; -} - - - -void -get_dev_port_access(const enum clock_access_method clock_access, - int * dev_port_p) { - - if (clock_access == DEV_PORT) { - /* Get the /dev/port file open */ - *dev_port_p = open("/dev/port", O_RDWR); - if (*dev_port_p < 0) { - fprintf(stderr, MYNAME "is unable to open the /dev/port file. " - "I.e. open() of the file failed with errno = %s (%d).\n" - "Run with the --debug option and check documentation " - "to find out why we are trying " - "to use /dev/port instead of some other means to access " - "the Hardware Clock.", - strerror(errno), errno); - } - } else *dev_port_p = 0; -} - - - |