1/* $NetBSD: cs80bus.c,v 1.19 2021/08/07 16:19:10 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.19 2021/08/07 16:19:10 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 49int 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 62int cs80busmatch(device_t, cfdata_t, void *); 63void cs80busattach(device_t, device_t, void *); 64 65CFATTACH_DECL_NEW(cs80bus, sizeof(struct cs80bus_softc), 66 cs80busmatch, cs80busattach, NULL, NULL); 67 68static int cs80bus_alloc(struct cs80bus_softc *, int, int); 69static int cs80bussearch(device_t, cfdata_t, 70 const int *, void *); 71static 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 95int 96cs80busmatch(device_t parent, cfdata_t match, void *aux) 97{ 98 99 return (1); 100} 101 102void 103cs80busattach(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 CFARGS(.search = cs80bussearch)); 139 } 140} 141 142int 143cs80bussearch(device_t parent, cfdata_t cf, const int *ldesc, void *aux) 144{ 145 struct cs80bus_softc *sc = device_private(parent); 146 struct cs80bus_attach_args *ca = aux; 147 148 /* 149 * Set punit if operator specified one in the kernel 150 * configuration file. 151 */ 152 if (cf->cs80buscf_punit != CS80BUSCF_PUNIT_DEFAULT && 153 cf->cs80buscf_punit < CS80BUS_NPUNITS) 154 ca->ca_punit = cf->cs80buscf_punit; 155 else 156 /* default punit */ 157 ca->ca_punit = 0; 158 159 DPRINTF(DBG_FOLLOW, ("cs80bussearch: id=0x%x slave=%d punit=%d\n", 160 ca->ca_id, ca->ca_slave, ca->ca_punit)); 161 162 if (config_probe(parent, cf, ca)) { 163 164 DPRINTF(DBG_FOLLOW, 165 ("cs80bussearch: got id=0x%x slave=%d punit %d\n", 166 ca->ca_id, ca->ca_slave, ca->ca_punit)); 167 168 /* 169 * The device probe has succeeded, and filled in 170 * the punit information. Make sure the configuration 171 * allows for this slave/punit combination. 172 */ 173 if (cf->cs80buscf_slave != CS80BUSCF_SLAVE_DEFAULT && 174 cf->cs80buscf_slave != ca->ca_slave) 175 goto out; 176 if (cf->cs80buscf_punit != CS80BUSCF_PUNIT_DEFAULT && 177 cf->cs80buscf_punit != ca->ca_punit) 178 goto out; 179 180 /* 181 * Allocate the device's address from the bus's 182 * resource map. 183 */ 184 if (cs80bus_alloc(sc, ca->ca_slave, ca->ca_punit)) 185 goto out; 186 187 /* 188 * This device is allowed; attach it. 189 */ 190 config_attach(parent, cf, ca, cs80busprint, CFARGS_NONE); 191 } 192out: 193 return (0); 194} 195 196int 197cs80busprint(void *aux, const char *pnp) 198{ 199 struct cs80bus_attach_args *ca = aux; 200 201 printf(" slave %d punit %d", ca->ca_slave, ca->ca_punit); 202 return (UNCONF); 203} 204 205static int 206cs80bus_alloc(struct cs80bus_softc *sc, int slave, int punit) 207{ 208 209 DPRINTF(DBG_FOLLOW, ("cs80bus_alloc: sc=%p\n", sc)); 210 211 if (slave >= CS80BUS_NSLAVES || punit >= CS80BUS_NPUNITS) 212 panic("cs80bus_alloc: device address out of range"); 213 214 gpib_alloc(device_private(device_parent(sc->sc_dev)), slave); 215 216 if (sc->sc_rmap[slave][punit] == 0) { 217 sc->sc_rmap[slave][punit] = 1; 218 return (0); 219 } 220 return (1); 221} 222 223 224 225/* 226 * CS80/SS80 (secondary) command functions 227 */ 228 229int 230cs80describe(void *v, int slave, int punit, struct cs80_description *csd) 231{ 232 struct cs80bus_softc *sc = v; 233 struct cs80_describecmd desc; 234 u_int8_t stat; 235 236 DPRINTF(DBG_FOLLOW, ("cs80describe: sc=%p slave=%d\n", sc, slave)); 237 238 /* 239 * Note command is always issued to unit 0. 240 */ 241 242 desc.c_unit = CS80CMD_SUNIT(0); 243 desc.c_vol = CS80CMD_SVOL(0); 244 desc.c_cmd = CS80CMD_DESC; 245 (void) gpibsend(sc->sc_ic, slave, CS80CMD_SCMD, &desc, sizeof(desc)); 246 (void) gpibrecv(sc->sc_ic, slave, CS80CMD_EXEC, csd, sizeof(*csd)); 247 (void) gpibrecv(sc->sc_ic, slave, CS80CMD_QSTAT, &stat, 1); 248 if (stat != 0) { 249 DPRINTF(DBG_FAIL, ("cs80describe: failed, stat=0x%x\n", stat)); 250 return (1); 251 } 252 BE16TOH(csd->d_iuw); 253 BE16TOH(csd->d_cmaxxfr); 254 BE16TOH(csd->d_sectsize); 255 BE16TOH(csd->d_blocktime); 256 BE16TOH(csd->d_uavexfr); 257 BE16TOH(csd->d_retry); 258 BE16TOH(csd->d_access); 259 BE32TOH(csd->d_maxcylhead); 260 BE16TOH(csd->d_maxsect); 261 BE16TOH(csd->d_maxvsecth); 262 BE32TOH(csd->d_maxvsectl); 263 264 return (0); 265} 266 267int 268cs80reset(void *v, int slave, int punit) 269{ 270 struct cs80bus_softc *sc = v; 271 struct cs80_clearcmd clear; 272 struct cs80_srcmd sr; 273 struct cs80_ssmcmd ssm; 274 275 DPRINTF(DBG_FOLLOW, ("cs80reset: sc=%p slave=%d punit=%d\n", sc, 276 slave, punit)); 277 278 clear.c_unit = CS80CMD_SUNIT(punit); 279 clear.c_cmd = CS80CMD_CLEAR; 280 if (cs80send(sc, slave, punit, CS80CMD_TCMD, &clear, sizeof(clear))) { 281 DPRINTF(DBG_FAIL, ("cs80reset: CLEAR failed\n")); 282 return (1); 283 } 284 285 sr.c_unit = CS80CMD_SUNIT(15); /* XXX */ 286 sr.c_nop = CS80CMD_NOP; 287 sr.c_cmd = CS80CMD_SREL; 288 sr.c_param = 0xc0; /* XXX */ 289 if (cs80send(sc, slave, punit, CS80CMD_SCMD, &sr, sizeof(sr))) { 290 DPRINTF(DBG_FAIL, ("cs80reset: SREL failed\n")); 291 return (1); 292 } 293 294 ssm.c_unit = CS80CMD_SUNIT(punit); 295 ssm.c_cmd = CS80CMD_SSM; 296 ssm.c_refm = htobe16(REF_MASK); 297 ssm.c_fefm = htobe16(FEF_MASK); 298 ssm.c_aefm = htobe16(AEF_MASK); 299 ssm.c_iefm = htobe16(IEF_MASK); 300 if (cs80send(sc, slave, punit, CS80CMD_SCMD, &ssm, sizeof(ssm))) { 301 DPRINTF(DBG_FAIL, ("cs80reset: SSM failed\n")); 302 return (1); 303 } 304 305 return (0); 306} 307 308int 309cs80status(void *v, int slave, int punit, struct cs80_stat *css) 310{ 311 struct cs80bus_softc *sc = v; 312 struct cs80_statuscmd rs; 313 u_int8_t stat; 314 315 rs.c_unit = CS80CMD_SUNIT(punit); 316 rs.c_sram = CS80CMD_SRAM; 317 rs.c_param = 0; /* single vector (i.e. sector number) */ 318 rs.c_cmd = CS80CMD_STATUS; 319 memset((void *)css, 0, sizeof(*css)); 320 (void) gpibsend(sc->sc_ic, slave, CS80CMD_SCMD, &rs, sizeof(rs)); 321 (void) gpibrecv(sc->sc_ic, slave, CS80CMD_EXEC, css, sizeof(*css)); 322 (void) gpibrecv(sc->sc_ic, slave, CS80CMD_QSTAT, &stat, 1); 323 if (stat != 0) { 324 DPRINTF(DBG_FAIL, ("cs80status: failed, stat=0x%x\n", stat)); 325 return (1); 326 } 327 BE16TOH(css->c_ref); 328 BE16TOH(css->c_fef); 329 BE16TOH(css->c_aef); 330 BE16TOH(css->c_ief); 331 BE32TOH(css->c_blk); 332 333 return (0); 334} 335 336int 337cs80setoptions(void *v, int slave, int punit, u_int8_t options) 338{ 339 struct cs80bus_softc *sc = v; 340 struct cs80_soptcmd opt; 341 342 opt.c_unit = CS80CMD_SUNIT(punit); 343 opt.c_nop = CS80CMD_NOP; 344 opt.c_opt = CS80CMD_SOPT; 345 opt.c_param = options; 346 if (cs80send(sc, slave, punit, CS80CMD_SCMD, &opt, sizeof(opt))) { 347 DPRINTF(DBG_FAIL, ("cs80setoptions: failed\n")); 348 return (1); 349 } 350 351 return (0); 352} 353 354int 355cs80send(void *v, int slave, int punit, int cmd, void *ptr, int cnt) 356{ 357 struct cs80bus_softc *sc = v; 358 u_int8_t *buf = ptr; 359 u_int8_t stat; 360 361 DPRINTF(DBG_FOLLOW, 362 ("cs80send: sc=%p slave=%d punit=%d cmd=%d ptr=%p cnt=%d\n", sc, 363 slave, punit, cmd, buf, cnt)); 364 365 if (gpibsend(sc->sc_ic, slave, cmd, buf, cnt) != cnt) { 366 DPRINTF(DBG_FAIL, ("cs80send: SCMD failed\n")); 367 return (1); 368 } 369 if (gpibswait(sc->sc_ic, slave)) { 370 DPRINTF(DBG_FAIL, ("cs80send: wait failed\n")); 371 return (1); 372 } 373 if (gpibrecv(sc->sc_ic, slave, CS80CMD_QSTAT, &stat, 1) != 1) { 374 DPRINTF(DBG_FAIL, ("cs80send: QSTAT failed\n")); 375 return (1); 376 } 377 if (stat != 0) { 378 DPRINTF(DBG_FAIL, ("cs80send: failed, stat=0x%x\n", stat)); 379 return (1); 380 } 381 382 return (0); 383} 384