TOC PREV NEXT INDEX

Writing Device Drivers for LynxOS


Sample Device Driver

Header Files

ptrinfo.h

/* ptrinfo.h */
struct ptrinfo {
  int port;
};

prtioclt.h

/* ptrioctl.h */
  #define PTRSTATUS 500
  struct ptrstatus {
  int chars; /* characters printed */
  int lines; /* lines printed */
};

Driver Code

/* ptrdrvr.c - using threads */
#include <kernel.h>
#include <mem.h>
#include <sys/file.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <conf.h>
#include <st.h>
#include "ptrinfo.h"
#include "ptrioctl.h"

/* ports */

#define PP_DATA   0     /* data port offset */
#define PP_STATUS   1   /* status port offset */
#define PP_CONTROL  2   /* control port offset */

/* status bits */

#define PP_BUSY     0x80   /* printer busy */
#define PP_PE       0x20   /* out of paper */
#define PP_SLCT     0x10   /* printer is selected */
#define PP_ERROR    0x08   /* printer detected error */

/* control bits */

#define PP_IENABLE 0x10     /* interrupt enable */
#define PP_SLCTIN  0x08     /* select printer */
#define PP_INIT    0x04     /* start printer */
#define PP_AUTOLF  0x02     /* auto line feed */
#define PP_STROBE  0x01     /* strobe printer */

#define port_in(addr) __inb(addr)
#define port_out(data,addr) __outb(addr,data)

typedef unsigned short ptype;

#define STACKSIZE PAGESIZE

struct qentry {
char c;          /* character */
int pri;         /* its priority */
};

struct ptrstatics {
ptype datap;             /* data port address */
ptype controlp;          /* cntrl port address */
char control;            /* control bits */
int irq;                 /* IRQ number */
int closing;             /* closing device */
int close_sem;           /* sempahore for close */
int expecting;           /* expecting an int.? */
int nextnl;              /* output a '\r' next? */
int chars;               /* printed since open */
int lines;               /* printed since open */
int qlen;                /* characters in queue */
struct qentry *q;        /* the queue itself */
int head;                /* head of queue */
int tail;                /* tail of queue */
int qdata;               /* data in the queue */
int free_sem;            /* free queue space */
int stid;                /* thread id */
int int_sem;             /* interrupt semaphore */
int qsem;                /* queue protection */
struct priotrack pt;     /* pri. tracking */
int curpri;              /* current priority */
int prio_sem;            /* pri. trking sem */
};

/*
static port_in(), port_out();

asm {
  port_in:          /* byte = port_in(port) *
  mov EAX, 0
  mov EDX, 4[ESP]
  in AL, DX
  ret

  port_out:        /* port_out(byte, port) *
  mov EDX, 8[ESP]
  mov EAX, 4[ESP]
  out DX, AL
  ret
}
*/

#define PERR (char *) SYSERR

char *ptrinstall(info)
struct ptrinfo *info;
{
  struct ptrstatics *s;
  static void ptrint(), ptrthread(), ptruninstall();
  int i;

/* probe for the printer */

  port_out(1, info->port+PP_DATA);
  if (port_in(info->port+PP_DATA) != 1)
    return PERR;
  s = (struct ptrstatics *)
  sysbrk((long)sizeof *s);
  if (!s) return PERR;
    s->q = (struct qentry *)
  sysbrk((long)info->qlen * sizeof(struct qentry));
  if (!s->q) {
    sysfree(s, (long)sizeof *s);
  return PERR;
}

/* initialize statics */

  s->datap = info->port + PP_DATA;
  s->controlp = info->port + PP_CONTROL;
  s->control = PP_SLCTIN | PP_INIT;
  s->irq = info->irq;
  s->expecting = 0;
  s->lines = s->chars = 0;
  s->closing = s->close_sem = 0;
  s->nextnl = 0;
  s->free_sem = s->qlen = info->qlen;
  s->qdata = s->head = s->tail = 0;
  s->int_sem = 0;
  s->qsem = -1;
  bzero(&s->pt, sizeof(struct priotrack));
  s->curpri = 0;
  s->prio_sem = -1;

  /* initialize printer */

  iointset(32+s->irq, ptrint, s);
  port_out(PP_SLCTIN, s->controlp);
  for (i = 0; i < 100; i++) ;
    port_out(s->control, s->controlp);

  s->stid = ststart(ptrthread, STACKSIZE,
                    s->curpri, "ptr thread", 1, s);
  if (s->stid == SYSERR) {
    ptruninstall(s);
    return PERR;
  }
return (char *) s;
}

int ptruninstall(s)

struct ptrstatics *s;
{
if (s->stid != SYSERR) stremove(s->stid);
iointclr(32+s->irq);
sysfree(s->q, (long)s->qlen *
sizeof(struct qentry));
sysfree(s, (long)sizeof *s);
}

int ptropen(s, devno, f)
struct ptrstatics *s;
int devno;
struct file *f;
{
if (f->access_mode & FREAD) {
pseterr(EINVAL);
return SYSERR;
}
if (minor(devno)) {
pseterr(ENXIO);
return SYSERR;
}
return OK;
}

int ptrclose(s, f)
struct ptrstatics *s;
struct file *f;
{
swait(&s->qsem, SEM_SIGIGNORE);
if (s->expecting) {
s->closing = 1;
ssignal(&s->qsem);
swait(&s->close_sem, SEM_SIGIGNORE);
s->closing = 0;
} else {
ssignal(&s->qsem);
}
s->lines = s->chars = 0;
return OK;
}
int ptrselect()
{
return OK;
}

/* assumes:
** data in the queue
** queue access disabled
*/

void send(s)
struct ptrstatics *s;
{
int prio;
char c;

if (s->nextnl) {
c = '\r';
prio = s->nextnl;
s->nextnl = 0;
s->lines++;
} else {
c = s->q[s->head].c;
prio = s->q[s->head++].pri;
s->head %= s->qlen;
s->qdata--;
ssignal(&s->free_sem);
s->nextnl = c == '\n'? prio : 0;
s->chars++;
}

port_out(c, s->datap);
port_out(s->control | PP_STROBE, s->controlp);
port_out(s->control | PP_IENABLE, s->controlp);

if (!s->nextnl) {
swait(&s->prio_sem, SEM_SIGIGNORE);
priot_remove(&s->pt, prio);
if (prio == s->curpri) {
prio = priot_max(&s->pt);
if (prio != s->curpri) {
s->curpri = prio;
stsetprio(s->stid, (prio<<1)+1);
}
}
ssignal(&s->prio_sem);
}
}


void ptrint(s)
struct ptrstatics *s;
{
ssignal(&s->int_sem);
}


void ptrthread(s)
struct ptrstatics *s;
{
for (;;) {
swait(&s->int_sem, SEM_SIGIGNORE);
swait(&s->qsem, SEM_SIGIGNORE);
if (s->qdata || s->nextnl) {
send(s);
} else {
s->expecting = 0;
/* disable ptr interrupts: */
port_out(s->control, s->controlp);
if (s->closing) ssignal(&s->close_sem);
}
ssignal(&s->qsem);
}
}

/* This function ptrthread() is really the only difference between this
driver and the others. It is a kernel thread used by the driver to send
characters out to the printer. */

int ptrwrite(s, f, buff, count)
struct ptrstatics *s;
struct file *f;
char *buff;
int count;
{
int i = count, myprio;
myprio = _getpriority();
swait(&s->prio_sem, SEM_SIGIGNORE);
priot_addn(&s->pt, myprio, count);
if (myprio > s->curpri) {
s->curpri = myprio;
stsetprio(s->stid, (myprio<<1)+1);
}

ssignal(&s->prio_sem);

while (i--) {
while (swait(&s->free_sem, SEM_SIGABORT)) {
swait(&s->prio_sem, SEM_SIGABORT);
priot_removen(&s->pt, myprio, i+1);
if (s->curpri == myprio) {
s->curpri = priot_max(&s->pt);
if (s->curpri < myprio) {
stsetprio(s->stid,(s->curpri<<1)+1);
}
}
ssignal(&s->prio_sem);
deliversigs();

priot_addn(&s->pt, myprio, i+1);
if (myprio > s->curpri) {
s->curpri = myprio;
stsetprio(s->stid, (myprio<<1)+1);
}
ssignal(&s->prio_sem);
}

swait(&s->qsem, SEM_SIGIGNORE);

s->q[s->tail].c = *buff++;
s->q[s->tail++].pri = myprio;
s->tail %= s->qlen;
s->qdata++;
if (!s->expecting) {
send(s);
s->expecting = 1;
}
ssignal(&s->qsem);
}
return count;
}

int ptrioctl(s, f, command, arg)
struct ptrstatics *s;
struct file *f;
int command;
char *arg;
{
switch (command) {
case PTRSTATUS:
if (wbounds((int)arg) < sizeof(struct ptrstatus)) {
pseterr(EFAULT);
return SYSERR;
}
((struct ptrstatus *)arg)->chars = s->chars;
((struct ptrstatus *)arg)->lines = s->lines;
break;

case FIOPRIO:

case FIOASYNC:

break;

default:

pseterr(EINVAL);
return SYSERR;
}

return OK;
}

#include <dldd.h>

static struct dldd entry_points = {
ptropen, ptrclose, 0, ptrwrite,
ptrselect, ptrioctl,
ptrinstall, ptruninstall, (char *) 0
};



LynuxWorks, Inc.
855 Branham Lane East
San Jose, CA 95138
http://www.lynuxworks.com
1.800.255.5969
TOC PREV NEXT INDEX