1 | /* $NetBSD: ld_cac.c,v 1.30 2016/09/27 03:33:32 pgoyette Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2000, 2006 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Andrew Doran. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | /* |
33 | * Compaq array controller front-end for ld(4) driver. |
34 | */ |
35 | |
36 | #include <sys/cdefs.h> |
37 | __KERNEL_RCSID(0, "$NetBSD: ld_cac.c,v 1.30 2016/09/27 03:33:32 pgoyette Exp $" ); |
38 | |
39 | #include <sys/param.h> |
40 | #include <sys/systm.h> |
41 | #include <sys/kernel.h> |
42 | #include <sys/device.h> |
43 | #include <sys/buf.h> |
44 | #include <sys/bufq.h> |
45 | #include <sys/endian.h> |
46 | #include <sys/dkio.h> |
47 | #include <sys/disk.h> |
48 | #include <sys/module.h> |
49 | #include <sys/bus.h> |
50 | |
51 | #include <dev/ldvar.h> |
52 | |
53 | #include <dev/ic/cacreg.h> |
54 | #include <dev/ic/cacvar.h> |
55 | |
56 | #include "ioconf.h" |
57 | |
58 | struct ld_cac_softc { |
59 | struct ld_softc sc_ld; |
60 | kmutex_t *sc_mutex; |
61 | int sc_hwunit; |
62 | int sc_serrcnt; |
63 | struct timeval sc_serrtm; |
64 | }; |
65 | |
66 | void ld_cac_attach(device_t, device_t, void *); |
67 | void ld_cac_done(device_t, void *, int); |
68 | int ld_cac_dump(struct ld_softc *, void *, int, int); |
69 | int ld_cac_match(device_t, cfdata_t, void *); |
70 | int ld_cac_start(struct ld_softc *, struct buf *); |
71 | |
72 | static const struct timeval ld_cac_serrintvl = { 60, 0 }; |
73 | |
74 | CFATTACH_DECL_NEW(ld_cac, sizeof(struct ld_cac_softc), |
75 | ld_cac_match, ld_cac_attach, NULL, NULL); |
76 | |
77 | int |
78 | ld_cac_match(device_t parent, cfdata_t match, void *aux) |
79 | { |
80 | |
81 | return (1); |
82 | } |
83 | |
84 | void |
85 | ld_cac_attach(device_t parent, device_t self, void *aux) |
86 | { |
87 | struct cac_drive_info dinfo; |
88 | struct cac_attach_args *caca; |
89 | struct ld_cac_softc *sc = device_private(self); |
90 | struct cac_softc *cac = device_private(parent); |
91 | struct ld_softc *ld = &sc->sc_ld; |
92 | const char *type; |
93 | |
94 | caca = aux; |
95 | ld->sc_dv = self; |
96 | sc->sc_mutex = &cac->sc_mutex; |
97 | sc->sc_hwunit = caca->caca_unit; |
98 | |
99 | if (cac_cmd(cac, CAC_CMD_GET_LOG_DRV_INFO, &dinfo, sizeof(dinfo), |
100 | sc->sc_hwunit, 0, CAC_CCB_DATA_IN, NULL)) { |
101 | aprint_error(": CMD_GET_LOG_DRV_INFO failed\n" ); |
102 | return; |
103 | } |
104 | |
105 | ld->sc_secsize = CAC_GET2(dinfo.secsize); |
106 | ld->sc_maxxfer = CAC_MAX_XFER; |
107 | ld->sc_maxqueuecnt = (CAC_MAX_CCBS - 1) / cac->sc_nunits; |
108 | ld->sc_secperunit = CAC_GET2(dinfo.ncylinders) * |
109 | CAC_GET1(dinfo.nheads) * CAC_GET1(dinfo.nsectors); |
110 | ld->sc_start = ld_cac_start; |
111 | ld->sc_dump = ld_cac_dump; |
112 | |
113 | switch (CAC_GET1(dinfo.mirror)) { |
114 | case 0: |
115 | type = "standalone disk or RAID0" ; |
116 | break; |
117 | case 1: |
118 | type = "RAID4" ; |
119 | break; |
120 | case 2: |
121 | type = "RAID1" ; |
122 | break; |
123 | case 3: |
124 | type = "RAID5" ; |
125 | break; |
126 | default: |
127 | type = "unknown type of" ; |
128 | break; |
129 | } |
130 | |
131 | aprint_normal(": %s array\n" , type); |
132 | |
133 | /* XXX We should verify this... */ |
134 | ld->sc_flags = LDF_ENABLED; |
135 | ldattach(ld, BUFQ_DISK_DEFAULT_STRAT); |
136 | } |
137 | |
138 | int |
139 | ld_cac_start(struct ld_softc *ld, struct buf *bp) |
140 | { |
141 | int flags, cmd; |
142 | struct cac_softc *cac; |
143 | struct ld_cac_softc *sc; |
144 | struct cac_context cc; |
145 | |
146 | sc = (struct ld_cac_softc *)ld; |
147 | cac = device_private(device_parent(ld->sc_dv)); |
148 | |
149 | cc.cc_handler = ld_cac_done; |
150 | cc.cc_context = bp; |
151 | cc.cc_dv = ld->sc_dv; |
152 | |
153 | if ((bp->b_flags & B_READ) == 0) { |
154 | cmd = CAC_CMD_WRITE; |
155 | flags = CAC_CCB_DATA_OUT; |
156 | } else { |
157 | cmd = CAC_CMD_READ; |
158 | flags = CAC_CCB_DATA_IN; |
159 | } |
160 | |
161 | return (cac_cmd(cac, cmd, bp->b_data, bp->b_bcount, sc->sc_hwunit, |
162 | bp->b_rawblkno, flags, &cc)); |
163 | } |
164 | |
165 | int |
166 | ld_cac_dump(struct ld_softc *ld, void *data, int blkno, int blkcnt) |
167 | { |
168 | struct ld_cac_softc *sc; |
169 | |
170 | sc = (struct ld_cac_softc *)ld; |
171 | |
172 | return (cac_cmd(device_private(device_parent(ld->sc_dv)), |
173 | CAC_CMD_WRITE_MEDIA, data, blkcnt * ld->sc_secsize, |
174 | sc->sc_hwunit, blkno, CAC_CCB_DATA_OUT, NULL)); |
175 | } |
176 | |
177 | void |
178 | ld_cac_done(device_t dv, void *context, int error) |
179 | { |
180 | struct buf *bp; |
181 | struct ld_cac_softc *sc; |
182 | int rv; |
183 | |
184 | bp = context; |
185 | rv = 0; |
186 | sc = device_private(dv); |
187 | |
188 | if ((error & CAC_RET_CMD_REJECTED) == CAC_RET_CMD_REJECTED) { |
189 | aprint_error_dev(dv, "command rejected\n" ); |
190 | rv = EIO; |
191 | } |
192 | if (rv == 0 && (error & CAC_RET_INVAL_BLOCK) != 0) { |
193 | aprint_error_dev(dv, "invalid request block\n" ); |
194 | rv = EIO; |
195 | } |
196 | if (rv == 0 && (error & CAC_RET_HARD_ERROR) != 0) { |
197 | aprint_error_dev(dv, "hard error\n" ); |
198 | rv = EIO; |
199 | } |
200 | if (rv == 0 && (error & CAC_RET_SOFT_ERROR) != 0) { |
201 | sc->sc_serrcnt++; |
202 | if (ratecheck(&sc->sc_serrtm, &ld_cac_serrintvl)) { |
203 | sc->sc_serrcnt = 0; |
204 | aprint_error_dev(dv, |
205 | "%d soft errors; array may be degraded\n" , |
206 | sc->sc_serrcnt); |
207 | } |
208 | } |
209 | |
210 | if (rv) { |
211 | bp->b_error = rv; |
212 | bp->b_resid = bp->b_bcount; |
213 | } else |
214 | bp->b_resid = 0; |
215 | |
216 | mutex_exit(sc->sc_mutex); |
217 | lddone(&sc->sc_ld, bp); |
218 | mutex_enter(sc->sc_mutex); |
219 | } |
220 | |
221 | MODULE(MODULE_CLASS_DRIVER, ld_cac, "ld,cac" ); |
222 | |
223 | #ifdef _MODULE |
224 | /* |
225 | * XXX Don't allow ioconf.c to redefine the "struct cfdriver ld_cd" |
226 | * XXX it will be defined in the common-code module |
227 | */ |
228 | #undef CFDRIVER_DECL |
229 | #define CFDRIVER_DECL(name, class, attr) |
230 | #include "ioconf.c" |
231 | #endif |
232 | |
233 | static int |
234 | ld_cac_modcmd(modcmd_t cmd, void *opaque) |
235 | { |
236 | int error = 0; |
237 | #ifdef _MODULE |
238 | /* |
239 | * We ignore the cfdriver_vec[] that ioconf provides, since |
240 | * the cfdrivers are attached already. |
241 | */ |
242 | static struct cfdriver * const no_cfdriver_vec[] = { NULL }; |
243 | #endif |
244 | |
245 | #ifdef _MODULE |
246 | switch (cmd) { |
247 | case MODULE_CMD_INIT: |
248 | error = config_init_component(no_cfdriver_vec, |
249 | cfattach_ioconf_ld_cac, cfdata_ioconf_ld_cac); |
250 | break; |
251 | case MODULE_CMD_FINI: |
252 | error = config_fini_component(no_cfdriver_vec, |
253 | cfattach_ioconf_ld_cac, cfdata_ioconf_ld_cac); |
254 | break; |
255 | default: |
256 | error = ENOTTY; |
257 | break; |
258 | } |
259 | #endif |
260 | |
261 | return error; |
262 | } |
263 | |