From: beto@plan9.cs.su.oz.au beto@plan9.cs.su.oz.au
Subject: ATAPI driver's src
Date: Fri, 25 Aug 1995 14:50:39 -0400 [thread overview]
Message-ID: <19950825185039.ZjsctKhUnxW1z43FqN2VZx_UyT7nSFZfp8l4GR01bsk@z> (raw)
Here is an ATAPI driver which I'm using with my Creative Quadspeed
CD. It's a quick hack of devata.c.
It's serves:
'#T'/cmd
'#T'/data
'#T'/cd
'#T'/cdctl
'#T'/debug
To read data cds uses '#T'/cd, to execute an ATAPI command uses
'#T'/cmd and '#T'/cmd/data similar to the SCSI interface.
Actually, ATAPI cmds are extremely similar to SCSI's cmds.
I've only tried with my CD, but I should work with any
ATAPI CDROM. Any comment or problem mail me, and I'll
try to solve it, if I can :-).
You have to put this entry in plan9.ini
cdrom0=type=atapi port=xxx irq=yyy
We also have a vcd program which plays and reads audio
CDs, and even a remote-ctl command using the panel library. If
any one is interesting I could put it in my ftp directory.
/*
* Alberto Nava beto@plan9.cs.su.oz.au
* 16/6/95
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "devtab.h"
#define DPRINT if(debug) print
typedef struct Drive Drive;
typedef struct Ident Ident;
typedef struct Controller Controller;
typedef struct Atapicmd Atapicmd;
enum
{
/* ports */
Pbase0= 0x1F0,
Pbase1= 0x170,
Pbase2= 0x1E8,
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 */
Pstatus= 7, /* status port (read) */
Sbusy= (1<<7),
Sready= (1<<6),
Sdrq= (1<<3),
Serr= (1<<0),
Pcmd= 7, /* cmd port (write) */
/* ATA commands */
Crecal= 0x10,
Cread= 0x20,
Cwrite= 0x30,
Cident= 0xEC,
Cident2= 0xFF, /* pseudo command for post Cident interrupt */
Cinitparam= 0x91,
/* ATAPI commands */
Cpktcmd= 0xA0,
Cpktcmd2= 0xFE, /* pseudo command for last Cpktcmd interrupt */
Cidentd= 0xA1,
Ccapacity= 0x25,
Cread2= 0x28,
/* something we have to or into the drive/head reg */
DHmagic= 0xA0,
/* file types */
Qdir= 0,
Qdir2,
Qcmd,
Qdata,
Qcd,
Qcdctl,
Qdebug,
Maxxfer= BY2PG, /* maximum transfer size/cmd */
Hardtimeout= 4000, /* disk access timeout */
Maxloop= 10000,
};
/*
* an atapi CD-ROM drive
*/
struct Drive
{
QLock; /* exclusive access to the disk */
Ref opens; /* how many Qcd opens */
Controller *cp; /* its controller */
int blocks; /* disk size in blocks */
int bsize; /* block size */
int online; /* ok */
int drive; /* drive number */
int bytes; /* for ATA ident*/
};
/*
* a controller for 2 drives
*/
struct Controller
{
QLock; /* exclusive access to the controller */
Lock reglock; /* exclusive access to the registers */
int pbase; /* base port */
/*
* current operation
*/
int cmd; /* current command */
int lastcmd; /* debugging info */
Rendez r; /* wait here for command termination */
char *buf; /* xfer buffer */
int nsecs; /* length of transfer (sectors) */
int sofar; /* sectors transferred so far */
int status; /* last operation status */
int error; /* last operation error code */
int count; /* last operation bytes transfered */
Drive *dp; /* drive being accessed */
Atapicmd *ac; /* Atapi command being processed */
};
struct Atapicmd
{
QLock;
int pid; /* process doing atapi cmd */
int len; /* lenght of buffer space */
char *buf; /* buffer for IN*/
ushort status; /* status after completition */
ushort error; /* error after completition */
ushort count; /* real count of bytes transfered */
uchar cmdblk[12];
};
Controller *atapic;
Drive *atapi;
Atapicmd *atapicmd;
int debug;
static void cdsize(Drive*);
static long cdio(Chan*, char*, ulong, ulong);
static void atapiintr(Ureg*, void*);
static void atapiexec(Drive*,Atapicmd *);
static void atapiident(Drive*);
Dirtab atapitab[]={
"cmd", {Qcmd}, 0, 0600,
"data", {Qdata}, 0, 0600,
"cd", {Qcd}, 0, 0600,
"cdctl", {Qcdctl}, 0, 0600,
"debug", {Qdebug}, 1, 0666,
};
#define Natapitab (sizeof(atapitab)/sizeof(Dirtab))
int
atapigen(Chan *c, void *vp, int ntab, int i, Dir *dp)
{
Qid q;
USED(vp);
USED(ntab);
q.vers = 0;
/* top level directory contains the directory atapi */
if(c->qid.path == CHDIR){
if(i)
return -1;
q.path = CHDIR | Qdir2;
devdir(c, q, "atapi", 0, eve, 0555, dp);
return 1;
}
/* next level uses table */
return devgen(c, atapitab, Natapitab, i, dp);
}
void
atapireset(void)
{
ISAConf atapiconf;
Drive *dp;
Controller *cp;
/*
* BUG: just one disk
*/
atapiconf.port = Pbase2;
atapiconf.irq = 11;
if(isaconfig("cdrom", 0, &atapiconf) == 0)
return;
if(strcmp(atapiconf.type, "atapi") != 0)
return;
switch(atapiconf.port){
case 0x170:
case 0x1E8:
case 0x168:
break;
default:
print("devatapi: bad atapi port 0x%x\n", atapiconf.port);
return;
}
switch(atapiconf.irq){
case 10:
case 11:
case 12:
case 15:
break;
default:
print("devatapi: bad atapi irq %d\n", atapiconf.irq);
return;
}
atapi = xalloc(sizeof(Drive));
atapic = xalloc(sizeof(Controller));
atapicmd = xalloc(sizeof(Atapicmd));
cp = atapic;
cp->buf = 0;
cp->lastcmd = cp->cmd;
cp->cmd = 0;
cp->pbase = atapiconf.port ;
setvec(Int0vec+atapiconf.irq, atapiintr, 0); /* 3th interface */
dp = atapi;
dp->drive = 1;
dp->online = 0;
dp->cp = cp;
DPRINT("drive 0 id %d online %d\n",dp->drive,dp->online);
}
void
atapiinit(void)
{
}
Chan*
atapiattach(char *spec)
{
Drive *dp;
dp = atapi;
if(waserror()){
dp->online = 0;
qunlock(dp);
return 0;
}
qlock(dp);
if(!dp->online){
dp->bytes = 512;
atapiident(dp);
dp->online = 1;
}
qunlock(dp);
poperror();
return devattach('T', spec);
}
Chan*
atapiclone(Chan *c, Chan *nc)
{
return devclone(c, nc);
}
int
atapiwalk(Chan *c, char *name)
{
return devwalk(c, name, atapitab, (long)Natapitab, atapigen);
}
void
atapistat(Chan *c, char *dp)
{
devstat(c, dp, atapitab, (long)Natapitab, atapigen);
}
Chan*
atapiopen(Chan *c, int omode)
{
switch(c->qid.path) {
case Qcd:
if(incref(&atapi->opens) == 1) {
if(waserror()) {
decref(&atapi->opens);
nexterror();
}
cdsize(atapi);
poperror();
}
break;
}
return devopen(c, omode, atapitab, (long)Natapitab, atapigen);
}
void
atapicreate(Chan *c, char *name, int omode, ulong perm)
{
USED(c, name, omode, perm);
error(Eperm);
}
void
atapiclose(Chan *c)
{
switch(c->qid.path) {
default:
break;
case Qcd:
if(c->flag & COPEN)
decref(&atapi->opens);
break;
}
}
void
atapiremove(Chan *c)
{
USED(c);
error(Eperm);
}
void
atapiwstat(Chan *c, char *dp)
{
USED(c, dp);
error(Eperm);
}
long
atapiread(Chan *c, char *a, long n, ulong offset)
{
char *t, buf[64];
USED(a, n, offset);
if(c->qid.path & CHDIR)
return devdirread(c, a, n, atapitab, Natapitab, atapigen);
switch (c->qid.path) {
case Qcmd:
if (n < 4)
error(Ebadarg);
if (canqlock(atapicmd)) {
qunlock(atapicmd);
error(Egreg);
}
if(atapicmd->pid != u->p->pid)
error(Egreg);
n = 4;
*a++ = 0;
*a++ = 0;
*a++ = atapicmd->error;
*a = atapicmd->status;
qunlock(atapicmd);
break;
case Qdata:
if (canqlock(atapicmd)) {
qunlock(atapicmd);
error(Egreg);
}
if(atapicmd->pid != u->p->pid)
error(Egreg);
if (n > Maxxfer)
error(Ebadarg);
atapicmd->len = n;
atapicmd->buf = 0;
if (n == 0) {
atapiexec(&atapi[0],atapicmd);
break;
}
atapicmd->buf = smalloc(Maxxfer);
if (waserror()) {
free(atapicmd->buf);
nexterror();
}
atapiexec(&atapi[0],atapicmd);
memmove(a,atapicmd->buf,atapicmd->count);
poperror();
free(atapicmd->buf);
n=atapicmd->count;
break;
case Qcd:
n = cdio(c,a,n,offset);
break;
case Qcdctl:
t = "atapi";
sprint(buf, "port=0x%ux drive=%s\n", atapic->pbase, t);
return readstr(offset, a, n, buf);
case Qdebug:
if(offset == 0){
n=1;
*a="01"[debug!=0];
}else
n = 0;
break;
default:
panic("atapiwrite");
}
return n;
}
long
atapiwrite(Chan *c, char *a, long n, ulong offset)
{
USED(c, a, n, offset);
switch (c->qid.path) {
case Qcmd:
qlock(atapicmd);
if (n != 12) {
qunlock(atapicmd);
error(Ebadarg);
}
atapicmd->pid = u->p->pid;
memmove(atapicmd->cmdblk,a,n);
break;
case Qdata:
error(Eperm);
case Qcd:
case Qcdctl:
error(Eperm);
break;
case Qdebug:
if(offset == 0){
debug = (*a=='1');
n = 1;
}else
n = 0;
break;
default:
panic("atapiwrite");
}
return n;
}
/*
* did an interrupt happen?
*/
static int
cmddone(void *a)
{
Controller *cp = a;
return cp->cmd == 0;
}
/*
* Wait for the controller to be ready to accept a command.
*/
static void
cmdreadywait(Drive *dp)
{
long start;
int period;
Controller *cp = dp->cp;
period = 10;
start = m->ticks;
while((inb(cp->pbase+Pstatus) & (Sready|Sbusy)) != Sready)
if(TK2MS(m->ticks - start) > period){
print("cmdreadywait:cmdreadywait failed\n");
error(Eio);
}
}
static void
cmddrqwait(Drive *dp)
{
long loop;
Controller *cp = dp->cp;
loop=0;
while((inb(cp->pbase+Pstatus) & (Serr|Sdrq)) == 0)
if(++loop > Maxloop) {
print("cmddrqwait:cmd=%lux status=%lux\n",
cp->cmd, inb(cp->pbase+Pstatus));
error(Eio);
}
}
static void
atapisleep(Controller *cp)
{
tsleep(&cp->r, cmddone, cp, Hardtimeout);
if(cp->cmd && cp->cmd != Cident2){
DPRINT("hard drive timeout\n");
error("ata drive timeout");
}
}
/*
* 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];
};
/*
* get parameters from the drive
*/
static void
atapiident(Drive *dp)
{
Controller *cp;
char *buf;
Ident *ip;
cp = dp->cp;
buf = smalloc(Maxxfer);
qlock(cp);
if(waserror()){
qunlock(cp);
nexterror();
}
cmdreadywait(dp);
ilock(&cp->reglock);
cp->nsecs = 1;
cp->sofar = 0;
cp->cmd = Cidentd;
cp->dp = dp;
cp->buf = buf;
outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4));
outb(cp->pbase+Pcmd, Cidentd);
iunlock(&cp->reglock);
atapisleep(cp);
if(cp->status & Serr){
print("bad disk ident status %ux\n",cp->error);
error(Eio);
}
ip = (Ident*)buf;
/*
* this function appears to respond with an extra interrupt after
* the ident information is read, except on the safari. The following
* delay gives this extra interrupt a chance to happen while we are quiet.
* Otherwise, the interrupt may come during a subsequent read or write,
* causing a panic and much confusion.
*/
if (cp->cmd == Cident2)
tsleep(&cp->r, return0, 0, Hardtimeout);
DPRINT("ident config = %ux cap %ux \n",ip->config,ip->capabilities);
cp->lastcmd = cp->cmd;
cp->cmd = 0;
cp->buf = 0;
free(buf);
poperror();
qunlock(cp);
}
static long
cdio(Chan *c, char *a, ulong len, ulong offset)
{
Drive *d;
ulong bn, n, o, m;
USED(c);
d = &atapi[0];
qlock(atapicmd);
atapicmd->buf = smalloc(Maxxfer);
atapicmd->len = d->bsize;
if (waserror()) {
free(atapicmd->buf);
qunlock(atapicmd);
nexterror();
}
n = len;
while(n > 0) {
bn = offset / d->bsize;
o = offset % d->bsize;
m = d->bsize - o;
if (m > n)
m = n;
if (bn > d->blocks) {
print("reading too far\n");
break;
}
memset(atapicmd->cmdblk,0,12);
atapicmd->cmdblk[0] = Cread2;
atapicmd->cmdblk[2] = bn >> 24;
atapicmd->cmdblk[3] = bn >> 16;
atapicmd->cmdblk[4] = bn >> 8;
atapicmd->cmdblk[5] = bn;
atapicmd->cmdblk[7] = 0;
atapicmd->cmdblk[8] = 1;
atapiexec(&atapi[0],atapicmd);
if (atapicmd->count!=d->bsize) {
print("short read\n");
break;
}
memmove(a, atapicmd->buf + o, m);
n -= m;
offset += m;
a += m;
}
poperror();
free(atapicmd->buf);
qunlock(atapicmd);
return len-n;
}
/*
* disk and block size
*/
static void
cdsize(Drive *d)
{
qlock(atapicmd);
atapicmd->buf = smalloc(Maxxfer);
atapicmd->len = 8;
if (waserror()) {
free(atapicmd->buf);
qunlock(atapicmd);
nexterror();
}
memset(atapicmd->cmdblk,0,12);
atapicmd->cmdblk[0] = Ccapacity;
atapiexec(d,atapicmd);
if (atapicmd->count!=8) {
print("weird cpacity\n");
error(Eio);
}
d->blocks = atapicmd->buf[0] << 24 | atapicmd->buf[1] <<16 |
atapicmd->buf[2] << 8 | atapicmd->buf[3] ;
d->bsize = atapicmd->buf[4] << 24 | atapicmd->buf[5] << 16 |
atapicmd->buf[6] << 8 |atapicmd->buf[7];
poperror();
free(atapicmd->buf);
qunlock(atapicmd);
return;
}
void atapiexec(Drive *dp,Atapicmd *ac)
{
Controller *cp;
cp = dp->cp;
qlock(cp);
if(waserror()){
qunlock(cp);
nexterror();
}
cmdreadywait(dp);
ilock(&cp->reglock);
cp->nsecs = 1;
cp->sofar = 0;
cp->cmd = Cpktcmd;
cp->dp = dp;
cp->buf = ac->buf;
outb(cp->pbase+Pcount, 0);
outb(cp->pbase+Psector, 0);
outb(cp->pbase+Pprecomp, 0);
outb(cp->pbase+Pcyllsb, ac->len);
outb(cp->pbase+Pcylmsb, ac->len>>8);
outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4));
outb(cp->pbase+Pcmd, Cpktcmd);
iunlock(&cp->reglock);
cmddrqwait(dp);
ilock(&cp->reglock);
outss(cp->pbase+Pdata, ac->cmdblk, 12/2);
iunlock(&cp->reglock);
DPRINT("CMD issue\n");
atapisleep(cp);
DPRINT("Wakeup %ux\n",ac);
ac->status = cp->status;
ac->error = cp->error;
ac->count = cp->count;
DPRINT("status %ux error %ux count %ux\n",cp->status,cp->error,cp->count);
if(cp->status & Serr){
DPRINT("Bad packet command %ux\n",cp->error);
error(Eio);
}
cp->buf = 0;
cp->lastcmd = cp->cmd;
cp->cmd = 0;
poperror();
qunlock(cp);
}
int lastcount;
static void
atapiintr(Ureg *ur, void *arg)
{
Controller *cp;
Drive *dp;
long loop;
int count;
char *addr;
USED(ur, arg);
/*
* BUG!! if there is ever more than one controller, we need a way to
* distinguish which interrupted (use arg).
*/
cp = atapic;
dp = cp->dp;
ilock(&cp->reglock);
loop = 0;
while((cp->status = inb(cp->pbase+Pstatus)) & Sbusy){
if(++loop > Maxloop) {
DPRINT("cmd=%lux status=%lux\n",
cp->cmd, inb(cp->pbase+Pstatus));
panic("ataintr: wait busy");
}
}
switch(cp->cmd){
case Cpktcmd:
DPRINT("pkt\n");
if(cp->status & Serr){
cp->lastcmd = cp->cmd;
cp->cmd = 0;
cp->error = inb(cp->pbase+Perror);
wakeup(&cp->r);
break;
}
addr = cp->buf;
if (addr == 0) { /* non-data command */
cp->lastcmd = cp->cmd;
cp->cmd = 0;
cp->count = 0;
if(cp->status & Serr)
cp->error = inb(cp->pbase+Perror);
wakeup(&cp->r);
break;
}
loop = 0;
while(((cp->status = inb(cp->pbase+Pstatus)) & Sdrq) == 0)
if(++loop > Maxloop) {
DPRINT("cmd=%lux status=%lux error=%lux\n",
cp->cmd, inb(cp->pbase+Pstatus),inb(cp->pbase+Perror));
/*
* No data for cmd, probably a user level error
* no Allocation length set or similar.
*/
cp->lastcmd = cp->cmd;
cp->cmd = 0;
cp->count = 0;
if(cp->status & Serr)
cp->error = inb(cp->pbase+Perror);
wakeup(&cp->r);
break;
}
lastcount = (count = inb(cp->pbase+Pcyllsb) | inb(cp->pbase+Pcylmsb) << 8);
if (count > Maxxfer)
count = Maxxfer;
inss(cp->pbase+Pdata, addr, count/2);
cp->count = count;
cp->lastcmd = cp->cmd;
cp->cmd = Cpktcmd2;
break;
case Cpktcmd2:
DPRINT("pkt2 last count %d\n", lastcount);
cp->lastcmd = cp->cmd;
cp->cmd = 0;
if(cp->status & Serr)
cp->error = inb(cp->pbase+Perror);
wakeup(&cp->r);
break;
case Cidentd:
loop = 0;
while((cp->status & (Serr|Sdrq)) == 0){
if(++loop > Maxloop) {
DPRINT("cmd=%lux status=%lux\n",
cp->cmd, inb(cp->pbase+Pstatus));
panic("ataintr: read/ident");
}
cp->status = inb(cp->pbase+Pstatus);
}
if(cp->status & Serr){
cp->lastcmd = cp->cmd;
cp->cmd = 0;
cp->error = inb(cp->pbase+Perror);
wakeup(&cp->r);
break;
}
addr = cp->buf;
if(addr){
addr += cp->sofar*dp->bytes;
inss(cp->pbase+Pdata, addr, dp->bytes/2);
}
cp->sofar++;
if(cp->sofar > cp->nsecs)
print("ataintr %d %d\n", cp->sofar, cp->nsecs);
if(cp->sofar >= cp->nsecs){
cp->lastcmd = cp->cmd;
if (cp->cmd == Cread)
cp->cmd = 0;
else
cp->cmd = Cident2;
wakeup(&cp->r);
}
break;
case Cident2:
cp->lastcmd = cp->cmd;
cp->cmd = 0;
break;
default:
count = inb(cp->pbase+Pcyllsb) | inb(cp->pbase+Pcylmsb);
print("count=%.2ux\n",count);
count = inb(cp->pbase+Psector);
print("sector=%.2ux\n",count);
print("weird disk interrupt, cmd=%.2ux, lastcmd= %.2ux status=%.2ux\n",
cp->cmd, cp->lastcmd, cp->status);
break;
}
iunlock(&cp->reglock);
}
next reply other threads:[~1995-08-25 18:50 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
1995-08-25 18:50 beto [this message]
1995-11-13 4:55 Scott
1995-11-13 5:12 Scott
1995-11-13 22:10 beto
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=19950825185039.ZjsctKhUnxW1z43FqN2VZx_UyT7nSFZfp8l4GR01bsk@z \
--to=beto@plan9.cs.su.oz.au \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).