/* * Copyright (c) 2001 Tommy Bohlin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* unix.c */ #include #include #include #include #include #include #include #include #include #include #include #include #include /********************************************************************** * Constants **********************************************************************/ #ifdef IRDA_KERNEL_DRIVER static const char id_kernel_framedevice[]="kernel frame device"; static const char id_kfdev_inbuffer[]="kernel frame device in buffer"; static const char id_kfdev_outbuffer[]="kernel frame device out buffer"; static const char* defaultKernelPort = "/dev/irframe0"; #endif /* IRDA_KERNEL_DRIVER */ /********************************************************************** * Data structures **********************************************************************/ typedef struct Memory { struct Memory* next; const char* id; void* ptr; int size; } Memory; typedef struct Timer { struct Timer* next; struct timeval expire; void (*func)(void*); void* handle; } Timer; typedef struct Source { struct Source* next; int fd; void (*func)(void* handle); void* handle; } Source; typedef struct UNIXSerialPort { SerialPort serial; struct UNIXSerialPort* next; int speedMask; int fd; u_char inBuf[1024]; int inHead; int inFree; int stopped; u_char outBuf[1024]; int outFree; } UNIXSerialPort; #ifdef IRDA_KERNEL_DRIVER typedef struct KernelFrameDevice { FrameDevice frame; char *devname; int fdesc; u_char* inBuf; u_char* outBuf; int maxSize; int maxOutSize; int maxspeed; } KernelFrameDevice; #endif /* IRDA_KERNEL_DRIVER */ /********************************************************************** * State **********************************************************************/ int evtDebug; static bool sysLog=FALSE; static FILE* logFile=NULL; static Source* sources; static UNIXSerialPort* ports; static Timer* timers; static Memory* allocs; /********************************************************************** * Support functions **********************************************************************/ static void addTime(struct timeval* t, int ms) { t->tv_sec+=ms/1000; t->tv_usec+=1000*(ms%1000); if(t->tv_usec<0) { t->tv_sec-=1; t->tv_usec+=1000000; } else if(t->tv_usec>=1000000) { t->tv_sec+=1; t->tv_usec-=1000000; } } static int cmpTime(const struct timeval* t1, const struct timeval* t2) { long t=t1->tv_sec-t2->tv_sec; return t ? t : t1->tv_usec-t2->tv_usec; } static Timer* unlinkTimer(void (*func)(void* handle), void* handle) { Timer** th=&timers; Timer* t; while((t=*th)) { if(t->func==func && t->handle==handle) { *th=t->next; return t; } th=&t->next; } return 0; } static void linkTimer(Timer* e) { Timer** th=&timers; Timer* t; while((t=*th)) { if(cmpTime(&e->expire,&t->expire)<0) break; th=&t->next; } e->next=*th; *th=e; } void evtRemoveSource(int fd) { Source** sh=&sources; Source* s; while((s=*sh)) { if(s->fd==fd) { *sh=s->next; free(s); break; } sh=&s->next; } } void evtAddSource(int fd, void (*func)(void* handle), void* handle) { Source* s; evtRemoveSource(fd); s=malloc(sizeof(Source)); s->fd=fd; s->func=func; s->handle=handle; s->next=sources; sources=s; } /********************************************************************** * Serial I/O **********************************************************************/ static void serialSetSpeed(SerialPort* sp, int speed) { UNIXSerialPort* usp=(UNIXSerialPort*)sp; struct termios t; int speedcode; if(tcgetattr(usp->fd,&t)) { birda_log("tcgetattr failed\n"); return; } switch(speed) { case 115200: speedcode=B115200; break; case 57600: speedcode=B57600; break; case 38400: speedcode=B38400; break; case 19200: speedcode=B19200; break; case 9600: speedcode=B9600; break; case 2400: speedcode=B2400; break; default: birda_log("unsupported speed\n"); return; } cfsetispeed(&t,speedcode); cfsetospeed(&t,speedcode); if(tcsetattr(usp->fd,TCSADRAIN,&t)) { birda_log("tcsetattr failed\n"); } } static void serialSetLine(SerialPort* sp, int line) { UNIXSerialPort* usp=(UNIXSerialPort*)sp; int flags; if(ioctl(usp->fd,TIOCMGET,&flags)==-1) { birda_log("ioctl failed %d\n", __LINE__); } flags&=~(TIOCM_DTR|TIOCM_RTS); if(line&LINE_DTR) flags|=TIOCM_DTR; if(line&LINE_RTS) flags|=TIOCM_RTS; if(ioctl(usp->fd,TIOCMSET,&flags)==-1) { birda_log("ioctl failed %d\n", __LINE__); } } static int serialGetSpeedMask(SerialPort* sp) { UNIXSerialPort* usp=(UNIXSerialPort*)sp; return usp->speedMask; } static void serialInput(void* sp) { UNIXSerialPort* usp=(UNIXSerialPort*)sp; int empty; int k=usp->inHead<=usp->inFree ? sizeof usp->inBuf-usp->inFree : usp->inHead-usp->inFree-1; int n=read(usp->fd,usp->inBuf+usp->inFree,k); if(usp->serial.debug&SP_DEBUG_INPUT) birda_log("serial port read %d bytes\n",n); if(n<=0) return; empty=usp->inHead==usp->inFree; usp->inFree=(usp->inFree+n)%sizeof usp->inBuf; if(((usp->inFree+1)%sizeof usp->inBuf)==usp->inHead) { usp->stopped=1; evtRemoveSource(usp->fd); } if(empty && usp->serial.status) usp->serial.status(&usp->serial, SERIAL_INPUT_AVAILABLE); } static int serialGetChar(SerialPort* sp) { UNIXSerialPort* usp=(UNIXSerialPort*)sp; int c; int nchars=usp->inFree-usp->inHead; if(nchars<0) nchars+=sizeof usp->inBuf; if(nchars==0) { usp->inHead=usp->inFree=0; return SERIAL_INBUF_EMPTY; } if(usp->stopped && ncharsinBuf/4) { usp->stopped=0; evtAddSource(usp->fd,serialInput,usp); } c=usp->inBuf[usp->inHead]; usp->inHead=(usp->inHead+1)%sizeof usp->inBuf; return c; } static void flush(UNIXSerialPort* usp) { int n=usp->outFree; int k=0; while(kfd,usp->outBuf+k,n-k); if(i>0) k+=i; else if(i==0 || (errno!=EAGAIN && errno!=EINTR)) break; } usp->outFree=0; } static void serialOutput(void* sp) { UNIXSerialPort* usp=(UNIXSerialPort*)sp; if(evtDebug&EVT_DEBUG_TIMERS) birda_log("serialOutput\n"); if(usp->outFree>0) flush(usp); } static void serialPutChar(SerialPort* sp, int c) { UNIXSerialPort* usp=(UNIXSerialPort*)sp; usp->outBuf[usp->outFree++]=c; if(usp->outFree==sizeof usp->outBuf) flush(usp); else evtSetTimer(0,serialOutput,usp); } static void serialClose(SerialPort* sp) { UNIXSerialPort* usp=(UNIXSerialPort*)sp; UNIXSerialPort** uh=&ports; UNIXSerialPort* u; while((u=*uh)) { if(u==usp) { *uh=u->next; break; } else { uh=&u->next; } } if(!u) { birda_log("ERROR: closing unallocated UNIX serial port\n"); return; } evtCancelTimer(serialOutput,usp); evtRemoveSource(usp->fd); free(usp); } /********************************************************************** * External interface **********************************************************************/ bool setFileLog(const char* file) { FILE* f=fopen(file,"a"); if(!f) return FALSE; logFile=f; return TRUE; } void setSysLog(void) { sysLog=TRUE; } void birda_log(const char* fmt, ...) { va_list ap; va_start(ap,fmt); if(sysLog) { vsyslog(LOG_INFO,fmt,ap); } else { if(!logFile) logFile=stderr; vfprintf(logFile,fmt,ap); fflush(logFile); } va_end(ap); } void* allocMem(const char* id, int size) { void* ptr=malloc(size); Memory* m=malloc(sizeof(Memory)); if(!ptr || !m) { birda_log("PANIC: malloc failed\n"); return 0; } m->ptr=ptr; m->id=id; m->size=size; m->next=allocs; allocs=m; return ptr; } void* growMem(void* ptr, int size) { Memory* m=allocs; void* nptr; while(m && m->ptr!=ptr) m=m->next; if(!m) { birda_log("ERROR: growing unallocated memory\n"); return 0; } nptr=realloc(ptr,size); if(!nptr) { birda_log("PANIC: realloc failed\n"); return 0; } m->ptr=nptr; m->size=size; return nptr; } void freeMem(void *ptr) { Memory** mh=&allocs; Memory* m; while((m=*mh)) { if(m->ptr==ptr) { *mh=m->next; free(m); free(ptr); return; } else { mh=&m->next; } } birda_log("ERROR: freeing unallocated memory\n"); } void showResources(void) { int n,b; Source* s; UNIXSerialPort* u; Timer* t; Memory* m; birda_log("-----------------\n"); for(n=0,u=ports;u;u=u->next) n++; birda_log("%d unixSerialPorts\n",n); for(n=0,s=sources;s;s=s->next) if(s->func!=serialInput) n++; birda_log("%d other sources\n",n); for(n=0,t=timers;t;t=t->next) n++; birda_log("%d timers\n",n); birda_log("Memory:\n"); for(n=0,b=0,m=allocs;m;m=m->next) { birda_log(" addr=%p size=%5d id=%s\n",m->ptr,m->size,m->id); n++; b+=m->size; } birda_log(" total: %d blocks, %d bytes\n",n,b); birda_log("-----------------\n"); } unsigned getRandom(unsigned max) { static bool inited=FALSE; unsigned mask; if(max==0) return 0; if(!inited) { struct timeval t; gettimeofday(&t,0); srandom(t.tv_sec^t.tv_usec); inited=TRUE; } for(mask=1;mask0) { if(maxspeed<115200) speedMask&=~SPEED_115200; if(maxspeed< 57600) speedMask&=~SPEED_57600; if(maxspeed< 38400) speedMask&=~SPEED_38400; if(maxspeed< 19200) speedMask&=~SPEED_19200; if(maxspeed< 9600) return 0; } if((fd=open(dev,O_RDWR|O_NOCTTY|O_NONBLOCK))<0) return 0; t.c_iflag=0; t.c_oflag=0; t.c_cflag=CREAD|CS8|CLOCAL; t.c_lflag=0; t.c_cc[VMIN]=1; t.c_cc[VTIME]=0; cfsetispeed(&t,B9600); cfsetospeed(&t,B9600); if(tcsetattr(fd,TCSANOW,&t) || fcntl(fd,F_SETFL,0)==-1) { close(fd); return 0; } usp=malloc(sizeof(UNIXSerialPort)); usp->serial.close=serialClose; usp->serial.setSpeed=serialSetSpeed; usp->serial.setLine=serialSetLine; usp->serial.getSpeedMask=serialGetSpeedMask; usp->serial.getChar=serialGetChar; usp->serial.putChar=serialPutChar; usp->serial.debug=0; usp->serial.handle=0; usp->serial.status=0; usp->speedMask=speedMask; usp->fd=fd; usp->inHead=usp->inFree=0; usp->outFree=0; evtAddSource(usp->fd,serialInput,usp); usp->next=ports; ports=usp; return &usp->serial; } void evtCancelTimer(void (*func)(void*), void* handle) { Timer* t=unlinkTimer(func,handle); if(t) free(t); } void evtSetTimer(int delay, void (*func)(void*), void* handle) { Timer* t=unlinkTimer(func,handle); if(!t) { t=malloc(sizeof(Timer)); t->func=func; t->handle=handle; } gettimeofday(&t->expire,0); addTime(&t->expire,delay); linkTimer(t); } void evtLoop(void) { while(timers || sources) { int fdmax; fd_set fds; struct timeval t; Source* s; if(timers) { gettimeofday(&t,0); t.tv_sec=timers->expire.tv_sec-t.tv_sec; t.tv_usec=timers->expire.tv_usec-t.tv_usec; if(t.tv_usec<0) { t.tv_sec-=1; t.tv_usec+=1000000; } if(t.tv_sec<0) { Timer* ti=timers; timers=ti->next; if(evtDebug&EVT_DEBUG_TIMERS) birda_log("timer called %p %p\n",ti->func,ti->handle); ti->func(ti->handle); if(evtDebug&EVT_DEBUG_TIMERS) birda_log("timer completed\n"); free(ti); continue; } } fdmax=0; FD_ZERO(&fds); for(s=sources;s;s=s->next) { FD_SET(s->fd,&fds); if(fdmax<=s->fd) fdmax=s->fd+1; } if(evtDebug&EVT_DEBUG_SELECT) birda_log("select %s timers\n",timers ? "with" : "without"); if(select(fdmax,&fds,0,0,timers ? &t : 0)==-1 && errno!=EINTR) perror("select"); if(evtDebug&EVT_DEBUG_SELECT) birda_log("wakeup\n"); for(s=sources;s && !FD_ISSET(s->fd,&fds);s=s->next); if(s) s->func(s->handle); } } /********************************************************************** * Kernel frame device support **********************************************************************/ #ifdef IRDA_KERNEL_DRIVER static void kfd_close(FrameDevice* fd) { KernelFrameDevice* kfd = (KernelFrameDevice*)fd; evtRemoveSource(kfd->fdesc); close(kfd->fdesc); freeMem(kfd); } static int kfd_setParams(FrameDevice* fd, int baud, int ebofs, int maxSize) { KernelFrameDevice* kfd = (KernelFrameDevice*)fd; struct irda_params params; params.speed = baud; params.ebofs = ebofs; params.maxsize = maxSize; if (ioctl(kfd->fdesc, IRDA_SET_PARAMS, ¶ms) == -1) { birda_log("ioctl failed %d\n", __LINE__); } if(kfd->maxSize != maxSize) { kfd->maxSize = maxSize; if (kfd->inBuf) freeMem(kfd->inBuf); kfd->inBuf = kfd->maxSize ? allocMem(id_kfdev_inbuffer, maxSize+2) : 0; } return 0; } static void kfd_resetParams(FrameDevice* fd) { KernelFrameDevice* kfd = (KernelFrameDevice*)fd; if (ioctl(kfd->fdesc, IRDA_RESET_PARAMS) == -1) { birda_log("ioctl failed %d\n", __LINE__); } } static void kfd_sendFrame(FrameDevice* fd, const void* buf0, int len) { KernelFrameDevice* kfd = (KernelFrameDevice*)fd; #if 0 len = createSIRFrame(kfd->ebofs, buf0, len, kfd->outBuf, kfd->maxOutSize); write(kfd->fdesc, kfd->outBuf, len); #else write(kfd->fdesc, buf0, len); #endif } static int kfd_getSpeedMask(FrameDevice* fd) { KernelFrameDevice* kfd = (KernelFrameDevice*)fd; int maxspeed = kfd->maxspeed; int speedmask; if (ioctl(kfd->fdesc, IRDA_GET_SPEEDMASK, &speedmask) == -1) { birda_log("ioctl failed %d\n", __LINE__); } if(maxspeed>0) { if(maxspeed<16000000) speedmask&=~SPEED_16000000; if(maxspeed<4000000) speedmask&=~SPEED_4000000; if(maxspeed<1152000) speedmask&=~SPEED_1152000; if(maxspeed<576000) speedmask&=~SPEED_576000; if(maxspeed<115200) speedmask&=~SPEED_115200; if(maxspeed< 57600) speedmask&=~SPEED_57600; if(maxspeed< 38400) speedmask&=~SPEED_38400; if(maxspeed< 19200) speedmask&=~SPEED_19200; if(maxspeed< 9600) speedmask&=~SPEED_9600; } return speedmask; } static int kfd_getMinTurnaroundMask(FrameDevice* fd) { KernelFrameDevice* kfd = (KernelFrameDevice*)fd; int tamask; if (ioctl(kfd->fdesc, IRDA_GET_TURNAROUNDMASK, &tamask) == -1) { birda_log("ioctl failed %d\n", __LINE__); } return tamask; } static void kfd_readFrame(void *handle) { KernelFrameDevice* kfd = (KernelFrameDevice*)handle; ssize_t len = read(kfd->fdesc, kfd->inBuf, (ssize_t)kfd->maxSize); if (len < 0) { perror("kfd_read"); } if (len > 0) { /* Must be? */ if (kfd->frame.frame) kfd->frame.frame(&kfd->frame, kfd->inBuf, len); /* dev driver skip ck */ } } FrameDevice* createKernelFrameDevice(char *dev, int maxspeed) { KernelFrameDevice *kfd = allocMem(id_kernel_framedevice, sizeof(KernelFrameDevice)); if (!dev) { dev = (char *)defaultKernelPort; } kfd->devname = dev; kfd->maxSize = 4096; kfd->maxspeed = maxspeed; if ((kfd->fdesc = open(kfd->devname, O_RDWR|O_NONBLOCK)) < 0) { freeMem(kfd); birda_log("Cannot open port %s\n", dev); exit(-16); } kfd->frame.close = kfd_close; kfd->frame.setParams = kfd_setParams; kfd->frame.resetParams = kfd_resetParams; kfd->frame.sendFrame = kfd_sendFrame; kfd->frame.getSpeedMask = kfd_getSpeedMask; kfd->frame.getMinTurnaroundMask = kfd_getMinTurnaroundMask; kfd->frame.debug = 0; kfd->frame.handle = 0; kfd->frame.frame = 0; kfd->maxOutSize = 8192; kfd->inBuf = allocMem(id_kfdev_inbuffer, kfd->maxSize+2); kfd->outBuf = allocMem(id_kfdev_outbuffer, kfd->maxOutSize); evtAddSource(kfd->fdesc, kfd_readFrame, kfd); return &kfd->frame; } #endif /* IRDA_KERNEL_DRIVER */