1/*	$OpenBSD: mpath_hds.c,v 1.26 2022/07/02 08:50:42 visa Exp $ */
2
3/*
4 * Copyright (c) 2011 David Gwynne <dlg@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19/* Hitachi Modular Storage support for mpath(4) */
20
21#include <sys/param.h>
22#include <sys/systm.h>
23#include <sys/kernel.h>
24#include <sys/malloc.h>
25#include <sys/device.h>
26#include <sys/conf.h>
27#include <sys/queue.h>
28#include <sys/rwlock.h>
29#include <sys/pool.h>
30#include <sys/ioctl.h>
31
32#include <scsi/scsi_all.h>
33#include <scsi/scsiconf.h>
34#include <scsi/mpathvar.h>
35
36#define HDS_INQ_LDEV_OFFSET	44
37#define HDS_INQ_LDEV_LEN	4
38#define HDS_INQ_CTRL_OFFSET	49
39#define HDS_INQ_PORT_OFFSET	50
40#define HDS_INQ_TYPE_OFFSET	128
41#define HDS_INQ_TYPE		0x44463030 /* "DF00" */
42
43#define HDS_VPD			0xe0
44
45struct hds_vpd {
46	struct scsi_vpd_hdr	hdr; /* HDS_VPD */
47	u_int8_t		state;
48#define HDS_VPD_VALID			0x80
49#define HDS_VPD_PREFERRED		0x40
50
51	/* followed by lots of unknown stuff */
52};
53
54#define HDS_SYMMETRIC		0
55#define HDS_ASYMMETRIC		1
56
57struct hds_softc {
58	struct device		sc_dev;
59	struct mpath_path	sc_path;
60	struct scsi_xshandler	sc_xsh;
61	struct hds_vpd		*sc_vpd;
62	int			sc_mode;
63	int			sc_ctrl;
64};
65#define DEVNAME(_s) ((_s)->sc_dev.dv_xname)
66
67int		hds_match(struct device *, void *, void *);
68void		hds_attach(struct device *, struct device *, void *);
69int		hds_detach(struct device *, int);
70int		hds_activate(struct device *, int);
71
72const struct cfattach hds_ca = {
73	sizeof(struct hds_softc),
74	hds_match,
75	hds_attach,
76	hds_detach,
77	hds_activate
78};
79
80struct cfdriver hds_cd = {
81	NULL,
82	"hds",
83	DV_DULL
84};
85
86void		hds_mpath_start(struct scsi_xfer *);
87int		hds_mpath_checksense(struct scsi_xfer *);
88void		hds_mpath_status(struct scsi_link *);
89
90const struct mpath_ops hds_mpath_ops = {
91	"hds",
92	hds_mpath_checksense,
93	hds_mpath_status
94};
95
96struct hds_device {
97	char *vendor;
98	char *product;
99};
100
101int		hds_inquiry(struct scsi_link *, int *);
102int		hds_info(struct hds_softc *);
103
104void		hds_status(struct scsi_xfer *);
105void		hds_status_done(struct scsi_xfer *);
106
107struct hds_device hds_devices[] = {
108/*	  " vendor "  "     device     " */
109/*	  "01234567"  "0123456789012345" */
110	{ "HITACHI ", "DF600F          " },
111	{ "HITACHI ", "DF600F-CM       " }
112};
113
114int
115hds_match(struct device *parent, void *match, void *aux)
116{
117	struct scsi_attach_args *sa = aux;
118	struct scsi_link *link = sa->sa_sc_link;
119	struct scsi_inquiry_data *inq = &link->inqdata;
120	struct hds_device *s;
121	int i, mode;
122
123	if (mpath_path_probe(sa->sa_sc_link) != 0)
124		return (0);
125
126	for (i = 0; i < nitems(hds_devices); i++) {
127		s = &hds_devices[i];
128
129		if (bcmp(s->vendor, inq->vendor, strlen(s->vendor)) == 0 &&
130		    bcmp(s->product, inq->product, strlen(s->product)) == 0 &&
131		    hds_inquiry(link, &mode) == 0)
132			return (8);
133	}
134
135	return (0);
136}
137
138void
139hds_attach(struct device *parent, struct device *self, void *aux)
140{
141	struct hds_softc *sc = (struct hds_softc *)self;
142	struct scsi_attach_args *sa = aux;
143	struct scsi_link *link = sa->sa_sc_link;
144
145	printf("\n");
146
147	/* init link */
148	link->device_softc = sc;
149
150	/* init path */
151	scsi_xsh_set(&sc->sc_path.p_xsh, link, hds_mpath_start);
152	sc->sc_path.p_link = link;
153
154	/* init status handler */
155	scsi_xsh_set(&sc->sc_xsh, link, hds_status);
156	sc->sc_vpd = dma_alloc(sizeof(*sc->sc_vpd), PR_WAITOK);
157
158	if (hds_inquiry(link, &sc->sc_mode) != 0) {
159		printf("%s: unable to query controller mode\n", DEVNAME(sc));
160		return;
161	}
162
163	if (hds_info(sc) != 0) {
164		printf("%s: unable to query path info\n", DEVNAME(sc));
165		return;
166	}
167
168	if (mpath_path_attach(&sc->sc_path,
169	    (sc->sc_mode == HDS_SYMMETRIC) ? 0 : sc->sc_ctrl,
170	    &hds_mpath_ops) != 0)
171		printf("%s: unable to attach path\n", DEVNAME(sc));
172}
173
174int
175hds_detach(struct device *self, int flags)
176{
177	struct hds_softc *sc = (struct hds_softc *)self;
178
179	dma_free(sc->sc_vpd, sizeof(*sc->sc_vpd));
180
181	return (0);
182}
183
184int
185hds_activate(struct device *self, int act)
186{
187	struct hds_softc *sc = (struct hds_softc *)self;
188
189	switch (act) {
190	case DVACT_DEACTIVATE:
191		if (sc->sc_path.p_group != NULL)
192			mpath_path_detach(&sc->sc_path);
193		break;
194	}
195	return (0);
196}
197
198void
199hds_mpath_start(struct scsi_xfer *xs)
200{
201	struct hds_softc *sc = xs->sc_link->device_softc;
202
203	mpath_start(&sc->sc_path, xs);
204}
205
206int
207hds_mpath_checksense(struct scsi_xfer *xs)
208{
209	return (MPATH_SENSE_DECLINED);
210}
211
212void
213hds_mpath_status(struct scsi_link *link)
214{
215	struct hds_softc *sc = link->device_softc;
216
217	if (sc->sc_mode == HDS_SYMMETRIC)
218		mpath_path_status(&sc->sc_path, MPATH_S_ACTIVE);
219	else
220		scsi_xsh_add(&sc->sc_xsh);
221}
222
223void
224hds_status(struct scsi_xfer *xs)
225{
226	struct scsi_link *link = xs->sc_link;
227	struct hds_softc *sc = link->device_softc;
228
229	scsi_init_inquiry(xs, SI_EVPD, HDS_VPD,
230	    sc->sc_vpd, sizeof(*sc->sc_vpd));
231
232	xs->done = hds_status_done;
233
234	scsi_xs_exec(xs);
235}
236
237void
238hds_status_done(struct scsi_xfer *xs)
239{
240	struct scsi_link *link = xs->sc_link;
241	struct hds_softc *sc = link->device_softc;
242	struct hds_vpd *vpd = sc->sc_vpd;
243	int status = MPATH_S_UNKNOWN;
244
245	if (xs->error == XS_NOERROR &&
246	    _2btol(vpd->hdr.page_length) >= sizeof(vpd->state) &&
247	    ISSET(vpd->state, HDS_VPD_VALID)) {
248		status = ISSET(vpd->state, HDS_VPD_PREFERRED) ?
249		    MPATH_S_ACTIVE : MPATH_S_PASSIVE;
250	}
251
252	scsi_xs_put(xs);
253
254	mpath_path_status(&sc->sc_path, status);
255}
256
257int
258hds_inquiry(struct scsi_link *link, int *mode)
259{
260	struct scsi_xfer *xs;
261	u_int8_t *buf;
262	size_t len = SID_SCSI2_HDRLEN + link->inqdata.additional_length;
263	int error;
264
265	if (len < HDS_INQ_TYPE_OFFSET + sizeof(int))
266		return (ENXIO);
267
268	xs = scsi_xs_get(link, scsi_autoconf);
269	if (xs == NULL)
270		return (ENOMEM);
271
272	buf = dma_alloc(len, PR_WAITOK);
273	scsi_init_inquiry(xs, 0, 0, buf, len);
274	error = scsi_xs_sync(xs);
275	scsi_xs_put(xs);
276	if (error)
277		goto done;
278
279	if (buf[128] == '\0')
280		*mode = HDS_ASYMMETRIC;
281	else if (_4btol(&buf[HDS_INQ_TYPE_OFFSET]) == HDS_INQ_TYPE)
282		*mode = HDS_SYMMETRIC;
283	else
284		error = ENXIO;
285
286done:
287	dma_free(buf, len);
288	return (error);
289}
290
291int
292hds_info(struct hds_softc *sc)
293{
294	struct scsi_link *link = sc->sc_path.p_link;
295	struct scsi_xfer *xs;
296	u_int8_t *buf;
297	size_t len = SID_SCSI2_HDRLEN + link->inqdata.additional_length;
298	char ldev[9], ctrl, port;
299	int error;
300
301	xs = scsi_xs_get(link, scsi_autoconf);
302	if (xs == NULL)
303		return (ENOMEM);
304
305	buf = dma_alloc(len, PR_WAITOK);
306	scsi_init_inquiry(xs, 0, 0, buf, len);
307	error = scsi_xs_sync(xs);
308	scsi_xs_put(xs);
309	if (error)
310		goto done;
311
312	bzero(ldev, sizeof(ldev));
313	scsi_strvis(ldev, &buf[HDS_INQ_LDEV_OFFSET], HDS_INQ_LDEV_LEN);
314	ctrl = buf[HDS_INQ_CTRL_OFFSET];
315	port = buf[HDS_INQ_PORT_OFFSET];
316
317	if (ctrl >= '0' && ctrl <= '9') {
318		printf("%s: ldev %s, controller %c, port %c, %s\n",
319		    DEVNAME(sc), ldev, ctrl, port,
320		    sc->sc_mode == HDS_SYMMETRIC ? "symmetric" : "asymmetric");
321
322		sc->sc_ctrl = ctrl;
323	} else
324		error = ENXIO;
325
326done:
327	dma_free(buf, len);
328	return (error);
329}
330