1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 1999 Michael Smith
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 *	$FreeBSD$
29 */
30
31#include <fcntl.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <unistd.h>
35#include <string.h>
36#include <cam/scsi/scsi_all.h>
37
38#include <dev/mlx/mlxio.h>
39#include <dev/mlx/mlxreg.h>
40
41#include "mlxcontrol.h"
42
43/********************************************************************************
44 * Iterate over all mlx devices, call (func) with each ones' path and (arg)
45 */
46void
47mlx_foreach(void (*func)(int unit, void *arg), void *arg)
48{
49    int		i, fd;
50
51    /* limit total count for sanity */
52    for (i = 0; i < 64; i++) {
53	/* verify we can open it */
54	if ((fd = open(ctrlrpath(i), 0)) >= 0)
55	    close(fd);
56	/* if we can, do */
57	if (fd >= 0) {
58	    func(i, arg);
59	}
60    }
61}
62
63/********************************************************************************
64 * Open the controller (unit) and give the fd to (func) along with (arg)
65 */
66void
67mlx_perform(int unit, void (*func)(int fd, void *arg), void *arg)
68{
69    int		fd;
70
71    if ((fd = open(ctrlrpath(unit), 0)) >= 0) {
72	func(fd, arg);
73	close(fd);
74    }
75}
76
77/********************************************************************************
78 * Iterate over all mlxd devices, call (func) with each ones' path and (arg)
79 */
80void
81mlxd_foreach_ctrlr(int unit, void *arg)
82{
83    struct mlxd_foreach_action	*ma = (struct mlxd_foreach_action *)arg;
84    int				i, fd, ctrlfd;
85
86    /* Get the device */
87    if ((ctrlfd = open(ctrlrpath(unit), 0)) < 0)
88	return;
89
90    for (i = -1; ;) {
91	/* Get the unit number of the next child device */
92	if (ioctl(ctrlfd, MLX_NEXT_CHILD, &i) < 0) {
93	    close(ctrlfd);
94	    return;
95	}
96
97	/* check that we can open this unit */
98	if ((fd = open(drivepath(i), 0)) >= 0)
99	    close(fd);
100	/* if we can, do */
101	if (fd >= 0) {
102	    ma->func(i, ma->arg);
103	}
104    }
105}
106
107void
108mlxd_foreach(void (*func)(int unit, void *arg), void *arg)
109{
110    struct mlxd_foreach_action ma;
111
112    ma.func = func;
113    ma.arg = arg;
114    mlx_foreach(mlxd_foreach_ctrlr, &ma);
115}
116
117/********************************************************************************
118 * Find the controller that manages the drive (unit), return controller number
119 * and system drive number on that controller.
120 */
121static struct
122{
123    int		unit;
124    int		ctrlr;
125    int		sysdrive;
126} mlxd_find_ctrlr_param;
127
128static void
129mlxd_find_ctrlr_search(int unit, void *arg)
130{
131    int				i, fd;
132
133    /* Get the device */
134    if ((fd = open(ctrlrpath(unit), 0)) >= 0) {
135	for (i = -1; ;) {
136	    /* Get the unit number of the next child device */
137	    if (ioctl(fd, MLX_NEXT_CHILD, &i) < 0)
138		break;
139
140	    /* is this child the unit we want? */
141	    if (i == mlxd_find_ctrlr_param.unit) {
142		mlxd_find_ctrlr_param.ctrlr = unit;
143		if (ioctl(fd, MLX_GET_SYSDRIVE, &i) == 0)
144		    mlxd_find_ctrlr_param.sysdrive = i;
145	    }
146	}
147	close(fd);
148    }
149}
150
151int
152mlxd_find_ctrlr(int unit, int *ctrlr, int *sysdrive)
153{
154    mlxd_find_ctrlr_param.unit = unit;
155    mlxd_find_ctrlr_param.ctrlr = -1;
156    mlxd_find_ctrlr_param.sysdrive = -1;
157
158    mlx_foreach(mlxd_find_ctrlr_search, NULL);
159    if ((mlxd_find_ctrlr_param.ctrlr != -1) && (mlxd_find_ctrlr_param.sysdrive != -1)) {
160	*ctrlr = mlxd_find_ctrlr_param.ctrlr;
161	*sysdrive = mlxd_find_ctrlr_param.sysdrive;
162	return(0);
163    }
164    return(1);
165}
166
167
168/********************************************************************************
169 * Send a command to the controller on (fd)
170 */
171
172void
173mlx_command(int fd, void *arg)
174{
175    struct mlx_usercommand	*cmd = (struct mlx_usercommand *)arg;
176    int				error;
177
178    error = ioctl(fd, MLX_COMMAND, cmd);
179    if (error != 0)
180	cmd->mu_error = error;
181}
182
183/********************************************************************************
184 * Perform an ENQUIRY2 command and return information related to the controller
185 * (unit)
186 */
187int
188mlx_enquiry(int unit, struct mlx_enquiry2 *enq)
189{
190    struct mlx_usercommand	cmd;
191
192    /* build the command */
193    cmd.mu_datasize = sizeof(*enq);
194    cmd.mu_buf = enq;
195    cmd.mu_bufptr = 8;
196    cmd.mu_command[0] = MLX_CMD_ENQUIRY2;
197
198    /* hand it off for processing */
199    mlx_perform(unit, mlx_command, (void *)&cmd);
200
201    return(cmd.mu_status != 0);
202}
203
204
205/********************************************************************************
206 * Perform a READ CONFIGURATION command and return information related to the controller
207 * (unit)
208 */
209int
210mlx_read_configuration(int unit, struct mlx_core_cfg *cfg)
211{
212    struct mlx_usercommand	cmd;
213
214    /* build the command */
215    cmd.mu_datasize = sizeof(*cfg);
216    cmd.mu_buf = cfg;
217    cmd.mu_bufptr = 8;
218    cmd.mu_command[0] = MLX_CMD_READ_CONFIG;
219
220    /* hand it off for processing */
221    mlx_perform(unit, mlx_command, (void *)&cmd);
222
223    return(cmd.mu_status != 0);
224}
225
226/********************************************************************************
227 * Perform a SCSI INQUIRY command and return pointers to the relevant data.
228 */
229int
230mlx_scsi_inquiry(int unit, int channel, int target, char **vendor, char **device, char **revision)
231{
232    struct mlx_usercommand	cmd;
233    static struct {
234	    struct mlx_dcdb		dcdb;
235	    union {
236		struct scsi_inquiry_data	inq;
237		u_int8_t			pad[SHORT_INQUIRY_LENGTH];
238	    } d;
239    } __attribute__ ((packed))		dcdb_cmd;
240    struct scsi_inquiry		*inq_cmd = (struct scsi_inquiry *)&dcdb_cmd.dcdb.dcdb_cdb[0];
241
242    /* build the command */
243    cmd.mu_datasize = sizeof(dcdb_cmd);
244    cmd.mu_buf = &dcdb_cmd;
245    cmd.mu_command[0] = MLX_CMD_DIRECT_CDB;
246
247    /* build the DCDB */
248    bzero(&dcdb_cmd, sizeof(dcdb_cmd));
249    dcdb_cmd.dcdb.dcdb_channel = channel;
250    dcdb_cmd.dcdb.dcdb_target = target;
251    dcdb_cmd.dcdb.dcdb_flags = MLX_DCDB_DATA_IN | MLX_DCDB_TIMEOUT_10S;
252    dcdb_cmd.dcdb.dcdb_datasize = SHORT_INQUIRY_LENGTH;
253    dcdb_cmd.dcdb.dcdb_cdb_length = 6;
254    dcdb_cmd.dcdb.dcdb_sense_length = SSD_FULL_SIZE;
255
256    /* build the cdb */
257    inq_cmd->opcode = INQUIRY;
258    scsi_ulto2b(SHORT_INQUIRY_LENGTH, inq_cmd->length);
259
260    /* hand it off for processing */
261    mlx_perform(unit, mlx_command, &cmd);
262
263    if (cmd.mu_status == 0) {
264	*vendor = &dcdb_cmd.d.inq.vendor[0];
265	*device = &dcdb_cmd.d.inq.product[0];
266	*revision = &dcdb_cmd.d.inq.revision[0];
267    }
268    return(cmd.mu_status);
269}
270
271/********************************************************************************
272 * Perform a GET DEVICE STATE command and return pointers to the relevant data.
273 */
274int
275mlx_get_device_state(int unit, int channel, int target, struct mlx_phys_drv *drv)
276{
277    struct mlx_usercommand	cmd;
278
279    /* build the command */
280    cmd.mu_datasize = sizeof(*drv);
281    cmd.mu_buf = drv;
282    cmd.mu_bufptr = 8;
283    cmd.mu_command[0] = MLX_CMD_DEVICE_STATE;
284    cmd.mu_command[2] = channel;
285    cmd.mu_command[3] = target;
286
287    /* hand it off for processing */
288    mlx_perform(unit, mlx_command, (void *)&cmd);
289
290    return(cmd.mu_status != 0);
291}
292