1 /* $NetBSD: cs80bus.c,v 1.18 2021/04/24 23:36:53 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2001 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Gregory McGarry. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: cs80bus.c,v 1.18 2021/04/24 23:36:53 thorpej Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/device.h> 38 #include <sys/endian.h> 39 #include <sys/malloc.h> 40 41 #include <dev/gpib/gpibvar.h> 42 #include <dev/gpib/cs80busvar.h> 43 44 #ifndef DEBUG 45 #define DEBUG 46 #endif 47 48 #ifdef DEBUG 49 int cs80busdebug = 0xff; 50 #define DBG_FOLLOW 0x01 51 #define DBG_STATUS 0x02 52 #define DBG_FAIL 0x04 53 #define DPRINTF(mask, str) if (cs80busdebug & (mask)) printf str 54 #else 55 #define DPRINTF(mask, str) /* nothing */ 56 #endif 57 58 #include "locators.h" 59 #define cs80buscf_slave cf_loc[CS80BUSCF_SLAVE] 60 #define cs80buscf_punit cf_loc[CS80BUSCF_PUNIT] 61 62 int cs80busmatch(device_t, cfdata_t, void *); 63 void cs80busattach(device_t, device_t, void *); 64 65 CFATTACH_DECL_NEW(cs80bus, sizeof(struct cs80bus_softc), 66 cs80busmatch, cs80busattach, NULL, NULL); 67 68 static int cs80bus_alloc(struct cs80bus_softc *, int, int); 69 static int cs80bussearch(device_t, cfdata_t, 70 const int *, void *); 71 static int cs80busprint(void *, const char *); 72 73 /* 74 * HP's CS80/SS80 command set can be found on `newer' devices, while 75 * the HP's Amigo command set is used on before-you-were-born 76 * devices. Devices that respond to CS80/SS80 (and probably Amigo, too) 77 * are tagged with a 16-bit ID. 78 * 79 * CS80/SS80 has a 2-level addressing scheme; slave, the analog 80 * of a SCSI ID, and punit, the analog of a SCSI LUN. Unforunately, 81 * IDs are on a per-slave basis; punits are often used for disk 82 * drives that have an accompanying tape drive on the second punit. 83 * 84 * We treat CS80/SS80 as an indirect bus. However, since we are given 85 * some ID information, it is unreasonable to disallow cloning of 86 * CS80/SS80 devices. 87 * 88 * To deal with all of this, we use the semi-twisted scheme 89 * in cs80bus_attach_children(). For each GPIB slave, we loop 90 * through all of the possibly-configured children, allowing 91 * them to modify the punit parameter (but NOT the slave!). 92 * 93 */ 94 95 int 96 cs80busmatch(device_t parent, cfdata_t match, void *aux) 97 { 98 99 return (1); 100 } 101 102 void 103 cs80busattach(device_t parent, device_t self, void *aux) 104 { 105 struct cs80bus_softc *sc = device_private(self); 106 struct gpib_attach_args *ga = aux; 107 struct cs80bus_attach_args ca; 108 int slave; 109 u_int16_t id; 110 111 printf("\n"); 112 113 sc->sc_dev = self; 114 sc->sc_ic = ga->ga_ic; 115 116 for (slave = 0; slave < 8; slave++) { 117 118 if (gpib_isalloc(device_private(device_parent(sc->sc_dev)), 119 slave)) 120 continue; 121 122 if (gpibrecv(sc->sc_ic, GPIB_BROADCAST_ADDR, 123 slave, &id, 2) != 2) 124 continue; 125 126 BE16TOH(id); 127 128 DPRINTF(DBG_STATUS, ("cs80busattach: found id 0x%x\n", id)); 129 130 if ((id & 0x200) == 0) 131 continue; 132 133 ca.ca_ic = sc->sc_ic; 134 ca.ca_slave = slave; 135 ca.ca_id = id; 136 137 config_search(sc->sc_dev, &ca, 138 CFARG_SEARCH, cs80bussearch, 139 CFARG_EOL); 140 } 141 } 142 143 int 144 cs80bussearch(device_t parent, cfdata_t cf, const int *ldesc, void *aux) 145 { 146 struct cs80bus_softc *sc = device_private(parent); 147 struct cs80bus_attach_args *ca = aux; 148 149 /* 150 * Set punit if operator specified one in the kernel 151 * configuration file. 152 */ 153 if (cf->cs80buscf_punit != CS80BUSCF_PUNIT_DEFAULT && 154 cf->cs80buscf_punit < CS80BUS_NPUNITS) 155 ca->ca_punit = cf->cs80buscf_punit; 156 else 157 /* default punit */ 158 ca->ca_punit = 0; 159 160 DPRINTF(DBG_FOLLOW, ("cs80bussearch: id=0x%x slave=%d punit=%d\n", 161 ca->ca_id, ca->ca_slave, ca->ca_punit)); 162 163 if (config_probe(parent, cf, ca)) { 164 165 DPRINTF(DBG_FOLLOW, 166 ("cs80bussearch: got id=0x%x slave=%d punit %d\n", 167 ca->ca_id, ca->ca_slave, ca->ca_punit)); 168 169 /* 170 * The device probe has succeeded, and filled in 171 * the punit information. Make sure the configuration 172 * allows for this slave/punit combination. 173 */ 174 if (cf->cs80buscf_slave != CS80BUSCF_SLAVE_DEFAULT && 175 cf->cs80buscf_slave != ca->ca_slave) 176 goto out; 177 if (cf->cs80buscf_punit != CS80BUSCF_PUNIT_DEFAULT && 178 cf->cs80buscf_punit != ca->ca_punit) 179 goto out; 180 181 /* 182 * Allocate the device's address from the bus's 183 * resource map. 184 */ 185 if (cs80bus_alloc(sc, ca->ca_slave, ca->ca_punit)) 186 goto out; 187 188 /* 189 * This device is allowed; attach it. 190 */ 191 config_attach(parent, cf, ca, cs80busprint, CFARG_EOL); 192 } 193 out: 194 return (0); 195 } 196 197 int 198 cs80busprint(void *aux, const char *pnp) 199 { 200 struct cs80bus_attach_args *ca = aux; 201 202 printf(" slave %d punit %d", ca->ca_slave, ca->ca_punit); 203 return (UNCONF); 204 } 205 206 static int 207 cs80bus_alloc(struct cs80bus_softc *sc, int slave, int punit) 208 { 209 210 DPRINTF(DBG_FOLLOW, ("cs80bus_alloc: sc=%p\n", sc)); 211 212 if (slave >= CS80BUS_NSLAVES || punit >= CS80BUS_NPUNITS) 213 panic("cs80bus_alloc: device address out of range"); 214 215 gpib_alloc(device_private(device_parent(sc->sc_dev)), slave); 216 217 if (sc->sc_rmap[slave][punit] == 0) { 218 sc->sc_rmap[slave][punit] = 1; 219 return (0); 220 } 221 return (1); 222 } 223 224 225 226 /* 227 * CS80/SS80 (secondary) command functions 228 */ 229 230 int 231 cs80describe(void *v, int slave, int punit, struct cs80_description *csd) 232 { 233 struct cs80bus_softc *sc = v; 234 struct cs80_describecmd desc; 235 u_int8_t stat; 236 237 DPRINTF(DBG_FOLLOW, ("cs80describe: sc=%p slave=%d\n", sc, slave)); 238 239 /* 240 * Note command is always issued to unit 0. 241 */ 242 243 desc.c_unit = CS80CMD_SUNIT(0); 244 desc.c_vol = CS80CMD_SVOL(0); 245 desc.c_cmd = CS80CMD_DESC; 246 (void) gpibsend(sc->sc_ic, slave, CS80CMD_SCMD, &desc, sizeof(desc)); 247 (void) gpibrecv(sc->sc_ic, slave, CS80CMD_EXEC, csd, sizeof(*csd)); 248 (void) gpibrecv(sc->sc_ic, slave, CS80CMD_QSTAT, &stat, 1); 249 if (stat != 0) { 250 DPRINTF(DBG_FAIL, ("cs80describe: failed, stat=0x%x\n", stat)); 251 return (1); 252 } 253 BE16TOH(csd->d_iuw); 254 BE16TOH(csd->d_cmaxxfr); 255 BE16TOH(csd->d_sectsize); 256 BE16TOH(csd->d_blocktime); 257 BE16TOH(csd->d_uavexfr); 258 BE16TOH(csd->d_retry); 259 BE16TOH(csd->d_access); 260 BE32TOH(csd->d_maxcylhead); 261 BE16TOH(csd->d_maxsect); 262 BE16TOH(csd->d_maxvsecth); 263 BE32TOH(csd->d_maxvsectl); 264 265 return (0); 266 } 267 268 int 269 cs80reset(void *v, int slave, int punit) 270 { 271 struct cs80bus_softc *sc = v; 272 struct cs80_clearcmd clear; 273 struct cs80_srcmd sr; 274 struct cs80_ssmcmd ssm; 275 276 DPRINTF(DBG_FOLLOW, ("cs80reset: sc=%p slave=%d punit=%d\n", sc, 277 slave, punit)); 278 279 clear.c_unit = CS80CMD_SUNIT(punit); 280 clear.c_cmd = CS80CMD_CLEAR; 281 if (cs80send(sc, slave, punit, CS80CMD_TCMD, &clear, sizeof(clear))) { 282 DPRINTF(DBG_FAIL, ("cs80reset: CLEAR failed\n")); 283 return (1); 284 } 285 286 sr.c_unit = CS80CMD_SUNIT(15); /* XXX */ 287 sr.c_nop = CS80CMD_NOP; 288 sr.c_cmd = CS80CMD_SREL; 289 sr.c_param = 0xc0; /* XXX */ 290 if (cs80send(sc, slave, punit, CS80CMD_SCMD, &sr, sizeof(sr))) { 291 DPRINTF(DBG_FAIL, ("cs80reset: SREL failed\n")); 292 return (1); 293 } 294 295 ssm.c_unit = CS80CMD_SUNIT(punit); 296 ssm.c_cmd = CS80CMD_SSM; 297 ssm.c_refm = htobe16(REF_MASK); 298 ssm.c_fefm = htobe16(FEF_MASK); 299 ssm.c_aefm = htobe16(AEF_MASK); 300 ssm.c_iefm = htobe16(IEF_MASK); 301 if (cs80send(sc, slave, punit, CS80CMD_SCMD, &ssm, sizeof(ssm))) { 302 DPRINTF(DBG_FAIL, ("cs80reset: SSM failed\n")); 303 return (1); 304 } 305 306 return (0); 307 } 308 309 int 310 cs80status(void *v, int slave, int punit, struct cs80_stat *css) 311 { 312 struct cs80bus_softc *sc = v; 313 struct cs80_statuscmd rs; 314 u_int8_t stat; 315 316 rs.c_unit = CS80CMD_SUNIT(punit); 317 rs.c_sram = CS80CMD_SRAM; 318 rs.c_param = 0; /* single vector (i.e. sector number) */ 319 rs.c_cmd = CS80CMD_STATUS; 320 memset((void *)css, 0, sizeof(*css)); 321 (void) gpibsend(sc->sc_ic, slave, CS80CMD_SCMD, &rs, sizeof(rs)); 322 (void) gpibrecv(sc->sc_ic, slave, CS80CMD_EXEC, css, sizeof(*css)); 323 (void) gpibrecv(sc->sc_ic, slave, CS80CMD_QSTAT, &stat, 1); 324 if (stat != 0) { 325 DPRINTF(DBG_FAIL, ("cs80status: failed, stat=0x%x\n", stat)); 326 return (1); 327 } 328 BE16TOH(css->c_ref); 329 BE16TOH(css->c_fef); 330 BE16TOH(css->c_aef); 331 BE16TOH(css->c_ief); 332 BE32TOH(css->c_blk); 333 334 return (0); 335 } 336 337 int 338 cs80setoptions(void *v, int slave, int punit, u_int8_t options) 339 { 340 struct cs80bus_softc *sc = v; 341 struct cs80_soptcmd opt; 342 343 opt.c_unit = CS80CMD_SUNIT(punit); 344 opt.c_nop = CS80CMD_NOP; 345 opt.c_opt = CS80CMD_SOPT; 346 opt.c_param = options; 347 if (cs80send(sc, slave, punit, CS80CMD_SCMD, &opt, sizeof(opt))) { 348 DPRINTF(DBG_FAIL, ("cs80setoptions: failed\n")); 349 return (1); 350 } 351 352 return (0); 353 } 354 355 int 356 cs80send(void *v, int slave, int punit, int cmd, void *ptr, int cnt) 357 { 358 struct cs80bus_softc *sc = v; 359 u_int8_t *buf = ptr; 360 u_int8_t stat; 361 362 DPRINTF(DBG_FOLLOW, 363 ("cs80send: sc=%p slave=%d punit=%d cmd=%d ptr=%p cnt=%d\n", sc, 364 slave, punit, cmd, buf, cnt)); 365 366 if (gpibsend(sc->sc_ic, slave, cmd, buf, cnt) != cnt) { 367 DPRINTF(DBG_FAIL, ("cs80send: SCMD failed\n")); 368 return (1); 369 } 370 if (gpibswait(sc->sc_ic, slave)) { 371 DPRINTF(DBG_FAIL, ("cs80send: wait failed\n")); 372 return (1); 373 } 374 if (gpibrecv(sc->sc_ic, slave, CS80CMD_QSTAT, &stat, 1) != 1) { 375 DPRINTF(DBG_FAIL, ("cs80send: QSTAT failed\n")); 376 return (1); 377 } 378 if (stat != 0) { 379 DPRINTF(DBG_FAIL, ("cs80send: failed, stat=0x%x\n", stat)); 380 return (1); 381 } 382 383 return (0); 384 } 385