Commit | Line | Data |
---|---|---|
60f56dfc KM |
1 | /* |
2 | * Copyright (c) 1990 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * This code is derived from software contributed to Berkeley by | |
6 | * Van Jacobson of Lawrence Berkeley Laboratory. | |
7 | * | |
8 | * %sccs.include.redist.c% | |
9 | * | |
dc153861 | 10 | * @(#)sd.c 7.15 (Berkeley) %G% |
60f56dfc KM |
11 | */ |
12 | ||
13 | /* | |
14 | * SCSI CCS (Command Command Set) disk driver. | |
15 | */ | |
16 | #include "sd.h" | |
17 | #if NSD > 0 | |
18 | ||
19 | #ifndef lint | |
9acfa6cd | 20 | static char rcsid[] = "$Header: /usr/src/sys/hp300/dev/RCS/sd.c,v 1.2 92/04/10 20:48:35 mike Exp $"; |
60f56dfc KM |
21 | #endif |
22 | ||
b28b3a13 KB |
23 | #include "sys/param.h" |
24 | #include "sys/systm.h" | |
25 | #include "sys/buf.h" | |
b28b3a13 KB |
26 | #include "sys/dkstat.h" |
27 | #include "sys/disklabel.h" | |
b28b3a13 | 28 | #include "sys/malloc.h" |
b28b3a13 | 29 | #include "sys/proc.h" |
dc153861 | 30 | #include "sys/ioctl.h" |
60f56dfc | 31 | |
9acfa6cd | 32 | #include "hp/dev/device.h" |
88f29710 | 33 | #include "scsireg.h" |
b28b3a13 | 34 | #include "vm/vm_param.h" |
88f29710 | 35 | #include "vm/lock.h" |
b28b3a13 | 36 | #include "vm/vm_prot.h" |
e4fc9b79 | 37 | #include "vm/pmap.h" |
22d09b27 | 38 | |
60f56dfc KM |
39 | extern int scsi_test_unit_rdy(); |
40 | extern int scsi_request_sense(); | |
41 | extern int scsi_inquiry(); | |
42 | extern int scsi_read_capacity(); | |
43 | extern int scsi_tt_write(); | |
44 | extern int scsireq(); | |
45 | extern int scsiustart(); | |
46 | extern int scsigo(); | |
47 | extern void scsifree(); | |
48 | extern void scsireset(); | |
98d7aa4d | 49 | extern void scsi_delay(); |
60f56dfc | 50 | |
60f56dfc | 51 | extern void disksort(); |
60f56dfc KM |
52 | extern void biodone(); |
53 | extern int physio(); | |
54 | extern void TBIS(); | |
55 | ||
56 | int sdinit(); | |
57 | void sdstrategy(), sdstart(), sdustart(), sdgo(), sdintr(); | |
58 | ||
59 | struct driver sddriver = { | |
60 | sdinit, "sd", (int (*)())sdstart, (int (*)())sdgo, (int (*)())sdintr, | |
61 | }; | |
62 | ||
63 | struct size { | |
64 | u_long strtblk; | |
65 | u_long endblk; | |
66 | int nblocks; | |
67 | }; | |
68 | ||
69 | struct sdinfo { | |
70 | struct size part[8]; | |
71 | }; | |
72 | ||
73 | /* | |
74 | * since the SCSI standard tends to hide the disk structure, we define | |
75 | * partitions in terms of DEV_BSIZE blocks. The default partition table | |
76 | * (for an unlabeled disk) reserves 512K for a boot area, has an 8 meg | |
77 | * root and 32 meg of swap. The rest of the space on the drive goes in | |
78 | * the G partition. As usual, the C partition covers the entire disk | |
79 | * (including the boot area). | |
80 | */ | |
81 | struct sdinfo sddefaultpart = { | |
82 | 1024, 17408, 16384 , /* A */ | |
83 | 17408, 82944, 65536 , /* B */ | |
84 | 0, 0, 0 , /* C */ | |
85 | 17408, 115712, 98304 , /* D */ | |
86 | 115712, 218112, 102400 , /* E */ | |
87 | 218112, 0, 0 , /* F */ | |
88 | 82944, 0, 0 , /* G */ | |
89 | 115712, 0, 0 , /* H */ | |
90 | }; | |
91 | ||
92 | struct sd_softc { | |
93 | struct hp_device *sc_hd; | |
94 | struct devqueue sc_dq; | |
95 | int sc_format_pid; /* process using "format" mode */ | |
96 | short sc_flags; | |
97 | short sc_type; /* drive type */ | |
98 | short sc_punit; /* physical unit (scsi lun) */ | |
99 | u_short sc_bshift; /* convert device blocks to DEV_BSIZE blks */ | |
100 | u_int sc_blks; /* number of blocks on device */ | |
101 | int sc_blksize; /* device block size in bytes */ | |
102 | u_int sc_wpms; /* average xfer rate in 16 bit wds/sec. */ | |
103 | struct sdinfo sc_info; /* drive partition table & label info */ | |
104 | } sd_softc[NSD]; | |
105 | ||
106 | /* sc_flags values */ | |
107 | #define SDF_ALIVE 0x1 | |
9acfa6cd MH |
108 | #define SDF_WANTED 0x2 |
109 | #define SDF_RMEDIA 0x4 | |
60f56dfc KM |
110 | |
111 | #ifdef DEBUG | |
112 | int sddebug = 1; | |
113 | #define SDB_ERROR 0x01 | |
114 | #define SDB_PARTIAL 0x02 | |
115 | #endif | |
116 | ||
117 | struct sdstats { | |
118 | long sdresets; | |
119 | long sdtransfers; | |
120 | long sdpartials; | |
121 | } sdstats[NSD]; | |
122 | ||
123 | struct buf sdtab[NSD]; | |
60f56dfc KM |
124 | struct scsi_fmt_cdb sdcmd[NSD]; |
125 | struct scsi_fmt_sense sdsense[NSD]; | |
126 | ||
127 | static struct scsi_fmt_cdb sd_read_cmd = { 10, CMD_READ_EXT }; | |
128 | static struct scsi_fmt_cdb sd_write_cmd = { 10, CMD_WRITE_EXT }; | |
129 | ||
88f29710 | 130 | #define sdunit(x) (minor(x) >> 3) |
60f56dfc KM |
131 | #define sdpart(x) (minor(x) & 0x7) |
132 | #define sdpunit(x) ((x) & 7) | |
133 | #define b_cylin b_resid | |
22d09b27 | 134 | |
60f56dfc KM |
135 | #define SDRETRY 2 |
136 | ||
137 | /* | |
138 | * Table of scsi commands users are allowed to access via "format" | |
139 | * mode. 0 means not legal. 1 means "immediate" (doesn't need dma). | |
140 | * -1 means needs dma and/or wait for intr. | |
141 | */ | |
142 | static char legal_cmds[256] = { | |
143 | /***** 0 1 2 3 4 5 6 7 8 9 A B C D E F */ | |
144 | /*00*/ 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
145 | /*10*/ 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, | |
146 | /*20*/ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
147 | /*30*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
148 | /*40*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
149 | /*50*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
150 | /*60*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
151 | /*70*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
152 | /*80*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
153 | /*90*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
154 | /*a0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
155 | /*b0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
156 | /*c0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
157 | /*d0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
158 | /*e0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
159 | /*f0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
160 | }; | |
161 | ||
162 | static struct scsi_inquiry inqbuf; | |
163 | static struct scsi_fmt_cdb inq = { | |
164 | 6, | |
165 | CMD_INQUIRY, 0, 0, 0, sizeof(inqbuf), 0 | |
166 | }; | |
167 | ||
168 | static u_char capbuf[8]; | |
169 | struct scsi_fmt_cdb cap = { | |
170 | 10, | |
171 | CMD_READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0 | |
172 | }; | |
173 | ||
174 | static int | |
175 | sdident(sc, hd) | |
176 | struct sd_softc *sc; | |
177 | struct hp_device *hd; | |
178 | { | |
179 | int unit; | |
180 | register int ctlr, slave; | |
181 | register int i; | |
182 | register int tries = 10; | |
98d7aa4d | 183 | char idstr[32]; |
22d09b27 | 184 | int ismo = 0; |
60f56dfc KM |
185 | |
186 | ctlr = hd->hp_ctlr; | |
187 | slave = hd->hp_slave; | |
188 | unit = sc->sc_punit; | |
98d7aa4d | 189 | scsi_delay(-1); |
60f56dfc KM |
190 | |
191 | /* | |
192 | * See if unit exists and is a disk then read block size & nblocks. | |
193 | */ | |
194 | while ((i = scsi_test_unit_rdy(ctlr, slave, unit)) != 0) { | |
22d09b27 KM |
195 | if (i == -1 || --tries < 0) { |
196 | if (ismo) | |
197 | break; | |
60f56dfc | 198 | /* doesn't exist or not a CCS device */ |
98d7aa4d | 199 | goto failed; |
22d09b27 | 200 | } |
60f56dfc KM |
201 | if (i == STS_CHECKCOND) { |
202 | u_char sensebuf[128]; | |
203 | struct scsi_xsense *sp = (struct scsi_xsense *)sensebuf; | |
204 | ||
205 | scsi_request_sense(ctlr, slave, unit, sensebuf, | |
206 | sizeof(sensebuf)); | |
22d09b27 KM |
207 | if (sp->class == 7) |
208 | switch (sp->key) { | |
209 | /* not ready -- might be MO with no media */ | |
210 | case 2: | |
211 | if (sp->len == 12 && | |
212 | sensebuf[12] == 10) /* XXX */ | |
213 | ismo = 1; | |
214 | break; | |
60f56dfc | 215 | /* drive doing an RTZ -- give it a while */ |
22d09b27 KM |
216 | case 6: |
217 | DELAY(1000000); | |
218 | break; | |
219 | default: | |
220 | break; | |
221 | } | |
60f56dfc KM |
222 | } |
223 | DELAY(1000); | |
224 | } | |
22d09b27 KM |
225 | /* |
226 | * Find out about device | |
227 | */ | |
228 | if (scsi_immed_command(ctlr, slave, unit, &inq, | |
229 | (u_char *)&inqbuf, sizeof(inqbuf), B_READ)) | |
98d7aa4d | 230 | goto failed; |
60f56dfc KM |
231 | switch (inqbuf.type) { |
232 | case 0: /* disk */ | |
233 | case 4: /* WORM */ | |
234 | case 5: /* CD-ROM */ | |
235 | case 7: /* Magneto-optical */ | |
236 | break; | |
237 | default: /* not a disk */ | |
98d7aa4d | 238 | goto failed; |
60f56dfc | 239 | } |
22d09b27 | 240 | /* |
98d7aa4d | 241 | * Get a usable id string |
22d09b27 | 242 | */ |
98d7aa4d MH |
243 | if (inqbuf.version != 1) { |
244 | bcopy("UNKNOWN", &idstr[0], 8); | |
245 | bcopy("DRIVE TYPE", &idstr[8], 11); | |
246 | } else { | |
247 | bcopy((caddr_t)&inqbuf.vendor_id, (caddr_t)idstr, 28); | |
248 | for (i = 27; i > 23; --i) | |
249 | if (idstr[i] != ' ') | |
250 | break; | |
251 | idstr[i+1] = 0; | |
252 | for (i = 23; i > 7; --i) | |
253 | if (idstr[i] != ' ') | |
254 | break; | |
255 | idstr[i+1] = 0; | |
256 | for (i = 7; i >= 0; --i) | |
257 | if (idstr[i] != ' ') | |
258 | break; | |
259 | idstr[i+1] = 0; | |
22d09b27 KM |
260 | } |
261 | i = scsi_immed_command(ctlr, slave, unit, &cap, | |
262 | (u_char *)&capbuf, sizeof(capbuf), B_READ); | |
263 | if (i) { | |
98d7aa4d MH |
264 | if (i != STS_CHECKCOND || |
265 | bcmp(&idstr[0], "HP", 3) || | |
266 | bcmp(&idstr[8], "S6300.650A", 11)) | |
267 | goto failed; | |
22d09b27 | 268 | /* XXX unformatted or non-existant MO media; fake it */ |
98d7aa4d MH |
269 | sc->sc_blks = 318664; |
270 | sc->sc_blksize = 1024; | |
22d09b27 KM |
271 | } else { |
272 | sc->sc_blks = *(u_int *)&capbuf[0]; | |
273 | sc->sc_blksize = *(int *)&capbuf[4]; | |
274 | } | |
275 | /* return value of read capacity is last valid block number */ | |
276 | sc->sc_blks++; | |
60f56dfc KM |
277 | |
278 | if (inqbuf.version != 1) | |
279 | printf("sd%d: type 0x%x, qual 0x%x, ver %d", hd->hp_unit, | |
280 | inqbuf.type, inqbuf.qual, inqbuf.version); | |
98d7aa4d | 281 | else |
60f56dfc KM |
282 | printf("sd%d: %s %s rev %s", hd->hp_unit, idstr, &idstr[8], |
283 | &idstr[24]); | |
60f56dfc | 284 | printf(", %d %d byte blocks\n", sc->sc_blks, sc->sc_blksize); |
9acfa6cd MH |
285 | if (inqbuf.qual & 0x80) |
286 | sc->sc_flags |= SDF_RMEDIA; | |
60f56dfc KM |
287 | if (sc->sc_blksize != DEV_BSIZE) { |
288 | if (sc->sc_blksize < DEV_BSIZE) { | |
289 | printf("sd%d: need %d byte blocks - drive ignored\n", | |
290 | unit, DEV_BSIZE); | |
98d7aa4d | 291 | goto failed; |
60f56dfc KM |
292 | } |
293 | for (i = sc->sc_blksize; i > DEV_BSIZE; i >>= 1) | |
294 | ++sc->sc_bshift; | |
295 | sc->sc_blks <<= sc->sc_bshift; | |
296 | } | |
297 | sc->sc_wpms = 32 * (60 * DEV_BSIZE / 2); /* XXX */ | |
98d7aa4d | 298 | scsi_delay(0); |
60f56dfc | 299 | return(inqbuf.type); |
98d7aa4d MH |
300 | failed: |
301 | scsi_delay(0); | |
302 | return(-1); | |
60f56dfc KM |
303 | } |
304 | ||
305 | int | |
306 | sdinit(hd) | |
307 | register struct hp_device *hd; | |
308 | { | |
309 | register struct sd_softc *sc = &sd_softc[hd->hp_unit]; | |
310 | ||
311 | sc->sc_hd = hd; | |
9acfa6cd | 312 | sc->sc_flags = 0; |
60f56dfc KM |
313 | sc->sc_punit = sdpunit(hd->hp_flags); |
314 | sc->sc_type = sdident(sc, hd); | |
315 | if (sc->sc_type < 0) | |
316 | return(0); | |
317 | sc->sc_dq.dq_ctlr = hd->hp_ctlr; | |
318 | sc->sc_dq.dq_unit = hd->hp_unit; | |
319 | sc->sc_dq.dq_slave = hd->hp_slave; | |
320 | sc->sc_dq.dq_driver = &sddriver; | |
321 | ||
322 | /* | |
323 | * If we don't have a disk label, build a default partition | |
324 | * table with 'standard' size root & swap and everything else | |
325 | * in the G partition. | |
326 | */ | |
327 | sc->sc_info = sddefaultpart; | |
328 | /* C gets everything */ | |
329 | sc->sc_info.part[2].nblocks = sc->sc_blks; | |
330 | sc->sc_info.part[2].endblk = sc->sc_blks; | |
331 | /* G gets from end of B to end of disk */ | |
332 | sc->sc_info.part[6].nblocks = sc->sc_blks - sc->sc_info.part[1].endblk; | |
333 | sc->sc_info.part[6].endblk = sc->sc_blks; | |
334 | /* | |
335 | * We also define the D, E and F paritions as an alternative to | |
336 | * B and G. D is 48Mb, starts after A and is intended for swapping. | |
337 | * E is 50Mb, starts after D and is intended for /usr. F starts | |
338 | * after E and is what ever is left. | |
339 | */ | |
340 | if (sc->sc_blks >= sc->sc_info.part[4].endblk) { | |
341 | sc->sc_info.part[5].nblocks = | |
342 | sc->sc_blks - sc->sc_info.part[4].endblk; | |
343 | sc->sc_info.part[5].endblk = sc->sc_blks; | |
344 | } else { | |
345 | sc->sc_info.part[5].strtblk = 0; | |
346 | sc->sc_info.part[3] = sc->sc_info.part[5]; | |
347 | sc->sc_info.part[4] = sc->sc_info.part[5]; | |
348 | } | |
349 | /* | |
350 | * H is a single partition alternative to E and F. | |
351 | */ | |
352 | if (sc->sc_blks >= sc->sc_info.part[3].endblk) { | |
353 | sc->sc_info.part[7].nblocks = | |
354 | sc->sc_blks - sc->sc_info.part[3].endblk; | |
355 | sc->sc_info.part[7].endblk = sc->sc_blks; | |
356 | } else { | |
357 | sc->sc_info.part[7].strtblk = 0; | |
358 | } | |
359 | ||
9acfa6cd | 360 | sc->sc_flags |= SDF_ALIVE; |
60f56dfc KM |
361 | return(1); |
362 | } | |
363 | ||
364 | void | |
365 | sdreset(sc, hd) | |
366 | register struct sd_softc *sc; | |
367 | register struct hp_device *hd; | |
368 | { | |
369 | sdstats[hd->hp_unit].sdresets++; | |
370 | } | |
371 | ||
372 | int | |
88f29710 | 373 | sdopen(dev, flags, mode, p) |
60f56dfc | 374 | dev_t dev; |
88f29710 MK |
375 | int flags, mode; |
376 | struct proc *p; | |
60f56dfc KM |
377 | { |
378 | register int unit = sdunit(dev); | |
379 | register struct sd_softc *sc = &sd_softc[unit]; | |
380 | ||
381 | if (unit >= NSD) | |
382 | return(ENXIO); | |
88f29710 | 383 | if ((sc->sc_flags & SDF_ALIVE) == 0 && suser(p->p_ucred, &p->p_acflag)) |
60f56dfc KM |
384 | return(ENXIO); |
385 | ||
386 | if (sc->sc_hd->hp_dk >= 0) | |
387 | dk_wpms[sc->sc_hd->hp_dk] = sc->sc_wpms; | |
388 | return(0); | |
389 | } | |
390 | ||
9acfa6cd MH |
391 | int |
392 | sdclose(dev, flag, mode, p) | |
393 | dev_t dev; | |
394 | int flag, mode; | |
395 | struct proc *p; | |
396 | { | |
397 | int unit = sdunit(dev); | |
398 | register struct sd_softc *sc = &sd_softc[unit]; | |
399 | int s; | |
400 | ||
401 | /* | |
402 | * XXX we should really do this for all drives. | |
403 | */ | |
404 | if (sc->sc_flags & SDF_RMEDIA) { | |
405 | s = splbio(); | |
406 | while (sdtab[unit].b_active) { | |
407 | sc->sc_flags |= SDF_WANTED; | |
408 | sleep((caddr_t)&sdtab[unit], PRIBIO); | |
409 | } | |
410 | splx(s); | |
411 | } | |
412 | sc->sc_format_pid = 0; | |
413 | } | |
414 | ||
60f56dfc KM |
415 | /* |
416 | * This routine is called for partial block transfers and non-aligned | |
417 | * transfers (the latter only being possible on devices with a block size | |
418 | * larger than DEV_BSIZE). The operation is performed in three steps | |
419 | * using a locally allocated buffer: | |
420 | * 1. transfer any initial partial block | |
421 | * 2. transfer full blocks | |
422 | * 3. transfer any final partial block | |
423 | */ | |
424 | static void | |
425 | sdlblkstrat(bp, bsize) | |
426 | register struct buf *bp; | |
427 | register int bsize; | |
428 | { | |
429 | register struct buf *cbp = (struct buf *)malloc(sizeof(struct buf), | |
430 | M_DEVBUF, M_WAITOK); | |
431 | caddr_t cbuf = (caddr_t)malloc(bsize, M_DEVBUF, M_WAITOK); | |
432 | register int bn, resid; | |
433 | register caddr_t addr; | |
434 | ||
435 | bzero((caddr_t)cbp, sizeof(*cbp)); | |
88f29710 | 436 | cbp->b_proc = curproc; /* XXX */ |
60f56dfc KM |
437 | cbp->b_dev = bp->b_dev; |
438 | bn = bp->b_blkno; | |
439 | resid = bp->b_bcount; | |
440 | addr = bp->b_un.b_addr; | |
441 | #ifdef DEBUG | |
442 | if (sddebug & SDB_PARTIAL) | |
443 | printf("sdlblkstrat: bp %x flags %x bn %x resid %x addr %x\n", | |
444 | bp, bp->b_flags, bn, resid, addr); | |
445 | #endif | |
446 | ||
447 | while (resid > 0) { | |
448 | register int boff = dbtob(bn) & (bsize - 1); | |
449 | register int count; | |
450 | ||
451 | if (boff || resid < bsize) { | |
452 | sdstats[sdunit(bp->b_dev)].sdpartials++; | |
479c0df7 | 453 | count = min(resid, bsize - boff); |
60f56dfc KM |
454 | cbp->b_flags = B_BUSY | B_PHYS | B_READ; |
455 | cbp->b_blkno = bn - btodb(boff); | |
456 | cbp->b_un.b_addr = cbuf; | |
457 | cbp->b_bcount = bsize; | |
458 | #ifdef DEBUG | |
459 | if (sddebug & SDB_PARTIAL) | |
460 | printf(" readahead: bn %x cnt %x off %x addr %x\n", | |
461 | cbp->b_blkno, count, boff, addr); | |
462 | #endif | |
463 | sdstrategy(cbp); | |
464 | biowait(cbp); | |
465 | if (cbp->b_flags & B_ERROR) { | |
466 | bp->b_flags |= B_ERROR; | |
467 | bp->b_error = cbp->b_error; | |
468 | break; | |
469 | } | |
470 | if (bp->b_flags & B_READ) { | |
471 | bcopy(&cbuf[boff], addr, count); | |
472 | goto done; | |
473 | } | |
474 | bcopy(addr, &cbuf[boff], count); | |
475 | #ifdef DEBUG | |
476 | if (sddebug & SDB_PARTIAL) | |
477 | printf(" writeback: bn %x cnt %x off %x addr %x\n", | |
478 | cbp->b_blkno, count, boff, addr); | |
479 | #endif | |
480 | } else { | |
481 | count = resid & ~(bsize - 1); | |
482 | cbp->b_blkno = bn; | |
483 | cbp->b_un.b_addr = addr; | |
484 | cbp->b_bcount = count; | |
485 | #ifdef DEBUG | |
486 | if (sddebug & SDB_PARTIAL) | |
487 | printf(" fulltrans: bn %x cnt %x addr %x\n", | |
488 | cbp->b_blkno, count, addr); | |
489 | #endif | |
490 | } | |
491 | cbp->b_flags = B_BUSY | B_PHYS | (bp->b_flags & B_READ); | |
492 | sdstrategy(cbp); | |
493 | biowait(cbp); | |
494 | if (cbp->b_flags & B_ERROR) { | |
495 | bp->b_flags |= B_ERROR; | |
496 | bp->b_error = cbp->b_error; | |
497 | break; | |
498 | } | |
499 | done: | |
500 | bn += btodb(count); | |
501 | resid -= count; | |
502 | addr += count; | |
503 | #ifdef DEBUG | |
504 | if (sddebug & SDB_PARTIAL) | |
505 | printf(" done: bn %x resid %x addr %x\n", | |
506 | bn, resid, addr); | |
507 | #endif | |
508 | } | |
509 | free(cbuf, M_DEVBUF); | |
510 | free(cbp, M_DEVBUF); | |
511 | } | |
512 | ||
513 | void | |
514 | sdstrategy(bp) | |
515 | register struct buf *bp; | |
516 | { | |
60f56dfc | 517 | register int unit = sdunit(bp->b_dev); |
60f56dfc | 518 | register struct sd_softc *sc = &sd_softc[unit]; |
22d09b27 | 519 | register struct size *pinfo = &sc->sc_info.part[sdpart(bp->b_dev)]; |
60f56dfc | 520 | register struct buf *dp = &sdtab[unit]; |
22d09b27 KM |
521 | register daddr_t bn; |
522 | register int sz, s; | |
60f56dfc KM |
523 | |
524 | if (sc->sc_format_pid) { | |
88f29710 | 525 | if (sc->sc_format_pid != curproc->p_pid) { /* XXX */ |
60f56dfc | 526 | bp->b_error = EPERM; |
22d09b27 KM |
527 | bp->b_flags |= B_ERROR; |
528 | goto done; | |
60f56dfc KM |
529 | } |
530 | bp->b_cylin = 0; | |
531 | } else { | |
532 | bn = bp->b_blkno; | |
22d09b27 KM |
533 | sz = howmany(bp->b_bcount, DEV_BSIZE); |
534 | if (bn < 0 || bn + sz > pinfo->nblocks) { | |
535 | sz = pinfo->nblocks - bn; | |
536 | if (sz == 0) { | |
60f56dfc KM |
537 | bp->b_resid = bp->b_bcount; |
538 | goto done; | |
539 | } | |
22d09b27 KM |
540 | if (sz < 0) { |
541 | bp->b_error = EINVAL; | |
542 | bp->b_flags |= B_ERROR; | |
543 | goto done; | |
544 | } | |
545 | bp->b_bcount = dbtob(sz); | |
60f56dfc KM |
546 | } |
547 | /* | |
548 | * Non-aligned or partial-block transfers handled specially. | |
549 | */ | |
550 | s = sc->sc_blksize - 1; | |
551 | if ((dbtob(bn) & s) || (bp->b_bcount & s)) { | |
552 | sdlblkstrat(bp, sc->sc_blksize); | |
553 | goto done; | |
554 | } | |
22d09b27 | 555 | bp->b_cylin = (bn + pinfo->strtblk) >> sc->sc_bshift; |
60f56dfc KM |
556 | } |
557 | s = splbio(); | |
558 | disksort(dp, bp); | |
559 | if (dp->b_active == 0) { | |
560 | dp->b_active = 1; | |
561 | sdustart(unit); | |
562 | } | |
563 | splx(s); | |
564 | return; | |
60f56dfc | 565 | done: |
22d09b27 | 566 | biodone(bp); |
60f56dfc KM |
567 | } |
568 | ||
569 | void | |
570 | sdustart(unit) | |
571 | register int unit; | |
572 | { | |
573 | if (scsireq(&sd_softc[unit].sc_dq)) | |
574 | sdstart(unit); | |
575 | } | |
576 | ||
761052c0 MK |
577 | /* |
578 | * Return: | |
579 | * 0 if not really an error | |
580 | * <0 if we should do a retry | |
581 | * >0 if a fatal error | |
582 | */ | |
22d09b27 | 583 | static int |
60f56dfc KM |
584 | sderror(unit, sc, hp, stat) |
585 | int unit, stat; | |
586 | register struct sd_softc *sc; | |
587 | register struct hp_device *hp; | |
588 | { | |
761052c0 | 589 | int cond = 1; |
22d09b27 | 590 | |
60f56dfc KM |
591 | sdsense[unit].status = stat; |
592 | if (stat & STS_CHECKCOND) { | |
593 | struct scsi_xsense *sp; | |
594 | ||
595 | scsi_request_sense(hp->hp_ctlr, hp->hp_slave, | |
596 | sc->sc_punit, sdsense[unit].sense, | |
597 | sizeof(sdsense[unit].sense)); | |
598 | sp = (struct scsi_xsense *)sdsense[unit].sense; | |
599 | printf("sd%d: scsi sense class %d, code %d", unit, | |
600 | sp->class, sp->code); | |
601 | if (sp->class == 7) { | |
602 | printf(", key %d", sp->key); | |
603 | if (sp->valid) | |
604 | printf(", blk %d", *(int *)&sp->info1); | |
761052c0 MK |
605 | switch (sp->key) { |
606 | /* no sense, try again */ | |
607 | case 0: | |
608 | cond = -1; | |
609 | break; | |
610 | /* recovered error, not a problem */ | |
611 | case 1: | |
612 | cond = 0; | |
613 | break; | |
614 | } | |
60f56dfc KM |
615 | } |
616 | printf("\n"); | |
617 | } | |
761052c0 | 618 | return(cond); |
60f56dfc KM |
619 | } |
620 | ||
621 | static void | |
622 | sdfinish(unit, sc, bp) | |
623 | int unit; | |
624 | register struct sd_softc *sc; | |
625 | register struct buf *bp; | |
626 | { | |
9acfa6cd MH |
627 | register struct buf *dp = &sdtab[unit]; |
628 | ||
629 | dp->b_errcnt = 0; | |
630 | dp->b_actf = bp->b_actf; | |
60f56dfc | 631 | bp->b_resid = 0; |
22d09b27 | 632 | biodone(bp); |
60f56dfc | 633 | scsifree(&sc->sc_dq); |
9acfa6cd | 634 | if (dp->b_actf) |
60f56dfc | 635 | sdustart(unit); |
9acfa6cd MH |
636 | else { |
637 | dp->b_active = 0; | |
638 | if (sc->sc_flags & SDF_WANTED) { | |
639 | sc->sc_flags &= ~SDF_WANTED; | |
640 | wakeup((caddr_t)dp); | |
641 | } | |
642 | } | |
60f56dfc KM |
643 | } |
644 | ||
645 | void | |
646 | sdstart(unit) | |
647 | register int unit; | |
648 | { | |
649 | register struct sd_softc *sc = &sd_softc[unit]; | |
650 | register struct hp_device *hp = sc->sc_hd; | |
651 | ||
652 | /* | |
653 | * we have the SCSI bus -- in format mode, we may or may not need dma | |
654 | * so check now. | |
655 | */ | |
656 | if (sc->sc_format_pid && legal_cmds[sdcmd[unit].cdb[0]] > 0) { | |
657 | register struct buf *bp = sdtab[unit].b_actf; | |
658 | register int sts; | |
659 | ||
660 | sts = scsi_immed_command(hp->hp_ctlr, hp->hp_slave, | |
661 | sc->sc_punit, &sdcmd[unit], | |
662 | bp->b_un.b_addr, bp->b_bcount, | |
663 | bp->b_flags & B_READ); | |
664 | sdsense[unit].status = sts; | |
665 | if (sts & 0xfe) { | |
22d09b27 | 666 | (void) sderror(unit, sc, hp, sts); |
60f56dfc KM |
667 | bp->b_flags |= B_ERROR; |
668 | bp->b_error = EIO; | |
669 | } | |
670 | sdfinish(unit, sc, bp); | |
671 | ||
672 | } else if (scsiustart(hp->hp_ctlr)) | |
673 | sdgo(unit); | |
674 | } | |
675 | ||
676 | void | |
677 | sdgo(unit) | |
678 | register int unit; | |
679 | { | |
680 | register struct sd_softc *sc = &sd_softc[unit]; | |
681 | register struct hp_device *hp = sc->sc_hd; | |
682 | register struct buf *bp = sdtab[unit].b_actf; | |
683 | register int pad; | |
684 | register struct scsi_fmt_cdb *cmd; | |
685 | ||
686 | if (sc->sc_format_pid) { | |
687 | cmd = &sdcmd[unit]; | |
688 | pad = 0; | |
689 | } else { | |
690 | cmd = bp->b_flags & B_READ? &sd_read_cmd : &sd_write_cmd; | |
691 | *(int *)(&cmd->cdb[2]) = bp->b_cylin; | |
692 | pad = howmany(bp->b_bcount, sc->sc_blksize); | |
693 | *(u_short *)(&cmd->cdb[7]) = pad; | |
694 | pad = (bp->b_bcount & (sc->sc_blksize - 1)) != 0; | |
695 | #ifdef DEBUG | |
696 | if (pad) | |
697 | printf("sd%d: partial block xfer -- %x bytes\n", | |
698 | unit, bp->b_bcount); | |
699 | #endif | |
700 | sdstats[unit].sdtransfers++; | |
701 | } | |
702 | if (scsigo(hp->hp_ctlr, hp->hp_slave, sc->sc_punit, bp, cmd, pad) == 0) { | |
703 | if (hp->hp_dk >= 0) { | |
704 | dk_busy |= 1 << hp->hp_dk; | |
705 | ++dk_seek[hp->hp_dk]; | |
706 | ++dk_xfer[hp->hp_dk]; | |
707 | dk_wds[hp->hp_dk] += bp->b_bcount >> 6; | |
708 | } | |
709 | return; | |
710 | } | |
711 | #ifdef DEBUG | |
712 | if (sddebug & SDB_ERROR) | |
713 | printf("sd%d: sdstart: %s adr %d blk %d len %d ecnt %d\n", | |
714 | unit, bp->b_flags & B_READ? "read" : "write", | |
715 | bp->b_un.b_addr, bp->b_cylin, bp->b_bcount, | |
716 | sdtab[unit].b_errcnt); | |
717 | #endif | |
718 | bp->b_flags |= B_ERROR; | |
719 | bp->b_error = EIO; | |
720 | sdfinish(unit, sc, bp); | |
721 | } | |
722 | ||
723 | void | |
724 | sdintr(unit, stat) | |
725 | register int unit; | |
726 | int stat; | |
727 | { | |
728 | register struct sd_softc *sc = &sd_softc[unit]; | |
729 | register struct buf *bp = sdtab[unit].b_actf; | |
730 | register struct hp_device *hp = sc->sc_hd; | |
761052c0 | 731 | int cond; |
60f56dfc KM |
732 | |
733 | if (bp == NULL) { | |
734 | printf("sd%d: bp == NULL\n", unit); | |
735 | return; | |
736 | } | |
737 | if (hp->hp_dk >= 0) | |
738 | dk_busy &=~ (1 << hp->hp_dk); | |
739 | if (stat) { | |
740 | #ifdef DEBUG | |
741 | if (sddebug & SDB_ERROR) | |
742 | printf("sd%d: sdintr: bad scsi status 0x%x\n", | |
743 | unit, stat); | |
744 | #endif | |
761052c0 MK |
745 | cond = sderror(unit, sc, hp, stat); |
746 | if (cond) { | |
747 | if (cond < 0 && sdtab[unit].b_errcnt++ < SDRETRY) { | |
ebe91f3c | 748 | #ifdef DEBUG |
761052c0 MK |
749 | if (sddebug & SDB_ERROR) |
750 | printf("sd%d: retry #%d\n", | |
751 | unit, sdtab[unit].b_errcnt); | |
ebe91f3c | 752 | #endif |
761052c0 MK |
753 | sdstart(unit); |
754 | return; | |
755 | } | |
756 | bp->b_flags |= B_ERROR; | |
757 | bp->b_error = EIO; | |
22d09b27 | 758 | } |
60f56dfc KM |
759 | } |
760 | sdfinish(unit, sc, bp); | |
761 | } | |
762 | ||
763 | int | |
88f29710 | 764 | sdread(dev, uio, flags) |
60f56dfc KM |
765 | dev_t dev; |
766 | struct uio *uio; | |
88f29710 | 767 | int flags; |
60f56dfc KM |
768 | { |
769 | register int unit = sdunit(dev); | |
770 | register int pid; | |
771 | ||
88f29710 MK |
772 | if ((pid = sd_softc[unit].sc_format_pid) && |
773 | pid != uio->uio_procp->p_pid) | |
60f56dfc KM |
774 | return (EPERM); |
775 | ||
88f29710 | 776 | return (physio(sdstrategy, NULL, dev, B_READ, minphys, uio)); |
60f56dfc KM |
777 | } |
778 | ||
779 | int | |
88f29710 | 780 | sdwrite(dev, uio, flags) |
60f56dfc KM |
781 | dev_t dev; |
782 | struct uio *uio; | |
88f29710 | 783 | int flags; |
60f56dfc KM |
784 | { |
785 | register int unit = sdunit(dev); | |
786 | register int pid; | |
787 | ||
88f29710 MK |
788 | if ((pid = sd_softc[unit].sc_format_pid) && |
789 | pid != uio->uio_procp->p_pid) | |
60f56dfc KM |
790 | return (EPERM); |
791 | ||
88f29710 | 792 | return (physio(sdstrategy, NULL, dev, B_WRITE, minphys, uio)); |
60f56dfc KM |
793 | } |
794 | ||
795 | int | |
88f29710 | 796 | sdioctl(dev, cmd, data, flag, p) |
60f56dfc KM |
797 | dev_t dev; |
798 | int cmd; | |
799 | caddr_t data; | |
800 | int flag; | |
88f29710 | 801 | struct proc *p; |
60f56dfc KM |
802 | { |
803 | register int unit = sdunit(dev); | |
804 | register struct sd_softc *sc = &sd_softc[unit]; | |
805 | ||
806 | switch (cmd) { | |
807 | default: | |
808 | return (EINVAL); | |
809 | ||
810 | case SDIOCSFORMAT: | |
811 | /* take this device into or out of "format" mode */ | |
88f29710 | 812 | if (suser(p->p_ucred, &p->p_acflag)) |
60f56dfc KM |
813 | return(EPERM); |
814 | ||
815 | if (*(int *)data) { | |
816 | if (sc->sc_format_pid) | |
817 | return (EPERM); | |
88f29710 | 818 | sc->sc_format_pid = p->p_pid; |
60f56dfc KM |
819 | } else |
820 | sc->sc_format_pid = 0; | |
821 | return (0); | |
822 | ||
823 | case SDIOCGFORMAT: | |
824 | /* find out who has the device in format mode */ | |
825 | *(int *)data = sc->sc_format_pid; | |
826 | return (0); | |
827 | ||
828 | case SDIOCSCSICOMMAND: | |
829 | /* | |
830 | * Save what user gave us as SCSI cdb to use with next | |
831 | * read or write to the char device. | |
832 | */ | |
88f29710 | 833 | if (sc->sc_format_pid != p->p_pid) |
60f56dfc KM |
834 | return (EPERM); |
835 | if (legal_cmds[((struct scsi_fmt_cdb *)data)->cdb[0]] == 0) | |
836 | return (EINVAL); | |
837 | bcopy(data, (caddr_t)&sdcmd[unit], sizeof(sdcmd[0])); | |
838 | return (0); | |
839 | ||
840 | case SDIOCSENSE: | |
841 | /* | |
842 | * return the SCSI sense data saved after the last | |
843 | * operation that completed with "check condition" status. | |
844 | */ | |
845 | bcopy((caddr_t)&sdsense[unit], data, sizeof(sdsense[0])); | |
846 | return (0); | |
847 | ||
848 | } | |
849 | /*NOTREACHED*/ | |
850 | } | |
851 | ||
852 | int | |
853 | sdsize(dev) | |
854 | dev_t dev; | |
855 | { | |
856 | register int unit = sdunit(dev); | |
857 | register struct sd_softc *sc = &sd_softc[unit]; | |
858 | ||
859 | if (unit >= NSD || (sc->sc_flags & SDF_ALIVE) == 0) | |
860 | return(-1); | |
861 | ||
862 | return(sc->sc_info.part[sdpart(dev)].nblocks); | |
863 | } | |
864 | ||
60f56dfc KM |
865 | /* |
866 | * Non-interrupt driven, non-dma dump routine. | |
867 | */ | |
868 | int | |
869 | sddump(dev) | |
870 | dev_t dev; | |
871 | { | |
872 | int part = sdpart(dev); | |
873 | int unit = sdunit(dev); | |
874 | register struct sd_softc *sc = &sd_softc[unit]; | |
875 | register struct hp_device *hp = sc->sc_hd; | |
876 | register daddr_t baddr; | |
877 | register int maddr; | |
878 | register int pages, i; | |
879 | int stat; | |
880 | extern int lowram; | |
881 | ||
882 | /* | |
883 | * Hmm... all vax drivers dump maxfree pages which is physmem minus | |
884 | * the message buffer. Is there a reason for not dumping the | |
885 | * message buffer? Savecore expects to read 'dumpsize' pages of | |
886 | * dump, where dumpsys() sets dumpsize to physmem! | |
887 | */ | |
888 | pages = physmem; | |
889 | ||
890 | /* is drive ok? */ | |
891 | if (unit >= NSD || (sc->sc_flags & SDF_ALIVE) == 0) | |
892 | return (ENXIO); | |
893 | /* dump parameters in range? */ | |
894 | if (dumplo < 0 || dumplo >= sc->sc_info.part[part].nblocks) | |
895 | return (EINVAL); | |
896 | if (dumplo + ctod(pages) > sc->sc_info.part[part].nblocks) | |
897 | pages = dtoc(sc->sc_info.part[part].nblocks - dumplo); | |
898 | maddr = lowram; | |
899 | baddr = dumplo + sc->sc_info.part[part].strtblk; | |
900 | /* scsi bus idle? */ | |
901 | if (!scsireq(&sc->sc_dq)) { | |
902 | scsireset(hp->hp_ctlr); | |
903 | sdreset(sc, sc->sc_hd); | |
904 | printf("[ drive %d reset ] ", unit); | |
905 | } | |
906 | for (i = 0; i < pages; i++) { | |
907 | #define NPGMB (1024*1024/NBPG) | |
908 | /* print out how many Mbs we have dumped */ | |
909 | if (i && (i % NPGMB) == 0) | |
910 | printf("%d ", i / NPGMB); | |
911 | #undef NPBMG | |
7cf88bec | 912 | pmap_enter(kernel_pmap, (vm_offset_t)vmmap, maddr, |
e0f6df7b | 913 | VM_PROT_READ, TRUE); |
60f56dfc KM |
914 | stat = scsi_tt_write(hp->hp_ctlr, hp->hp_slave, sc->sc_punit, |
915 | vmmap, NBPG, baddr, sc->sc_bshift); | |
916 | if (stat) { | |
917 | printf("sddump: scsi write error 0x%x\n", stat); | |
918 | return (EIO); | |
919 | } | |
920 | maddr += NBPG; | |
921 | baddr += ctod(1); | |
922 | } | |
923 | return (0); | |
924 | } | |
925 | #endif |