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