1 /* $NetBSD: sd.c,v 1.17 2023/02/12 08:25:09 tsutsui Exp $ */
2 /*
3 * Copyright (c) 1994 Rolf Grossmann
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by Rolf Grossmann.
17 * 4. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/param.h>
33 #include <sys/disklabel.h>
34 #include <sys/bootblock.h>
35 #include <dev/scsipi/scsi_spc.h>
36 #include <dev/scsipi/scsipi_all.h>
37 #include <dev/scsipi/scsi_all.h>
38 #include <dev/scsipi/scsipi_disk.h>
39 #include <lib/libsa/stand.h>
40 #include <lib/libkern/libkern.h> /* for bzero() */
41
42 #include "dmareg.h"
43 #include "scsivar.h"
44 #include "samachdep.h"
45
46 #ifdef SD_DEBUG
47 #define DPRINTF(x) printf x;
48 #else
49 #define DPRINTF(x)
50 #endif
51
52 struct sdminilabel {
53 u_short npart;
54 long offset[MAXPARTITIONS+1]; /* offset from absolute block 0! */
55 };
56
57 struct sd_softc {
58 int sc_unit;
59 int sc_lun;
60 int sc_part;
61 int sc_dev_bsize;
62 struct sdminilabel sc_pinfo;
63 };
64
65 #define NSD 7
66 #define MAXRETRIES 5 /* must be at least one */
67
68 int sdprobe(char target, char lun);
69 int sdgetinfo(struct sd_softc *ss);
70
71 int
sdprobe(char target,char lun)72 sdprobe(char target, char lun)
73 {
74 struct scsi_test_unit_ready cdb1;
75 struct scsipi_inquiry cdb2;
76 struct scsipi_inquiry_data inq;
77 int error, retries;
78 int count;
79
80 memset(&cdb1, 0, sizeof(cdb1));
81 cdb1.opcode = SCSI_TEST_UNIT_READY;
82
83 retries = 0;
84 do {
85 count = 0;
86 error = scsiicmd(target, lun, (u_char *)&cdb1, sizeof(cdb1), NULL, &count);
87 if (error == -SCSI_BUSY) {
88 register int N = 10000000; while (--N > 0);
89 }
90 } while ((error == -SCSI_CHECK || error == -SCSI_BUSY)
91 && retries++ < MAXRETRIES);
92
93 if (error)
94 return error<0 ? ENODEV : error;
95
96 memset(&cdb2, 0, sizeof(cdb2));
97 cdb2.opcode = INQUIRY;
98 cdb2.length = SCSIPI_INQUIRY_LENGTH_SCSI2;
99 count = SCSIPI_INQUIRY_LENGTH_SCSI2;
100 error = scsiicmd(target, lun, (u_char *)&cdb2, sizeof(cdb2),
101 (char *)&inq, &count);
102 if (error != 0)
103 return error<0 ? EHER : error;
104
105 if ((inq.device & SID_TYPE) != T_DIRECT
106 && (inq.device & SID_TYPE) != T_CDROM)
107 return EUNIT; /* not a disk */
108
109 DPRINTF(("booting disk %s.\n", inq.vendor));
110
111 return 0;
112 }
113
114 int
sdgetinfo(struct sd_softc * ss)115 sdgetinfo(struct sd_softc *ss)
116 {
117 struct scsipi_read_capacity_10 cdb;
118 struct scsipi_read_capacity_10_data cap;
119 struct sdminilabel *pi = &ss->sc_pinfo;
120 struct next68k_disklabel *label;
121 int error, i, blklen;
122 char io_buf[NEXT68K_LABEL_SIZE+NEXT68K_LABEL_OFFSET];
123 int count;
124 int sc_blkshift = 0;
125
126 memset(&cdb, 0, sizeof(cdb));
127 cdb.opcode = READ_CAPACITY_10;
128 count = sizeof(cap);
129 error = scsiicmd(ss->sc_unit, ss->sc_lun, (u_char *)&cdb, sizeof(cdb),
130 (char *)&cap, &count);
131 if (error != 0)
132 return error<0 ? EHER : error;
133 blklen = (cap.length[0]<<24) + (cap.length[1]<<16)
134 + (cap.length[2]<<8) + cap.length[3];
135
136 /* avoid division by zero trap even on possible xfer errors */
137 if (blklen == 0)
138 blklen = DEV_BSIZE;
139 ss->sc_dev_bsize = blklen;
140
141 ss->sc_pinfo.offset[ss->sc_part] = 0; /* read absolute sector */
142 error = sdstrategy(ss, F_READ, NEXT68K_LABEL_SECTOR,
143 NEXT68K_LABEL_SIZE+NEXT68K_LABEL_OFFSET, io_buf, (unsigned int *)&i);
144 if (error != 0) {
145 DPRINTF(("sdgetinfo: sdstrategy error %d\n", error));
146 return(ERDLAB);
147 }
148 label = (struct next68k_disklabel *)(io_buf+NEXT68K_LABEL_OFFSET);
149
150 if (!IS_DISKLABEL(label)) /* || (label->cd_flags & CD_UNINIT)!=0) */
151 return EUNLAB; /* bad magic */
152
153 /* XXX calculate checksum ... for now we rely on the magic number */
154 DPRINTF(("Disk is %s (%s, %s).\n",
155 label->cd_label,label->cd_name, label->cd_type));
156
157 while (label->cd_secsize > blklen)
158 {
159 blklen <<= 1;
160 ++sc_blkshift;
161 }
162 if (label->cd_secsize < blklen)
163 {
164 printf("bad label sectorsize (%d) or device blocksize (%d).\n",
165 label->cd_secsize, blklen>>sc_blkshift);
166 return ENXIO;
167 }
168 pi->npart = 0;
169 for(i=0; i<MAXPARTITIONS; i++) {
170 if (label->cd_partitions[i].cp_size > 0) {
171 pi->offset[pi->npart] = (label->cd_partitions[i].cp_offset
172 + label->cd_front) << sc_blkshift;
173 }
174 else
175 pi->offset[pi->npart] = -1;
176 DPRINTF (("%d: [%d]=%ld\n", i, pi->npart, pi->offset[pi->npart]));
177 pi->npart++;
178 if (pi->npart == RAW_PART)
179 pi->npart++;
180 }
181 pi->offset[RAW_PART] = -1;
182
183 return 0;
184 }
185
186 int
sdopen(struct open_file * f,...)187 sdopen(struct open_file *f, ...)
188 {
189 va_list ap;
190 int count, lun, part;
191 register struct sd_softc *ss;
192 char unit, cnt;
193 int error;
194
195 va_start(ap, f);
196 count = va_arg(ap, int);
197 lun = va_arg(ap, int);
198 part = va_arg(ap, int);
199 va_end(ap);
200
201 DPRINTF(("open: sd(%d,%d,%d)\n", count, lun, part));
202
203 if (lun >= NSD)
204 return EUNIT;
205
206 scsi_init();
207
208 for(cnt=0, unit=0; unit < NSD; unit++)
209 {
210 DPRINTF(("trying target %d lun %d.\n", unit, lun));
211 error = sdprobe(unit, lun);
212 if (error == 0)
213 {
214 if (cnt++ == count)
215 break;
216 }
217 else if (error != EUNIT)
218 return error;
219 }
220
221 if (unit >= NSD)
222 return EUNIT;
223
224 ss = alloc(sizeof(struct sd_softc));
225 ss->sc_unit = unit;
226 ss->sc_lun = lun;
227 ss->sc_part = part;
228
229 if ((error = sdgetinfo(ss)) != 0)
230 return error;
231
232 if ((unsigned char)part >= ss->sc_pinfo.npart
233 || ss->sc_pinfo.offset[(int)part] == -1)
234 return EPART;
235
236 f->f_devdata = ss;
237 return 0;
238 }
239
240 int
sdclose(struct open_file * f)241 sdclose(struct open_file *f)
242 {
243 register struct sd_softc *ss = f->f_devdata;
244
245 dealloc(ss, sizeof(struct sd_softc));
246 return 0;
247 }
248
249 int
sdstrategy(void * devdata,int rw,daddr_t dblk,size_t size,void * buf,size_t * rsize)250 sdstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
251 void *buf, size_t *rsize)
252 {
253 struct sd_softc *ss = devdata;
254 u_long blk = dblk + ss->sc_pinfo.offset[ss->sc_part];
255 struct scsipi_rw_10 cdb;
256 int error;
257
258 if (size == 0)
259 return 0;
260
261 if (rw != F_READ)
262 {
263 printf("sdstrategy: write not implemented.\n");
264 return EOPNOTSUPP;
265 }
266
267 *rsize = 0;
268 while (size > 0) {
269 u_long nblks;
270 int tsize;
271 if (size > MAX_DMASIZE)
272 tsize = MAX_DMASIZE;
273 else
274 tsize = size;
275
276 nblks = howmany(tsize, ss->sc_dev_bsize);
277
278 DPRINTF(("sdstrategy: read block %ld, %d bytes (%ld blks a %d bytes).\n",
279 blk, tsize, nblks, ss->sc_dev_bsize));
280
281 memset(&cdb, 0, sizeof(cdb));
282 cdb.opcode = READ_10;
283 cdb.addr[0] = (blk & 0xff000000) >> 24;
284 cdb.addr[1] = (blk & 0xff0000) >> 16;
285 cdb.addr[2] = (blk & 0xff00) >> 8;
286 cdb.addr[3] = blk & 0xff;
287 cdb.length[0] = (nblks & 0xff00) >> 8;
288 cdb.length[1] = nblks & 0xff;
289
290 error = scsiicmd(ss->sc_unit, ss->sc_lun,
291 (u_char *)&cdb, sizeof(cdb), (char *)buf + *rsize, &tsize);
292 if (error != 0)
293 {
294 DPRINTF(("sdstrategy: scsiicmd failed: %d = %s.\n", error, strerror(error)));
295 return error<0 ? EIO : error;
296 }
297 *rsize += tsize;
298 size -= tsize;
299 blk += nblks;
300 }
301 DPRINTF(("sdstrategy: read %d bytes\n", *rsize));
302 return 0;
303 }
304