xref: /plan9-contrib/sys/src/libdisk/scsi.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
1 #include <u.h>
2 #include <libc.h>
3 #include <disk.h>
4 
5 int scsiverbose;
6 
7 #define codefile "/sys/lib/scsicodes"
8 
9 static char *codes;
10 static void
11 getcodes(void)
12 {
13 	Dir d;
14 	int n, fd;
15 
16 	if(codes != nil)
17 		return;
18 
19 	if(dirstat(codefile, &d) < 0 || (fd = open(codefile, OREAD)) < 0)
20 		return;
21 
22 	codes = emalloc(d.length+1+1);
23 	codes[0] = '\n';	/* for searches */
24 	n = readn(fd, codes+1, d.length);
25 	close(fd);
26 
27 	if(n < 0) {
28 		free(codes);
29 		codes = nil;
30 		return;
31 	}
32 	codes[n] = '\0';
33 }
34 
35 char*
36 scsierror(int asc, int ascq)
37 {
38 	char *p, *q;
39 	static char search[32];
40 	static char buf[128];
41 
42 	getcodes();
43 
44 	if(codes) {
45 		sprint(search, "\n%.2ux%.2ux ", asc, ascq);
46 		if(p = strstr(codes, search)) {
47 			p += 6;
48 			if((q = strchr(p, '\n')) == nil)
49 				q = p+strlen(p);
50 			snprint(buf, sizeof buf, "%.*s", (int)(q-p), p);
51 			return buf;
52 		}
53 
54 		sprint(search, "\n%.2ux00", asc);
55 		if(p = strstr(codes, search)) {
56 			p += 6;
57 			if((q = strchr(p, '\n')) == nil)
58 				q = p+strlen(p);
59 			snprint(buf, sizeof buf, "(ascq #%.2ux) %.*s", ascq, (int)(q-p), p);
60 			return buf;
61 		}
62 	}
63 
64 	sprint(buf, "scsi #%.2ux %.2ux", asc, ascq);
65 	return buf;
66 }
67 
68 
69 int
70 scsicmd(Scsi *s, uchar *cmd, int ccount, void *data, int dcount, int io)
71 {
72 	uchar resp[16];
73 	int n;
74 	long status;
75 
76 	if(write(s->rawfd, cmd, ccount) != ccount) {
77 		werrstr("cmd write: %r");
78 		return -1;
79 	}
80 
81 	switch(io){
82 	case Sread:
83 		n = read(s->rawfd, data, dcount);
84 		if(n < 0 && scsiverbose)
85 			fprint(2, "dat read: %r: cmd 0x%2.2uX\n", cmd[0]);
86 		break;
87 	case Swrite:
88 		n = write(s->rawfd, data, dcount);
89 		if(n != dcount && scsiverbose)
90 			fprint(2, "dat write: %r: cmd 0x%2.2uX\n", cmd[0]);
91 		break;
92 	default:
93 	case Snone:
94 		n = write(s->rawfd, resp, 0);
95 		if(n != 0 && scsiverbose)
96 			fprint(2, "none write: %r: cmd 0x%2.2uX\n", cmd[0]);
97 		break;
98 	}
99 
100 	memset(resp, 0, sizeof(resp));
101 	if(read(s->rawfd, resp, sizeof(resp)) < 0) {
102 		werrstr("resp read: %r\n");
103 		return -1;
104 	}
105 
106 	resp[sizeof(resp)-1] = '\0';
107 	status = atoi((char*)resp);
108 	if(status == 0)
109 		return n;
110 
111 	werrstr("cmd %2.2uX: status %luX dcount %d n %d", cmd[0], status, dcount, n);
112 	return -1;
113 }
114 
115 int
116 scsiready(Scsi *s)
117 {
118 	uchar cmd[6], resp[16];
119 	int status, i;
120 
121 	for(i=0; i<3; i++) {
122 		memset(cmd, 0, sizeof(cmd));
123 		cmd[0] = 0x00;	/* unit ready */
124 		if(write(s->rawfd, cmd, sizeof(cmd)) != sizeof(cmd)) {
125 			if(scsiverbose)
126 				fprint(2, "ur cmd write: %r\n");
127 			goto bad;
128 		}
129 		write(s->rawfd, resp, 0);
130 		if(read(s->rawfd, resp, sizeof(resp)) < 0) {
131 			if(scsiverbose)
132 				fprint(2, "ur resp read: %r\n");
133 			goto bad;
134 		}
135 		resp[sizeof(resp)-1] = '\0';
136 		status = atoi((char*)resp);
137 		if(status == 0 || status == 0x02)
138 			return 0;
139 		if(scsiverbose)
140 			fprint(2, "target: bad status: %x\n", status);
141 	bad:;
142 	}
143 	return -1;
144 }
145 
146 int
147 scsi(Scsi *s, uchar *cmd, int ccount, void *v, int dcount, int io)
148 {
149 	uchar req[6], sense[255], *data;
150 	int tries, code, key, n;
151 	char *p;
152 
153 	data = v;
154 	SET(key, code);
155 	for(tries=0; tries<2; tries++) {
156 		n = scsicmd(s, cmd, ccount, data, dcount, io);
157 		if(n >= 0)
158 			return n;
159 
160 		/*
161 		 * request sense
162 		 */
163 		memset(req, 0, sizeof(req));
164 		req[0] = 0x03;
165 		req[4] = sizeof(sense);
166 		memset(sense, 0xFF, sizeof(sense));
167 		if((n=scsicmd(s, req, sizeof(req), sense, sizeof(sense), Sread)) < 14)
168 			if(scsiverbose)
169 				fprint(2, "reqsense scsicmd %d: %r\n", n);
170 
171 		if(scsiready(s) < 0)
172 			if(scsiverbose)
173 				fprint(2, "unit not ready\n");
174 
175 		key = sense[2];
176 		code = sense[12];
177 		if(code == 0x17 || code == 0x18)	/* recovered errors */
178 			return dcount;
179 		if(code == 0x28 && cmd[0] == 0x43) {	/* get info and media changed */
180 			s->nchange++;
181 			s->changetime = time(0);
182 			continue;
183 		}
184 	}
185 
186 	/* drive not ready, or medium not present */
187 	if(cmd[0] == 0x43 && key == 2 && (code == 0x3a || code == 0x04)) {
188 		s->changetime = 0;
189 		return -1;
190 	}
191 
192 	if(cmd[0] == 0x43 && key == 5 && code == 0x24)	/* blank media */
193 		return -1;
194 
195 	p = scsierror(code, sense[13]);
196 
197 	werrstr("cmd #%.2ux: %s", cmd[0], p);
198 
199 	if(scsiverbose)
200 		fprint(2, "scsi cmd #%.2ux: %.2ux %.2ux %.2ux: %s\n", cmd[0], key, code, sense[13], p);
201 
202 //	if(key == 0)
203 //		return dcount;
204 	return -1;
205 }
206 
207 Scsi*
208 openscsi(char *dev)
209 {
210 	Scsi *s;
211 	int rawfd, ctlfd, l, n;
212 	char *name, *p, buf[512];
213 
214 	l = strlen(dev)+1+3+1;
215 	name = emalloc(l);
216 
217 	snprint(name, l, "%s/raw", dev);
218 	if((rawfd = open(name, ORDWR)) < 0)
219 		return nil;
220 
221 	snprint(name, l, "%s/ctl", dev);
222 	if((ctlfd = open(name, ORDWR)) < 0) {
223 	Error:
224 		close(rawfd);
225 		return nil;
226 	}
227 
228 	n = readn(ctlfd, buf, sizeof buf);
229 	close(ctlfd);
230 	if(n <= 0)
231 		goto Error;
232 
233 	if(strncmp(buf, "inquiry ", 8) != 0 || (p = strchr(buf, '\n')) == nil)
234 		goto Error;
235 	*p = '\0';
236 
237 	if((p = strdup(buf+8)) == nil)
238 		goto Error;
239 
240 	s = emalloc(sizeof(*s));
241 	s->rawfd = rawfd;
242 	s->inquire = p;
243 	s->changetime = time(0);
244 
245 	if(scsiready(s) < 0){
246 		free(p);
247 		goto Error;
248 	}
249 
250 	return s;
251 }
252