Deleted Added
full compact
scsi_enc.c (242173) scsi_enc.c (244014)
1/*-
2 * Copyright (c) 2000 Matthew Jacob
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions, and the following disclaimer,
10 * without modification, immediately at the beginning of the file.
11 * 2. The name of the author may not be used to endorse or promote products
12 * derived from this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
1/*-
2 * Copyright (c) 2000 Matthew Jacob
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions, and the following disclaimer,
10 * without modification, immediately at the beginning of the file.
11 * 2. The name of the author may not be used to endorse or promote products
12 * derived from this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/cam/scsi/scsi_enc.c 242173 2012-10-27 08:52:33Z mav $");
28__FBSDID("$FreeBSD: head/sys/cam/scsi/scsi_enc.c 244014 2012-12-08 04:03:04Z ken $");
29
30#include <sys/param.h>
31
32#include <sys/conf.h>
33#include <sys/errno.h>
34#include <sys/fcntl.h>
35#include <sys/kernel.h>
36#include <sys/kthread.h>
37#include <sys/lock.h>
38#include <sys/malloc.h>
39#include <sys/mutex.h>
40#include <sys/queue.h>
41#include <sys/sx.h>
42#include <sys/systm.h>
43#include <sys/sysctl.h>
44#include <sys/types.h>
45
46#include <machine/stdarg.h>
47
48#include <cam/cam.h>
49#include <cam/cam_ccb.h>
50#include <cam/cam_debug.h>
51#include <cam/cam_periph.h>
52#include <cam/cam_xpt_periph.h>
53
54#include <cam/scsi/scsi_all.h>
55#include <cam/scsi/scsi_message.h>
56#include <cam/scsi/scsi_enc.h>
57#include <cam/scsi/scsi_enc_internal.h>
58
59MALLOC_DEFINE(M_SCSIENC, "SCSI ENC", "SCSI ENC buffers");
60
61/* Enclosure type independent driver */
62
63static d_open_t enc_open;
64static d_close_t enc_close;
65static d_ioctl_t enc_ioctl;
66static periph_init_t enc_init;
67static periph_ctor_t enc_ctor;
68static periph_oninv_t enc_oninvalidate;
69static periph_dtor_t enc_dtor;
70static periph_start_t enc_start;
71
72static void enc_async(void *, uint32_t, struct cam_path *, void *);
73static enctyp enc_type(struct ccb_getdev *);
74
75SYSCTL_NODE(_kern_cam, OID_AUTO, enc, CTLFLAG_RD, 0,
76 "CAM Enclosure Services driver");
77
78static struct periph_driver encdriver = {
79 enc_init, "ses",
80 TAILQ_HEAD_INITIALIZER(encdriver.units), /* generation */ 0
81};
82
83PERIPHDRIVER_DECLARE(enc, encdriver);
84
85static struct cdevsw enc_cdevsw = {
86 .d_version = D_VERSION,
87 .d_open = enc_open,
88 .d_close = enc_close,
89 .d_ioctl = enc_ioctl,
90 .d_name = "ses",
91 .d_flags = D_TRACKCLOSE,
92};
93
94static void
95enc_init(void)
96{
97 cam_status status;
98
99 /*
100 * Install a global async callback. This callback will
101 * receive async callbacks like "new device found".
102 */
103 status = xpt_register_async(AC_FOUND_DEVICE, enc_async, NULL, NULL);
104
105 if (status != CAM_REQ_CMP) {
106 printf("enc: Failed to attach master async callback "
107 "due to status 0x%x!\n", status);
108 }
109}
110
111static void
112enc_devgonecb(void *arg)
113{
29
30#include <sys/param.h>
31
32#include <sys/conf.h>
33#include <sys/errno.h>
34#include <sys/fcntl.h>
35#include <sys/kernel.h>
36#include <sys/kthread.h>
37#include <sys/lock.h>
38#include <sys/malloc.h>
39#include <sys/mutex.h>
40#include <sys/queue.h>
41#include <sys/sx.h>
42#include <sys/systm.h>
43#include <sys/sysctl.h>
44#include <sys/types.h>
45
46#include <machine/stdarg.h>
47
48#include <cam/cam.h>
49#include <cam/cam_ccb.h>
50#include <cam/cam_debug.h>
51#include <cam/cam_periph.h>
52#include <cam/cam_xpt_periph.h>
53
54#include <cam/scsi/scsi_all.h>
55#include <cam/scsi/scsi_message.h>
56#include <cam/scsi/scsi_enc.h>
57#include <cam/scsi/scsi_enc_internal.h>
58
59MALLOC_DEFINE(M_SCSIENC, "SCSI ENC", "SCSI ENC buffers");
60
61/* Enclosure type independent driver */
62
63static d_open_t enc_open;
64static d_close_t enc_close;
65static d_ioctl_t enc_ioctl;
66static periph_init_t enc_init;
67static periph_ctor_t enc_ctor;
68static periph_oninv_t enc_oninvalidate;
69static periph_dtor_t enc_dtor;
70static periph_start_t enc_start;
71
72static void enc_async(void *, uint32_t, struct cam_path *, void *);
73static enctyp enc_type(struct ccb_getdev *);
74
75SYSCTL_NODE(_kern_cam, OID_AUTO, enc, CTLFLAG_RD, 0,
76 "CAM Enclosure Services driver");
77
78static struct periph_driver encdriver = {
79 enc_init, "ses",
80 TAILQ_HEAD_INITIALIZER(encdriver.units), /* generation */ 0
81};
82
83PERIPHDRIVER_DECLARE(enc, encdriver);
84
85static struct cdevsw enc_cdevsw = {
86 .d_version = D_VERSION,
87 .d_open = enc_open,
88 .d_close = enc_close,
89 .d_ioctl = enc_ioctl,
90 .d_name = "ses",
91 .d_flags = D_TRACKCLOSE,
92};
93
94static void
95enc_init(void)
96{
97 cam_status status;
98
99 /*
100 * Install a global async callback. This callback will
101 * receive async callbacks like "new device found".
102 */
103 status = xpt_register_async(AC_FOUND_DEVICE, enc_async, NULL, NULL);
104
105 if (status != CAM_REQ_CMP) {
106 printf("enc: Failed to attach master async callback "
107 "due to status 0x%x!\n", status);
108 }
109}
110
111static void
112enc_devgonecb(void *arg)
113{
114 struct cam_sim *sim;
114 struct cam_periph *periph;
115 struct cam_periph *periph;
116 struct enc_softc *enc;
117 int i;
115
116 periph = (struct cam_periph *)arg;
118
119 periph = (struct cam_periph *)arg;
120 sim = periph->sim;
121 enc = (struct enc_softc *)periph->softc;
117
122
118 cam_periph_release(periph);
123 mtx_lock(sim->mtx);
124
125 /*
126 * When we get this callback, we will get no more close calls from
127 * devfs. So if we have any dangling opens, we need to release the
128 * reference held for that particular context.
129 */
130 for (i = 0; i < enc->open_count; i++)
131 cam_periph_release_locked(periph);
132
133 enc->open_count = 0;
134
135 /*
136 * Release the reference held for the device node, it is gone now.
137 */
138 cam_periph_release_locked(periph);
139
140 /*
141 * We reference the SIM lock directly here, instead of using
142 * cam_periph_unlock(). The reason is that the final call to
143 * cam_periph_release_locked() above could result in the periph
144 * getting freed. If that is the case, dereferencing the periph
145 * with a cam_periph_unlock() call would cause a page fault.
146 */
147 mtx_unlock(sim->mtx);
119}
120
121static void
122enc_oninvalidate(struct cam_periph *periph)
123{
124 struct enc_softc *enc;
125
126 enc = periph->softc;
127
128 enc->enc_flags |= ENC_FLAG_INVALID;
129
130 /* If the sub-driver has an invalidate routine, call it */
131 if (enc->enc_vec.softc_invalidate != NULL)
132 enc->enc_vec.softc_invalidate(enc);
133
134 /*
135 * Unregister any async callbacks.
136 */
137 xpt_register_async(0, enc_async, periph, periph->path);
138
139 /*
140 * Shutdown our daemon.
141 */
142 enc->enc_flags |= ENC_FLAG_SHUTDOWN;
143 if (enc->enc_daemon != NULL) {
144 /* Signal the ses daemon to terminate. */
145 wakeup(enc->enc_daemon);
146 }
147 callout_drain(&enc->status_updater);
148
149 destroy_dev_sched_cb(enc->enc_dev, enc_devgonecb, periph);
150
151 xpt_print(periph->path, "lost device\n");
152}
153
154static void
155enc_dtor(struct cam_periph *periph)
156{
157 struct enc_softc *enc;
158
159 enc = periph->softc;
160
161 xpt_print(periph->path, "removing device entry\n");
162
163
164 /* If the sub-driver has a cleanup routine, call it */
165 if (enc->enc_vec.softc_cleanup != NULL)
166 enc->enc_vec.softc_cleanup(enc);
167
168 if (enc->enc_boot_hold_ch.ich_func != NULL) {
169 config_intrhook_disestablish(&enc->enc_boot_hold_ch);
170 enc->enc_boot_hold_ch.ich_func = NULL;
171 }
172
173 ENC_FREE(enc);
174}
175
176static void
177enc_async(void *callback_arg, uint32_t code, struct cam_path *path, void *arg)
178{
179 struct cam_periph *periph;
180
181 periph = (struct cam_periph *)callback_arg;
182
183 switch(code) {
184 case AC_FOUND_DEVICE:
185 {
186 struct ccb_getdev *cgd;
187 cam_status status;
188 path_id_t path_id;
189
190 cgd = (struct ccb_getdev *)arg;
191 if (arg == NULL) {
192 break;
193 }
194
195 if (enc_type(cgd) == ENC_NONE) {
196 /*
197 * Schedule announcement of the ENC bindings for
198 * this device if it is managed by a SEP.
199 */
200 path_id = xpt_path_path_id(path);
201 xpt_lock_buses();
202 TAILQ_FOREACH(periph, &encdriver.units, unit_links) {
203 struct enc_softc *softc;
204
205 softc = (struct enc_softc *)periph->softc;
206 if (xpt_path_path_id(periph->path) != path_id
207 || softc == NULL
208 || (softc->enc_flags & ENC_FLAG_INITIALIZED)
209 == 0
210 || softc->enc_vec.device_found == NULL)
211 continue;
212
213 softc->enc_vec.device_found(softc);
214 }
215 xpt_unlock_buses();
216 return;
217 }
218
219 status = cam_periph_alloc(enc_ctor, enc_oninvalidate,
220 enc_dtor, enc_start, "ses", CAM_PERIPH_BIO,
221 cgd->ccb_h.path, enc_async, AC_FOUND_DEVICE, cgd);
222
223 if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) {
224 printf("enc_async: Unable to probe new device due to "
225 "status 0x%x\n", status);
226 }
227 break;
228 }
229 default:
230 cam_periph_async(periph, code, path, arg);
231 break;
232 }
233}
234
235static int
236enc_open(struct cdev *dev, int flags, int fmt, struct thread *td)
237{
238 struct cam_periph *periph;
239 struct enc_softc *softc;
240 int error = 0;
241
242 periph = (struct cam_periph *)dev->si_drv1;
243 if (periph == NULL) {
244 return (ENXIO);
245 }
246
247 if (cam_periph_acquire(periph) != CAM_REQ_CMP)
248 return (ENXIO);
249
250 cam_periph_lock(periph);
251
252 softc = (struct enc_softc *)periph->softc;
253
254 if ((softc->enc_flags & ENC_FLAG_INITIALIZED) == 0) {
255 error = ENXIO;
256 goto out;
257 }
258 if (softc->enc_flags & ENC_FLAG_INVALID) {
259 error = ENXIO;
260 goto out;
261 }
262out:
263 if (error != 0)
264 cam_periph_release_locked(periph);
148}
149
150static void
151enc_oninvalidate(struct cam_periph *periph)
152{
153 struct enc_softc *enc;
154
155 enc = periph->softc;
156
157 enc->enc_flags |= ENC_FLAG_INVALID;
158
159 /* If the sub-driver has an invalidate routine, call it */
160 if (enc->enc_vec.softc_invalidate != NULL)
161 enc->enc_vec.softc_invalidate(enc);
162
163 /*
164 * Unregister any async callbacks.
165 */
166 xpt_register_async(0, enc_async, periph, periph->path);
167
168 /*
169 * Shutdown our daemon.
170 */
171 enc->enc_flags |= ENC_FLAG_SHUTDOWN;
172 if (enc->enc_daemon != NULL) {
173 /* Signal the ses daemon to terminate. */
174 wakeup(enc->enc_daemon);
175 }
176 callout_drain(&enc->status_updater);
177
178 destroy_dev_sched_cb(enc->enc_dev, enc_devgonecb, periph);
179
180 xpt_print(periph->path, "lost device\n");
181}
182
183static void
184enc_dtor(struct cam_periph *periph)
185{
186 struct enc_softc *enc;
187
188 enc = periph->softc;
189
190 xpt_print(periph->path, "removing device entry\n");
191
192
193 /* If the sub-driver has a cleanup routine, call it */
194 if (enc->enc_vec.softc_cleanup != NULL)
195 enc->enc_vec.softc_cleanup(enc);
196
197 if (enc->enc_boot_hold_ch.ich_func != NULL) {
198 config_intrhook_disestablish(&enc->enc_boot_hold_ch);
199 enc->enc_boot_hold_ch.ich_func = NULL;
200 }
201
202 ENC_FREE(enc);
203}
204
205static void
206enc_async(void *callback_arg, uint32_t code, struct cam_path *path, void *arg)
207{
208 struct cam_periph *periph;
209
210 periph = (struct cam_periph *)callback_arg;
211
212 switch(code) {
213 case AC_FOUND_DEVICE:
214 {
215 struct ccb_getdev *cgd;
216 cam_status status;
217 path_id_t path_id;
218
219 cgd = (struct ccb_getdev *)arg;
220 if (arg == NULL) {
221 break;
222 }
223
224 if (enc_type(cgd) == ENC_NONE) {
225 /*
226 * Schedule announcement of the ENC bindings for
227 * this device if it is managed by a SEP.
228 */
229 path_id = xpt_path_path_id(path);
230 xpt_lock_buses();
231 TAILQ_FOREACH(periph, &encdriver.units, unit_links) {
232 struct enc_softc *softc;
233
234 softc = (struct enc_softc *)periph->softc;
235 if (xpt_path_path_id(periph->path) != path_id
236 || softc == NULL
237 || (softc->enc_flags & ENC_FLAG_INITIALIZED)
238 == 0
239 || softc->enc_vec.device_found == NULL)
240 continue;
241
242 softc->enc_vec.device_found(softc);
243 }
244 xpt_unlock_buses();
245 return;
246 }
247
248 status = cam_periph_alloc(enc_ctor, enc_oninvalidate,
249 enc_dtor, enc_start, "ses", CAM_PERIPH_BIO,
250 cgd->ccb_h.path, enc_async, AC_FOUND_DEVICE, cgd);
251
252 if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) {
253 printf("enc_async: Unable to probe new device due to "
254 "status 0x%x\n", status);
255 }
256 break;
257 }
258 default:
259 cam_periph_async(periph, code, path, arg);
260 break;
261 }
262}
263
264static int
265enc_open(struct cdev *dev, int flags, int fmt, struct thread *td)
266{
267 struct cam_periph *periph;
268 struct enc_softc *softc;
269 int error = 0;
270
271 periph = (struct cam_periph *)dev->si_drv1;
272 if (periph == NULL) {
273 return (ENXIO);
274 }
275
276 if (cam_periph_acquire(periph) != CAM_REQ_CMP)
277 return (ENXIO);
278
279 cam_periph_lock(periph);
280
281 softc = (struct enc_softc *)periph->softc;
282
283 if ((softc->enc_flags & ENC_FLAG_INITIALIZED) == 0) {
284 error = ENXIO;
285 goto out;
286 }
287 if (softc->enc_flags & ENC_FLAG_INVALID) {
288 error = ENXIO;
289 goto out;
290 }
291out:
292 if (error != 0)
293 cam_periph_release_locked(periph);
294 else
295 softc->open_count++;
265
266 cam_periph_unlock(periph);
267
268 return (error);
269}
270
271static int
272enc_close(struct cdev *dev, int flag, int fmt, struct thread *td)
273{
296
297 cam_periph_unlock(periph);
298
299 return (error);
300}
301
302static int
303enc_close(struct cdev *dev, int flag, int fmt, struct thread *td)
304{
305 struct cam_sim *sim;
274 struct cam_periph *periph;
306 struct cam_periph *periph;
307 struct enc_softc *enc;
275
276 periph = (struct cam_periph *)dev->si_drv1;
277 if (periph == NULL)
278 return (ENXIO);
279
308
309 periph = (struct cam_periph *)dev->si_drv1;
310 if (periph == NULL)
311 return (ENXIO);
312
280 cam_periph_release(periph);
313 sim = periph->sim;
314 enc = periph->softc;
281
315
316 mtx_lock(sim->mtx);
317
318 enc->open_count--;
319
320 cam_periph_release_locked(periph);
321
322 /*
323 * We reference the SIM lock directly here, instead of using
324 * cam_periph_unlock(). The reason is that the call to
325 * cam_periph_release_locked() above could result in the periph
326 * getting freed. If that is the case, dereferencing the periph
327 * with a cam_periph_unlock() call would cause a page fault.
328 *
329 * cam_periph_release() avoids this problem using the same method,
330 * but we're manually acquiring and dropping the lock here to
331 * protect the open count and avoid another lock acquisition and
332 * release.
333 */
334 mtx_unlock(sim->mtx);
335
282 return (0);
283}
284
285static void
286enc_start(struct cam_periph *p, union ccb *sccb)
287{
288 struct enc_softc *enc;
289
290 enc = p->softc;
291 ENC_DLOG(enc, "%s enter imm=%d prio=%d\n",
292 __func__, p->immediate_priority, p->pinfo.priority);
293 if (p->immediate_priority <= p->pinfo.priority) {
294 SLIST_INSERT_HEAD(&p->ccb_list, &sccb->ccb_h, periph_links.sle);
295 p->immediate_priority = CAM_PRIORITY_NONE;
296 wakeup(&p->ccb_list);
297 } else
298 xpt_release_ccb(sccb);
299 ENC_DLOG(enc, "%s exit\n", __func__);
300}
301
302void
303enc_done(struct cam_periph *periph, union ccb *dccb)
304{
305 wakeup(&dccb->ccb_h.cbfcnp);
306}
307
308int
309enc_error(union ccb *ccb, uint32_t cflags, uint32_t sflags)
310{
311 struct enc_softc *softc;
312 struct cam_periph *periph;
313
314 periph = xpt_path_periph(ccb->ccb_h.path);
315 softc = (struct enc_softc *)periph->softc;
316
317 return (cam_periph_error(ccb, cflags, sflags, &softc->saved_ccb));
318}
319
320static int
321enc_ioctl(struct cdev *dev, u_long cmd, caddr_t arg_addr, int flag,
322 struct thread *td)
323{
324 struct cam_periph *periph;
325 encioc_enc_status_t tmp;
326 encioc_string_t sstr;
327 encioc_elm_status_t elms;
328 encioc_elm_desc_t elmd;
329 encioc_elm_devnames_t elmdn;
330 encioc_element_t *uelm;
331 enc_softc_t *enc;
332 enc_cache_t *cache;
333 void *addr;
334 int error, i;
335
336
337 if (arg_addr)
338 addr = *((caddr_t *) arg_addr);
339 else
340 addr = NULL;
341
342 periph = (struct cam_periph *)dev->si_drv1;
343 if (periph == NULL)
344 return (ENXIO);
345
346 CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering encioctl\n"));
347
348 cam_periph_lock(periph);
349 enc = (struct enc_softc *)periph->softc;
350 cache = &enc->enc_cache;
351
352 /*
353 * Now check to see whether we're initialized or not.
354 * This actually should never fail as we're not supposed
355 * to get past enc_open w/o successfully initializing
356 * things.
357 */
358 if ((enc->enc_flags & ENC_FLAG_INITIALIZED) == 0) {
359 cam_periph_unlock(periph);
360 return (ENXIO);
361 }
362 cam_periph_unlock(periph);
363
364 error = 0;
365
366 CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
367 ("trying to do ioctl %#lx\n", cmd));
368
369 /*
370 * If this command can change the device's state,
371 * we must have the device open for writing.
372 *
373 * For commands that get information about the
374 * device- we don't need to lock the peripheral
375 * if we aren't running a command. The periph
376 * also can't go away while a user process has
377 * it open.
378 */
379 switch (cmd) {
380 case ENCIOC_GETNELM:
381 case ENCIOC_GETELMMAP:
382 case ENCIOC_GETENCSTAT:
383 case ENCIOC_GETELMSTAT:
384 case ENCIOC_GETELMDESC:
385 case ENCIOC_GETELMDEVNAMES:
386 break;
387 default:
388 if ((flag & FWRITE) == 0) {
389 return (EBADF);
390 }
391 }
392
393 /*
394 * XXX The values read here are only valid for the current
395 * configuration generation. We need these ioctls
396 * to also pass in/out a generation number.
397 */
398 sx_slock(&enc->enc_cache_lock);
399 switch (cmd) {
400 case ENCIOC_GETNELM:
401 error = copyout(&cache->nelms, addr, sizeof (cache->nelms));
402 break;
403
404 case ENCIOC_GETELMMAP:
405 for (uelm = addr, i = 0; i != cache->nelms; i++) {
406 encioc_element_t kelm;
407 kelm.elm_idx = i;
408 kelm.elm_subenc_id = cache->elm_map[i].subenclosure;
409 kelm.elm_type = cache->elm_map[i].enctype;
410 error = copyout(&kelm, &uelm[i], sizeof(kelm));
411 if (error)
412 break;
413 }
414 break;
415
416 case ENCIOC_GETENCSTAT:
417 cam_periph_lock(periph);
418 error = enc->enc_vec.get_enc_status(enc, 1);
419 if (error) {
420 cam_periph_unlock(periph);
421 break;
422 }
423 tmp = cache->enc_status;
424 cam_periph_unlock(periph);
425 error = copyout(&tmp, addr, sizeof(tmp));
426 cache->enc_status = tmp;
427 break;
428
429 case ENCIOC_SETENCSTAT:
430 error = copyin(addr, &tmp, sizeof(tmp));
431 if (error)
432 break;
433 cam_periph_lock(periph);
434 error = enc->enc_vec.set_enc_status(enc, tmp, 1);
435 cam_periph_unlock(periph);
436 break;
437
438 case ENCIOC_GETSTRING:
439 case ENCIOC_SETSTRING:
440 if (enc->enc_vec.handle_string == NULL) {
441 error = EINVAL;
442 break;
443 }
444 error = copyin(addr, &sstr, sizeof(sstr));
445 if (error)
446 break;
447 cam_periph_lock(periph);
448 error = enc->enc_vec.handle_string(enc, &sstr, cmd);
449 cam_periph_unlock(periph);
450 break;
451
452 case ENCIOC_GETELMSTAT:
453 error = copyin(addr, &elms, sizeof(elms));
454 if (error)
455 break;
456 if (elms.elm_idx >= cache->nelms) {
457 error = EINVAL;
458 break;
459 }
460 cam_periph_lock(periph);
461 error = enc->enc_vec.get_elm_status(enc, &elms, 1);
462 cam_periph_unlock(periph);
463 if (error)
464 break;
465 error = copyout(&elms, addr, sizeof(elms));
466 break;
467
468 case ENCIOC_GETELMDESC:
469 error = copyin(addr, &elmd, sizeof(elmd));
470 if (error)
471 break;
472 if (elmd.elm_idx >= cache->nelms) {
473 error = EINVAL;
474 break;
475 }
476 if (enc->enc_vec.get_elm_desc != NULL) {
477 error = enc->enc_vec.get_elm_desc(enc, &elmd);
478 if (error)
479 break;
480 } else
481 elmd.elm_desc_len = 0;
482 error = copyout(&elmd, addr, sizeof(elmd));
483 break;
484
485 case ENCIOC_GETELMDEVNAMES:
486 if (enc->enc_vec.get_elm_devnames == NULL) {
487 error = EINVAL;
488 break;
489 }
490 error = copyin(addr, &elmdn, sizeof(elmdn));
491 if (error)
492 break;
493 if (elmdn.elm_idx >= cache->nelms) {
494 error = EINVAL;
495 break;
496 }
497 cam_periph_lock(periph);
498 error = (*enc->enc_vec.get_elm_devnames)(enc, &elmdn);
499 cam_periph_unlock(periph);
500 if (error)
501 break;
502 error = copyout(&elmdn, addr, sizeof(elmdn));
503 break;
504
505 case ENCIOC_SETELMSTAT:
506 error = copyin(addr, &elms, sizeof(elms));
507 if (error)
508 break;
509
510 if (elms.elm_idx >= cache->nelms) {
511 error = EINVAL;
512 break;
513 }
514 cam_periph_lock(periph);
515 error = enc->enc_vec.set_elm_status(enc, &elms, 1);
516 cam_periph_unlock(periph);
517
518 break;
519
520 case ENCIOC_INIT:
521
522 cam_periph_lock(periph);
523 error = enc->enc_vec.init_enc(enc);
524 cam_periph_unlock(periph);
525 break;
526
527 default:
528 cam_periph_lock(periph);
529 error = cam_periph_ioctl(periph, cmd, arg_addr, enc_error);
530 cam_periph_unlock(periph);
531 break;
532 }
533 sx_sunlock(&enc->enc_cache_lock);
534 return (error);
535}
536
537int
538enc_runcmd(struct enc_softc *enc, char *cdb, int cdbl, char *dptr, int *dlenp)
539{
540 int error, dlen, tdlen;
541 ccb_flags ddf;
542 union ccb *ccb;
543
544 CAM_DEBUG(enc->periph->path, CAM_DEBUG_TRACE,
545 ("entering enc_runcmd\n"));
546 if (dptr) {
547 if ((dlen = *dlenp) < 0) {
548 dlen = -dlen;
549 ddf = CAM_DIR_OUT;
550 } else {
551 ddf = CAM_DIR_IN;
552 }
553 } else {
554 dlen = 0;
555 ddf = CAM_DIR_NONE;
556 }
557
558 if (cdbl > IOCDBLEN) {
559 cdbl = IOCDBLEN;
560 }
561
562 ccb = cam_periph_getccb(enc->periph, CAM_PRIORITY_NORMAL);
563 if (enc->enc_type == ENC_SEMB_SES || enc->enc_type == ENC_SEMB_SAFT) {
564 tdlen = min(dlen, 1020);
565 tdlen = (tdlen + 3) & ~3;
566 cam_fill_ataio(&ccb->ataio, 0, enc_done, ddf, 0, dptr, tdlen,
567 30 * 1000);
568 if (cdb[0] == RECEIVE_DIAGNOSTIC)
569 ata_28bit_cmd(&ccb->ataio,
570 ATA_SEP_ATTN, cdb[2], 0x02, tdlen / 4);
571 else if (cdb[0] == SEND_DIAGNOSTIC)
572 ata_28bit_cmd(&ccb->ataio,
573 ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0,
574 0x82, tdlen / 4);
575 else if (cdb[0] == READ_BUFFER)
576 ata_28bit_cmd(&ccb->ataio,
577 ATA_SEP_ATTN, cdb[2], 0x00, tdlen / 4);
578 else
579 ata_28bit_cmd(&ccb->ataio,
580 ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0,
581 0x80, tdlen / 4);
582 } else {
583 tdlen = dlen;
584 cam_fill_csio(&ccb->csio, 0, enc_done, ddf, MSG_SIMPLE_Q_TAG,
585 dptr, dlen, sizeof (struct scsi_sense_data), cdbl,
586 60 * 1000);
587 bcopy(cdb, ccb->csio.cdb_io.cdb_bytes, cdbl);
588 }
589
590 error = cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, ENC_FLAGS, NULL);
591 if (error) {
592 if (dptr) {
593 *dlenp = dlen;
594 }
595 } else {
596 if (dptr) {
597 if (ccb->ccb_h.func_code == XPT_ATA_IO)
598 *dlenp = ccb->ataio.resid;
599 else
600 *dlenp = ccb->csio.resid;
601 *dlenp += tdlen - dlen;
602 }
603 }
604 xpt_release_ccb(ccb);
605 CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE,
606 ("exiting enc_runcmd: *dlenp = %d\n", *dlenp));
607 return (error);
608}
609
610void
611enc_log(struct enc_softc *enc, const char *fmt, ...)
612{
613 va_list ap;
614
615 printf("%s%d: ", enc->periph->periph_name, enc->periph->unit_number);
616 va_start(ap, fmt);
617 vprintf(fmt, ap);
618 va_end(ap);
619}
620
621/*
622 * The code after this point runs on many platforms,
623 * so forgive the slightly awkward and nonconforming
624 * appearance.
625 */
626
627/*
628 * Is this a device that supports enclosure services?
629 *
630 * It's a pretty simple ruleset- if it is device type
631 * 0x0D (13), it's an ENCLOSURE device.
632 */
633
634#define SAFTE_START 44
635#define SAFTE_END 50
636#define SAFTE_LEN SAFTE_END-SAFTE_START
637
638static enctyp
639enc_type(struct ccb_getdev *cgd)
640{
641 int buflen;
642 unsigned char *iqd;
643
644 if (cgd->protocol == PROTO_SEMB) {
645 iqd = (unsigned char *)&cgd->ident_data;
646 if (STRNCMP(iqd + 43, "S-E-S", 5) == 0)
647 return (ENC_SEMB_SES);
648 else if (STRNCMP(iqd + 43, "SAF-TE", 6) == 0)
649 return (ENC_SEMB_SAFT);
650 return (ENC_NONE);
651
652 } else if (cgd->protocol != PROTO_SCSI)
653 return (ENC_NONE);
654
655 iqd = (unsigned char *)&cgd->inq_data;
656 buflen = min(sizeof(cgd->inq_data),
657 SID_ADDITIONAL_LENGTH(&cgd->inq_data));
658
659 if ((iqd[0] & 0x1f) == T_ENCLOSURE) {
660 if ((iqd[2] & 0x7) > 2) {
661 return (ENC_SES);
662 } else {
663 return (ENC_SES_SCSI2);
664 }
665 return (ENC_NONE);
666 }
667
668#ifdef ENC_ENABLE_PASSTHROUGH
669 if ((iqd[6] & 0x40) && (iqd[2] & 0x7) >= 2) {
670 /*
671 * PassThrough Device.
672 */
673 return (ENC_ENC_PASSTHROUGH);
674 }
675#endif
676
677 /*
678 * The comparison is short for a reason-
679 * some vendors were chopping it short.
680 */
681
682 if (buflen < SAFTE_END - 2) {
683 return (ENC_NONE);
684 }
685
686 if (STRNCMP((char *)&iqd[SAFTE_START], "SAF-TE", SAFTE_LEN - 2) == 0) {
687 return (ENC_SAFT);
688 }
689 return (ENC_NONE);
690}
691
692/*================== Enclosure Monitoring/Processing Daemon ==================*/
693/**
694 * \brief Queue an update request for a given action, if needed.
695 *
696 * \param enc SES softc to queue the request for.
697 * \param action Action requested.
698 */
699void
700enc_update_request(enc_softc_t *enc, uint32_t action)
701{
702 if ((enc->pending_actions & (0x1 << action)) == 0) {
703 enc->pending_actions |= (0x1 << action);
704 ENC_DLOG(enc, "%s: queing requested action %d\n",
705 __func__, action);
706 if (enc->current_action == ENC_UPDATE_NONE)
707 wakeup(enc->enc_daemon);
708 } else {
709 ENC_DLOG(enc, "%s: ignoring requested action %d - "
710 "Already queued\n", __func__, action);
711 }
712}
713
714/**
715 * \brief Invoke the handler of the highest priority pending
716 * state in the SES state machine.
717 *
718 * \param enc The SES instance invoking the state machine.
719 */
720static void
721enc_fsm_step(enc_softc_t *enc)
722{
723 union ccb *ccb;
724 uint8_t *buf;
725 struct enc_fsm_state *cur_state;
726 int error;
727 uint32_t xfer_len;
728
729 ENC_DLOG(enc, "%s enter %p\n", __func__, enc);
730
731 enc->current_action = ffs(enc->pending_actions) - 1;
732 enc->pending_actions &= ~(0x1 << enc->current_action);
733
734 cur_state = &enc->enc_fsm_states[enc->current_action];
735
736 buf = NULL;
737 if (cur_state->buf_size != 0) {
738 cam_periph_unlock(enc->periph);
739 buf = malloc(cur_state->buf_size, M_SCSIENC, M_WAITOK|M_ZERO);
740 cam_periph_lock(enc->periph);
741 }
742
743 error = 0;
744 ccb = NULL;
745 if (cur_state->fill != NULL) {
746 ccb = cam_periph_getccb(enc->periph, CAM_PRIORITY_NORMAL);
747
748 error = cur_state->fill(enc, cur_state, ccb, buf);
749 if (error != 0)
750 goto done;
751
752 error = cam_periph_runccb(ccb, cur_state->error,
753 ENC_CFLAGS,
754 ENC_FLAGS|SF_QUIET_IR, NULL);
755 }
756
757 if (ccb != NULL) {
758 if (ccb->ccb_h.func_code == XPT_ATA_IO)
759 xfer_len = ccb->ataio.dxfer_len - ccb->ataio.resid;
760 else
761 xfer_len = ccb->csio.dxfer_len - ccb->csio.resid;
762 } else
763 xfer_len = 0;
764
765 cam_periph_unlock(enc->periph);
766 cur_state->done(enc, cur_state, ccb, &buf, error, xfer_len);
767 cam_periph_lock(enc->periph);
768
769done:
770 ENC_DLOG(enc, "%s exit - result %d\n", __func__, error);
771 ENC_FREE_AND_NULL(buf);
772 if (ccb != NULL)
773 xpt_release_ccb(ccb);
774}
775
776/**
777 * \invariant Called with cam_periph mutex held.
778 */
779static void
780enc_status_updater(void *arg)
781{
782 enc_softc_t *enc;
783
784 enc = arg;
785 if (enc->enc_vec.poll_status != NULL)
786 enc->enc_vec.poll_status(enc);
787}
788
789static void
790enc_daemon(void *arg)
791{
792 enc_softc_t *enc;
793
794 enc = arg;
795
796 cam_periph_lock(enc->periph);
797 while ((enc->enc_flags & ENC_FLAG_SHUTDOWN) == 0) {
798 if (enc->pending_actions == 0) {
799 struct intr_config_hook *hook;
800
801 /*
802 * Reset callout and msleep, or
803 * issue timed task completion
804 * status command.
805 */
806 enc->current_action = ENC_UPDATE_NONE;
807
808 /*
809 * We've been through our state machine at least
810 * once. Allow the transition to userland.
811 */
812 hook = &enc->enc_boot_hold_ch;
813 if (hook->ich_func != NULL) {
814 config_intrhook_disestablish(hook);
815 hook->ich_func = NULL;
816 }
817
818 callout_reset(&enc->status_updater, 60*hz,
819 enc_status_updater, enc);
820
821 cam_periph_sleep(enc->periph, enc->enc_daemon,
822 PUSER, "idle", 0);
823 } else {
824 enc_fsm_step(enc);
825 }
826 }
827 enc->enc_daemon = NULL;
828 cam_periph_unlock(enc->periph);
829 cam_periph_release(enc->periph);
830 kproc_exit(0);
831}
832
833static int
834enc_kproc_init(enc_softc_t *enc)
835{
836 int result;
837
838 callout_init_mtx(&enc->status_updater, enc->periph->sim->mtx, 0);
839
840 if (cam_periph_acquire(enc->periph) != CAM_REQ_CMP)
841 return (ENXIO);
842
843 result = kproc_create(enc_daemon, enc, &enc->enc_daemon, /*flags*/0,
844 /*stackpgs*/0, "enc_daemon%d",
845 enc->periph->unit_number);
846 if (result == 0) {
847 /* Do an initial load of all page data. */
848 cam_periph_lock(enc->periph);
849 enc->enc_vec.poll_status(enc);
850 cam_periph_unlock(enc->periph);
851 } else
852 cam_periph_release(enc->periph);
853 return (result);
854}
855
856/**
857 * \brief Interrupt configuration hook callback associated with
858 * enc_boot_hold_ch.
859 *
860 * Since interrupts are always functional at the time of enclosure
861 * configuration, there is nothing to be done when the callback occurs.
862 * This hook is only registered to hold up boot processing while initial
863 * eclosure processing occurs.
864 *
865 * \param arg The enclosure softc, but currently unused in this callback.
866 */
867static void
868enc_nop_confighook_cb(void *arg __unused)
869{
870}
871
872static cam_status
873enc_ctor(struct cam_periph *periph, void *arg)
874{
875 cam_status status = CAM_REQ_CMP_ERR;
876 int err;
877 enc_softc_t *enc;
878 struct ccb_getdev *cgd;
879 char *tname;
880
881 cgd = (struct ccb_getdev *)arg;
882 if (cgd == NULL) {
883 printf("enc_ctor: no getdev CCB, can't register device\n");
884 goto out;
885 }
886
887 enc = ENC_MALLOCZ(sizeof(*enc));
888 if (enc == NULL) {
889 printf("enc_ctor: Unable to probe new device. "
890 "Unable to allocate enc\n");
891 goto out;
892 }
893 enc->periph = periph;
894 enc->current_action = ENC_UPDATE_INVALID;
895
896 enc->enc_type = enc_type(cgd);
897 sx_init(&enc->enc_cache_lock, "enccache");
898
899 switch (enc->enc_type) {
900 case ENC_SES:
901 case ENC_SES_SCSI2:
902 case ENC_SES_PASSTHROUGH:
903 case ENC_SEMB_SES:
904 err = ses_softc_init(enc);
905 break;
906 case ENC_SAFT:
907 case ENC_SEMB_SAFT:
908 err = safte_softc_init(enc);
909 break;
910 case ENC_NONE:
911 default:
912 ENC_FREE(enc);
913 return (CAM_REQ_CMP_ERR);
914 }
915
916 if (err) {
917 xpt_print(periph->path, "error %d initializing\n", err);
918 goto out;
919 }
920
921 /*
922 * Hold off userland until we have made at least one pass
923 * through our state machine so that physical path data is
924 * present.
925 */
926 if (enc->enc_vec.poll_status != NULL) {
927 enc->enc_boot_hold_ch.ich_func = enc_nop_confighook_cb;
928 enc->enc_boot_hold_ch.ich_arg = enc;
929 config_intrhook_establish(&enc->enc_boot_hold_ch);
930 }
931
932 /*
933 * The softc field is set only once the enc is fully initialized
934 * so that we can rely on this field to detect partially
935 * initialized periph objects in the AC_FOUND_DEVICE handler.
936 */
937 periph->softc = enc;
938
939 cam_periph_unlock(periph);
940 if (enc->enc_vec.poll_status != NULL) {
941 err = enc_kproc_init(enc);
942 if (err) {
943 xpt_print(periph->path,
944 "error %d starting enc_daemon\n", err);
945 goto out;
946 }
947 }
948
336 return (0);
337}
338
339static void
340enc_start(struct cam_periph *p, union ccb *sccb)
341{
342 struct enc_softc *enc;
343
344 enc = p->softc;
345 ENC_DLOG(enc, "%s enter imm=%d prio=%d\n",
346 __func__, p->immediate_priority, p->pinfo.priority);
347 if (p->immediate_priority <= p->pinfo.priority) {
348 SLIST_INSERT_HEAD(&p->ccb_list, &sccb->ccb_h, periph_links.sle);
349 p->immediate_priority = CAM_PRIORITY_NONE;
350 wakeup(&p->ccb_list);
351 } else
352 xpt_release_ccb(sccb);
353 ENC_DLOG(enc, "%s exit\n", __func__);
354}
355
356void
357enc_done(struct cam_periph *periph, union ccb *dccb)
358{
359 wakeup(&dccb->ccb_h.cbfcnp);
360}
361
362int
363enc_error(union ccb *ccb, uint32_t cflags, uint32_t sflags)
364{
365 struct enc_softc *softc;
366 struct cam_periph *periph;
367
368 periph = xpt_path_periph(ccb->ccb_h.path);
369 softc = (struct enc_softc *)periph->softc;
370
371 return (cam_periph_error(ccb, cflags, sflags, &softc->saved_ccb));
372}
373
374static int
375enc_ioctl(struct cdev *dev, u_long cmd, caddr_t arg_addr, int flag,
376 struct thread *td)
377{
378 struct cam_periph *periph;
379 encioc_enc_status_t tmp;
380 encioc_string_t sstr;
381 encioc_elm_status_t elms;
382 encioc_elm_desc_t elmd;
383 encioc_elm_devnames_t elmdn;
384 encioc_element_t *uelm;
385 enc_softc_t *enc;
386 enc_cache_t *cache;
387 void *addr;
388 int error, i;
389
390
391 if (arg_addr)
392 addr = *((caddr_t *) arg_addr);
393 else
394 addr = NULL;
395
396 periph = (struct cam_periph *)dev->si_drv1;
397 if (periph == NULL)
398 return (ENXIO);
399
400 CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering encioctl\n"));
401
402 cam_periph_lock(periph);
403 enc = (struct enc_softc *)periph->softc;
404 cache = &enc->enc_cache;
405
406 /*
407 * Now check to see whether we're initialized or not.
408 * This actually should never fail as we're not supposed
409 * to get past enc_open w/o successfully initializing
410 * things.
411 */
412 if ((enc->enc_flags & ENC_FLAG_INITIALIZED) == 0) {
413 cam_periph_unlock(periph);
414 return (ENXIO);
415 }
416 cam_periph_unlock(periph);
417
418 error = 0;
419
420 CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
421 ("trying to do ioctl %#lx\n", cmd));
422
423 /*
424 * If this command can change the device's state,
425 * we must have the device open for writing.
426 *
427 * For commands that get information about the
428 * device- we don't need to lock the peripheral
429 * if we aren't running a command. The periph
430 * also can't go away while a user process has
431 * it open.
432 */
433 switch (cmd) {
434 case ENCIOC_GETNELM:
435 case ENCIOC_GETELMMAP:
436 case ENCIOC_GETENCSTAT:
437 case ENCIOC_GETELMSTAT:
438 case ENCIOC_GETELMDESC:
439 case ENCIOC_GETELMDEVNAMES:
440 break;
441 default:
442 if ((flag & FWRITE) == 0) {
443 return (EBADF);
444 }
445 }
446
447 /*
448 * XXX The values read here are only valid for the current
449 * configuration generation. We need these ioctls
450 * to also pass in/out a generation number.
451 */
452 sx_slock(&enc->enc_cache_lock);
453 switch (cmd) {
454 case ENCIOC_GETNELM:
455 error = copyout(&cache->nelms, addr, sizeof (cache->nelms));
456 break;
457
458 case ENCIOC_GETELMMAP:
459 for (uelm = addr, i = 0; i != cache->nelms; i++) {
460 encioc_element_t kelm;
461 kelm.elm_idx = i;
462 kelm.elm_subenc_id = cache->elm_map[i].subenclosure;
463 kelm.elm_type = cache->elm_map[i].enctype;
464 error = copyout(&kelm, &uelm[i], sizeof(kelm));
465 if (error)
466 break;
467 }
468 break;
469
470 case ENCIOC_GETENCSTAT:
471 cam_periph_lock(periph);
472 error = enc->enc_vec.get_enc_status(enc, 1);
473 if (error) {
474 cam_periph_unlock(periph);
475 break;
476 }
477 tmp = cache->enc_status;
478 cam_periph_unlock(periph);
479 error = copyout(&tmp, addr, sizeof(tmp));
480 cache->enc_status = tmp;
481 break;
482
483 case ENCIOC_SETENCSTAT:
484 error = copyin(addr, &tmp, sizeof(tmp));
485 if (error)
486 break;
487 cam_periph_lock(periph);
488 error = enc->enc_vec.set_enc_status(enc, tmp, 1);
489 cam_periph_unlock(periph);
490 break;
491
492 case ENCIOC_GETSTRING:
493 case ENCIOC_SETSTRING:
494 if (enc->enc_vec.handle_string == NULL) {
495 error = EINVAL;
496 break;
497 }
498 error = copyin(addr, &sstr, sizeof(sstr));
499 if (error)
500 break;
501 cam_periph_lock(periph);
502 error = enc->enc_vec.handle_string(enc, &sstr, cmd);
503 cam_periph_unlock(periph);
504 break;
505
506 case ENCIOC_GETELMSTAT:
507 error = copyin(addr, &elms, sizeof(elms));
508 if (error)
509 break;
510 if (elms.elm_idx >= cache->nelms) {
511 error = EINVAL;
512 break;
513 }
514 cam_periph_lock(periph);
515 error = enc->enc_vec.get_elm_status(enc, &elms, 1);
516 cam_periph_unlock(periph);
517 if (error)
518 break;
519 error = copyout(&elms, addr, sizeof(elms));
520 break;
521
522 case ENCIOC_GETELMDESC:
523 error = copyin(addr, &elmd, sizeof(elmd));
524 if (error)
525 break;
526 if (elmd.elm_idx >= cache->nelms) {
527 error = EINVAL;
528 break;
529 }
530 if (enc->enc_vec.get_elm_desc != NULL) {
531 error = enc->enc_vec.get_elm_desc(enc, &elmd);
532 if (error)
533 break;
534 } else
535 elmd.elm_desc_len = 0;
536 error = copyout(&elmd, addr, sizeof(elmd));
537 break;
538
539 case ENCIOC_GETELMDEVNAMES:
540 if (enc->enc_vec.get_elm_devnames == NULL) {
541 error = EINVAL;
542 break;
543 }
544 error = copyin(addr, &elmdn, sizeof(elmdn));
545 if (error)
546 break;
547 if (elmdn.elm_idx >= cache->nelms) {
548 error = EINVAL;
549 break;
550 }
551 cam_periph_lock(periph);
552 error = (*enc->enc_vec.get_elm_devnames)(enc, &elmdn);
553 cam_periph_unlock(periph);
554 if (error)
555 break;
556 error = copyout(&elmdn, addr, sizeof(elmdn));
557 break;
558
559 case ENCIOC_SETELMSTAT:
560 error = copyin(addr, &elms, sizeof(elms));
561 if (error)
562 break;
563
564 if (elms.elm_idx >= cache->nelms) {
565 error = EINVAL;
566 break;
567 }
568 cam_periph_lock(periph);
569 error = enc->enc_vec.set_elm_status(enc, &elms, 1);
570 cam_periph_unlock(periph);
571
572 break;
573
574 case ENCIOC_INIT:
575
576 cam_periph_lock(periph);
577 error = enc->enc_vec.init_enc(enc);
578 cam_periph_unlock(periph);
579 break;
580
581 default:
582 cam_periph_lock(periph);
583 error = cam_periph_ioctl(periph, cmd, arg_addr, enc_error);
584 cam_periph_unlock(periph);
585 break;
586 }
587 sx_sunlock(&enc->enc_cache_lock);
588 return (error);
589}
590
591int
592enc_runcmd(struct enc_softc *enc, char *cdb, int cdbl, char *dptr, int *dlenp)
593{
594 int error, dlen, tdlen;
595 ccb_flags ddf;
596 union ccb *ccb;
597
598 CAM_DEBUG(enc->periph->path, CAM_DEBUG_TRACE,
599 ("entering enc_runcmd\n"));
600 if (dptr) {
601 if ((dlen = *dlenp) < 0) {
602 dlen = -dlen;
603 ddf = CAM_DIR_OUT;
604 } else {
605 ddf = CAM_DIR_IN;
606 }
607 } else {
608 dlen = 0;
609 ddf = CAM_DIR_NONE;
610 }
611
612 if (cdbl > IOCDBLEN) {
613 cdbl = IOCDBLEN;
614 }
615
616 ccb = cam_periph_getccb(enc->periph, CAM_PRIORITY_NORMAL);
617 if (enc->enc_type == ENC_SEMB_SES || enc->enc_type == ENC_SEMB_SAFT) {
618 tdlen = min(dlen, 1020);
619 tdlen = (tdlen + 3) & ~3;
620 cam_fill_ataio(&ccb->ataio, 0, enc_done, ddf, 0, dptr, tdlen,
621 30 * 1000);
622 if (cdb[0] == RECEIVE_DIAGNOSTIC)
623 ata_28bit_cmd(&ccb->ataio,
624 ATA_SEP_ATTN, cdb[2], 0x02, tdlen / 4);
625 else if (cdb[0] == SEND_DIAGNOSTIC)
626 ata_28bit_cmd(&ccb->ataio,
627 ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0,
628 0x82, tdlen / 4);
629 else if (cdb[0] == READ_BUFFER)
630 ata_28bit_cmd(&ccb->ataio,
631 ATA_SEP_ATTN, cdb[2], 0x00, tdlen / 4);
632 else
633 ata_28bit_cmd(&ccb->ataio,
634 ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0,
635 0x80, tdlen / 4);
636 } else {
637 tdlen = dlen;
638 cam_fill_csio(&ccb->csio, 0, enc_done, ddf, MSG_SIMPLE_Q_TAG,
639 dptr, dlen, sizeof (struct scsi_sense_data), cdbl,
640 60 * 1000);
641 bcopy(cdb, ccb->csio.cdb_io.cdb_bytes, cdbl);
642 }
643
644 error = cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, ENC_FLAGS, NULL);
645 if (error) {
646 if (dptr) {
647 *dlenp = dlen;
648 }
649 } else {
650 if (dptr) {
651 if (ccb->ccb_h.func_code == XPT_ATA_IO)
652 *dlenp = ccb->ataio.resid;
653 else
654 *dlenp = ccb->csio.resid;
655 *dlenp += tdlen - dlen;
656 }
657 }
658 xpt_release_ccb(ccb);
659 CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE,
660 ("exiting enc_runcmd: *dlenp = %d\n", *dlenp));
661 return (error);
662}
663
664void
665enc_log(struct enc_softc *enc, const char *fmt, ...)
666{
667 va_list ap;
668
669 printf("%s%d: ", enc->periph->periph_name, enc->periph->unit_number);
670 va_start(ap, fmt);
671 vprintf(fmt, ap);
672 va_end(ap);
673}
674
675/*
676 * The code after this point runs on many platforms,
677 * so forgive the slightly awkward and nonconforming
678 * appearance.
679 */
680
681/*
682 * Is this a device that supports enclosure services?
683 *
684 * It's a pretty simple ruleset- if it is device type
685 * 0x0D (13), it's an ENCLOSURE device.
686 */
687
688#define SAFTE_START 44
689#define SAFTE_END 50
690#define SAFTE_LEN SAFTE_END-SAFTE_START
691
692static enctyp
693enc_type(struct ccb_getdev *cgd)
694{
695 int buflen;
696 unsigned char *iqd;
697
698 if (cgd->protocol == PROTO_SEMB) {
699 iqd = (unsigned char *)&cgd->ident_data;
700 if (STRNCMP(iqd + 43, "S-E-S", 5) == 0)
701 return (ENC_SEMB_SES);
702 else if (STRNCMP(iqd + 43, "SAF-TE", 6) == 0)
703 return (ENC_SEMB_SAFT);
704 return (ENC_NONE);
705
706 } else if (cgd->protocol != PROTO_SCSI)
707 return (ENC_NONE);
708
709 iqd = (unsigned char *)&cgd->inq_data;
710 buflen = min(sizeof(cgd->inq_data),
711 SID_ADDITIONAL_LENGTH(&cgd->inq_data));
712
713 if ((iqd[0] & 0x1f) == T_ENCLOSURE) {
714 if ((iqd[2] & 0x7) > 2) {
715 return (ENC_SES);
716 } else {
717 return (ENC_SES_SCSI2);
718 }
719 return (ENC_NONE);
720 }
721
722#ifdef ENC_ENABLE_PASSTHROUGH
723 if ((iqd[6] & 0x40) && (iqd[2] & 0x7) >= 2) {
724 /*
725 * PassThrough Device.
726 */
727 return (ENC_ENC_PASSTHROUGH);
728 }
729#endif
730
731 /*
732 * The comparison is short for a reason-
733 * some vendors were chopping it short.
734 */
735
736 if (buflen < SAFTE_END - 2) {
737 return (ENC_NONE);
738 }
739
740 if (STRNCMP((char *)&iqd[SAFTE_START], "SAF-TE", SAFTE_LEN - 2) == 0) {
741 return (ENC_SAFT);
742 }
743 return (ENC_NONE);
744}
745
746/*================== Enclosure Monitoring/Processing Daemon ==================*/
747/**
748 * \brief Queue an update request for a given action, if needed.
749 *
750 * \param enc SES softc to queue the request for.
751 * \param action Action requested.
752 */
753void
754enc_update_request(enc_softc_t *enc, uint32_t action)
755{
756 if ((enc->pending_actions & (0x1 << action)) == 0) {
757 enc->pending_actions |= (0x1 << action);
758 ENC_DLOG(enc, "%s: queing requested action %d\n",
759 __func__, action);
760 if (enc->current_action == ENC_UPDATE_NONE)
761 wakeup(enc->enc_daemon);
762 } else {
763 ENC_DLOG(enc, "%s: ignoring requested action %d - "
764 "Already queued\n", __func__, action);
765 }
766}
767
768/**
769 * \brief Invoke the handler of the highest priority pending
770 * state in the SES state machine.
771 *
772 * \param enc The SES instance invoking the state machine.
773 */
774static void
775enc_fsm_step(enc_softc_t *enc)
776{
777 union ccb *ccb;
778 uint8_t *buf;
779 struct enc_fsm_state *cur_state;
780 int error;
781 uint32_t xfer_len;
782
783 ENC_DLOG(enc, "%s enter %p\n", __func__, enc);
784
785 enc->current_action = ffs(enc->pending_actions) - 1;
786 enc->pending_actions &= ~(0x1 << enc->current_action);
787
788 cur_state = &enc->enc_fsm_states[enc->current_action];
789
790 buf = NULL;
791 if (cur_state->buf_size != 0) {
792 cam_periph_unlock(enc->periph);
793 buf = malloc(cur_state->buf_size, M_SCSIENC, M_WAITOK|M_ZERO);
794 cam_periph_lock(enc->periph);
795 }
796
797 error = 0;
798 ccb = NULL;
799 if (cur_state->fill != NULL) {
800 ccb = cam_periph_getccb(enc->periph, CAM_PRIORITY_NORMAL);
801
802 error = cur_state->fill(enc, cur_state, ccb, buf);
803 if (error != 0)
804 goto done;
805
806 error = cam_periph_runccb(ccb, cur_state->error,
807 ENC_CFLAGS,
808 ENC_FLAGS|SF_QUIET_IR, NULL);
809 }
810
811 if (ccb != NULL) {
812 if (ccb->ccb_h.func_code == XPT_ATA_IO)
813 xfer_len = ccb->ataio.dxfer_len - ccb->ataio.resid;
814 else
815 xfer_len = ccb->csio.dxfer_len - ccb->csio.resid;
816 } else
817 xfer_len = 0;
818
819 cam_periph_unlock(enc->periph);
820 cur_state->done(enc, cur_state, ccb, &buf, error, xfer_len);
821 cam_periph_lock(enc->periph);
822
823done:
824 ENC_DLOG(enc, "%s exit - result %d\n", __func__, error);
825 ENC_FREE_AND_NULL(buf);
826 if (ccb != NULL)
827 xpt_release_ccb(ccb);
828}
829
830/**
831 * \invariant Called with cam_periph mutex held.
832 */
833static void
834enc_status_updater(void *arg)
835{
836 enc_softc_t *enc;
837
838 enc = arg;
839 if (enc->enc_vec.poll_status != NULL)
840 enc->enc_vec.poll_status(enc);
841}
842
843static void
844enc_daemon(void *arg)
845{
846 enc_softc_t *enc;
847
848 enc = arg;
849
850 cam_periph_lock(enc->periph);
851 while ((enc->enc_flags & ENC_FLAG_SHUTDOWN) == 0) {
852 if (enc->pending_actions == 0) {
853 struct intr_config_hook *hook;
854
855 /*
856 * Reset callout and msleep, or
857 * issue timed task completion
858 * status command.
859 */
860 enc->current_action = ENC_UPDATE_NONE;
861
862 /*
863 * We've been through our state machine at least
864 * once. Allow the transition to userland.
865 */
866 hook = &enc->enc_boot_hold_ch;
867 if (hook->ich_func != NULL) {
868 config_intrhook_disestablish(hook);
869 hook->ich_func = NULL;
870 }
871
872 callout_reset(&enc->status_updater, 60*hz,
873 enc_status_updater, enc);
874
875 cam_periph_sleep(enc->periph, enc->enc_daemon,
876 PUSER, "idle", 0);
877 } else {
878 enc_fsm_step(enc);
879 }
880 }
881 enc->enc_daemon = NULL;
882 cam_periph_unlock(enc->periph);
883 cam_periph_release(enc->periph);
884 kproc_exit(0);
885}
886
887static int
888enc_kproc_init(enc_softc_t *enc)
889{
890 int result;
891
892 callout_init_mtx(&enc->status_updater, enc->periph->sim->mtx, 0);
893
894 if (cam_periph_acquire(enc->periph) != CAM_REQ_CMP)
895 return (ENXIO);
896
897 result = kproc_create(enc_daemon, enc, &enc->enc_daemon, /*flags*/0,
898 /*stackpgs*/0, "enc_daemon%d",
899 enc->periph->unit_number);
900 if (result == 0) {
901 /* Do an initial load of all page data. */
902 cam_periph_lock(enc->periph);
903 enc->enc_vec.poll_status(enc);
904 cam_periph_unlock(enc->periph);
905 } else
906 cam_periph_release(enc->periph);
907 return (result);
908}
909
910/**
911 * \brief Interrupt configuration hook callback associated with
912 * enc_boot_hold_ch.
913 *
914 * Since interrupts are always functional at the time of enclosure
915 * configuration, there is nothing to be done when the callback occurs.
916 * This hook is only registered to hold up boot processing while initial
917 * eclosure processing occurs.
918 *
919 * \param arg The enclosure softc, but currently unused in this callback.
920 */
921static void
922enc_nop_confighook_cb(void *arg __unused)
923{
924}
925
926static cam_status
927enc_ctor(struct cam_periph *periph, void *arg)
928{
929 cam_status status = CAM_REQ_CMP_ERR;
930 int err;
931 enc_softc_t *enc;
932 struct ccb_getdev *cgd;
933 char *tname;
934
935 cgd = (struct ccb_getdev *)arg;
936 if (cgd == NULL) {
937 printf("enc_ctor: no getdev CCB, can't register device\n");
938 goto out;
939 }
940
941 enc = ENC_MALLOCZ(sizeof(*enc));
942 if (enc == NULL) {
943 printf("enc_ctor: Unable to probe new device. "
944 "Unable to allocate enc\n");
945 goto out;
946 }
947 enc->periph = periph;
948 enc->current_action = ENC_UPDATE_INVALID;
949
950 enc->enc_type = enc_type(cgd);
951 sx_init(&enc->enc_cache_lock, "enccache");
952
953 switch (enc->enc_type) {
954 case ENC_SES:
955 case ENC_SES_SCSI2:
956 case ENC_SES_PASSTHROUGH:
957 case ENC_SEMB_SES:
958 err = ses_softc_init(enc);
959 break;
960 case ENC_SAFT:
961 case ENC_SEMB_SAFT:
962 err = safte_softc_init(enc);
963 break;
964 case ENC_NONE:
965 default:
966 ENC_FREE(enc);
967 return (CAM_REQ_CMP_ERR);
968 }
969
970 if (err) {
971 xpt_print(periph->path, "error %d initializing\n", err);
972 goto out;
973 }
974
975 /*
976 * Hold off userland until we have made at least one pass
977 * through our state machine so that physical path data is
978 * present.
979 */
980 if (enc->enc_vec.poll_status != NULL) {
981 enc->enc_boot_hold_ch.ich_func = enc_nop_confighook_cb;
982 enc->enc_boot_hold_ch.ich_arg = enc;
983 config_intrhook_establish(&enc->enc_boot_hold_ch);
984 }
985
986 /*
987 * The softc field is set only once the enc is fully initialized
988 * so that we can rely on this field to detect partially
989 * initialized periph objects in the AC_FOUND_DEVICE handler.
990 */
991 periph->softc = enc;
992
993 cam_periph_unlock(periph);
994 if (enc->enc_vec.poll_status != NULL) {
995 err = enc_kproc_init(enc);
996 if (err) {
997 xpt_print(periph->path,
998 "error %d starting enc_daemon\n", err);
999 goto out;
1000 }
1001 }
1002
1003 /*
1004 * Acquire a reference to the periph before we create the devfs
1005 * instance for it. We'll release this reference once the devfs
1006 * instance has been freed.
1007 */
949 if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
950 xpt_print(periph->path, "%s: lost periph during "
951 "registration!\n", __func__);
952 cam_periph_lock(periph);
953
954 return (CAM_REQ_CMP_ERR);
955 }
956
957 enc->enc_dev = make_dev(&enc_cdevsw, periph->unit_number,
958 UID_ROOT, GID_OPERATOR, 0600, "%s%d",
959 periph->periph_name, periph->unit_number);
960
961 cam_periph_lock(periph);
962 enc->enc_dev->si_drv1 = periph;
963
964 enc->enc_flags |= ENC_FLAG_INITIALIZED;
965
966 /*
967 * Add an async callback so that we get notified if this
968 * device goes away.
969 */
970 xpt_register_async(AC_LOST_DEVICE, enc_async, periph, periph->path);
971
972 switch (enc->enc_type) {
973 default:
974 case ENC_NONE:
975 tname = "No ENC device";
976 break;
977 case ENC_SES_SCSI2:
978 tname = "SCSI-2 ENC Device";
979 break;
980 case ENC_SES:
981 tname = "SCSI-3 ENC Device";
982 break;
983 case ENC_SES_PASSTHROUGH:
984 tname = "ENC Passthrough Device";
985 break;
986 case ENC_SAFT:
987 tname = "SAF-TE Compliant Device";
988 break;
989 case ENC_SEMB_SES:
990 tname = "SEMB SES Device";
991 break;
992 case ENC_SEMB_SAFT:
993 tname = "SEMB SAF-TE Device";
994 break;
995 }
996 xpt_announce_periph(periph, tname);
997 status = CAM_REQ_CMP;
998
999out:
1000 if (status != CAM_REQ_CMP)
1001 enc_dtor(periph);
1002 return (status);
1003}
1004
1008 if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
1009 xpt_print(periph->path, "%s: lost periph during "
1010 "registration!\n", __func__);
1011 cam_periph_lock(periph);
1012
1013 return (CAM_REQ_CMP_ERR);
1014 }
1015
1016 enc->enc_dev = make_dev(&enc_cdevsw, periph->unit_number,
1017 UID_ROOT, GID_OPERATOR, 0600, "%s%d",
1018 periph->periph_name, periph->unit_number);
1019
1020 cam_periph_lock(periph);
1021 enc->enc_dev->si_drv1 = periph;
1022
1023 enc->enc_flags |= ENC_FLAG_INITIALIZED;
1024
1025 /*
1026 * Add an async callback so that we get notified if this
1027 * device goes away.
1028 */
1029 xpt_register_async(AC_LOST_DEVICE, enc_async, periph, periph->path);
1030
1031 switch (enc->enc_type) {
1032 default:
1033 case ENC_NONE:
1034 tname = "No ENC device";
1035 break;
1036 case ENC_SES_SCSI2:
1037 tname = "SCSI-2 ENC Device";
1038 break;
1039 case ENC_SES:
1040 tname = "SCSI-3 ENC Device";
1041 break;
1042 case ENC_SES_PASSTHROUGH:
1043 tname = "ENC Passthrough Device";
1044 break;
1045 case ENC_SAFT:
1046 tname = "SAF-TE Compliant Device";
1047 break;
1048 case ENC_SEMB_SES:
1049 tname = "SEMB SES Device";
1050 break;
1051 case ENC_SEMB_SAFT:
1052 tname = "SEMB SAF-TE Device";
1053 break;
1054 }
1055 xpt_announce_periph(periph, tname);
1056 status = CAM_REQ_CMP;
1057
1058out:
1059 if (status != CAM_REQ_CMP)
1060 enc_dtor(periph);
1061 return (status);
1062}
1063