| Commit | Line | Data |
|---|---|---|
| 984263bc | 1 | /* $FreeBSD: src/sys/cam/scsi/scsi_ses.c,v 1.8.2.2 2000/08/08 23:19:21 mjacob Exp $ */ |
| 1c8b7a9a | 2 | /* $DragonFly: src/sys/bus/cam/scsi/scsi_ses.c,v 1.29 2008/05/18 20:30:20 pavalos Exp $ */ |
| 984263bc MD |
3 | /* |
| 4 | * Copyright (c) 2000 Matthew Jacob | |
| 5 | * All rights reserved. | |
| 6 | * | |
| 7 | * Redistribution and use in source and binary forms, with or without | |
| 8 | * modification, are permitted provided that the following conditions | |
| 9 | * are met: | |
| 10 | * 1. Redistributions of source code must retain the above copyright | |
| 11 | * notice, this list of conditions, and the following disclaimer, | |
| 12 | * without modification, immediately at the beginning of the file. | |
| 13 | * 2. The name of the author may not be used to endorse or promote products | |
| 14 | * derived from this software without specific prior written permission. | |
| 15 | * | |
| 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR | |
| 20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 26 | * SUCH DAMAGE. | |
| 27 | * | |
| 28 | */ | |
| 29 | #include <sys/param.h> | |
| 30 | #include <sys/queue.h> | |
| 31 | #include <sys/systm.h> | |
| 32 | #include <sys/kernel.h> | |
| 33 | #include <sys/types.h> | |
| 34 | #include <sys/malloc.h> | |
| 35 | #include <sys/fcntl.h> | |
| 984263bc MD |
36 | #include <sys/conf.h> |
| 37 | #include <sys/buf.h> | |
| 38 | #include <sys/errno.h> | |
| 39 | #include <sys/devicestat.h> | |
| 4e01b467 | 40 | #include <sys/thread2.h> |
| 984263bc MD |
41 | #include <machine/stdarg.h> |
| 42 | ||
| 1f2de5d4 MD |
43 | #include "../cam.h" |
| 44 | #include "../cam_ccb.h" | |
| 45 | #include "../cam_extend.h" | |
| 46 | #include "../cam_periph.h" | |
| 47 | #include "../cam_xpt_periph.h" | |
| 1f2de5d4 | 48 | #include "../cam_debug.h" |
| 1c8b7a9a | 49 | #include "../cam_sim.h" |
| 984263bc | 50 | |
| 1f2de5d4 MD |
51 | #include "scsi_all.h" |
| 52 | #include "scsi_message.h" | |
| 1f2de5d4 | 53 | #include "scsi_ses.h" |
| 984263bc MD |
54 | |
| 55 | #include <opt_ses.h> | |
| 56 | ||
| bc6e3c73 PA |
57 | MALLOC_DEFINE(M_SCSISES, "SCSI SES", "SCSI SES buffers"); |
| 58 | ||
| 984263bc MD |
59 | /* |
| 60 | * Platform Independent Driver Internal Definitions for SES devices. | |
| 61 | */ | |
| 62 | typedef enum { | |
| 63 | SES_NONE, | |
| 64 | SES_SES_SCSI2, | |
| 65 | SES_SES, | |
| 66 | SES_SES_PASSTHROUGH, | |
| 67 | SES_SEN, | |
| 68 | SES_SAFT | |
| 69 | } enctyp; | |
| 70 | ||
| 71 | struct ses_softc; | |
| 72 | typedef struct ses_softc ses_softc_t; | |
| 73 | typedef struct { | |
| 74 | int (*softc_init)(ses_softc_t *, int); | |
| 75 | int (*init_enc)(ses_softc_t *); | |
| 76 | int (*get_encstat)(ses_softc_t *, int); | |
| 77 | int (*set_encstat)(ses_softc_t *, ses_encstat, int); | |
| 78 | int (*get_objstat)(ses_softc_t *, ses_objstat *, int); | |
| 79 | int (*set_objstat)(ses_softc_t *, ses_objstat *, int); | |
| 80 | } encvec; | |
| 81 | ||
| 82 | #define ENCI_SVALID 0x80 | |
| 83 | ||
| 84 | typedef struct { | |
| 85 | uint32_t | |
| 86 | enctype : 8, /* enclosure type */ | |
| 87 | subenclosure : 8, /* subenclosure id */ | |
| 88 | svalid : 1, /* enclosure information valid */ | |
| 89 | priv : 15; /* private data, per object */ | |
| 90 | uint8_t encstat[4]; /* state && stats */ | |
| 91 | } encobj; | |
| 92 | ||
| 93 | #define SEN_ID "UNISYS SUN_SEN" | |
| 94 | #define SEN_ID_LEN 24 | |
| 95 | ||
| 96 | ||
| 97 | static enctyp ses_type(void *, int); | |
| 98 | ||
| 99 | ||
| 100 | /* Forward reference to Enclosure Functions */ | |
| 101 | static int ses_softc_init(ses_softc_t *, int); | |
| 102 | static int ses_init_enc(ses_softc_t *); | |
| 103 | static int ses_get_encstat(ses_softc_t *, int); | |
| 104 | static int ses_set_encstat(ses_softc_t *, uint8_t, int); | |
| 105 | static int ses_get_objstat(ses_softc_t *, ses_objstat *, int); | |
| 106 | static int ses_set_objstat(ses_softc_t *, ses_objstat *, int); | |
| 107 | ||
| 108 | static int safte_softc_init(ses_softc_t *, int); | |
| 109 | static int safte_init_enc(ses_softc_t *); | |
| 110 | static int safte_get_encstat(ses_softc_t *, int); | |
| 111 | static int safte_set_encstat(ses_softc_t *, uint8_t, int); | |
| 112 | static int safte_get_objstat(ses_softc_t *, ses_objstat *, int); | |
| 113 | static int safte_set_objstat(ses_softc_t *, ses_objstat *, int); | |
| 114 | ||
| 115 | /* | |
| 116 | * Platform implementation defines/functions for SES internal kernel stuff | |
| 117 | */ | |
| 118 | ||
| 119 | #define STRNCMP strncmp | |
| 85f8e2ea | 120 | #define PRINTF kprintf |
| 984263bc MD |
121 | #define SES_LOG ses_log |
| 122 | #ifdef DEBUG | |
| 123 | #define SES_DLOG ses_log | |
| 124 | #else | |
| 125 | #define SES_DLOG if (0) ses_log | |
| 126 | #endif | |
| 127 | #define SES_VLOG if (bootverbose) ses_log | |
| bc6e3c73 PA |
128 | #define SES_MALLOC(amt) kmalloc(amt, M_SCSISES, M_INTWAIT) |
| 129 | #define SES_FREE(ptr, amt) kfree(ptr, M_SCSISES) | |
| 984263bc MD |
130 | #define MEMZERO bzero |
| 131 | #define MEMCPY(dest, src, amt) bcopy(src, dest, amt) | |
| 132 | ||
| 133 | static int ses_runcmd(struct ses_softc *, char *, int, char *, int *); | |
| 134 | static void ses_log(struct ses_softc *, const char *, ...); | |
| 135 | ||
| 136 | /* | |
| 137 | * Gerenal FreeBSD kernel stuff. | |
| 138 | */ | |
| 139 | ||
| 140 | ||
| 141 | #define ccb_state ppriv_field0 | |
| 81b5c339 | 142 | #define ccb_bio ppriv_ptr1 |
| 984263bc MD |
143 | |
| 144 | struct ses_softc { | |
| 145 | enctyp ses_type; /* type of enclosure */ | |
| 146 | encvec ses_vec; /* vector to handlers */ | |
| 147 | void * ses_private; /* per-type private data */ | |
| 148 | encobj * ses_objmap; /* objects */ | |
| 149 | u_int32_t ses_nobjects; /* number of objects */ | |
| 150 | ses_encstat ses_encstat; /* overall status */ | |
| 151 | u_int8_t ses_flags; | |
| 152 | union ccb ses_saved_ccb; | |
| 984263bc MD |
153 | struct cam_periph *periph; |
| 154 | }; | |
| 155 | #define SES_FLAG_INVALID 0x01 | |
| 156 | #define SES_FLAG_OPEN 0x02 | |
| 157 | #define SES_FLAG_INITIALIZED 0x04 | |
| 158 | ||
| 159 | #define SESUNIT(x) (minor((x))) | |
| 160 | #define SES_CDEV_MAJOR 110 | |
| 161 | ||
| 162 | static d_open_t sesopen; | |
| 163 | static d_close_t sesclose; | |
| 164 | static d_ioctl_t sesioctl; | |
| 165 | static periph_init_t sesinit; | |
| 166 | static periph_ctor_t sesregister; | |
| 167 | static periph_oninv_t sesoninvalidate; | |
| 168 | static periph_dtor_t sescleanup; | |
| 169 | static periph_start_t sesstart; | |
| 170 | ||
| 171 | static void sesasync(void *, u_int32_t, struct cam_path *, void *); | |
| 172 | static void sesdone(struct cam_periph *, union ccb *); | |
| 173 | static int seserror(union ccb *, u_int32_t, u_int32_t); | |
| 174 | ||
| 175 | static struct periph_driver sesdriver = { | |
| 176 | sesinit, "ses", | |
| 177 | TAILQ_HEAD_INITIALIZER(sesdriver.units), /* generation */ 0 | |
| 178 | }; | |
| 179 | ||
| 2ad14cb5 | 180 | PERIPHDRIVER_DECLARE(ses, sesdriver); |
| 984263bc | 181 | |
| fef8985e MD |
182 | static struct dev_ops ses_ops = { |
| 183 | { "ses", SES_CDEV_MAJOR, 0 }, | |
| 184 | .d_open = sesopen, | |
| 185 | .d_close = sesclose, | |
| 186 | .d_ioctl = sesioctl, | |
| 984263bc MD |
187 | }; |
| 188 | static struct extend_array *sesperiphs; | |
| 189 | ||
| fb2e6209 | 190 | static void |
| 984263bc MD |
191 | sesinit(void) |
| 192 | { | |
| 193 | cam_status status; | |
| 984263bc MD |
194 | |
| 195 | /* | |
| 196 | * Create our extend array for storing the devices we attach to. | |
| 197 | */ | |
| 198 | sesperiphs = cam_extend_new(); | |
| 199 | if (sesperiphs == NULL) { | |
| 85f8e2ea | 200 | kprintf("ses: Failed to alloc extend array!\n"); |
| 984263bc MD |
201 | return; |
| 202 | } | |
| 203 | ||
| 204 | /* | |
| 205 | * Install a global async callback. This callback will | |
| 206 | * receive async callbacks like "new device found". | |
| 207 | */ | |
| 1c8b7a9a | 208 | status = xpt_register_async(AC_FOUND_DEVICE, sesasync, NULL, NULL); |
| 984263bc MD |
209 | |
| 210 | if (status != CAM_REQ_CMP) { | |
| 85f8e2ea | 211 | kprintf("ses: Failed to attach master async callback " |
| 984263bc MD |
212 | "due to status 0x%x!\n", status); |
| 213 | } | |
| 214 | } | |
| 215 | ||
| 216 | static void | |
| 217 | sesoninvalidate(struct cam_periph *periph) | |
| 218 | { | |
| 219 | struct ses_softc *softc; | |
| 984263bc MD |
220 | |
| 221 | softc = (struct ses_softc *)periph->softc; | |
| 222 | ||
| 223 | /* | |
| 224 | * Unregister any async callbacks. | |
| 225 | */ | |
| 1c8b7a9a | 226 | xpt_register_async(0, sesasync, periph, periph->path); |
| 984263bc MD |
227 | |
| 228 | softc->ses_flags |= SES_FLAG_INVALID; | |
| 229 | ||
| 1c8b7a9a | 230 | xpt_print(periph->path, "lost device\n"); |
| 984263bc MD |
231 | } |
| 232 | ||
| 233 | static void | |
| 234 | sescleanup(struct cam_periph *periph) | |
| 235 | { | |
| 236 | struct ses_softc *softc; | |
| 237 | ||
| 238 | softc = (struct ses_softc *)periph->softc; | |
| 239 | ||
| 984263bc | 240 | cam_extend_release(sesperiphs, periph->unit_number); |
| 1c8b7a9a | 241 | xpt_print(periph->path, "removing device entry\n"); |
| cd29885a | 242 | dev_ops_remove_minor(&ses_ops, periph->unit_number); |
| bc6e3c73 | 243 | kfree(softc, M_SCSISES); |
| 984263bc MD |
244 | } |
| 245 | ||
| 246 | static void | |
| 247 | sesasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) | |
| 248 | { | |
| 249 | struct cam_periph *periph; | |
| 250 | ||
| 251 | periph = (struct cam_periph *)callback_arg; | |
| 252 | ||
| 253 | switch(code) { | |
| 254 | case AC_FOUND_DEVICE: | |
| 255 | { | |
| 256 | cam_status status; | |
| 257 | struct ccb_getdev *cgd; | |
| d878b746 | 258 | int inq_len; |
| 984263bc MD |
259 | |
| 260 | cgd = (struct ccb_getdev *)arg; | |
| e9936c96 PA |
261 | if (arg == NULL) { |
| 262 | break; | |
| 263 | } | |
| 984263bc | 264 | |
| d878b746 PA |
265 | inq_len = cgd->inq_data.additional_length + 4; |
| 266 | ||
| 984263bc MD |
267 | /* |
| 268 | * PROBLEM: WE NEED TO LOOK AT BYTES 48-53 TO SEE IF THIS IS | |
| 269 | * PROBLEM: IS A SAF-TE DEVICE. | |
| 270 | */ | |
| d878b746 | 271 | switch (ses_type(&cgd->inq_data, inq_len)) { |
| 984263bc MD |
272 | case SES_SES: |
| 273 | case SES_SES_SCSI2: | |
| 274 | case SES_SES_PASSTHROUGH: | |
| 275 | case SES_SEN: | |
| 276 | case SES_SAFT: | |
| 277 | break; | |
| 278 | default: | |
| 279 | return; | |
| 280 | } | |
| 281 | ||
| 282 | status = cam_periph_alloc(sesregister, sesoninvalidate, | |
| 283 | sescleanup, sesstart, "ses", CAM_PERIPH_BIO, | |
| 284 | cgd->ccb_h.path, sesasync, AC_FOUND_DEVICE, cgd); | |
| 285 | ||
| 286 | if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) { | |
| 85f8e2ea | 287 | kprintf("sesasync: Unable to probe new device due to " |
| 984263bc MD |
288 | "status 0x%x\n", status); |
| 289 | } | |
| 290 | break; | |
| 291 | } | |
| 292 | default: | |
| 293 | cam_periph_async(periph, code, path, arg); | |
| 294 | break; | |
| 295 | } | |
| 296 | } | |
| 297 | ||
| 298 | static cam_status | |
| 299 | sesregister(struct cam_periph *periph, void *arg) | |
| 300 | { | |
| 301 | struct ses_softc *softc; | |
| 984263bc MD |
302 | struct ccb_getdev *cgd; |
| 303 | char *tname; | |
| 304 | ||
| 305 | cgd = (struct ccb_getdev *)arg; | |
| 306 | if (periph == NULL) { | |
| 85f8e2ea | 307 | kprintf("sesregister: periph was NULL!!\n"); |
| 984263bc MD |
308 | return (CAM_REQ_CMP_ERR); |
| 309 | } | |
| 310 | ||
| 311 | if (cgd == NULL) { | |
| 85f8e2ea | 312 | kprintf("sesregister: no getdev CCB, can't register device\n"); |
| 984263bc MD |
313 | return (CAM_REQ_CMP_ERR); |
| 314 | } | |
| 315 | ||
| bc6e3c73 | 316 | softc = kmalloc(sizeof (struct ses_softc), M_SCSISES, M_INTWAIT | M_ZERO); |
| 984263bc MD |
317 | periph->softc = softc; |
| 318 | softc->periph = periph; | |
| 319 | ||
| 320 | softc->ses_type = ses_type(&cgd->inq_data, sizeof (cgd->inq_data)); | |
| 321 | ||
| 322 | switch (softc->ses_type) { | |
| 323 | case SES_SES: | |
| 324 | case SES_SES_SCSI2: | |
| 325 | case SES_SES_PASSTHROUGH: | |
| 326 | softc->ses_vec.softc_init = ses_softc_init; | |
| 327 | softc->ses_vec.init_enc = ses_init_enc; | |
| 328 | softc->ses_vec.get_encstat = ses_get_encstat; | |
| 329 | softc->ses_vec.set_encstat = ses_set_encstat; | |
| 330 | softc->ses_vec.get_objstat = ses_get_objstat; | |
| 331 | softc->ses_vec.set_objstat = ses_set_objstat; | |
| 332 | break; | |
| 333 | case SES_SAFT: | |
| 334 | softc->ses_vec.softc_init = safte_softc_init; | |
| 335 | softc->ses_vec.init_enc = safte_init_enc; | |
| 336 | softc->ses_vec.get_encstat = safte_get_encstat; | |
| 337 | softc->ses_vec.set_encstat = safte_set_encstat; | |
| 338 | softc->ses_vec.get_objstat = safte_get_objstat; | |
| 339 | softc->ses_vec.set_objstat = safte_set_objstat; | |
| 340 | break; | |
| 341 | case SES_SEN: | |
| 342 | break; | |
| 343 | case SES_NONE: | |
| 344 | default: | |
| bc6e3c73 | 345 | kfree(softc, M_SCSISES); |
| 984263bc MD |
346 | return (CAM_REQ_CMP_ERR); |
| 347 | } | |
| 348 | ||
| 349 | cam_extend_set(sesperiphs, periph->unit_number, periph); | |
| 350 | ||
| 1c8b7a9a | 351 | cam_periph_unlock(periph); |
| fef8985e | 352 | make_dev(&ses_ops, periph->unit_number, |
| e4c9c0c8 MD |
353 | UID_ROOT, GID_OPERATOR, 0600, "%s%d", |
| 354 | periph->periph_name, periph->unit_number); | |
| 1c8b7a9a | 355 | cam_periph_lock(periph); |
| 984263bc MD |
356 | |
| 357 | /* | |
| 358 | * Add an async callback so that we get | |
| 359 | * notified if this device goes away. | |
| 360 | */ | |
| 1c8b7a9a | 361 | xpt_register_async(AC_LOST_DEVICE, sesasync, periph, periph->path); |
| 984263bc MD |
362 | |
| 363 | switch (softc->ses_type) { | |
| 364 | default: | |
| 365 | case SES_NONE: | |
| 366 | tname = "No SES device"; | |
| 367 | break; | |
| 368 | case SES_SES_SCSI2: | |
| 369 | tname = "SCSI-2 SES Device"; | |
| 370 | break; | |
| 371 | case SES_SES: | |
| 372 | tname = "SCSI-3 SES Device"; | |
| 373 | break; | |
| 374 | case SES_SES_PASSTHROUGH: | |
| 375 | tname = "SES Passthrough Device"; | |
| 376 | break; | |
| 377 | case SES_SEN: | |
| 378 | tname = "UNISYS SEN Device (NOT HANDLED YET)"; | |
| 379 | break; | |
| 380 | case SES_SAFT: | |
| 381 | tname = "SAF-TE Compliant Device"; | |
| 382 | break; | |
| 383 | } | |
| 384 | xpt_announce_periph(periph, tname); | |
| 385 | return (CAM_REQ_CMP); | |
| 386 | } | |
| 387 | ||
| 388 | static int | |
| fef8985e | 389 | sesopen(struct dev_open_args *ap) |
| 984263bc | 390 | { |
| b13267a5 | 391 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
392 | struct cam_periph *periph; |
| 393 | struct ses_softc *softc; | |
| 1c8b7a9a | 394 | int error = 0; |
| 984263bc | 395 | |
| 984263bc MD |
396 | periph = cam_extend_get(sesperiphs, SESUNIT(dev)); |
| 397 | if (periph == NULL) { | |
| 984263bc MD |
398 | return (ENXIO); |
| 399 | } | |
| 984263bc MD |
400 | |
| 401 | if (cam_periph_acquire(periph) != CAM_REQ_CMP) { | |
| 402 | cam_periph_unlock(periph); | |
| 403 | return (ENXIO); | |
| 404 | } | |
| 405 | ||
| 1c8b7a9a PA |
406 | cam_periph_lock(periph); |
| 407 | ||
| 984263bc MD |
408 | softc = (struct ses_softc *)periph->softc; |
| 409 | ||
| 410 | if (softc->ses_flags & SES_FLAG_INVALID) { | |
| 411 | error = ENXIO; | |
| 412 | goto out; | |
| 413 | } | |
| 414 | if (softc->ses_flags & SES_FLAG_OPEN) { | |
| 415 | error = EBUSY; | |
| 416 | goto out; | |
| 417 | } | |
| 418 | if (softc->ses_vec.softc_init == NULL) { | |
| 419 | error = ENXIO; | |
| 420 | goto out; | |
| 421 | } | |
| 422 | ||
| 423 | softc->ses_flags |= SES_FLAG_OPEN; | |
| 424 | if ((softc->ses_flags & SES_FLAG_INITIALIZED) == 0) { | |
| 425 | error = (*softc->ses_vec.softc_init)(softc, 1); | |
| 426 | if (error) | |
| 427 | softc->ses_flags &= ~SES_FLAG_OPEN; | |
| 428 | else | |
| 429 | softc->ses_flags |= SES_FLAG_INITIALIZED; | |
| 430 | } | |
| 431 | ||
| 432 | out: | |
| 1c8b7a9a | 433 | cam_periph_unlock(periph); |
| 984263bc MD |
434 | if (error) { |
| 435 | cam_periph_release(periph); | |
| 436 | } | |
| 984263bc MD |
437 | return (error); |
| 438 | } | |
| 439 | ||
| 440 | static int | |
| fef8985e | 441 | sesclose(struct dev_close_args *ap) |
| 984263bc | 442 | { |
| b13267a5 | 443 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
444 | struct cam_periph *periph; |
| 445 | struct ses_softc *softc; | |
| 446 | int unit, error; | |
| 447 | ||
| 448 | error = 0; | |
| 449 | ||
| 450 | unit = SESUNIT(dev); | |
| 451 | periph = cam_extend_get(sesperiphs, unit); | |
| 452 | if (periph == NULL) | |
| 453 | return (ENXIO); | |
| 454 | ||
| 1c8b7a9a | 455 | cam_periph_lock(periph); |
| 984263bc | 456 | |
| 1c8b7a9a | 457 | softc = (struct ses_softc *)periph->softc; |
| 984263bc MD |
458 | softc->ses_flags &= ~SES_FLAG_OPEN; |
| 459 | ||
| 460 | cam_periph_unlock(periph); | |
| 461 | cam_periph_release(periph); | |
| 462 | ||
| 463 | return (0); | |
| 464 | } | |
| 465 | ||
| 466 | static void | |
| 467 | sesstart(struct cam_periph *p, union ccb *sccb) | |
| 468 | { | |
| 984263bc MD |
469 | if (p->immediate_priority <= p->pinfo.priority) { |
| 470 | SLIST_INSERT_HEAD(&p->ccb_list, &sccb->ccb_h, periph_links.sle); | |
| 471 | p->immediate_priority = CAM_PRIORITY_NONE; | |
| 472 | wakeup(&p->ccb_list); | |
| 473 | } | |
| 984263bc MD |
474 | } |
| 475 | ||
| 476 | static void | |
| 477 | sesdone(struct cam_periph *periph, union ccb *dccb) | |
| 478 | { | |
| 479 | wakeup(&dccb->ccb_h.cbfcnp); | |
| 480 | } | |
| 481 | ||
| 482 | static int | |
| 483 | seserror(union ccb *ccb, u_int32_t cflags, u_int32_t sflags) | |
| 484 | { | |
| 485 | struct ses_softc *softc; | |
| 486 | struct cam_periph *periph; | |
| 487 | ||
| 488 | periph = xpt_path_periph(ccb->ccb_h.path); | |
| 489 | softc = (struct ses_softc *)periph->softc; | |
| 490 | ||
| 491 | return (cam_periph_error(ccb, cflags, sflags, &softc->ses_saved_ccb)); | |
| 492 | } | |
| 493 | ||
| 494 | static int | |
| fef8985e | 495 | sesioctl(struct dev_ioctl_args *ap) |
| 984263bc | 496 | { |
| b13267a5 | 497 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
498 | struct cam_periph *periph; |
| 499 | ses_encstat tmp; | |
| 500 | ses_objstat objs; | |
| 501 | ses_object obj, *uobj; | |
| 502 | struct ses_softc *ssc; | |
| 503 | void *addr; | |
| 504 | int error, i; | |
| 505 | ||
| 506 | ||
| fef8985e MD |
507 | if (ap->a_data) |
| 508 | addr = *((caddr_t *)ap->a_data); | |
| 984263bc MD |
509 | else |
| 510 | addr = NULL; | |
| 511 | ||
| 512 | periph = cam_extend_get(sesperiphs, SESUNIT(dev)); | |
| 513 | if (periph == NULL) | |
| 514 | return (ENXIO); | |
| 515 | ||
| 516 | CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering sesioctl\n")); | |
| 517 | ||
| 1c8b7a9a | 518 | cam_periph_lock(periph); |
| 984263bc MD |
519 | ssc = (struct ses_softc *)periph->softc; |
| 520 | ||
| 521 | /* | |
| 522 | * Now check to see whether we're initialized or not. | |
| 523 | */ | |
| 524 | if ((ssc->ses_flags & SES_FLAG_INITIALIZED) == 0) { | |
| 1c8b7a9a | 525 | cam_periph_unlock(periph); |
| 984263bc MD |
526 | return (ENXIO); |
| 527 | } | |
| 1c8b7a9a | 528 | cam_periph_unlock(periph); |
| 984263bc MD |
529 | |
| 530 | error = 0; | |
| 531 | ||
| 532 | CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, | |
| fef8985e | 533 | ("trying to do ioctl %#lx\n", ap->a_cmd)); |
| 984263bc MD |
534 | |
| 535 | /* | |
| 536 | * If this command can change the device's state, | |
| 537 | * we must have the device open for writing. | |
| 538 | */ | |
| fef8985e | 539 | switch (ap->a_cmd) { |
| 984263bc MD |
540 | case SESIOC_GETNOBJ: |
| 541 | case SESIOC_GETOBJMAP: | |
| 542 | case SESIOC_GETENCSTAT: | |
| 543 | case SESIOC_GETOBJSTAT: | |
| 544 | break; | |
| 545 | default: | |
| fef8985e | 546 | if ((ap->a_fflag & FWRITE) == 0) { |
| 984263bc MD |
547 | return (EBADF); |
| 548 | } | |
| 549 | } | |
| 550 | ||
| fef8985e | 551 | switch (ap->a_cmd) { |
| 984263bc MD |
552 | case SESIOC_GETNOBJ: |
| 553 | error = copyout(&ssc->ses_nobjects, addr, | |
| 554 | sizeof (ssc->ses_nobjects)); | |
| 555 | break; | |
| 556 | ||
| 557 | case SESIOC_GETOBJMAP: | |
| 1c8b7a9a PA |
558 | /* |
| 559 | * XXX Dropping the lock while copying multiple segments is | |
| 560 | * bogus. | |
| 561 | */ | |
| 562 | cam_periph_lock(periph); | |
| 984263bc MD |
563 | for (uobj = addr, i = 0; i != ssc->ses_nobjects; i++, uobj++) { |
| 564 | obj.obj_id = i; | |
| 565 | obj.subencid = ssc->ses_objmap[i].subenclosure; | |
| 566 | obj.object_type = ssc->ses_objmap[i].enctype; | |
| 1c8b7a9a | 567 | cam_periph_unlock(periph); |
| 984263bc | 568 | error = copyout(&obj, uobj, sizeof (ses_object)); |
| 1c8b7a9a | 569 | cam_periph_lock(periph); |
| 984263bc MD |
570 | if (error) { |
| 571 | break; | |
| 572 | } | |
| 573 | } | |
| 1c8b7a9a | 574 | cam_periph_unlock(periph); |
| 984263bc MD |
575 | break; |
| 576 | ||
| 577 | case SESIOC_GETENCSTAT: | |
| 1c8b7a9a | 578 | cam_periph_lock(periph); |
| 984263bc | 579 | error = (*ssc->ses_vec.get_encstat)(ssc, 1); |
| 1c8b7a9a PA |
580 | if (error) { |
| 581 | cam_periph_unlock(periph); | |
| 984263bc | 582 | break; |
| 1c8b7a9a | 583 | } |
| 984263bc | 584 | tmp = ssc->ses_encstat & ~ENCI_SVALID; |
| 1c8b7a9a | 585 | cam_periph_unlock(periph); |
| 984263bc MD |
586 | error = copyout(&tmp, addr, sizeof (ses_encstat)); |
| 587 | ssc->ses_encstat = tmp; | |
| 588 | break; | |
| 589 | ||
| 590 | case SESIOC_SETENCSTAT: | |
| 591 | error = copyin(addr, &tmp, sizeof (ses_encstat)); | |
| 592 | if (error) | |
| 593 | break; | |
| 1c8b7a9a | 594 | cam_periph_lock(periph); |
| 984263bc | 595 | error = (*ssc->ses_vec.set_encstat)(ssc, tmp, 1); |
| 1c8b7a9a | 596 | cam_periph_unlock(periph); |
| 984263bc MD |
597 | break; |
| 598 | ||
| 599 | case SESIOC_GETOBJSTAT: | |
| 600 | error = copyin(addr, &objs, sizeof (ses_objstat)); | |
| 601 | if (error) | |
| 602 | break; | |
| 603 | if (objs.obj_id >= ssc->ses_nobjects) { | |
| 604 | error = EINVAL; | |
| 605 | break; | |
| 606 | } | |
| 1c8b7a9a | 607 | cam_periph_lock(periph); |
| 984263bc | 608 | error = (*ssc->ses_vec.get_objstat)(ssc, &objs, 1); |
| 1c8b7a9a | 609 | cam_periph_unlock(periph); |
| 984263bc MD |
610 | if (error) |
| 611 | break; | |
| 612 | error = copyout(&objs, addr, sizeof (ses_objstat)); | |
| 613 | /* | |
| 614 | * Always (for now) invalidate entry. | |
| 615 | */ | |
| 616 | ssc->ses_objmap[objs.obj_id].svalid = 0; | |
| 617 | break; | |
| 618 | ||
| 619 | case SESIOC_SETOBJSTAT: | |
| 620 | error = copyin(addr, &objs, sizeof (ses_objstat)); | |
| 621 | if (error) | |
| 622 | break; | |
| 623 | ||
| 624 | if (objs.obj_id >= ssc->ses_nobjects) { | |
| 625 | error = EINVAL; | |
| 626 | break; | |
| 627 | } | |
| 1c8b7a9a | 628 | cam_periph_lock(periph); |
| 984263bc | 629 | error = (*ssc->ses_vec.set_objstat)(ssc, &objs, 1); |
| 1c8b7a9a | 630 | cam_periph_unlock(periph); |
| 984263bc MD |
631 | |
| 632 | /* | |
| 633 | * Always (for now) invalidate entry. | |
| 634 | */ | |
| 635 | ssc->ses_objmap[objs.obj_id].svalid = 0; | |
| 636 | break; | |
| 637 | ||
| 638 | case SESIOC_INIT: | |
| 639 | ||
| 1c8b7a9a | 640 | cam_periph_lock(periph); |
| 984263bc | 641 | error = (*ssc->ses_vec.init_enc)(ssc); |
| 1c8b7a9a | 642 | cam_periph_unlock(periph); |
| 984263bc MD |
643 | break; |
| 644 | ||
| 645 | default: | |
| 1c8b7a9a | 646 | cam_periph_lock(periph); |
| fef8985e | 647 | error = cam_periph_ioctl(periph, ap->a_cmd, ap->a_data, seserror); |
| 1c8b7a9a | 648 | cam_periph_unlock(periph); |
| 984263bc MD |
649 | break; |
| 650 | } | |
| 651 | return (error); | |
| 652 | } | |
| 653 | ||
| b05e84c9 PA |
654 | #define SES_CFLAGS CAM_RETRY_SELTO |
| 655 | #define SES_FLAGS SF_NO_PRINT | SF_RETRY_UA | |
| 984263bc MD |
656 | static int |
| 657 | ses_runcmd(struct ses_softc *ssc, char *cdb, int cdbl, char *dptr, int *dlenp) | |
| 658 | { | |
| 659 | int error, dlen; | |
| 660 | ccb_flags ddf; | |
| 661 | union ccb *ccb; | |
| 662 | ||
| 663 | if (dptr) { | |
| 664 | if ((dlen = *dlenp) < 0) { | |
| 665 | dlen = -dlen; | |
| 666 | ddf = CAM_DIR_OUT; | |
| 667 | } else { | |
| 668 | ddf = CAM_DIR_IN; | |
| 669 | } | |
| 670 | } else { | |
| 671 | dlen = 0; | |
| 672 | ddf = CAM_DIR_NONE; | |
| 673 | } | |
| 674 | ||
| 675 | if (cdbl > IOCDBLEN) { | |
| 676 | cdbl = IOCDBLEN; | |
| 677 | } | |
| 678 | ||
| 679 | ccb = cam_periph_getccb(ssc->periph, 1); | |
| 680 | cam_fill_csio(&ccb->csio, 0, sesdone, ddf, MSG_SIMPLE_Q_TAG, dptr, | |
| 681 | dlen, sizeof (struct scsi_sense_data), cdbl, 60 * 1000); | |
| 682 | bcopy(cdb, ccb->csio.cdb_io.cdb_bytes, cdbl); | |
| 683 | ||
| b05e84c9 | 684 | error = cam_periph_runccb(ccb, seserror, SES_CFLAGS, SES_FLAGS, NULL); |
| 984263bc MD |
685 | if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) |
| 686 | cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE); | |
| 687 | if (error) { | |
| 688 | if (dptr) { | |
| 689 | *dlenp = dlen; | |
| 690 | } | |
| 691 | } else { | |
| 692 | if (dptr) { | |
| 693 | *dlenp = ccb->csio.resid; | |
| 694 | } | |
| 695 | } | |
| 696 | xpt_release_ccb(ccb); | |
| 697 | return (error); | |
| 698 | } | |
| 699 | ||
| 700 | static void | |
| 701 | ses_log(struct ses_softc *ssc, const char *fmt, ...) | |
| 702 | { | |
| e2565a42 | 703 | __va_list ap; |
| 984263bc | 704 | |
| 85f8e2ea | 705 | kprintf("%s%d: ", ssc->periph->periph_name, ssc->periph->unit_number); |
| e2565a42 | 706 | __va_start(ap, fmt); |
| 379210cb | 707 | kvprintf(fmt, ap); |
| e2565a42 | 708 | __va_end(ap); |
| 984263bc MD |
709 | } |
| 710 | ||
| 711 | /* | |
| 712 | * The code after this point runs on many platforms, | |
| 713 | * so forgive the slightly awkward and nonconforming | |
| 714 | * appearance. | |
| 715 | */ | |
| 716 | ||
| 717 | /* | |
| 718 | * Is this a device that supports enclosure services? | |
| 719 | * | |
| 720 | * It's a a pretty simple ruleset- if it is device type 0x0D (13), it's | |
| 721 | * an SES device. If it happens to be an old UNISYS SEN device, we can | |
| 722 | * handle that too. | |
| 723 | */ | |
| 724 | ||
| 725 | #define SAFTE_START 44 | |
| 726 | #define SAFTE_END 50 | |
| 727 | #define SAFTE_LEN SAFTE_END-SAFTE_START | |
| 728 | ||
| 729 | static enctyp | |
| 730 | ses_type(void *buf, int buflen) | |
| 731 | { | |
| 732 | unsigned char *iqd = buf; | |
| 733 | ||
| 984263bc MD |
734 | if (buflen < 8+SEN_ID_LEN) |
| 735 | return (SES_NONE); | |
| 736 | ||
| 737 | if ((iqd[0] & 0x1f) == T_ENCLOSURE) { | |
| 738 | if (STRNCMP(&iqd[8], SEN_ID, SEN_ID_LEN) == 0) { | |
| 739 | return (SES_SEN); | |
| 740 | } else if ((iqd[2] & 0x7) > 2) { | |
| 741 | return (SES_SES); | |
| 742 | } else { | |
| 743 | return (SES_SES_SCSI2); | |
| 744 | } | |
| 745 | return (SES_NONE); | |
| 746 | } | |
| 747 | ||
| 748 | #ifdef SES_ENABLE_PASSTHROUGH | |
| 749 | if ((iqd[6] & 0x40) && (iqd[2] & 0x7) >= 2) { | |
| 750 | /* | |
| 751 | * PassThrough Device. | |
| 752 | */ | |
| 753 | return (SES_SES_PASSTHROUGH); | |
| 754 | } | |
| 755 | #endif | |
| 756 | ||
| 757 | /* | |
| 758 | * The comparison is short for a reason- | |
| 759 | * some vendors were chopping it short. | |
| 760 | */ | |
| 761 | ||
| 762 | if (buflen < SAFTE_END - 2) { | |
| 763 | return (SES_NONE); | |
| 764 | } | |
| 765 | ||
| 766 | if (STRNCMP((char *)&iqd[SAFTE_START], "SAF-TE", SAFTE_LEN - 2) == 0) { | |
| 767 | return (SES_SAFT); | |
| 768 | } | |
| 769 | return (SES_NONE); | |
| 770 | } | |
| 771 | ||
| 772 | /* | |
| 773 | * SES Native Type Device Support | |
| 774 | */ | |
| 775 | ||
| 776 | /* | |
| 777 | * SES Diagnostic Page Codes | |
| 778 | */ | |
| 779 | ||
| 780 | typedef enum { | |
| 781 | SesConfigPage = 0x1, | |
| 782 | SesControlPage, | |
| 783 | #define SesStatusPage SesControlPage | |
| 784 | SesHelpTxt, | |
| 785 | SesStringOut, | |
| 786 | #define SesStringIn SesStringOut | |
| 787 | SesThresholdOut, | |
| 788 | #define SesThresholdIn SesThresholdOut | |
| 789 | SesArrayControl, | |
| 790 | #define SesArrayStatus SesArrayControl | |
| 791 | SesElementDescriptor, | |
| 792 | SesShortStatus | |
| 793 | } SesDiagPageCodes; | |
| 794 | ||
| 795 | /* | |
| 796 | * minimal amounts | |
| 797 | */ | |
| 798 | ||
| 799 | /* | |
| 800 | * Minimum amount of data, starting from byte 0, to have | |
| 801 | * the config header. | |
| 802 | */ | |
| 803 | #define SES_CFGHDR_MINLEN 12 | |
| 804 | ||
| 805 | /* | |
| 806 | * Minimum amount of data, starting from byte 0, to have | |
| 807 | * the config header and one enclosure header. | |
| 808 | */ | |
| 809 | #define SES_ENCHDR_MINLEN 48 | |
| 810 | ||
| 811 | /* | |
| 812 | * Take this value, subtract it from VEnclen and you know | |
| 813 | * the length of the vendor unique bytes. | |
| 814 | */ | |
| 815 | #define SES_ENCHDR_VMIN 36 | |
| 816 | ||
| 817 | /* | |
| 818 | * SES Data Structures | |
| 819 | */ | |
| 820 | ||
| 821 | typedef struct { | |
| 822 | uint32_t GenCode; /* Generation Code */ | |
| 823 | uint8_t Nsubenc; /* Number of Subenclosures */ | |
| 824 | } SesCfgHdr; | |
| 825 | ||
| 826 | typedef struct { | |
| 827 | uint8_t Subencid; /* SubEnclosure Identifier */ | |
| 828 | uint8_t Ntypes; /* # of supported types */ | |
| 829 | uint8_t VEnclen; /* Enclosure Descriptor Length */ | |
| 830 | } SesEncHdr; | |
| 831 | ||
| 832 | typedef struct { | |
| 833 | uint8_t encWWN[8]; /* XXX- Not Right Yet */ | |
| 834 | uint8_t encVid[8]; | |
| 835 | uint8_t encPid[16]; | |
| 836 | uint8_t encRev[4]; | |
| 837 | uint8_t encVen[1]; | |
| 838 | } SesEncDesc; | |
| 839 | ||
| 840 | typedef struct { | |
| 841 | uint8_t enc_type; /* type of element */ | |
| 842 | uint8_t enc_maxelt; /* maximum supported */ | |
| 843 | uint8_t enc_subenc; /* in SubEnc # N */ | |
| 844 | uint8_t enc_tlen; /* Type Descriptor Text Length */ | |
| 845 | } SesThdr; | |
| 846 | ||
| 847 | typedef struct { | |
| 848 | uint8_t comstatus; | |
| 849 | uint8_t comstat[3]; | |
| 850 | } SesComStat; | |
| 851 | ||
| 852 | struct typidx { | |
| 853 | int ses_tidx; | |
| 854 | int ses_oidx; | |
| 855 | }; | |
| 856 | ||
| 857 | struct sscfg { | |
| 858 | uint8_t ses_ntypes; /* total number of types supported */ | |
| 859 | ||
| 860 | /* | |
| 861 | * We need to keep a type index as well as an | |
| 862 | * object index for each object in an enclosure. | |
| 863 | */ | |
| 864 | struct typidx *ses_typidx; | |
| 865 | ||
| 866 | /* | |
| 867 | * We also need to keep track of the number of elements | |
| 868 | * per type of element. This is needed later so that we | |
| 869 | * can find precisely in the returned status data the | |
| 870 | * status for the Nth element of the Kth type. | |
| 871 | */ | |
| 872 | uint8_t * ses_eltmap; | |
| 873 | }; | |
| 874 | ||
| 875 | ||
| 876 | /* | |
| 877 | * (de)canonicalization defines | |
| 878 | */ | |
| 879 | #define sbyte(x, byte) ((((uint32_t)(x)) >> (byte * 8)) & 0xff) | |
| 880 | #define sbit(x, bit) (((uint32_t)(x)) << bit) | |
| 881 | #define sset8(outp, idx, sval) (((uint8_t *)(outp))[idx++]) = sbyte(sval, 0) | |
| 882 | ||
| 883 | #define sset16(outp, idx, sval) \ | |
| 884 | (((uint8_t *)(outp))[idx++]) = sbyte(sval, 1), \ | |
| 885 | (((uint8_t *)(outp))[idx++]) = sbyte(sval, 0) | |
| 886 | ||
| 887 | ||
| 888 | #define sset24(outp, idx, sval) \ | |
| 889 | (((uint8_t *)(outp))[idx++]) = sbyte(sval, 2), \ | |
| 890 | (((uint8_t *)(outp))[idx++]) = sbyte(sval, 1), \ | |
| 891 | (((uint8_t *)(outp))[idx++]) = sbyte(sval, 0) | |
| 892 | ||
| 893 | ||
| 894 | #define sset32(outp, idx, sval) \ | |
| 895 | (((uint8_t *)(outp))[idx++]) = sbyte(sval, 3), \ | |
| 896 | (((uint8_t *)(outp))[idx++]) = sbyte(sval, 2), \ | |
| 897 | (((uint8_t *)(outp))[idx++]) = sbyte(sval, 1), \ | |
| 898 | (((uint8_t *)(outp))[idx++]) = sbyte(sval, 0) | |
| 899 | ||
| 900 | #define gbyte(x, byte) ((((uint32_t)(x)) & 0xff) << (byte * 8)) | |
| 901 | #define gbit(lv, in, idx, shft, mask) lv = ((in[idx] >> shft) & mask) | |
| 902 | #define sget8(inp, idx, lval) lval = (((uint8_t *)(inp))[idx++]) | |
| 903 | #define gget8(inp, idx, lval) lval = (((uint8_t *)(inp))[idx]) | |
| 904 | ||
| 905 | #define sget16(inp, idx, lval) \ | |
| 906 | lval = gbyte((((uint8_t *)(inp))[idx]), 1) | \ | |
| 907 | (((uint8_t *)(inp))[idx+1]), idx += 2 | |
| 908 | ||
| 909 | #define gget16(inp, idx, lval) \ | |
| 910 | lval = gbyte((((uint8_t *)(inp))[idx]), 1) | \ | |
| 911 | (((uint8_t *)(inp))[idx+1]) | |
| 912 | ||
| 913 | #define sget24(inp, idx, lval) \ | |
| 914 | lval = gbyte((((uint8_t *)(inp))[idx]), 2) | \ | |
| 915 | gbyte((((uint8_t *)(inp))[idx+1]), 1) | \ | |
| 916 | (((uint8_t *)(inp))[idx+2]), idx += 3 | |
| 917 | ||
| 918 | #define gget24(inp, idx, lval) \ | |
| 919 | lval = gbyte((((uint8_t *)(inp))[idx]), 2) | \ | |
| 920 | gbyte((((uint8_t *)(inp))[idx+1]), 1) | \ | |
| 921 | (((uint8_t *)(inp))[idx+2]) | |
| 922 | ||
| 923 | #define sget32(inp, idx, lval) \ | |
| 924 | lval = gbyte((((uint8_t *)(inp))[idx]), 3) | \ | |
| 925 | gbyte((((uint8_t *)(inp))[idx+1]), 2) | \ | |
| 926 | gbyte((((uint8_t *)(inp))[idx+2]), 1) | \ | |
| 927 | (((uint8_t *)(inp))[idx+3]), idx += 4 | |
| 928 | ||
| 929 | #define gget32(inp, idx, lval) \ | |
| 930 | lval = gbyte((((uint8_t *)(inp))[idx]), 3) | \ | |
| 931 | gbyte((((uint8_t *)(inp))[idx+1]), 2) | \ | |
| 932 | gbyte((((uint8_t *)(inp))[idx+2]), 1) | \ | |
| 933 | (((uint8_t *)(inp))[idx+3]) | |
| 934 | ||
| 935 | #define SCSZ 0x2000 | |
| 936 | #define CFLEN (256 + SES_ENCHDR_MINLEN) | |
| 937 | ||
| 938 | /* | |
| 939 | * Routines specific && private to SES only | |
| 940 | */ | |
| 941 | ||
| 942 | static int ses_getconfig(ses_softc_t *); | |
| 943 | static int ses_getputstat(ses_softc_t *, int, SesComStat *, int, int); | |
| 944 | static int ses_cfghdr(uint8_t *, int, SesCfgHdr *); | |
| 945 | static int ses_enchdr(uint8_t *, int, uint8_t, SesEncHdr *); | |
| 946 | static int ses_encdesc(uint8_t *, int, uint8_t, SesEncDesc *); | |
| 947 | static int ses_getthdr(uint8_t *, int, int, SesThdr *); | |
| 948 | static int ses_decode(char *, int, uint8_t *, int, int, SesComStat *); | |
| 949 | static int ses_encode(char *, int, uint8_t *, int, int, SesComStat *); | |
| 950 | ||
| 951 | static int | |
| 952 | ses_softc_init(ses_softc_t *ssc, int doinit) | |
| 953 | { | |
| 954 | if (doinit == 0) { | |
| 955 | struct sscfg *cc; | |
| 956 | if (ssc->ses_nobjects) { | |
| 957 | SES_FREE(ssc->ses_objmap, | |
| 958 | ssc->ses_nobjects * sizeof (encobj)); | |
| 959 | ssc->ses_objmap = NULL; | |
| 960 | } | |
| 961 | if ((cc = ssc->ses_private) != NULL) { | |
| 962 | if (cc->ses_eltmap && cc->ses_ntypes) { | |
| 963 | SES_FREE(cc->ses_eltmap, cc->ses_ntypes); | |
| 964 | cc->ses_eltmap = NULL; | |
| 965 | cc->ses_ntypes = 0; | |
| 966 | } | |
| 967 | if (cc->ses_typidx && ssc->ses_nobjects) { | |
| 968 | SES_FREE(cc->ses_typidx, | |
| 969 | ssc->ses_nobjects * sizeof (struct typidx)); | |
| 970 | cc->ses_typidx = NULL; | |
| 971 | } | |
| 972 | SES_FREE(cc, sizeof (struct sscfg)); | |
| 973 | ssc->ses_private = NULL; | |
| 974 | } | |
| 975 | ssc->ses_nobjects = 0; | |
| 976 | return (0); | |
| 977 | } | |
| 978 | if (ssc->ses_private == NULL) { | |
| 979 | ssc->ses_private = SES_MALLOC(sizeof (struct sscfg)); | |
| 980 | } | |
| 981 | if (ssc->ses_private == NULL) { | |
| 982 | return (ENOMEM); | |
| 983 | } | |
| 984 | ssc->ses_nobjects = 0; | |
| 985 | ssc->ses_encstat = 0; | |
| 986 | return (ses_getconfig(ssc)); | |
| 987 | } | |
| 988 | ||
| 989 | static int | |
| 990 | ses_init_enc(ses_softc_t *ssc) | |
| 991 | { | |
| 992 | return (0); | |
| 993 | } | |
| 994 | ||
| 995 | static int | |
| 996 | ses_get_encstat(ses_softc_t *ssc, int slpflag) | |
| 997 | { | |
| 998 | SesComStat ComStat; | |
| 999 | int status; | |
| 1000 | ||
| 1001 | if ((status = ses_getputstat(ssc, -1, &ComStat, slpflag, 1)) != 0) { | |
| 1002 | return (status); | |
| 1003 | } | |
| 1004 | ssc->ses_encstat = ComStat.comstatus | ENCI_SVALID; | |
| 1005 | return (0); | |
| 1006 | } | |
| 1007 | ||
| 1008 | static int | |
| 1009 | ses_set_encstat(ses_softc_t *ssc, uint8_t encstat, int slpflag) | |
| 1010 | { | |
| 1011 | SesComStat ComStat; | |
| 1012 | int status; | |
| 1013 | ||
| 1014 | ComStat.comstatus = encstat & 0xf; | |
| 1015 | if ((status = ses_getputstat(ssc, -1, &ComStat, slpflag, 0)) != 0) { | |
| 1016 | return (status); | |
| 1017 | } | |
| 1018 | ssc->ses_encstat = encstat & 0xf; /* note no SVALID set */ | |
| 1019 | return (0); | |
| 1020 | } | |
| 1021 | ||
| 1022 | static int | |
| 1023 | ses_get_objstat(ses_softc_t *ssc, ses_objstat *obp, int slpflag) | |
| 1024 | { | |
| 1025 | int i = (int)obp->obj_id; | |
| 1026 | ||
| 1027 | if (ssc->ses_objmap[i].svalid == 0) { | |
| 1028 | SesComStat ComStat; | |
| 1029 | int err = ses_getputstat(ssc, i, &ComStat, slpflag, 1); | |
| 1030 | if (err) | |
| 1031 | return (err); | |
| 1032 | ssc->ses_objmap[i].encstat[0] = ComStat.comstatus; | |
| 1033 | ssc->ses_objmap[i].encstat[1] = ComStat.comstat[0]; | |
| 1034 | ssc->ses_objmap[i].encstat[2] = ComStat.comstat[1]; | |
| 1035 | ssc->ses_objmap[i].encstat[3] = ComStat.comstat[2]; | |
| 1036 | ssc->ses_objmap[i].svalid = 1; | |
| 1037 | } | |
| 1038 | obp->cstat[0] = ssc->ses_objmap[i].encstat[0]; | |
| 1039 | obp->cstat[1] = ssc->ses_objmap[i].encstat[1]; | |
| 1040 | obp->cstat[2] = ssc->ses_objmap[i].encstat[2]; | |
| 1041 | obp->cstat[3] = ssc->ses_objmap[i].encstat[3]; | |
| 1042 | return (0); | |
| 1043 | } | |
| 1044 | ||
| 1045 | static int | |
| 1046 | ses_set_objstat(ses_softc_t *ssc, ses_objstat *obp, int slpflag) | |
| 1047 | { | |
| 1048 | SesComStat ComStat; | |
| 1049 | int err; | |
| 1050 | /* | |
| 1051 | * If this is clear, we don't do diddly. | |
| 1052 | */ | |
| 1053 | if ((obp->cstat[0] & SESCTL_CSEL) == 0) { | |
| 1054 | return (0); | |
| 1055 | } | |
| 1056 | ComStat.comstatus = obp->cstat[0]; | |
| 1057 | ComStat.comstat[0] = obp->cstat[1]; | |
| 1058 | ComStat.comstat[1] = obp->cstat[2]; | |
| 1059 | ComStat.comstat[2] = obp->cstat[3]; | |
| 1060 | err = ses_getputstat(ssc, (int)obp->obj_id, &ComStat, slpflag, 0); | |
| 1061 | ssc->ses_objmap[(int)obp->obj_id].svalid = 0; | |
| 1062 | return (err); | |
| 1063 | } | |
| 1064 | ||
| 1065 | static int | |
| 1066 | ses_getconfig(ses_softc_t *ssc) | |
| 1067 | { | |
| 1068 | struct sscfg *cc; | |
| 1069 | SesCfgHdr cf; | |
| 1070 | SesEncHdr hd; | |
| 1071 | SesEncDesc *cdp; | |
| 1072 | SesThdr thdr; | |
| 1073 | int err, amt, i, nobj, ntype, maxima; | |
| 1074 | char storage[CFLEN], *sdata; | |
| 1075 | static char cdb[6] = { | |
| 1076 | RECEIVE_DIAGNOSTIC, 0x1, SesConfigPage, SCSZ >> 8, SCSZ & 0xff, 0 | |
| 1077 | }; | |
| 1078 | ||
| 1079 | cc = ssc->ses_private; | |
| 1080 | if (cc == NULL) { | |
| 1081 | return (ENXIO); | |
| 1082 | } | |
| 1083 | ||
| 1084 | sdata = SES_MALLOC(SCSZ); | |
| 1085 | if (sdata == NULL) | |
| 1086 | return (ENOMEM); | |
| 1087 | ||
| 1088 | amt = SCSZ; | |
| 1089 | err = ses_runcmd(ssc, cdb, 6, sdata, &amt); | |
| 1090 | if (err) { | |
| 1091 | SES_FREE(sdata, SCSZ); | |
| 1092 | return (err); | |
| 1093 | } | |
| 1094 | amt = SCSZ - amt; | |
| 1095 | ||
| 1096 | if (ses_cfghdr((uint8_t *) sdata, amt, &cf)) { | |
| 1097 | SES_LOG(ssc, "Unable to parse SES Config Header\n"); | |
| 1098 | SES_FREE(sdata, SCSZ); | |
| 1099 | return (EIO); | |
| 1100 | } | |
| 1101 | if (amt < SES_ENCHDR_MINLEN) { | |
| 1102 | SES_LOG(ssc, "runt enclosure length (%d)\n", amt); | |
| 1103 | SES_FREE(sdata, SCSZ); | |
| 1104 | return (EIO); | |
| 1105 | } | |
| 1106 | ||
| 1107 | SES_VLOG(ssc, "GenCode %x %d Subenclosures\n", cf.GenCode, cf.Nsubenc); | |
| 1108 | ||
| 1109 | /* | |
| 1110 | * Now waltz through all the subenclosures toting up the | |
| 1111 | * number of types available in each. For this, we only | |
| 1112 | * really need the enclosure header. However, we get the | |
| 1113 | * enclosure descriptor for debug purposes, as well | |
| 1114 | * as self-consistency checking purposes. | |
| 1115 | */ | |
| 1116 | ||
| 1117 | maxima = cf.Nsubenc + 1; | |
| 1118 | cdp = (SesEncDesc *) storage; | |
| 1119 | for (ntype = i = 0; i < maxima; i++) { | |
| 1120 | MEMZERO((caddr_t)cdp, sizeof (*cdp)); | |
| 1121 | if (ses_enchdr((uint8_t *) sdata, amt, i, &hd)) { | |
| 1122 | SES_LOG(ssc, "Cannot Extract Enclosure Header %d\n", i); | |
| 1123 | SES_FREE(sdata, SCSZ); | |
| 1124 | return (EIO); | |
| 1125 | } | |
| 1126 | SES_VLOG(ssc, " SubEnclosure ID %d, %d Types With this ID, En" | |
| 1127 | "closure Length %d\n", hd.Subencid, hd.Ntypes, hd.VEnclen); | |
| 1128 | ||
| 1129 | if (ses_encdesc((uint8_t *)sdata, amt, i, cdp)) { | |
| 1130 | SES_LOG(ssc, "Can't get Enclosure Descriptor %d\n", i); | |
| 1131 | SES_FREE(sdata, SCSZ); | |
| 1132 | return (EIO); | |
| 1133 | } | |
| 1134 | SES_VLOG(ssc, " WWN: %02x%02x%02x%02x%02x%02x%02x%02x\n", | |
| 1135 | cdp->encWWN[0], cdp->encWWN[1], cdp->encWWN[2], | |
| 1136 | cdp->encWWN[3], cdp->encWWN[4], cdp->encWWN[5], | |
| 1137 | cdp->encWWN[6], cdp->encWWN[7]); | |
| 1138 | ntype += hd.Ntypes; | |
| 1139 | } | |
| 1140 | ||
| 1141 | /* | |
| 1142 | * Now waltz through all the types that are available, getting | |
| 1143 | * the type header so we can start adding up the number of | |
| 1144 | * objects available. | |
| 1145 | */ | |
| 1146 | for (nobj = i = 0; i < ntype; i++) { | |
| 1147 | if (ses_getthdr((uint8_t *)sdata, amt, i, &thdr)) { | |
| 1148 | SES_LOG(ssc, "Can't get Enclosure Type Header %d\n", i); | |
| 1149 | SES_FREE(sdata, SCSZ); | |
| 1150 | return (EIO); | |
| 1151 | } | |
| 1152 | SES_LOG(ssc, " Type Desc[%d]: Type 0x%x, MaxElt %d, In Subenc " | |
| 1153 | "%d, Text Length %d\n", i, thdr.enc_type, thdr.enc_maxelt, | |
| 1154 | thdr.enc_subenc, thdr.enc_tlen); | |
| 1155 | nobj += thdr.enc_maxelt; | |
| 1156 | } | |
| 1157 | ||
| 1158 | ||
| 1159 | /* | |
| 1160 | * Now allocate the object array and type map. | |
| 1161 | */ | |
| 1162 | ||
| 1163 | ssc->ses_objmap = SES_MALLOC(nobj * sizeof (encobj)); | |
| 1164 | cc->ses_typidx = SES_MALLOC(nobj * sizeof (struct typidx)); | |
| 1165 | cc->ses_eltmap = SES_MALLOC(ntype); | |
| 1166 | ||
| 1167 | if (ssc->ses_objmap == NULL || cc->ses_typidx == NULL || | |
| 1168 | cc->ses_eltmap == NULL) { | |
| 1169 | if (ssc->ses_objmap) { | |
| 1170 | SES_FREE(ssc->ses_objmap, (nobj * sizeof (encobj))); | |
| 1171 | ssc->ses_objmap = NULL; | |
| 1172 | } | |
| 1173 | if (cc->ses_typidx) { | |
| 1174 | SES_FREE(cc->ses_typidx, | |
| 1175 | (nobj * sizeof (struct typidx))); | |
| 1176 | cc->ses_typidx = NULL; | |
| 1177 | } | |
| 1178 | if (cc->ses_eltmap) { | |
| 1179 | SES_FREE(cc->ses_eltmap, ntype); | |
| 1180 | cc->ses_eltmap = NULL; | |
| 1181 | } | |
| 1182 | SES_FREE(sdata, SCSZ); | |
| 1183 | return (ENOMEM); | |
| 1184 | } | |
| 1185 | MEMZERO(ssc->ses_objmap, nobj * sizeof (encobj)); | |
| 1186 | MEMZERO(cc->ses_typidx, nobj * sizeof (struct typidx)); | |
| 1187 | MEMZERO(cc->ses_eltmap, ntype); | |
| 1188 | cc->ses_ntypes = (uint8_t) ntype; | |
| 1189 | ssc->ses_nobjects = nobj; | |
| 1190 | ||
| 1191 | /* | |
| 1192 | * Now waltz through the # of types again to fill in the types | |
| 1193 | * (and subenclosure ids) of the allocated objects. | |
| 1194 | */ | |
| 1195 | nobj = 0; | |
| 1196 | for (i = 0; i < ntype; i++) { | |
| 1197 | int j; | |
| 1198 | if (ses_getthdr((uint8_t *)sdata, amt, i, &thdr)) { | |
| 1199 | continue; | |
| 1200 | } | |
| 1201 | cc->ses_eltmap[i] = thdr.enc_maxelt; | |
| 1202 | for (j = 0; j < thdr.enc_maxelt; j++) { | |
| 1203 | cc->ses_typidx[nobj].ses_tidx = i; | |
| 1204 | cc->ses_typidx[nobj].ses_oidx = j; | |
| 1205 | ssc->ses_objmap[nobj].subenclosure = thdr.enc_subenc; | |
| 1206 | ssc->ses_objmap[nobj++].enctype = thdr.enc_type; | |
| 1207 | } | |
| 1208 | } | |
| 1209 | SES_FREE(sdata, SCSZ); | |
| 1210 | return (0); | |
| 1211 | } | |
| 1212 | ||
| 1213 | static int | |
| 1214 | ses_getputstat(ses_softc_t *ssc, int objid, SesComStat *sp, int slp, int in) | |
| 1215 | { | |
| 1216 | struct sscfg *cc; | |
| 1217 | int err, amt, bufsiz, tidx, oidx; | |
| 1218 | char cdb[6], *sdata; | |
| 1219 | ||
| 1220 | cc = ssc->ses_private; | |
| 1221 | if (cc == NULL) { | |
| 1222 | return (ENXIO); | |
| 1223 | } | |
| 1224 | ||
| 1225 | /* | |
| 1226 | * If we're just getting overall enclosure status, | |
| 1227 | * we only need 2 bytes of data storage. | |
| 1228 | * | |
| 1229 | * If we're getting anything else, we know how much | |
| 1230 | * storage we need by noting that starting at offset | |
| 1231 | * 8 in returned data, all object status bytes are 4 | |
| 1232 | * bytes long, and are stored in chunks of types(M) | |
| 1233 | * and nth+1 instances of type M. | |
| 1234 | */ | |
| 1235 | if (objid == -1) { | |
| 1236 | bufsiz = 2; | |
| 1237 | } else { | |
| 1238 | bufsiz = (ssc->ses_nobjects * 4) + (cc->ses_ntypes * 4) + 8; | |
| 1239 | } | |
| 1240 | sdata = SES_MALLOC(bufsiz); | |
| 1241 | if (sdata == NULL) | |
| 1242 | return (ENOMEM); | |
| 1243 | ||
| 1244 | cdb[0] = RECEIVE_DIAGNOSTIC; | |
| 1245 | cdb[1] = 1; | |
| 1246 | cdb[2] = SesStatusPage; | |
| 1247 | cdb[3] = bufsiz >> 8; | |
| 1248 | cdb[4] = bufsiz & 0xff; | |
| 1249 | cdb[5] = 0; | |
| 1250 | amt = bufsiz; | |
| 1251 | err = ses_runcmd(ssc, cdb, 6, sdata, &amt); | |
| 1252 | if (err) { | |
| 1253 | SES_FREE(sdata, bufsiz); | |
| 1254 | return (err); | |
| 1255 | } | |
| 1256 | amt = bufsiz - amt; | |
| 1257 | ||
| 1258 | if (objid == -1) { | |
| 1259 | tidx = -1; | |
| 1260 | oidx = -1; | |
| 1261 | } else { | |
| 1262 | tidx = cc->ses_typidx[objid].ses_tidx; | |
| 1263 | oidx = cc->ses_typidx[objid].ses_oidx; | |
| 1264 | } | |
| 1265 | if (in) { | |
| 1266 | if (ses_decode(sdata, amt, cc->ses_eltmap, tidx, oidx, sp)) { | |
| 1267 | err = ENODEV; | |
| 1268 | } | |
| 1269 | } else { | |
| 1270 | if (ses_encode(sdata, amt, cc->ses_eltmap, tidx, oidx, sp)) { | |
| 1271 | err = ENODEV; | |
| 1272 | } else { | |
| 1273 | cdb[0] = SEND_DIAGNOSTIC; | |
| 1274 | cdb[1] = 0x10; | |
| 1275 | cdb[2] = 0; | |
| 1276 | cdb[3] = bufsiz >> 8; | |
| 1277 | cdb[4] = bufsiz & 0xff; | |
| 1278 | cdb[5] = 0; | |
| 1279 | amt = -bufsiz; | |
| 1280 | err = ses_runcmd(ssc, cdb, 6, sdata, &amt); | |
| 1281 | } | |
| 1282 | } | |
| 1283 | SES_FREE(sdata, bufsiz); | |
| 1284 | return (0); | |
| 1285 | } | |
| 1286 | ||
| 1287 | ||
| 1288 | /* | |
| 1289 | * Routines to parse returned SES data structures. | |
| 1290 | * Architecture and compiler independent. | |
| 1291 | */ | |
| 1292 | ||
| 1293 | static int | |
| 1294 | ses_cfghdr(uint8_t *buffer, int buflen, SesCfgHdr *cfp) | |
| 1295 | { | |
| 1296 | if (buflen < SES_CFGHDR_MINLEN) { | |
| 1297 | return (-1); | |
| 1298 | } | |
| 1299 | gget8(buffer, 1, cfp->Nsubenc); | |
| 1300 | gget32(buffer, 4, cfp->GenCode); | |
| 1301 | return (0); | |
| 1302 | } | |
| 1303 | ||
| 1304 | static int | |
| 1305 | ses_enchdr(uint8_t *buffer, int amt, uint8_t SubEncId, SesEncHdr *chp) | |
| 1306 | { | |
| 1307 | int s, off = 8; | |
| 1308 | for (s = 0; s < SubEncId; s++) { | |
| 1309 | if (off + 3 > amt) | |
| 1310 | return (-1); | |
| 1311 | off += buffer[off+3] + 4; | |
| 1312 | } | |
| 1313 | if (off + 3 > amt) { | |
| 1314 | return (-1); | |
| 1315 | } | |
| 1316 | gget8(buffer, off+1, chp->Subencid); | |
| 1317 | gget8(buffer, off+2, chp->Ntypes); | |
| 1318 | gget8(buffer, off+3, chp->VEnclen); | |
| 1319 | return (0); | |
| 1320 | } | |
| 1321 | ||
| 1322 | static int | |
| 1323 | ses_encdesc(uint8_t *buffer, int amt, uint8_t SubEncId, SesEncDesc *cdp) | |
| 1324 | { | |
| 1325 | int s, e, enclen, off = 8; | |
| 1326 | for (s = 0; s < SubEncId; s++) { | |
| 1327 | if (off + 3 > amt) | |
| 1328 | return (-1); | |
| 1329 | off += buffer[off+3] + 4; | |
| 1330 | } | |
| 1331 | if (off + 3 > amt) { | |
| 1332 | return (-1); | |
| 1333 | } | |
| 1334 | gget8(buffer, off+3, enclen); | |
| 1335 | off += 4; | |
| 1336 | if (off >= amt) | |
| 1337 | return (-1); | |
| 1338 | ||
| 1339 | e = off + enclen; | |
| 1340 | if (e > amt) { | |
| 1341 | e = amt; | |
| 1342 | } | |
| 1343 | MEMCPY(cdp, &buffer[off], e - off); | |
| 1344 | return (0); | |
| 1345 | } | |
| 1346 | ||
| 1347 | static int | |
| 1348 | ses_getthdr(uint8_t *buffer, int amt, int nth, SesThdr *thp) | |
| 1349 | { | |
| 1350 | int s, off = 8; | |
| 1351 | ||
| 1352 | if (amt < SES_CFGHDR_MINLEN) { | |
| 1353 | return (-1); | |
| 1354 | } | |
| 1355 | for (s = 0; s < buffer[1]; s++) { | |
| 1356 | if (off + 3 > amt) | |
| 1357 | return (-1); | |
| 1358 | off += buffer[off+3] + 4; | |
| 1359 | } | |
| 1360 | if (off + 3 > amt) { | |
| 1361 | return (-1); | |
| 1362 | } | |
| 1363 | off += buffer[off+3] + 4 + (nth * 4); | |
| 1364 | if (amt < (off + 4)) | |
| 1365 | return (-1); | |
| 1366 | ||
| 1367 | gget8(buffer, off++, thp->enc_type); | |
| 1368 | gget8(buffer, off++, thp->enc_maxelt); | |
| 1369 | gget8(buffer, off++, thp->enc_subenc); | |
| 1370 | gget8(buffer, off, thp->enc_tlen); | |
| 1371 | return (0); | |
| 1372 | } | |
| 1373 | ||
| 1374 | /* | |
| 1375 | * This function needs a little explanation. | |
| 1376 | * | |
| 1377 | * The arguments are: | |
| 1378 | * | |
| 1379 | * | |
| 1380 | * char *b, int amt | |
| 1381 | * | |
| 1382 | * These describes the raw input SES status data and length. | |
| 1383 | * | |
| 1384 | * uint8_t *ep | |
| 1385 | * | |
| 1386 | * This is a map of the number of types for each element type | |
| 1387 | * in the enclosure. | |
| 1388 | * | |
| 1389 | * int elt | |
| 1390 | * | |
| 1391 | * This is the element type being sought. If elt is -1, | |
| 1392 | * then overall enclosure status is being sought. | |
| 1393 | * | |
| 1394 | * int elm | |
| 1395 | * | |
| 1396 | * This is the ordinal Mth element of type elt being sought. | |
| 1397 | * | |
| 1398 | * SesComStat *sp | |
| 1399 | * | |
| 1400 | * This is the output area to store the status for | |
| 1401 | * the Mth element of type Elt. | |
| 1402 | */ | |
| 1403 | ||
| 1404 | static int | |
| 1405 | ses_decode(char *b, int amt, uint8_t *ep, int elt, int elm, SesComStat *sp) | |
| 1406 | { | |
| 1407 | int idx, i; | |
| 1408 | ||
| 1409 | /* | |
| 1410 | * If it's overall enclosure status being sought, get that. | |
| 1411 | * We need at least 2 bytes of status data to get that. | |
| 1412 | */ | |
| 1413 | if (elt == -1) { | |
| 1414 | if (amt < 2) | |
| 1415 | return (-1); | |
| 1416 | gget8(b, 1, sp->comstatus); | |
| 1417 | sp->comstat[0] = 0; | |
| 1418 | sp->comstat[1] = 0; | |
| 1419 | sp->comstat[2] = 0; | |
| 1420 | return (0); | |
| 1421 | } | |
| 1422 | ||
| 1423 | /* | |
| 1424 | * Check to make sure that the Mth element is legal for type Elt. | |
| 1425 | */ | |
| 1426 | ||
| 1427 | if (elm >= ep[elt]) | |
| 1428 | return (-1); | |
| 1429 | ||
| 1430 | /* | |
| 1431 | * Starting at offset 8, start skipping over the storage | |
| 1432 | * for the element types we're not interested in. | |
| 1433 | */ | |
| 1434 | for (idx = 8, i = 0; i < elt; i++) { | |
| 1435 | idx += ((ep[i] + 1) * 4); | |
| 1436 | } | |
| 1437 | ||
| 1438 | /* | |
| 1439 | * Skip over Overall status for this element type. | |
| 1440 | */ | |
| 1441 | idx += 4; | |
| 1442 | ||
| 1443 | /* | |
| 1444 | * And skip to the index for the Mth element that we're going for. | |
| 1445 | */ | |
| 1446 | idx += (4 * elm); | |
| 1447 | ||
| 1448 | /* | |
| 1449 | * Make sure we haven't overflowed the buffer. | |
| 1450 | */ | |
| 1451 | if (idx+4 > amt) | |
| 1452 | return (-1); | |
| 1453 | ||
| 1454 | /* | |
| 1455 | * Retrieve the status. | |
| 1456 | */ | |
| 1457 | gget8(b, idx++, sp->comstatus); | |
| 1458 | gget8(b, idx++, sp->comstat[0]); | |
| 1459 | gget8(b, idx++, sp->comstat[1]); | |
| 1460 | gget8(b, idx++, sp->comstat[2]); | |
| 1461 | #if 0 | |
| 1462 | PRINTF("Get Elt 0x%x Elm 0x%x (idx %d)\n", elt, elm, idx-4); | |
| 1463 | #endif | |
| 1464 | return (0); | |
| 1465 | } | |
| 1466 | ||
| 1467 | /* | |
| 1468 | * This is the mirror function to ses_decode, but we set the 'select' | |
| 1469 | * bit for the object which we're interested in. All other objects, | |
| 1470 | * after a status fetch, should have that bit off. Hmm. It'd be easy | |
| 1471 | * enough to ensure this, so we will. | |
| 1472 | */ | |
| 1473 | ||
| 1474 | static int | |
| 1475 | ses_encode(char *b, int amt, uint8_t *ep, int elt, int elm, SesComStat *sp) | |
| 1476 | { | |
| 1477 | int idx, i; | |
| 1478 | ||
| 1479 | /* | |
| 1480 | * If it's overall enclosure status being sought, get that. | |
| 1481 | * We need at least 2 bytes of status data to get that. | |
| 1482 | */ | |
| 1483 | if (elt == -1) { | |
| 1484 | if (amt < 2) | |
| 1485 | return (-1); | |
| 1486 | i = 0; | |
| 1487 | sset8(b, i, 0); | |
| 1488 | sset8(b, i, sp->comstatus & 0xf); | |
| 1489 | #if 0 | |
| 1490 | PRINTF("set EncStat %x\n", sp->comstatus); | |
| 1491 | #endif | |
| 1492 | return (0); | |
| 1493 | } | |
| 1494 | ||
| 1495 | /* | |
| 1496 | * Check to make sure that the Mth element is legal for type Elt. | |
| 1497 | */ | |
| 1498 | ||
| 1499 | if (elm >= ep[elt]) | |
| 1500 | return (-1); | |
| 1501 | ||
| 1502 | /* | |
| 1503 | * Starting at offset 8, start skipping over the storage | |
| 1504 | * for the element types we're not interested in. | |
| 1505 | */ | |
| 1506 | for (idx = 8, i = 0; i < elt; i++) { | |
| 1507 | idx += ((ep[i] + 1) * 4); | |
| 1508 | } | |
| 1509 | ||
| 1510 | /* | |
| 1511 | * Skip over Overall status for this element type. | |
| 1512 | */ | |
| 1513 | idx += 4; | |
| 1514 | ||
| 1515 | /* | |
| 1516 | * And skip to the index for the Mth element that we're going for. | |
| 1517 | */ | |
| 1518 | idx += (4 * elm); | |
| 1519 | ||
| 1520 | /* | |
| 1521 | * Make sure we haven't overflowed the buffer. | |
| 1522 | */ | |
| 1523 | if (idx+4 > amt) | |
| 1524 | return (-1); | |
| 1525 | ||
| 1526 | /* | |
| 1527 | * Set the status. | |
| 1528 | */ | |
| 1529 | sset8(b, idx, sp->comstatus); | |
| 1530 | sset8(b, idx, sp->comstat[0]); | |
| 1531 | sset8(b, idx, sp->comstat[1]); | |
| 1532 | sset8(b, idx, sp->comstat[2]); | |
| 1533 | idx -= 4; | |
| 1534 | ||
| 1535 | #if 0 | |
| 1536 | PRINTF("Set Elt 0x%x Elm 0x%x (idx %d) with %x %x %x %x\n", | |
| 1537 | elt, elm, idx, sp->comstatus, sp->comstat[0], | |
| 1538 | sp->comstat[1], sp->comstat[2]); | |
| 1539 | #endif | |
| 1540 | ||
| 1541 | /* | |
| 1542 | * Now make sure all other 'Select' bits are off. | |
| 1543 | */ | |
| 1544 | for (i = 8; i < amt; i += 4) { | |
| 1545 | if (i != idx) | |
| 1546 | b[i] &= ~0x80; | |
| 1547 | } | |
| 1548 | /* | |
| 1549 | * And make sure the INVOP bit is clear. | |
| 1550 | */ | |
| 1551 | b[2] &= ~0x10; | |
| 1552 | ||
| 1553 | return (0); | |
| 1554 | } | |
| 1555 | ||
| 1556 | /* | |
| 1557 | * SAF-TE Type Device Emulation | |
| 1558 | */ | |
| 1559 | ||
| 1560 | static int safte_getconfig(ses_softc_t *); | |
| fc6d0222 | 1561 | static int safte_rdstat(ses_softc_t *, int); |
| 984263bc MD |
1562 | static int set_objstat_sel(ses_softc_t *, ses_objstat *, int); |
| 1563 | static int wrbuf16(ses_softc_t *, uint8_t, uint8_t, uint8_t, uint8_t, int); | |
| 1564 | static void wrslot_stat(ses_softc_t *, int); | |
| 1565 | static int perf_slotop(ses_softc_t *, uint8_t, uint8_t, int); | |
| 1566 | ||
| 1567 | #define ALL_ENC_STAT (SES_ENCSTAT_CRITICAL | SES_ENCSTAT_UNRECOV | \ | |
| 1568 | SES_ENCSTAT_NONCRITICAL | SES_ENCSTAT_INFO) | |
| 1569 | /* | |
| 1570 | * SAF-TE specific defines- Mandatory ones only... | |
| 1571 | */ | |
| 1572 | ||
| 1573 | /* | |
| 1574 | * READ BUFFER ('get' commands) IDs- placed in offset 2 of cdb | |
| 1575 | */ | |
| 1576 | #define SAFTE_RD_RDCFG 0x00 /* read enclosure configuration */ | |
| 1577 | #define SAFTE_RD_RDESTS 0x01 /* read enclosure status */ | |
| 1578 | #define SAFTE_RD_RDDSTS 0x04 /* read drive slot status */ | |
| 1579 | ||
| 1580 | /* | |
| 1581 | * WRITE BUFFER ('set' commands) IDs- placed in offset 0 of databuf | |
| 1582 | */ | |
| 1583 | #define SAFTE_WT_DSTAT 0x10 /* write device slot status */ | |
| 1584 | #define SAFTE_WT_SLTOP 0x12 /* perform slot operation */ | |
| 1585 | #define SAFTE_WT_FANSPD 0x13 /* set fan speed */ | |
| 1586 | #define SAFTE_WT_ACTPWS 0x14 /* turn on/off power supply */ | |
| 1587 | #define SAFTE_WT_GLOBAL 0x15 /* send global command */ | |
| 1588 | ||
| 1589 | ||
| 1590 | #define SAFT_SCRATCH 64 | |
| 1591 | #define NPSEUDO_THERM 16 | |
| 1592 | #define NPSEUDO_ALARM 1 | |
| 1593 | struct scfg { | |
| 1594 | /* | |
| 1595 | * Cached Configuration | |
| 1596 | */ | |
| 1597 | uint8_t Nfans; /* Number of Fans */ | |
| 1598 | uint8_t Npwr; /* Number of Power Supplies */ | |
| 1599 | uint8_t Nslots; /* Number of Device Slots */ | |
| 1600 | uint8_t DoorLock; /* Door Lock Installed */ | |
| 1601 | uint8_t Ntherm; /* Number of Temperature Sensors */ | |
| 1602 | uint8_t Nspkrs; /* Number of Speakers */ | |
| 1603 | uint8_t Nalarm; /* Number of Alarms (at least one) */ | |
| 1604 | /* | |
| 1605 | * Cached Flag Bytes for Global Status | |
| 1606 | */ | |
| 1607 | uint8_t flag1; | |
| 1608 | uint8_t flag2; | |
| 1609 | /* | |
| 1610 | * What object index ID is where various slots start. | |
| 1611 | */ | |
| 1612 | uint8_t pwroff; | |
| 1613 | uint8_t slotoff; | |
| 1614 | #define SAFT_ALARM_OFFSET(cc) (cc)->slotoff - 1 | |
| 1615 | }; | |
| 1616 | ||
| 1617 | #define SAFT_FLG1_ALARM 0x1 | |
| 1618 | #define SAFT_FLG1_GLOBFAIL 0x2 | |
| 1619 | #define SAFT_FLG1_GLOBWARN 0x4 | |
| 1620 | #define SAFT_FLG1_ENCPWROFF 0x8 | |
| 1621 | #define SAFT_FLG1_ENCFANFAIL 0x10 | |
| 1622 | #define SAFT_FLG1_ENCPWRFAIL 0x20 | |
| 1623 | #define SAFT_FLG1_ENCDRVFAIL 0x40 | |
| 1624 | #define SAFT_FLG1_ENCDRVWARN 0x80 | |
| 1625 | ||
| 1626 | #define SAFT_FLG2_LOCKDOOR 0x4 | |
| 1627 | #define SAFT_PRIVATE sizeof (struct scfg) | |
| 1628 | ||
| 1629 | static char *safte_2little = "Too Little Data Returned (%d) at line %d\n"; | |
| 1630 | #define SAFT_BAIL(r, x, k, l) \ | |
| 4c6990b9 | 1631 | if ((r) >= (x)) { \ |
| 984263bc | 1632 | SES_LOG(ssc, safte_2little, x, __LINE__);\ |
| 4c6990b9 | 1633 | SES_FREE((k), (l)); \ |
| 984263bc MD |
1634 | return (EIO); \ |
| 1635 | } | |
| 1636 | ||
| 1637 | ||
| fb2e6209 | 1638 | static int |
| 984263bc MD |
1639 | safte_softc_init(ses_softc_t *ssc, int doinit) |
| 1640 | { | |
| 1641 | int err, i, r; | |
| 1642 | struct scfg *cc; | |
| 1643 | ||
| 1644 | if (doinit == 0) { | |
| 1645 | if (ssc->ses_nobjects) { | |
| 1646 | if (ssc->ses_objmap) { | |
| 1647 | SES_FREE(ssc->ses_objmap, | |
| 1648 | ssc->ses_nobjects * sizeof (encobj)); | |
| 1649 | ssc->ses_objmap = NULL; | |
| 1650 | } | |
| 1651 | ssc->ses_nobjects = 0; | |
| 1652 | } | |
| 1653 | if (ssc->ses_private) { | |
| 1654 | SES_FREE(ssc->ses_private, SAFT_PRIVATE); | |
| 1655 | ssc->ses_private = NULL; | |
| 1656 | } | |
| 1657 | return (0); | |
| 1658 | } | |
| 1659 | ||
| 1660 | if (ssc->ses_private == NULL) { | |
| 1661 | ssc->ses_private = SES_MALLOC(SAFT_PRIVATE); | |
| 1662 | if (ssc->ses_private == NULL) { | |
| 1663 | return (ENOMEM); | |
| 1664 | } | |
| 1665 | MEMZERO(ssc->ses_private, SAFT_PRIVATE); | |
| 1666 | } | |
| 1667 | ||
| 1668 | ssc->ses_nobjects = 0; | |
| 1669 | ssc->ses_encstat = 0; | |
| 1670 | ||
| 1671 | if ((err = safte_getconfig(ssc)) != 0) { | |
| 1672 | return (err); | |
| 1673 | } | |
| 1674 | ||
| 1675 | /* | |
| 1676 | * The number of objects here, as well as that reported by the | |
| 1677 | * READ_BUFFER/GET_CONFIG call, are the over-temperature flags (15) | |
| 1678 | * that get reported during READ_BUFFER/READ_ENC_STATUS. | |
| 1679 | */ | |
| 1680 | cc = ssc->ses_private; | |
| 1681 | ssc->ses_nobjects = cc->Nfans + cc->Npwr + cc->Nslots + cc->DoorLock + | |
| 1682 | cc->Ntherm + cc->Nspkrs + NPSEUDO_THERM + NPSEUDO_ALARM; | |
| 1683 | ssc->ses_objmap = (encobj *) | |
| 1684 | SES_MALLOC(ssc->ses_nobjects * sizeof (encobj)); | |
| 1685 | if (ssc->ses_objmap == NULL) { | |
| 1686 | return (ENOMEM); | |
| 1687 | } | |
| 1688 | MEMZERO(ssc->ses_objmap, ssc->ses_nobjects * sizeof (encobj)); | |
| 1689 | ||
| 1690 | r = 0; | |
| 1691 | /* | |
| 1692 | * Note that this is all arranged for the convenience | |
| 1693 | * in later fetches of status. | |
| 1694 | */ | |
| 1695 | for (i = 0; i < cc->Nfans; i++) | |
| 1696 | ssc->ses_objmap[r++].enctype = SESTYP_FAN; | |
| 1697 | cc->pwroff = (uint8_t) r; | |
| 1698 | for (i = 0; i < cc->Npwr; i++) | |
| 1699 | ssc->ses_objmap[r++].enctype = SESTYP_POWER; | |
| 1700 | for (i = 0; i < cc->DoorLock; i++) | |
| 1701 | ssc->ses_objmap[r++].enctype = SESTYP_DOORLOCK; | |
| 1702 | for (i = 0; i < cc->Nspkrs; i++) | |
| 1703 | ssc->ses_objmap[r++].enctype = SESTYP_ALARM; | |
| 1704 | for (i = 0; i < cc->Ntherm; i++) | |
| 1705 | ssc->ses_objmap[r++].enctype = SESTYP_THERM; | |
| 1706 | for (i = 0; i < NPSEUDO_THERM; i++) | |
| 1707 | ssc->ses_objmap[r++].enctype = SESTYP_THERM; | |
| 1708 | ssc->ses_objmap[r++].enctype = SESTYP_ALARM; | |
| 1709 | cc->slotoff = (uint8_t) r; | |
| 1710 | for (i = 0; i < cc->Nslots; i++) | |
| 1711 | ssc->ses_objmap[r++].enctype = SESTYP_DEVICE; | |
| 1712 | return (0); | |
| 1713 | } | |
| 1714 | ||
| fb2e6209 | 1715 | static int |
| 984263bc MD |
1716 | safte_init_enc(ses_softc_t *ssc) |
| 1717 | { | |
| 1718 | int err; | |
| 1719 | static char cdb0[6] = { SEND_DIAGNOSTIC }; | |
| 1720 | ||
| 1721 | err = ses_runcmd(ssc, cdb0, 6, NULL, 0); | |
| 1722 | if (err) { | |
| 1723 | return (err); | |
| 1724 | } | |
| 1725 | DELAY(5000); | |
| 1726 | err = wrbuf16(ssc, SAFTE_WT_GLOBAL, 0, 0, 0, 1); | |
| 1727 | return (err); | |
| 1728 | } | |
| 1729 | ||
| fb2e6209 | 1730 | static int |
| 984263bc MD |
1731 | safte_get_encstat(ses_softc_t *ssc, int slpflg) |
| 1732 | { | |
| 1733 | return (safte_rdstat(ssc, slpflg)); | |
| 1734 | } | |
| 1735 | ||
| fb2e6209 | 1736 | static int |
| 984263bc MD |
1737 | safte_set_encstat(ses_softc_t *ssc, uint8_t encstat, int slpflg) |
| 1738 | { | |
| 1739 | struct scfg *cc = ssc->ses_private; | |
| 1740 | if (cc == NULL) | |
| 1741 | return (0); | |
| 1742 | /* | |
| 1743 | * Since SAF-TE devices aren't necessarily sticky in terms | |
| 1744 | * of state, make our soft copy of enclosure status 'sticky'- | |
| 1745 | * that is, things set in enclosure status stay set (as implied | |
| 1746 | * by conditions set in reading object status) until cleared. | |
| 1747 | */ | |
| 1748 | ssc->ses_encstat &= ~ALL_ENC_STAT; | |
| 1749 | ssc->ses_encstat |= (encstat & ALL_ENC_STAT); | |
| 1750 | ssc->ses_encstat |= ENCI_SVALID; | |
| 1751 | cc->flag1 &= ~(SAFT_FLG1_ALARM|SAFT_FLG1_GLOBFAIL|SAFT_FLG1_GLOBWARN); | |
| 1752 | if ((encstat & (SES_ENCSTAT_CRITICAL|SES_ENCSTAT_UNRECOV)) != 0) { | |
| 1753 | cc->flag1 |= SAFT_FLG1_ALARM|SAFT_FLG1_GLOBFAIL; | |
| 1754 | } else if ((encstat & SES_ENCSTAT_NONCRITICAL) != 0) { | |
| 1755 | cc->flag1 |= SAFT_FLG1_GLOBWARN; | |
| 1756 | } | |
| 1757 | return (wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, cc->flag2, 0, slpflg)); | |
| 1758 | } | |
| 1759 | ||
| fb2e6209 | 1760 | static int |
| 984263bc MD |
1761 | safte_get_objstat(ses_softc_t *ssc, ses_objstat *obp, int slpflg) |
| 1762 | { | |
| 1763 | int i = (int)obp->obj_id; | |
| 1764 | ||
| 1765 | if ((ssc->ses_encstat & ENCI_SVALID) == 0 || | |
| 1766 | (ssc->ses_objmap[i].svalid) == 0) { | |
| 1767 | int err = safte_rdstat(ssc, slpflg); | |
| 1768 | if (err) | |
| 1769 | return (err); | |
| 1770 | } | |
| 1771 | obp->cstat[0] = ssc->ses_objmap[i].encstat[0]; | |
| 1772 | obp->cstat[1] = ssc->ses_objmap[i].encstat[1]; | |
| 1773 | obp->cstat[2] = ssc->ses_objmap[i].encstat[2]; | |
| 1774 | obp->cstat[3] = ssc->ses_objmap[i].encstat[3]; | |
| 1775 | return (0); | |
| 1776 | } | |
| 1777 | ||
| 1778 | ||
| fb2e6209 | 1779 | static int |
| 984263bc MD |
1780 | safte_set_objstat(ses_softc_t *ssc, ses_objstat *obp, int slp) |
| 1781 | { | |
| 1782 | int idx, err; | |
| 1783 | encobj *ep; | |
| 1784 | struct scfg *cc; | |
| 1785 | ||
| 1786 | ||
| 1787 | SES_DLOG(ssc, "safte_set_objstat(%d): %x %x %x %x\n", | |
| 1788 | (int)obp->obj_id, obp->cstat[0], obp->cstat[1], obp->cstat[2], | |
| 1789 | obp->cstat[3]); | |
| 1790 | ||
| 1791 | /* | |
| 1792 | * If this is clear, we don't do diddly. | |
| 1793 | */ | |
| 1794 | if ((obp->cstat[0] & SESCTL_CSEL) == 0) { | |
| 1795 | return (0); | |
| 1796 | } | |
| 1797 | ||
| 1798 | err = 0; | |
| 1799 | /* | |
| 1800 | * Check to see if the common bits are set and do them first. | |
| 1801 | */ | |
| 1802 | if (obp->cstat[0] & ~SESCTL_CSEL) { | |
| 1803 | err = set_objstat_sel(ssc, obp, slp); | |
| 1804 | if (err) | |
| 1805 | return (err); | |
| 1806 | } | |
| 1807 | ||
| 1808 | cc = ssc->ses_private; | |
| 1809 | if (cc == NULL) | |
| 1810 | return (0); | |
| 1811 | ||
| 1812 | idx = (int)obp->obj_id; | |
| 1813 | ep = &ssc->ses_objmap[idx]; | |
| 1814 | ||
| 1815 | switch (ep->enctype) { | |
| 1816 | case SESTYP_DEVICE: | |
| 1817 | { | |
| 1818 | uint8_t slotop = 0; | |
| 1819 | /* | |
| 1820 | * XXX: I should probably cache the previous state | |
| 1821 | * XXX: of SESCTL_DEVOFF so that when it goes from | |
| 1822 | * XXX: true to false I can then set PREPARE FOR OPERATION | |
| 1823 | * XXX: flag in PERFORM SLOT OPERATION write buffer command. | |
| 1824 | */ | |
| 1825 | if (obp->cstat[2] & (SESCTL_RQSINS|SESCTL_RQSRMV)) { | |
| 1826 | slotop |= 0x2; | |
| 1827 | } | |
| 1828 | if (obp->cstat[2] & SESCTL_RQSID) { | |
| 1829 | slotop |= 0x4; | |
| 1830 | } | |
| 1831 | err = perf_slotop(ssc, (uint8_t) idx - (uint8_t) cc->slotoff, | |
| 1832 | slotop, slp); | |
| 1833 | if (err) | |
| 1834 | return (err); | |
| 1835 | if (obp->cstat[3] & SESCTL_RQSFLT) { | |
| 1836 | ep->priv |= 0x2; | |
| 1837 | } else { | |
| 1838 | ep->priv &= ~0x2; | |
| 1839 | } | |
| 1840 | if (ep->priv & 0xc6) { | |
| 1841 | ep->priv &= ~0x1; | |
| 1842 | } else { | |
| 1843 | ep->priv |= 0x1; /* no errors */ | |
| 1844 | } | |
| 1845 | wrslot_stat(ssc, slp); | |
| 1846 | break; | |
| 1847 | } | |
| 1848 | case SESTYP_POWER: | |
| 1849 | if (obp->cstat[3] & SESCTL_RQSTFAIL) { | |
| 1850 | cc->flag1 |= SAFT_FLG1_ENCPWRFAIL; | |
| 1851 | } else { | |
| 1852 | cc->flag1 &= ~SAFT_FLG1_ENCPWRFAIL; | |
| 1853 | } | |
| 1854 | err = wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, | |
| 1855 | cc->flag2, 0, slp); | |
| 1856 | if (err) | |
| 1857 | return (err); | |
| 1858 | if (obp->cstat[3] & SESCTL_RQSTON) { | |
| 0e224b5d | 1859 | wrbuf16(ssc, SAFTE_WT_ACTPWS, |
| 984263bc MD |
1860 | idx - cc->pwroff, 0, 0, slp); |
| 1861 | } else { | |
| 0e224b5d | 1862 | wrbuf16(ssc, SAFTE_WT_ACTPWS, |
| 984263bc MD |
1863 | idx - cc->pwroff, 0, 1, slp); |
| 1864 | } | |
| 1865 | break; | |
| 1866 | case SESTYP_FAN: | |
| 1867 | if (obp->cstat[3] & SESCTL_RQSTFAIL) { | |
| 1868 | cc->flag1 |= SAFT_FLG1_ENCFANFAIL; | |
| 1869 | } else { | |
| 1870 | cc->flag1 &= ~SAFT_FLG1_ENCFANFAIL; | |
| 1871 | } | |
| 1872 | err = wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, | |
| 1873 | cc->flag2, 0, slp); | |
| 1874 | if (err) | |
| 1875 | return (err); | |
| 1876 | if (obp->cstat[3] & SESCTL_RQSTON) { | |
| 1877 | uint8_t fsp; | |
| 1878 | if ((obp->cstat[3] & 0x7) == 7) { | |
| 1879 | fsp = 4; | |
| 1880 | } else if ((obp->cstat[3] & 0x7) == 6) { | |
| 1881 | fsp = 3; | |
| 1882 | } else if ((obp->cstat[3] & 0x7) == 4) { | |
| 1883 | fsp = 2; | |
| 1884 | } else { | |
| 1885 | fsp = 1; | |
| 1886 | } | |
| 0e224b5d | 1887 | wrbuf16(ssc, SAFTE_WT_FANSPD, idx, fsp, 0, slp); |
| 984263bc | 1888 | } else { |
| 0e224b5d | 1889 | wrbuf16(ssc, SAFTE_WT_FANSPD, idx, 0, 0, slp); |
| 984263bc MD |
1890 | } |
| 1891 | break; | |
| 1892 | case SESTYP_DOORLOCK: | |
| 1893 | if (obp->cstat[3] & 0x1) { | |
| 1894 | cc->flag2 &= ~SAFT_FLG2_LOCKDOOR; | |
| 1895 | } else { | |
| 1896 | cc->flag2 |= SAFT_FLG2_LOCKDOOR; | |
| 1897 | } | |
| 0e224b5d | 1898 | wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, cc->flag2, 0, slp); |
| 984263bc MD |
1899 | break; |
| 1900 | case SESTYP_ALARM: | |
| 1901 | /* | |
| 1902 | * On all nonzero but the 'muted' bit, we turn on the alarm, | |
| 1903 | */ | |
| 1904 | obp->cstat[3] &= ~0xa; | |
| 1905 | if (obp->cstat[3] & 0x40) { | |
| 1906 | cc->flag2 &= ~SAFT_FLG1_ALARM; | |
| 1907 | } else if (obp->cstat[3] != 0) { | |
| 1908 | cc->flag2 |= SAFT_FLG1_ALARM; | |
| 1909 | } else { | |
| 1910 | cc->flag2 &= ~SAFT_FLG1_ALARM; | |
| 1911 | } | |
| 1912 | ep->priv = obp->cstat[3]; | |
| 0e224b5d | 1913 | wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, cc->flag2, 0, slp); |
| 984263bc MD |
1914 | break; |
| 1915 | default: | |
| 1916 | break; | |
| 1917 | } | |
| 1918 | ep->svalid = 0; | |
| 1919 | return (0); | |
| 1920 | } | |
| 1921 | ||
| 1922 | static int | |
| 1923 | safte_getconfig(ses_softc_t *ssc) | |
| 1924 | { | |
| 1925 | struct scfg *cfg; | |
| 1926 | int err, amt; | |
| 1927 | char *sdata; | |
| 1928 | static char cdb[10] = | |
| 1929 | { READ_BUFFER, 1, SAFTE_RD_RDCFG, 0, 0, 0, 0, 0, SAFT_SCRATCH, 0 }; | |
| 1930 | ||
| 1931 | cfg = ssc->ses_private; | |
| 1932 | if (cfg == NULL) | |
| 1933 | return (ENXIO); | |
| 1934 | ||
| 1935 | sdata = SES_MALLOC(SAFT_SCRATCH); | |
| 1936 | if (sdata == NULL) | |
| 1937 | return (ENOMEM); | |
| 1938 | ||
| 1939 | amt = SAFT_SCRATCH; | |
| 1940 | err = ses_runcmd(ssc, cdb, 10, sdata, &amt); | |
| 1941 | if (err) { | |
| 1942 | SES_FREE(sdata, SAFT_SCRATCH); | |
| 1943 | return (err); | |
| 1944 | } | |
| 1945 | amt = SAFT_SCRATCH - amt; | |
| 1946 | if (amt < 6) { | |
| 1947 | SES_LOG(ssc, "too little data (%d) for configuration\n", amt); | |
| 1948 | SES_FREE(sdata, SAFT_SCRATCH); | |
| 1949 | return (EIO); | |
| 1950 | } | |
| 1951 | SES_VLOG(ssc, "Nfans %d Npwr %d Nslots %d Lck %d Ntherm %d Nspkrs %d\n", | |
| 1952 | sdata[0], sdata[1], sdata[2], sdata[3], sdata[4], sdata[5]); | |
| 1953 | cfg->Nfans = sdata[0]; | |
| 1954 | cfg->Npwr = sdata[1]; | |
| 1955 | cfg->Nslots = sdata[2]; | |
| 1956 | cfg->DoorLock = sdata[3]; | |
| 1957 | cfg->Ntherm = sdata[4]; | |
| 1958 | cfg->Nspkrs = sdata[5]; | |
| 1959 | cfg->Nalarm = NPSEUDO_ALARM; | |
| 1960 | SES_FREE(sdata, SAFT_SCRATCH); | |
| 1961 | return (0); | |
| 1962 | } | |
| 1963 | ||
| 1964 | static int | |
| 1965 | safte_rdstat(ses_softc_t *ssc, int slpflg) | |
| 1966 | { | |
| 1967 | int err, oid, r, i, hiwater, nitems, amt; | |
| 1968 | uint16_t tempflags; | |
| 1969 | size_t buflen; | |
| 1970 | uint8_t status, oencstat; | |
| 1971 | char *sdata, cdb[10]; | |
| 1972 | struct scfg *cc = ssc->ses_private; | |
| 1973 | ||
| 1974 | ||
| 1975 | /* | |
| 1976 | * The number of objects overstates things a bit, | |
| 1977 | * both for the bogus 'thermometer' entries and | |
| 1978 | * the drive status (which isn't read at the same | |
| 1979 | * time as the enclosure status), but that's okay. | |
| 1980 | */ | |
| 1981 | buflen = 4 * cc->Nslots; | |
| 1982 | if (ssc->ses_nobjects > buflen) | |
| 1983 | buflen = ssc->ses_nobjects; | |
| 1984 | sdata = SES_MALLOC(buflen); | |
| 1985 | if (sdata == NULL) | |
| 1986 | return (ENOMEM); | |
| 1987 | ||
| 1988 | cdb[0] = READ_BUFFER; | |
| 1989 | cdb[1] = 1; | |
| 1990 | cdb[2] = SAFTE_RD_RDESTS; | |
| 1991 | cdb[3] = 0; | |
| 1992 | cdb[4] = 0; | |
| 1993 | cdb[5] = 0; | |
| 1994 | cdb[6] = 0; | |
| 1995 | cdb[7] = (buflen >> 8) & 0xff; | |
| 1996 | cdb[8] = buflen & 0xff; | |
| 1997 | cdb[9] = 0; | |
| 1998 | amt = buflen; | |
| 1999 | err = ses_runcmd(ssc, cdb, 10, sdata, &amt); | |
| 2000 | if (err) { | |
| 2001 | SES_FREE(sdata, buflen); | |
| 2002 | return (err); | |
| 2003 | } | |
| 2004 | hiwater = buflen - amt; | |
| 2005 | ||
| 2006 | ||
| 2007 | /* | |
| 2008 | * invalidate all status bits. | |
| 2009 | */ | |
| 2010 | for (i = 0; i < ssc->ses_nobjects; i++) | |
| 2011 | ssc->ses_objmap[i].svalid = 0; | |
| 2012 | oencstat = ssc->ses_encstat & ALL_ENC_STAT; | |
| 2013 | ssc->ses_encstat = 0; | |
| 2014 | ||
| 2015 | ||
| 2016 | /* | |
| 2017 | * Now parse returned buffer. | |
| 2018 | * If we didn't get enough data back, | |
| 2019 | * that's considered a fatal error. | |
| 2020 | */ | |
| 2021 | oid = r = 0; | |
| 2022 | ||
| 2023 | for (nitems = i = 0; i < cc->Nfans; i++) { | |
| 2024 | SAFT_BAIL(r, hiwater, sdata, buflen); | |
| 2025 | /* | |
| 2026 | * 0 = Fan Operational | |
| 2027 | * 1 = Fan is malfunctioning | |
| 2028 | * 2 = Fan is not present | |
| 2029 | * 0x80 = Unknown or Not Reportable Status | |
| 2030 | */ | |
| 2031 | ssc->ses_objmap[oid].encstat[1] = 0; /* resvd */ | |
| 2032 | ssc->ses_objmap[oid].encstat[2] = 0; /* resvd */ | |
| 2033 | switch ((int)(uint8_t)sdata[r]) { | |
| 2034 | case 0: | |
| 2035 | nitems++; | |
| 2036 | ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; | |
| 2037 | /* | |
| 2038 | * We could get fancier and cache | |
| 2039 | * fan speeds that we have set, but | |
| 2040 | * that isn't done now. | |
| 2041 | */ | |
| 2042 | ssc->ses_objmap[oid].encstat[3] = 7; | |
| 2043 | break; | |
| 2044 | ||
| 2045 | case 1: | |
| 2046 | ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_CRIT; | |
| 2047 | /* | |
| 2048 | * FAIL and FAN STOPPED synthesized | |
| 2049 | */ | |
| 2050 | ssc->ses_objmap[oid].encstat[3] = 0x40; | |
| 2051 | /* | |
| 2052 | * Enclosure marked with CRITICAL error | |
| 2053 | * if only one fan or no thermometers, | |
| 2054 | * else the NONCRITICAL error is set. | |
| 2055 | */ | |
| 2056 | if (cc->Nfans == 1 || cc->Ntherm == 0) | |
| 2057 | ssc->ses_encstat |= SES_ENCSTAT_CRITICAL; | |
| 2058 | else | |
| 2059 | ssc->ses_encstat |= SES_ENCSTAT_NONCRITICAL; | |
| 2060 | break; | |
| 2061 | case 2: | |
| 2062 | ssc->ses_objmap[oid].encstat[0] = | |
| 2063 | SES_OBJSTAT_NOTINSTALLED; | |
| 2064 | ssc->ses_objmap[oid].encstat[3] = 0; | |
| 2065 | /* | |
| 2066 | * Enclosure marked with CRITICAL error | |
| 2067 | * if only one fan or no thermometers, | |
| 2068 | * else the NONCRITICAL error is set. | |
| 2069 | */ | |
| 2070 | if (cc->Nfans == 1) | |
| 2071 | ssc->ses_encstat |= SES_ENCSTAT_CRITICAL; | |
| 2072 | else | |
| 2073 | ssc->ses_encstat |= SES_ENCSTAT_NONCRITICAL; | |
| 2074 | break; | |
| 2075 | case 0x80: | |
| 2076 | ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; | |
| 2077 | ssc->ses_objmap[oid].encstat[3] = 0; | |
| 2078 | ssc->ses_encstat |= SES_ENCSTAT_INFO; | |
| 2079 | break; | |
| 2080 | default: | |
| 2081 | ssc->ses_objmap[oid].encstat[0] = | |
| 2082 | SES_OBJSTAT_UNSUPPORTED; | |
| 2083 | SES_LOG(ssc, "Unknown fan%d status 0x%x\n", i, | |
| 2084 | sdata[r] & 0xff); | |
| 2085 | break; | |
| 2086 | } | |
| 2087 | ssc->ses_objmap[oid++].svalid = 1; | |
| 2088 | r++; | |
| 2089 | } | |
| 2090 | ||
| 2091 | /* | |
| 2092 | * No matter how you cut it, no cooling elements when there | |
| 2093 | * should be some there is critical. | |
| 2094 | */ | |
| 2095 | if (cc->Nfans && nitems == 0) { | |
| 2096 | ssc->ses_encstat |= SES_ENCSTAT_CRITICAL; | |
| 2097 | } | |
| 2098 | ||
| 2099 | ||
| 2100 | for (i = 0; i < cc->Npwr; i++) { | |
| 2101 | SAFT_BAIL(r, hiwater, sdata, buflen); | |
| 2102 | ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; | |
| 2103 | ssc->ses_objmap[oid].encstat[1] = 0; /* resvd */ | |
| 2104 | ssc->ses_objmap[oid].encstat[2] = 0; /* resvd */ | |
| 2105 | ssc->ses_objmap[oid].encstat[3] = 0x20; /* requested on */ | |
| 2106 | switch ((uint8_t)sdata[r]) { | |
| 2107 | case 0x00: /* pws operational and on */ | |
| 2108 | ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; | |
| 2109 | break; | |
| 2110 | case 0x01: /* pws operational and off */ | |
| 2111 | ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; | |
| 2112 | ssc->ses_objmap[oid].encstat[3] = 0x10; | |
| 2113 | ssc->ses_encstat |= SES_ENCSTAT_INFO; | |
| 2114 | break; | |
| 2115 | case 0x10: /* pws is malfunctioning and commanded on */ | |
| 2116 | ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_CRIT; | |
| 2117 | ssc->ses_objmap[oid].encstat[3] = 0x61; | |
| 2118 | ssc->ses_encstat |= SES_ENCSTAT_NONCRITICAL; | |
| 2119 | break; | |
| 2120 | ||
| 2121 | case 0x11: /* pws is malfunctioning and commanded off */ | |
| 2122 | ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_NONCRIT; | |
| 2123 | ssc->ses_objmap[oid].encstat[3] = 0x51; | |
| 2124 | ssc->ses_encstat |= SES_ENCSTAT_NONCRITICAL; | |
| 2125 | break; | |
| 2126 | case 0x20: /* pws is not present */ | |
| 2127 | ssc->ses_objmap[oid].encstat[0] = | |
| 2128 | SES_OBJSTAT_NOTINSTALLED; | |
| 2129 | ssc->ses_objmap[oid].encstat[3] = 0; | |
| 2130 | ssc->ses_encstat |= SES_ENCSTAT_INFO; | |
| 2131 | break; | |
| 2132 | case 0x21: /* pws is present */ | |
| 2133 | /* | |
| 2134 | * This is for enclosures that cannot tell whether the | |
| 2135 | * device is on or malfunctioning, but know that it is | |
| 2136 | * present. Just fall through. | |
| 2137 | */ | |
| 2138 | /* FALLTHROUGH */ | |
| 2139 | case 0x80: /* Unknown or Not Reportable Status */ | |
| 2140 | ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; | |
| 2141 | ssc->ses_objmap[oid].encstat[3] = 0; | |
| 2142 | ssc->ses_encstat |= SES_ENCSTAT_INFO; | |
| 2143 | break; | |
| 2144 | default: | |
| 2145 | SES_LOG(ssc, "unknown power supply %d status (0x%x)\n", | |
| 2146 | i, sdata[r] & 0xff); | |
| 2147 | break; | |
| 2148 | } | |
| 2149 | ssc->ses_objmap[oid++].svalid = 1; | |
| 2150 | r++; | |
| 2151 | } | |
| 2152 | ||
| 2153 | /* | |
| 2154 | * Skip over Slot SCSI IDs | |
| 2155 | */ | |
| 2156 | r += cc->Nslots; | |
| 2157 | ||
| 2158 | /* | |
| 2159 | * We always have doorlock status, no matter what, | |
| 2160 | * but we only save the status if we have one. | |
| 2161 | */ | |
| 2162 | SAFT_BAIL(r, hiwater, sdata, buflen); | |
| 2163 | if (cc->DoorLock) { | |
| 2164 | /* | |
| 2165 | * 0 = Door Locked | |
| 2166 | * 1 = Door Unlocked, or no Lock Installed | |
| 2167 | * 0x80 = Unknown or Not Reportable Status | |
| 2168 | */ | |
| 2169 | ssc->ses_objmap[oid].encstat[1] = 0; | |
| 2170 | ssc->ses_objmap[oid].encstat[2] = 0; | |
| 2171 | switch ((uint8_t)sdata[r]) { | |
| 2172 | case 0: | |
| 2173 | ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; | |
| 2174 | ssc->ses_objmap[oid].encstat[3] = 0; | |
| 2175 | break; | |
| 2176 | case 1: | |
| 2177 | ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; | |
| 2178 | ssc->ses_objmap[oid].encstat[3] = 1; | |
| 2179 | break; | |
| 2180 | case 0x80: | |
| 2181 | ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; | |
| 2182 | ssc->ses_objmap[oid].encstat[3] = 0; | |
| 2183 | ssc->ses_encstat |= SES_ENCSTAT_INFO; | |
| 2184 | break; | |
| 2185 | default: | |
| 2186 | ssc->ses_objmap[oid].encstat[0] = | |
| 2187 | SES_OBJSTAT_UNSUPPORTED; | |
| 2188 | SES_LOG(ssc, "unknown lock status 0x%x\n", | |
| 2189 | sdata[r] & 0xff); | |
| 2190 | break; | |
| 2191 | } | |
| 2192 | ssc->ses_objmap[oid++].svalid = 1; | |
| 2193 | } | |
| 2194 | r++; | |
| 2195 | ||
| 2196 | /* | |
| 2197 | * We always have speaker status, no matter what, | |
| 2198 | * but we only save the status if we have one. | |
| 2199 | */ | |
| 2200 | SAFT_BAIL(r, hiwater, sdata, buflen); | |
| 2201 | if (cc->Nspkrs) { | |
| 2202 | ssc->ses_objmap[oid].encstat[1] = 0; | |
| 2203 | ssc->ses_objmap[oid].encstat[2] = 0; | |
| 2204 | if (sdata[r] == 1) { | |
| 2205 | /* | |
| 2206 | * We need to cache tone urgency indicators. | |
| 2207 | * Someday. | |
| 2208 | */ | |
| 2209 | ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_NONCRIT; | |
| 2210 | ssc->ses_objmap[oid].encstat[3] = 0x8; | |
| 2211 | ssc->ses_encstat |= SES_ENCSTAT_NONCRITICAL; | |
| 2212 | } else if (sdata[r] == 0) { | |
| 2213 | ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; | |
| 2214 | ssc->ses_objmap[oid].encstat[3] = 0; | |
| 2215 | } else { | |
| 2216 | ssc->ses_objmap[oid].encstat[0] = | |
| 2217 | SES_OBJSTAT_UNSUPPORTED; | |
| 2218 | ssc->ses_objmap[oid].encstat[3] = 0; | |
| 2219 | SES_LOG(ssc, "unknown spkr status 0x%x\n", | |
| 2220 | sdata[r] & 0xff); | |
| 2221 | } | |
| 2222 | ssc->ses_objmap[oid++].svalid = 1; | |
| 2223 | } | |
| 2224 | r++; | |
| 2225 | ||
| 2226 | for (i = 0; i < cc->Ntherm; i++) { | |
| 2227 | SAFT_BAIL(r, hiwater, sdata, buflen); | |
| 2228 | /* | |
| 2229 | * Status is a range from -10 to 245 deg Celsius, | |
| 2230 | * which we need to normalize to -20 to -245 according | |
| 2231 | * to the latest SCSI spec, which makes little | |
| 2232 | * sense since this would overflow an 8bit value. | |
| 2233 | * Well, still, the base normalization is -20, | |
| 2234 | * not -10, so we have to adjust. | |
| 2235 | * | |
| 2236 | * So what's over and under temperature? | |
| 2237 | * Hmm- we'll state that 'normal' operating | |
| 2238 | * is 10 to 40 deg Celsius. | |
| 2239 | */ | |
| 2240 | ||
| 2241 | /* | |
| 2242 | * Actually.... All of the units that people out in the world | |
| 2243 | * seem to have do not come even close to setting a value that | |
| 2244 | * complies with this spec. | |
| 2245 | * | |
| 2246 | * The closest explanation I could find was in an | |
| 2247 | * LSI-Logic manual, which seemed to indicate that | |
| 2248 | * this value would be set by whatever the I2C code | |
| 2249 | * would interpolate from the output of an LM75 | |
| 2250 | * temperature sensor. | |
| 2251 | * | |
| 2252 | * This means that it is impossible to use the actual | |
| 2253 | * numeric value to predict anything. But we don't want | |
| 2254 | * to lose the value. So, we'll propagate the *uncorrected* | |
| 2255 | * value and set SES_OBJSTAT_NOTAVAIL. We'll depend on the | |
| 2256 | * temperature flags for warnings. | |
| 2257 | */ | |
| 2258 | ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_NOTAVAIL; | |
| 2259 | ssc->ses_objmap[oid].encstat[1] = 0; | |
| 2260 | ssc->ses_objmap[oid].encstat[2] = sdata[r]; | |
| fc6d0222 | 2261 | ssc->ses_objmap[oid].encstat[3] = 0; |
| 984263bc MD |
2262 | ssc->ses_objmap[oid++].svalid = 1; |
| 2263 | r++; | |
| 2264 | } | |
| 2265 | ||
| 2266 | /* | |
| 2267 | * Now, for "pseudo" thermometers, we have two bytes | |
| 2268 | * of information in enclosure status- 16 bits. Actually, | |
| 2269 | * the MSB is a single TEMP ALERT flag indicating whether | |
| 2270 | * any other bits are set, but, thanks to fuzzy thinking, | |
| 2271 | * in the SAF-TE spec, this can also be set even if no | |
| 2272 | * other bits are set, thus making this really another | |
| 2273 | * binary temperature sensor. | |
| 2274 | */ | |
| 2275 | ||
| 2276 | SAFT_BAIL(r, hiwater, sdata, buflen); | |
| 2277 | tempflags = sdata[r++]; | |
| 2278 | SAFT_BAIL(r, hiwater, sdata, buflen); | |
| 2279 | tempflags |= (tempflags << 8) | sdata[r++]; | |
| 2280 | ||
| 2281 | for (i = 0; i < NPSEUDO_THERM; i++) { | |
| 2282 | ssc->ses_objmap[oid].encstat[1] = 0; | |
| 2283 | if (tempflags & (1 << (NPSEUDO_THERM - i - 1))) { | |
| 2284 | ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_CRIT; | |
| 2285 | ssc->ses_objmap[4].encstat[2] = 0xff; | |
| 2286 | /* | |
| 2287 | * Set 'over temperature' failure. | |
| 2288 | */ | |
| 2289 | ssc->ses_objmap[oid].encstat[3] = 8; | |
| 2290 | ssc->ses_encstat |= SES_ENCSTAT_CRITICAL; | |
| 2291 | } else { | |
| 2292 | /* | |
| 2293 | * We used to say 'not available' and synthesize a | |
| 2294 | * nominal 30 deg (C)- that was wrong. Actually, | |
| 2295 | * Just say 'OK', and use the reserved value of | |
| 2296 | * zero. | |
| 2297 | */ | |
| 2298 | ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; | |
| 2299 | ssc->ses_objmap[oid].encstat[2] = 0; | |
| 2300 | ssc->ses_objmap[oid].encstat[3] = 0; | |
| 2301 | } | |
| 2302 | ssc->ses_objmap[oid++].svalid = 1; | |
| 2303 | } | |
| 2304 | ||
| 2305 | /* | |
| 2306 | * Get alarm status. | |
| 2307 | */ | |
| 2308 | ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; | |
| 2309 | ssc->ses_objmap[oid].encstat[3] = ssc->ses_objmap[oid].priv; | |
| 2310 | ssc->ses_objmap[oid++].svalid = 1; | |
| 2311 | ||
| 2312 | /* | |
| 2313 | * Now get drive slot status | |
| 2314 | */ | |
| 2315 | cdb[2] = SAFTE_RD_RDDSTS; | |
| 2316 | amt = buflen; | |
| 2317 | err = ses_runcmd(ssc, cdb, 10, sdata, &amt); | |
| 2318 | if (err) { | |
| 2319 | SES_FREE(sdata, buflen); | |
| 2320 | return (err); | |
| 2321 | } | |
| 2322 | hiwater = buflen - amt; | |
| 2323 | for (r = i = 0; i < cc->Nslots; i++, r += 4) { | |
| 2324 | SAFT_BAIL(r+3, hiwater, sdata, buflen); | |
| 2325 | ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_UNSUPPORTED; | |
| 2326 | ssc->ses_objmap[oid].encstat[1] = (uint8_t) i; | |
| 2327 | ssc->ses_objmap[oid].encstat[2] = 0; | |
| 2328 | ssc->ses_objmap[oid].encstat[3] = 0; | |
| 2329 | status = sdata[r+3]; | |
| 2330 | if ((status & 0x1) == 0) { /* no device */ | |
| 2331 | ssc->ses_objmap[oid].encstat[0] = | |
| 2332 | SES_OBJSTAT_NOTINSTALLED; | |
| 2333 | } else { | |
| 2334 | ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; | |
| 2335 | } | |
| 2336 | if (status & 0x2) { | |
| 2337 | ssc->ses_objmap[oid].encstat[2] = 0x8; | |
| 2338 | } | |
| 2339 | if ((status & 0x4) == 0) { | |
| 2340 | ssc->ses_objmap[oid].encstat[3] = 0x10; | |
| 2341 | } | |
| 2342 | ssc->ses_objmap[oid++].svalid = 1; | |
| 2343 | } | |
| 2344 | /* see comment below about sticky enclosure status */ | |
| 2345 | ssc->ses_encstat |= ENCI_SVALID | oencstat; | |
| 2346 | SES_FREE(sdata, buflen); | |
| 2347 | return (0); | |
| 2348 | } | |
| 2349 | ||
| 2350 | static int | |
| 2351 | set_objstat_sel(ses_softc_t *ssc, ses_objstat *obp, int slp) | |
| 2352 | { | |
| 2353 | int idx; | |
| 2354 | encobj *ep; | |
| 2355 | struct scfg *cc = ssc->ses_private; | |
| 2356 | ||
| 2357 | if (cc == NULL) | |
| 2358 | return (0); | |
| 2359 | ||
| 2360 | idx = (int)obp->obj_id; | |
| 2361 | ep = &ssc->ses_objmap[idx]; | |
| 2362 | ||
| 2363 | switch (ep->enctype) { | |
| 2364 | case SESTYP_DEVICE: | |
| 2365 | if (obp->cstat[0] & SESCTL_PRDFAIL) { | |
| 2366 | ep->priv |= 0x40; | |
| 2367 | } | |
| 2368 | /* SESCTL_RSTSWAP has no correspondence in SAF-TE */ | |
| 2369 | if (obp->cstat[0] & SESCTL_DISABLE) { | |
| 2370 | ep->priv |= 0x80; | |
| 2371 | /* | |
| 2372 | * Hmm. Try to set the 'No Drive' flag. | |
| 2373 | * Maybe that will count as a 'disable'. | |
| 2374 | */ | |
| 2375 | } | |
| 2376 | if (ep->priv & 0xc6) { | |
| 2377 | ep->priv &= ~0x1; | |
| 2378 | } else { | |
| 2379 | ep->priv |= 0x1; /* no errors */ | |
| 2380 | } | |
| 2381 | wrslot_stat(ssc, slp); | |
| 2382 | break; | |
| 2383 | case SESTYP_POWER: | |
| 2384 | /* | |
| 2385 | * Okay- the only one that makes sense here is to | |
| 2386 | * do the 'disable' for a power supply. | |
| 2387 | */ | |
| 2388 | if (obp->cstat[0] & SESCTL_DISABLE) { | |
| 0e224b5d | 2389 | wrbuf16(ssc, SAFTE_WT_ACTPWS, |
| 984263bc MD |
2390 | idx - cc->pwroff, 0, 0, slp); |
| 2391 | } | |
| 2392 | break; | |
| 2393 | case SESTYP_FAN: | |
| 2394 | /* | |
| 2395 | * Okay- the only one that makes sense here is to | |
| 2396 | * set fan speed to zero on disable. | |
| 2397 | */ | |
| 2398 | if (obp->cstat[0] & SESCTL_DISABLE) { | |
| 2399 | /* remember- fans are the first items, so idx works */ | |
| 0e224b5d | 2400 | wrbuf16(ssc, SAFTE_WT_FANSPD, idx, 0, 0, slp); |
| 984263bc MD |
2401 | } |
| 2402 | break; | |
| 2403 | case SESTYP_DOORLOCK: | |
| 2404 | /* | |
| 2405 | * Well, we can 'disable' the lock. | |
| 2406 | */ | |
| 2407 | if (obp->cstat[0] & SESCTL_DISABLE) { | |
| 2408 | cc->flag2 &= ~SAFT_FLG2_LOCKDOOR; | |
| 0e224b5d | 2409 | wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, |
| 984263bc MD |
2410 | cc->flag2, 0, slp); |
| 2411 | } | |
| 2412 | break; | |
| 2413 | case SESTYP_ALARM: | |
| 2414 | /* | |
| 2415 | * Well, we can 'disable' the alarm. | |
| 2416 | */ | |
| 2417 | if (obp->cstat[0] & SESCTL_DISABLE) { | |
| 2418 | cc->flag2 &= ~SAFT_FLG1_ALARM; | |
| 2419 | ep->priv |= 0x40; /* Muted */ | |
| 0e224b5d | 2420 | wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, |
| 984263bc MD |
2421 | cc->flag2, 0, slp); |
| 2422 | } | |
| 2423 | break; | |
| 2424 | default: | |
| 2425 | break; | |
| 2426 | } | |
| 2427 | ep->svalid = 0; | |
| 2428 | return (0); | |
| 2429 | } | |
| 2430 | ||
| 2431 | /* | |
| 2432 | * This function handles all of the 16 byte WRITE BUFFER commands. | |
| 2433 | */ | |
| 2434 | static int | |
| 2435 | wrbuf16(ses_softc_t *ssc, uint8_t op, uint8_t b1, uint8_t b2, | |
| 2436 | uint8_t b3, int slp) | |
| 2437 | { | |
| 2438 | int err, amt; | |
| 2439 | char *sdata; | |
| 2440 | struct scfg *cc = ssc->ses_private; | |
| 2441 | static char cdb[10] = { WRITE_BUFFER, 1, 0, 0, 0, 0, 0, 0, 16, 0 }; | |
| 2442 | ||
| 2443 | if (cc == NULL) | |
| 2444 | return (0); | |
| 2445 | ||
| 2446 | sdata = SES_MALLOC(16); | |
| 2447 | if (sdata == NULL) | |
| 2448 | return (ENOMEM); | |
| 2449 | ||
| 2450 | SES_DLOG(ssc, "saf_wrbuf16 %x %x %x %x\n", op, b1, b2, b3); | |
| 2451 | ||
| 2452 | sdata[0] = op; | |
| 2453 | sdata[1] = b1; | |
| 2454 | sdata[2] = b2; | |
| 2455 | sdata[3] = b3; | |
| 2456 | MEMZERO(&sdata[4], 12); | |
| 2457 | amt = -16; | |
| 2458 | err = ses_runcmd(ssc, cdb, 10, sdata, &amt); | |
| 2459 | SES_FREE(sdata, 16); | |
| 2460 | return (err); | |
| 2461 | } | |
| 2462 | ||
| 2463 | /* | |
| 2464 | * This function updates the status byte for the device slot described. | |
| 2465 | * | |
| 2466 | * Since this is an optional SAF-TE command, there's no point in | |
| 2467 | * returning an error. | |
| 2468 | */ | |
| 2469 | static void | |
| 2470 | wrslot_stat(ses_softc_t *ssc, int slp) | |
| 2471 | { | |
| 2472 | int i, amt; | |
| 2473 | encobj *ep; | |
| 2474 | char cdb[10], *sdata; | |
| 2475 | struct scfg *cc = ssc->ses_private; | |
| 2476 | ||
| 2477 | if (cc == NULL) | |
| 2478 | return; | |
| 2479 | ||
| 2480 | SES_DLOG(ssc, "saf_wrslot\n"); | |
| 2481 | cdb[0] = WRITE_BUFFER; | |
| 2482 | cdb[1] = 1; | |
| 2483 | cdb[2] = 0; | |
| 2484 | cdb[3] = 0; | |
| 2485 | cdb[4] = 0; | |
| 2486 | cdb[5] = 0; | |
| 2487 | cdb[6] = 0; | |
| 2488 | cdb[7] = 0; | |
| 2489 | cdb[8] = cc->Nslots * 3 + 1; | |
| 2490 | cdb[9] = 0; | |
| 2491 | ||
| 2492 | sdata = SES_MALLOC(cc->Nslots * 3 + 1); | |
| 2493 | if (sdata == NULL) | |
| 2494 | return; | |
| 2495 | MEMZERO(sdata, cc->Nslots * 3 + 1); | |
| 2496 | ||
| 2497 | sdata[0] = SAFTE_WT_DSTAT; | |
| 2498 | for (i = 0; i < cc->Nslots; i++) { | |
| 2499 | ep = &ssc->ses_objmap[cc->slotoff + i]; | |
| 2500 | SES_DLOG(ssc, "saf_wrslot %d <- %x\n", i, ep->priv & 0xff); | |
| 2501 | sdata[1 + (3 * i)] = ep->priv & 0xff; | |
| 2502 | } | |
| 2503 | amt = -(cc->Nslots * 3 + 1); | |
| 0e224b5d | 2504 | ses_runcmd(ssc, cdb, 10, sdata, &amt); |
| 984263bc MD |
2505 | SES_FREE(sdata, cc->Nslots * 3 + 1); |
| 2506 | } | |
| 2507 | ||
| 2508 | /* | |
| 2509 | * This function issues the "PERFORM SLOT OPERATION" command. | |
| 2510 | */ | |
| 2511 | static int | |
| 2512 | perf_slotop(ses_softc_t *ssc, uint8_t slot, uint8_t opflag, int slp) | |
| 2513 | { | |
| 2514 | int err, amt; | |
| 2515 | char *sdata; | |
| 2516 | struct scfg *cc = ssc->ses_private; | |
| 2517 | static char cdb[10] = | |
| 2518 | { WRITE_BUFFER, 1, 0, 0, 0, 0, 0, 0, SAFT_SCRATCH, 0 }; | |
| 2519 | ||
| 2520 | if (cc == NULL) | |
| 2521 | return (0); | |
| 2522 | ||
| 2523 | sdata = SES_MALLOC(SAFT_SCRATCH); | |
| 2524 | if (sdata == NULL) | |
| 2525 | return (ENOMEM); | |
| 2526 | MEMZERO(sdata, SAFT_SCRATCH); | |
| 2527 | ||
| 2528 | sdata[0] = SAFTE_WT_SLTOP; | |
| 2529 | sdata[1] = slot; | |
| 2530 | sdata[2] = opflag; | |
| 2531 | SES_DLOG(ssc, "saf_slotop slot %d op %x\n", slot, opflag); | |
| 2532 | amt = -SAFT_SCRATCH; | |
| 2533 | err = ses_runcmd(ssc, cdb, 10, sdata, &amt); | |
| 2534 | SES_FREE(sdata, SAFT_SCRATCH); | |
| 2535 | return (err); | |
| 2536 | } |