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