summaryrefslogtreecommitdiff
path: root/os/boot/puma/hard.c
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 21:39:35 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 21:39:35 +0000
commit74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a (patch)
treec6e220ba61db3a6ea4052e6841296d829654e664 /os/boot/puma/hard.c
parent46439007cf417cbd9ac8049bb4122c890097a0fa (diff)
20060303
Diffstat (limited to 'os/boot/puma/hard.c')
-rw-r--r--os/boot/puma/hard.c773
1 files changed, 773 insertions, 0 deletions
diff --git a/os/boot/puma/hard.c b/os/boot/puma/hard.c
new file mode 100644
index 00000000..1f1104c3
--- /dev/null
+++ b/os/boot/puma/hard.c
@@ -0,0 +1,773 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#define DPRINT if(0)print
+
+typedef struct Drive Drive;
+typedef struct Ident Ident;
+typedef struct Controller Controller;
+
+enum
+{
+ /* ports */
+ Pbase0= 0x1F0, /* primary */
+ Pbase1= 0x170, /* secondary */
+ Pbase2= 0x1E8, /* tertiary */
+ Pbase3= 0x168, /* quaternary */
+ Pdata= 0, /* data port (16 bits) */
+ Perror= 1, /* error port (read) */
+ Pprecomp= 1, /* buffer mode port (write) */
+ Pcount= 2, /* sector count port */
+ Psector= 3, /* sector number port */
+ Pcyllsb= 4, /* least significant byte cylinder # */
+ Pcylmsb= 5, /* most significant byte cylinder # */
+ Pdh= 6, /* drive/head port */
+ DHmagic= 0xA0,
+ DHslave= 0x10,
+ Pstatus= 7, /* status port (read) */
+ Sbusy= (1<<7),
+ Sready= (1<<6),
+ Sdrq= (1<<3),
+ Serr= (1<<0),
+ Pcmd= 7, /* cmd port (write) */
+
+ /* commands */
+ Crecal= 0x10,
+ Cread= 0x20,
+ Cwrite= 0x30,
+ Cident= 0xEC,
+ Cident2= 0xFF, /* pseudo command for post Cident interrupt */
+ Csetbuf= 0xEF,
+
+ /* file types */
+ Qdir= 0,
+
+ Timeout= 5, /* seconds to wait for things to complete */
+
+ NCtlr= 4,
+ NDrive= NCtlr*2,
+};
+
+/*
+ * ident sector from drive. this is from ANSI X3.221-1994
+ */
+struct Ident
+{
+ ushort config; /* general configuration info */
+ ushort cyls; /* # of cylinders (default) */
+ ushort reserved0;
+ ushort heads; /* # of heads (default) */
+ ushort b2t; /* unformatted bytes/track */
+ ushort b2s; /* unformated bytes/sector */
+ ushort s2t; /* sectors/track (default) */
+ ushort reserved1[3];
+/* 10 */
+ ushort serial[10]; /* serial number */
+ ushort type; /* buffer type */
+ ushort bsize; /* buffer size/512 */
+ ushort ecc; /* ecc bytes returned by read long */
+ ushort firm[4]; /* firmware revision */
+ ushort model[20]; /* model number */
+/* 47 */
+ ushort s2i; /* number of sectors/interrupt */
+ ushort dwtf; /* double word transfer flag */
+ ushort capabilities;
+ ushort reserved2;
+ ushort piomode;
+ ushort dmamode;
+ ushort cvalid; /* (cvald&1) if next 4 words are valid */
+ ushort ccyls; /* current # cylinders */
+ ushort cheads; /* current # heads */
+ ushort cs2t; /* current sectors/track */
+ ushort ccap[2]; /* current capacity in sectors */
+ ushort cs2i; /* current number of sectors/interrupt */
+/* 60 */
+ ushort lbasecs[2]; /* # LBA user addressable sectors */
+ ushort dmasingle;
+ ushort dmadouble;
+/* 64 */
+ ushort reserved3[64];
+ ushort vendor[32]; /* vendor specific */
+ ushort reserved4[96];
+};
+
+/*
+ * a hard drive
+ */
+struct Drive
+{
+ Controller *cp;
+ uchar driveno;
+ uchar dh;
+
+ Disc;
+};
+
+/*
+ * a controller for 2 drives
+ */
+struct Controller
+{
+ int pbase; /* base port */
+ uchar ctlrno;
+
+ /*
+ * current operation
+ */
+ int cmd; /* current command */
+ char *buf; /* xfer buffer */
+ int tcyl; /* target cylinder */
+ int thead; /* target head */
+ int tsec; /* target sector */
+ int tbyte; /* target byte */
+ int nsecs; /* length of transfer (sectors) */
+ int sofar; /* bytes transferred so far */
+ int status;
+ int error;
+ Drive *dp; /* drive being accessed */
+};
+
+static int atactlrmask;
+static Controller *atactlr[NCtlr];
+static int atadrivemask;
+static Drive *atadrive[NDrive];
+static int pbase[NCtlr] = {
+ Pbase0, Pbase1, Pbase2, Pbase3,
+};
+
+static void hardintr(Ureg*, void*);
+static long hardxfer(Drive*, Partition*, int, ulong, long);
+static int hardident(Drive*);
+static void hardsetbuf(Drive*, int);
+static void hardpart(Drive*);
+static int hardparams(Drive*);
+static void hardrecal(Drive*);
+static int hardprobe(Drive*, int, int, int);
+
+static void
+atactlrprobe(int ctlrno, int irq)
+{
+ Controller *ctlr;
+ Drive *drive;
+ int driveno, port;
+
+ if(atactlrmask & (1<<ctlrno))
+ return;
+ atactlrmask |= 1<<ctlrno;
+
+ port = pbase[ctlrno];
+ outb(port+Pdh, DHmagic);
+ delay(1);
+ if((inb(port+Pdh) & 0xFF) != DHmagic){
+ DPRINT("ata%d: DHmagic not ok\n", ctlrno);
+ return;
+ }
+ DPRINT("ata%d: DHmagic ok\n", ctlrno);
+
+ atactlr[ctlrno] = ialloc(sizeof(Controller), 0);
+ ctlr = atactlr[ctlrno];
+ ctlr->pbase = port;
+ ctlr->ctlrno = ctlrno;
+ ctlr->buf = ialloc(Maxxfer, 0);
+ inb(ctlr->pbase+Pstatus);
+ setvec(irq, hardintr, ctlr);
+
+ driveno = ctlrno*2;
+ atadrive[driveno] = ialloc(sizeof(Drive), 0);
+ drive = atadrive[driveno];
+ drive->cp = ctlr;
+ drive->driveno = driveno;
+ drive->dh = DHmagic;
+
+ driveno++;
+ atadrive[driveno] = ialloc(sizeof(Drive), 0);
+ drive = atadrive[driveno];
+ drive->cp = ctlr;
+ drive->driveno = driveno;
+ drive->dh = DHmagic|DHslave;
+}
+
+static Drive*
+atadriveprobe(int driveno)
+{
+ Drive *drive;
+ int ctlrno;
+ ISAConf isa;
+
+ ctlrno = driveno/2;
+ if(atactlr[ctlrno] == 0){
+ if(atactlrmask & (1<<ctlrno))
+ return 0;
+ memset(&isa, 0, sizeof(ISAConf));
+ if(isaconfig("ata", ctlrno, &isa) == 0)
+ return 0;
+ if(ctlrno && isa.irq)
+ atactlrprobe(ctlrno, Int0vec+isa.irq);
+ if(atactlr[ctlrno] == 0)
+ return 0;
+ }
+
+ drive = atadrive[driveno];
+ if(drive->online == 0){
+ if(atadrivemask & (1<<driveno))
+ return 0;
+ atadrivemask |= 1<<driveno;
+ if(hardparams(drive))
+ return 0;
+ if(drive->lba)
+ print("hd%d: LBA %d sectors, %ud bytes\n",
+ drive->driveno, drive->sectors, drive->cap);
+ else
+ print("hd%d: CHS %d/%d/%d %d bytes\n",
+ drive->driveno, drive->cyl, drive->heads,
+ drive->sectors, drive->cap);
+ drive->online = 1;
+ hardpart(drive);
+ hardsetbuf(drive, 1);
+ }
+
+ return drive;
+}
+
+int
+hardinit(void)
+{
+ atactlrprobe(0, ATAvec0);
+ return 0xFF;
+}
+
+long
+hardseek(int driveno, long offset)
+{
+ Drive *drive;
+
+ if((drive = atadriveprobe(driveno)) == 0)
+ return -1;
+ drive->offset = offset;
+ return offset;
+}
+
+/*
+ * did an interrupt happen?
+ */
+static void
+hardwait(Controller *cp)
+{
+ ulong start;
+ int x;
+
+ x = spllo();
+ for(start = m->ticks; TK2SEC(m->ticks - start) < Timeout && cp->cmd;)
+ if(cp->cmd == Cident2 && TK2SEC(m->ticks - start) >= 1)
+ break;
+ if(TK2SEC(m->ticks - start) >= Timeout){
+ DPRINT("hardwait timed out %ux\n", inb(cp->pbase+Pstatus));
+ hardintr(0, cp);
+ }
+ splx(x);
+}
+
+Partition*
+sethardpart(int driveno, char *p)
+{
+ Partition *pp;
+ Drive *dp;
+
+ if((dp = atadriveprobe(driveno)) == 0)
+ return 0;
+
+ for(pp = dp->p; pp < &dp->p[dp->npart]; pp++)
+ if(strcmp(pp->name, p) == 0){
+ dp->current = pp;
+ return pp;
+ }
+ return 0;
+}
+
+long
+hardread(int driveno, void *a, long n)
+{
+ Drive *dp;
+ long rv, i;
+ int skip;
+ uchar *aa = a;
+ Partition *pp;
+ Controller *cp;
+
+ if((dp = atadriveprobe(driveno)) == 0)
+ return 0;
+
+ pp = dp->current;
+ if(pp == 0)
+ return -1;
+ cp = dp->cp;
+
+ skip = dp->offset % dp->bytes;
+ for(rv = 0; rv < n; rv += i){
+ i = hardxfer(dp, pp, Cread, dp->offset+rv-skip, n-rv+skip);
+ if(i == 0)
+ break;
+ if(i < 0)
+ return -1;
+ i -= skip;
+ if(i > n - rv)
+ i = n - rv;
+ memmove(aa+rv, cp->buf + skip, i);
+ skip = 0;
+ }
+ dp->offset += rv;
+
+ return rv;
+}
+
+/*
+ * wait for the controller to be ready to accept a command
+ */
+static int
+cmdreadywait(Drive *drive)
+{
+ ulong end;
+ uchar dh, status;
+ Controller *ctlr;
+
+ ctlr = drive->cp;
+ end = m->ticks+MS2TK(10)+1;
+ dh = (inb(ctlr->pbase+Pdh) & DHslave)^(drive->dh & DHslave);
+
+ status = 0;
+ while(m->ticks < end){
+ status = inb(ctlr->pbase+Pstatus);
+ if(status & Sbusy)
+ continue;
+ if(dh){
+ outb(ctlr->pbase+Pdh, drive->dh);
+ dh = 0;
+ continue;
+ }
+ if(status & Sready)
+ return 0;
+ }
+ USED(status);
+
+ DPRINT("hd%d: cmdreadywait failed %uX\n", drive->driveno, status);
+ outb(ctlr->pbase+Pdh, DHmagic);
+ return -1;
+}
+
+/*
+ * transfer a number of sectors. hardintr will perform all the iterative
+ * parts.
+ */
+static long
+hardxfer(Drive *dp, Partition *pp, int cmd, ulong start, long len)
+{
+ Controller *cp;
+ long lsec;
+
+ if(dp->online == 0){
+ DPRINT("disk not on line\n");
+ return -1;
+ }
+
+ if(cmd == Cwrite)
+ return -1;
+
+ /*
+ * cut transfer size down to disk buffer size
+ */
+ start = start / dp->bytes;
+ if(len > Maxxfer)
+ len = Maxxfer;
+ len = (len + dp->bytes - 1) / dp->bytes;
+
+ /*
+ * calculate physical address
+ */
+ cp = dp->cp;
+ lsec = start + pp->start;
+ if(lsec >= pp->end){
+ DPRINT("read past end of partition\n");
+ return 0;
+ }
+ if(dp->lba){
+ cp->tsec = lsec & 0xff;
+ cp->tcyl = (lsec>>8) & 0xffff;
+ cp->thead = (lsec>>24) & 0xf;
+ } else {
+ cp->tcyl = lsec/(dp->sectors*dp->heads);
+ cp->tsec = (lsec % dp->sectors) + 1;
+ cp->thead = (lsec/dp->sectors) % dp->heads;
+ }
+
+ /*
+ * can't xfer past end of disk
+ */
+ if(lsec+len > pp->end)
+ len = pp->end - lsec;
+ cp->nsecs = len;
+
+ if(cmdreadywait(dp) < 0)
+ return -1;
+
+ /*
+ * start the transfer
+ */
+ cp->cmd = cmd;
+ cp->dp = dp;
+ cp->sofar = 0;
+ cp->status = 0;
+ DPRINT("xfer:\ttcyl %d, tsec %d, thead %d\n", cp->tcyl, cp->tsec, cp->thead);
+ DPRINT("\tnsecs %d, sofar %d\n", cp->nsecs, cp->sofar);
+ outb(cp->pbase+Pcount, cp->nsecs);
+ outb(cp->pbase+Psector, cp->tsec);
+ outb(cp->pbase+Pdh, dp->dh | (dp->lba<<6) | cp->thead);
+ outb(cp->pbase+Pcyllsb, cp->tcyl);
+ outb(cp->pbase+Pcylmsb, cp->tcyl>>8);
+ outb(cp->pbase+Pcmd, cmd);
+
+ hardwait(cp);
+
+ if(cp->status & Serr){
+ DPRINT("hd%d err: status %lux, err %lux\n",
+ dp->driveno, cp->status, cp->error);
+ DPRINT("\ttcyl %d, tsec %d, thead %d\n",
+ cp->tcyl, cp->tsec, cp->thead);
+ DPRINT("\tnsecs %d, sofar %d\n", cp->nsecs, cp->sofar);
+ return -1;
+ }
+
+ return cp->nsecs*dp->bytes;
+}
+
+/*
+ * set read ahead mode (1 == on, 0 == off)
+ */
+static void
+hardsetbuf(Drive *dp, int on)
+{
+ Controller *cp = dp->cp;
+
+ if(cmdreadywait(dp) < 0)
+ return;
+
+ cp->cmd = Csetbuf;
+ /* BUG: precomp varies by hard drive...this is safari-specific? */
+ outb(cp->pbase+Pprecomp, on ? 0xAA : 0x55);
+ outb(cp->pbase+Pdh, dp->dh);
+ outb(cp->pbase+Pcmd, Csetbuf);
+
+ hardwait(cp);
+}
+
+static int
+isatapi(Drive *drive)
+{
+ Controller *cp;
+
+ cp = drive->cp;
+ outb(cp->pbase+Pdh, drive->dh);
+ microdelay(1);
+ if(inb(cp->pbase+Pstatus))
+ return 0;
+ if(inb(cp->pbase+Pcylmsb) != 0xEB || inb(cp->pbase+Pcyllsb) != 0x14)
+ return 0;
+ return 1;
+}
+
+/*
+ * get parameters from the drive
+ */
+static int
+hardident(Drive *dp)
+{
+ Controller *cp;
+ Ident *ip;
+
+ dp->bytes = 512;
+ cp = dp->cp;
+
+ if(isatapi(dp) || cmdreadywait(dp) < 0)
+ return -1;
+
+ cp->nsecs = 1;
+ cp->sofar = 0;
+ cp->cmd = Cident;
+ cp->dp = dp;
+ outb(cp->pbase+Pdh, dp->dh);
+ outb(cp->pbase+Pcmd, Cident);
+
+ hardwait(cp);
+
+ if(cp->status & Serr)
+ return -1;
+
+ hardwait(cp);
+
+ ip = (Ident*)cp->buf;
+ DPRINT("LBA%d: %lud\n",
+ ip->capabilities & (1<<9) == 1, (ip->lbasecs[0]) | (ip->lbasecs[1]<<16));
+ DPRINT("DEF: %ud/%ud/%ud\nMAP %ud/%ud/%ud\n",
+ ip->cyls, ip->heads, ip->s2t,
+ ip->ccyls, ip->cheads, ip->cs2t);
+ if(ip->capabilities & (1<<9)){
+ dp->lba = 1;
+ dp->sectors = (ip->lbasecs[0]) | (ip->lbasecs[1]<<16);
+ dp->cap = dp->bytes * dp->sectors;
+/*print("\nata%d model %s with %d lba sectors\n", dp->driveno, id, dp->sectors);/**/
+ } else {
+ dp->lba = 0;
+
+ /* use default (unformatted) settings */
+ dp->cyl = ip->cyls;
+ dp->heads = ip->heads;
+ dp->sectors = ip->s2t;
+/*print("\nata%d model %s with default %d cyl %d head %d sec\n", dp->driveno,
+ id, dp->cyl, dp->heads, dp->sectors);/**/
+
+ if(ip->cvalid&(1<<0)){
+ /* use current settings */
+ dp->cyl = ip->ccyls;
+ dp->heads = ip->cheads;
+ dp->sectors = ip->cs2t;
+/*print("\tchanged to %d cyl %d head %d sec\n", dp->cyl, dp->heads, dp->sectors);/**/
+ }
+ dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors;
+ }
+
+ return 0;
+}
+
+/*
+ * probe the given sector to see if it exists
+ */
+static int
+hardprobe(Drive *dp, int cyl, int sec, int head)
+{
+ Controller *cp;
+
+ cp = dp->cp;
+ if(cmdreadywait(dp) < 0)
+ return -1;
+
+ /*
+ * start the transfer
+ */
+ cp->cmd = Cread;
+ cp->dp = dp;
+ cp->sofar = 0;
+ cp->nsecs = 1;
+ cp->status = 0;
+ outb(cp->pbase+Pcount, 1);
+ outb(cp->pbase+Psector, sec+1);
+ outb(cp->pbase+Pdh, dp->dh | (dp->lba<<6) | head);
+ outb(cp->pbase+Pcyllsb, cyl);
+ outb(cp->pbase+Pcylmsb, cyl>>8);
+ outb(cp->pbase+Pcmd, Cread);
+
+ hardwait(cp);
+
+ if(cp->status & Serr)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * figure out the drive parameters
+ */
+static int
+hardparams(Drive *dp)
+{
+ int i, hi, lo;
+
+ /*
+ * first try the easy way, ask the drive and make sure it
+ * isn't lying.
+ */
+ dp->bytes = 512;
+ if(hardident(dp) < 0)
+ return -1;
+ if(dp->lba){
+ i = dp->sectors - 1;
+ if(hardprobe(dp, (i>>8)&0xffff, (i&0xff)-1, (i>>24)&0xf) == 0)
+ return 0;
+ } else {
+ if(hardprobe(dp, dp->cyl-1, dp->sectors-1, dp->heads-1) == 0)
+ return 0;
+ }
+
+ DPRINT("hardparam: cyl %d sectors %d heads %d\n", dp->cyl, dp->sectors, dp->heads);
+ /*
+ * the drive lied, determine parameters by seeing which ones
+ * work to read sectors.
+ */
+ dp->lba = 0;
+ for(i = 0; i < 16; i++)
+ if(hardprobe(dp, 0, 0, i) < 0)
+ break;
+ dp->heads = i;
+ for(i = 0; i < 64; i++)
+ if(hardprobe(dp, 0, i, 0) < 0)
+ break;
+ dp->sectors = i;
+ for(i = 512; ; i += 512)
+ if(hardprobe(dp, i, dp->sectors-1, dp->heads-1) < 0)
+ break;
+ lo = i - 512;
+ hi = i;
+ for(; hi-lo > 1;){
+ i = lo + (hi - lo)/2;
+ if(hardprobe(dp, i, dp->sectors-1, dp->heads-1) < 0)
+ hi = i;
+ else
+ lo = i;
+ }
+ dp->cyl = lo + 1;
+ dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors;
+
+ if(dp->cyl == 0 || dp->heads == 0 || dp->sectors == 0 || dp->cap == 0)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * read partition table. The partition table is just ascii strings.
+ */
+#define MAGIC "plan9 partitions"
+static void
+hardpart(Drive *dp)
+{
+ Partition *pp;
+ Controller *cp;
+ char *field[3], *line[Npart+1], *p, buf[NAMELEN];
+ ulong n;
+ int i;
+
+ cp = dp->cp;
+
+ /*
+ * we always have a partition for the whole disk
+ * and one for the partition table
+ */
+ pp = &dp->p[0];
+ strcpy(pp->name, "disk");
+ pp->start = 0;
+ pp->end = dp->cap / dp->bytes;
+ pp++;
+ strcpy(pp->name, "partition");
+ pp->start = dp->p[0].end - 1;
+ pp->end = dp->p[0].end;
+ dp->npart = 2;
+
+ /*
+ * Check if the partitions are described in plan9.ini.
+ * If not, read the disc.
+ */
+ sprint(buf, "hd%dpartition", dp->driveno);
+ if((p = getconf(buf)) == 0){
+ /*
+ * read last sector from disk, null terminate. This used
+ * to be the sector we used for the partition tables.
+ * However, this sector is special on some PC's so we've
+ * started to use the second last sector as the partition
+ * table instead. To avoid reconfiguring all our old systems
+ * we first look to see if there is a valid partition
+ * table in the last sector. If so, we use it. Otherwise
+ * we switch to the second last.
+ */
+ hardxfer(dp, pp, Cread, 0, dp->bytes);
+ cp->buf[dp->bytes-1] = 0;
+ n = getcfields(cp->buf, line, Npart+1, "\n");
+ if(n == 0 || strncmp(line[0], MAGIC, sizeof(MAGIC)-1)){
+ dp->p[0].end--;
+ dp->p[1].start--;
+ dp->p[1].end--;
+ hardxfer(dp, pp, Cread, 0, dp->bytes);
+ cp->buf[dp->bytes-1] = 0;
+ n = getcfields(cp->buf, line, Npart+1, "\n");
+ }
+ }
+ else{
+ strcpy(cp->buf, p);
+ n = getcfields(cp->buf, line, Npart+1, "\n");
+ }
+
+ /*
+ * parse partition table.
+ */
+ if(n && strncmp(line[0], MAGIC, sizeof(MAGIC)-1) == 0){
+ for(i = 1; i < n; i++){
+ pp++;
+ if(getcfields(line[i], field, 3, " ") != 3)
+ break;
+ strncpy(pp->name, field[0], NAMELEN);
+ pp->start = strtoul(field[1], 0, 0);
+ pp->end = strtoul(field[2], 0, 0);
+ if(pp->start > pp->end || pp->start >= dp->p[0].end)
+ break;
+ dp->npart++;
+ }
+ }
+ return;
+}
+
+/*
+ * we get an interrupt for every sector transferred
+ */
+static void
+hardintr(Ureg*, void *arg)
+{
+ Controller *cp;
+ Drive *dp;
+ long loop;
+
+ cp = arg;
+ dp = cp->dp;
+
+ loop = 0;
+ while((cp->status = inb(cp->pbase+Pstatus)) & Sbusy)
+ if(++loop > 100000){
+ print("hardintr 0x%lux\n", cp->status);
+ break;
+ }
+ switch(cp->cmd){
+ case Cread:
+ case Cident:
+ if(cp->status & Serr){
+ cp->cmd = 0;
+ cp->error = inb(cp->pbase+Perror);
+ return;
+ }
+ loop = 0;
+ while((inb(cp->pbase+Pstatus) & Sdrq) == 0)
+ if(++loop > 100000){
+ print("hardintr 2 cmd %ux status %ux",
+ cp->cmd, inb(cp->pbase+Pstatus));
+ cp->cmd = 0;
+ return;
+ }
+ inss(cp->pbase+Pdata, &cp->buf[cp->sofar*dp->bytes],
+ dp->bytes/2);
+ cp->sofar++;
+ if(cp->sofar >= cp->nsecs){
+ if(cp->cmd == Cident && (cp->status & Sready) == 0)
+ cp->cmd = Cident2; /* sometimes we get a second intr */
+ else
+ cp->cmd = 0;
+ inb(cp->pbase+Pstatus);
+ }
+ break;
+ case Csetbuf:
+ case Cident2:
+ cp->cmd = 0;
+ break;
+ default:
+ cp->cmd = 0;
+ break;
+ }
+}