1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 2010, LSI Corp.
5 * All rights reserved.
6 * Author : Manjunath Ranganathaiah
7 * Support: [email protected]
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
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
17 * the documentation and/or other materials provided with the
18 * distribution.
19 * 3. Neither the name of the <ORGANIZATION> nor the names of its
20 * contributors may be used to endorse or promote products derived
21 * from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 *
36 * $FreeBSD$
37 */
38
39 #include <dev/tws/tws.h>
40 #include <dev/tws/tws_services.h>
41 #include <dev/tws/tws_hdm.h>
42 #include <dev/tws/tws_user.h>
43
44
45 int tws_ioctl(struct cdev *dev, long unsigned int cmd, caddr_t buf, int flags,
46 struct thread *td);
47 void tws_passthru_complete(struct tws_request *req);
48 extern void tws_circular_aenq_insert(struct tws_softc *sc,
49 struct tws_circular_q *cq, struct tws_event_packet *aen);
50
51
52 static int tws_passthru(struct tws_softc *sc, void *buf);
53 static int tws_ioctl_aen(struct tws_softc *sc, u_long cmd, void *buf);
54
55 extern int tws_bus_scan(struct tws_softc *sc);
56 extern struct tws_request *tws_get_request(struct tws_softc *sc,
57 u_int16_t type);
58 extern int32_t tws_map_request(struct tws_softc *sc, struct tws_request *req);
59 extern void tws_unmap_request(struct tws_softc *sc, struct tws_request *req);
60 extern uint8_t tws_get_state(struct tws_softc *sc);
61 extern void tws_timeout(void *arg);
62
63 int
tws_ioctl(struct cdev * dev,u_long cmd,caddr_t buf,int flags,struct thread * td)64 tws_ioctl(struct cdev *dev, u_long cmd, caddr_t buf, int flags,
65 struct thread *td)
66 {
67 struct tws_softc *sc = (struct tws_softc *)(dev->si_drv1);
68 int error;
69
70 TWS_TRACE_DEBUG(sc, "entry", sc, cmd);
71 sc->stats.ioctls++;
72 switch(cmd) {
73 case TWS_IOCTL_FIRMWARE_PASS_THROUGH :
74 error = tws_passthru(sc, (void *)buf);
75 break;
76 case TWS_IOCTL_SCAN_BUS :
77 TWS_TRACE_DEBUG(sc, "scan-bus", 0, 0);
78 error = tws_bus_scan(sc);
79 break;
80 default :
81 TWS_TRACE_DEBUG(sc, "ioctl-aen", cmd, buf);
82 error = tws_ioctl_aen(sc, cmd, (void *)buf);
83 break;
84
85 }
86 return(error);
87 }
88
89 static int
tws_passthru(struct tws_softc * sc,void * buf)90 tws_passthru(struct tws_softc *sc, void *buf)
91 {
92 struct tws_request *req;
93 struct tws_ioctl_no_data_buf *ubuf = (struct tws_ioctl_no_data_buf *)buf;
94 int error;
95 u_int32_t buffer_length;
96 u_int16_t lun4;
97
98 buffer_length = roundup2(ubuf->driver_pkt.buffer_length, 512);
99 if ( buffer_length > TWS_MAX_IO_SIZE ) {
100 return(EINVAL);
101 }
102 if ( tws_get_state(sc) != TWS_ONLINE) {
103 return(EBUSY);
104 }
105
106 //==============================================================================================
107 // Get a command
108 //
109 do {
110 req = tws_get_request(sc, TWS_REQ_TYPE_PASSTHRU);
111 if ( !req ) {
112 error = tsleep(sc, 0, "tws_sleep", TWS_IOCTL_TIMEOUT*hz);
113 if ( error == EWOULDBLOCK ) {
114 return(ETIMEDOUT);
115 }
116 } else {
117 // Make sure we are still ready for new commands...
118 if ( tws_get_state(sc) != TWS_ONLINE) {
119 return(EBUSY);
120 }
121 break;
122 }
123 } while(1);
124
125 req->length = buffer_length;
126 TWS_TRACE_DEBUG(sc, "datal,rid", req->length, req->request_id);
127 if ( req->length ) {
128 req->data = sc->ioctl_data_mem;
129 req->dma_map = sc->ioctl_data_map;
130
131 //==========================================================================================
132 // Copy data in from user space
133 //
134 error = copyin(ubuf->pdata, req->data, req->length);
135 }
136
137 //==============================================================================================
138 // Set command fields
139 //
140 req->flags = TWS_DIR_IN | TWS_DIR_OUT;
141 req->cb = tws_passthru_complete;
142
143 memcpy(&req->cmd_pkt->cmd, &ubuf->cmd_pkt.cmd,
144 sizeof(struct tws_command_apache));
145
146 if ( GET_OPCODE(req->cmd_pkt->cmd.pkt_a.res__opcode) ==
147 TWS_FW_CMD_EXECUTE_SCSI ) {
148 lun4 = req->cmd_pkt->cmd.pkt_a.lun_l4__req_id & 0xF000;
149 req->cmd_pkt->cmd.pkt_a.lun_l4__req_id = lun4 | req->request_id;
150 } else {
151 req->cmd_pkt->cmd.pkt_g.generic.request_id = (u_int8_t) req->request_id;
152 }
153
154 //==============================================================================================
155 // Send command to controller
156 //
157 error = tws_map_request(sc, req);
158 if (error) {
159 ubuf->driver_pkt.os_status = error;
160 goto out_data;
161 }
162
163 if ( req->state == TWS_REQ_STATE_COMPLETE ) {
164 ubuf->driver_pkt.os_status = req->error_code;
165 goto out_unmap;
166 }
167
168 mtx_lock(&sc->gen_lock);
169 error = mtx_sleep(req, &sc->gen_lock, 0, "tws_passthru", TWS_IOCTL_TIMEOUT*hz);
170 mtx_unlock(&sc->gen_lock);
171 if (( req->state != TWS_REQ_STATE_COMPLETE ) && ( error == EWOULDBLOCK )) {
172 TWS_TRACE_DEBUG(sc, "msleep timeout", error, req->request_id);
173 tws_timeout((void*) req);
174 }
175
176 out_unmap:
177 if ( req->error_code == TWS_REQ_RET_RESET ) {
178 error = EBUSY;
179 req->error_code = EBUSY;
180 TWS_TRACE_DEBUG(sc, "ioctl reset", error, req->request_id);
181 }
182
183 tws_unmap_request(sc, req);
184
185 //==============================================================================================
186 // Return command status to user space
187 //
188 memcpy(&ubuf->cmd_pkt.hdr, &req->cmd_pkt->hdr, sizeof(struct tws_command_apache));
189 memcpy(&ubuf->cmd_pkt.cmd, &req->cmd_pkt->cmd, sizeof(struct tws_command_apache));
190
191 out_data:
192 if ( req->length ) {
193 //==========================================================================================
194 // Copy data out to user space
195 //
196 if ( !error )
197 error = copyout(req->data, ubuf->pdata, ubuf->driver_pkt.buffer_length);
198 }
199
200 if ( error )
201 TWS_TRACE_DEBUG(sc, "errored", error, 0);
202
203 if ( req->error_code != TWS_REQ_RET_SUBMIT_SUCCESS )
204 ubuf->driver_pkt.os_status = error;
205
206 //==============================================================================================
207 // Free command
208 //
209 req->state = TWS_REQ_STATE_FREE;
210
211 wakeup_one(sc);
212
213 return(error);
214 }
215
216 void
tws_passthru_complete(struct tws_request * req)217 tws_passthru_complete(struct tws_request *req)
218 {
219 req->state = TWS_REQ_STATE_COMPLETE;
220 wakeup_one(req);
221
222 }
223
224 static void
tws_retrive_aen(struct tws_softc * sc,u_long cmd,struct tws_ioctl_packet * ubuf)225 tws_retrive_aen(struct tws_softc *sc, u_long cmd,
226 struct tws_ioctl_packet *ubuf)
227 {
228 u_int16_t index=0;
229 struct tws_event_packet eventp, *qp;
230
231 if ( sc->aen_q.head == sc->aen_q.tail ) {
232 ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS;
233 return;
234 }
235
236 ubuf->driver_pkt.status = 0;
237
238 /*
239 * once this flag is set cli will not display alarms
240 * needs a revisit from tools?
241 */
242 if ( sc->aen_q.overflow ) {
243 ubuf->driver_pkt.status = TWS_AEN_OVERFLOW;
244 sc->aen_q.overflow = 0; /* reset */
245 }
246
247 qp = (struct tws_event_packet *)sc->aen_q.q;
248
249 switch (cmd) {
250 case TWS_IOCTL_GET_FIRST_EVENT :
251 index = sc->aen_q.head;
252 break;
253 case TWS_IOCTL_GET_LAST_EVENT :
254 /* index = tail-1 */
255 index = (sc->aen_q.depth + sc->aen_q.tail - 1) % sc->aen_q.depth;
256 break;
257 case TWS_IOCTL_GET_NEXT_EVENT :
258 memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet));
259 index = sc->aen_q.head;
260 do {
261 if ( qp[index].sequence_id ==
262 (eventp.sequence_id + 1) )
263 break;
264 index = (index+1) % sc->aen_q.depth;
265 }while ( index != sc->aen_q.tail );
266 if ( index == sc->aen_q.tail ) {
267 ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS;
268 return;
269 }
270 break;
271 case TWS_IOCTL_GET_PREVIOUS_EVENT :
272 memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet));
273 index = sc->aen_q.head;
274 do {
275 if ( qp[index].sequence_id ==
276 (eventp.sequence_id - 1) )
277 break;
278 index = (index+1) % sc->aen_q.depth;
279 }while ( index != sc->aen_q.tail );
280 if ( index == sc->aen_q.tail ) {
281 ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS;
282 return;
283 }
284 break;
285 default :
286 TWS_TRACE_DEBUG(sc, "not a valid event", sc, cmd);
287 ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS;
288 return;
289 }
290
291 memcpy(ubuf->data_buf, &qp[index],
292 sizeof(struct tws_event_packet));
293 qp[index].retrieved = TWS_AEN_RETRIEVED;
294
295 return;
296
297 }
298
299 static int
tws_ioctl_aen(struct tws_softc * sc,u_long cmd,void * buf)300 tws_ioctl_aen(struct tws_softc *sc, u_long cmd, void *buf)
301 {
302
303 struct tws_ioctl_packet *ubuf = (struct tws_ioctl_packet *)buf;
304 struct tws_compatibility_packet cpkt;
305 struct tws_lock_packet lpkt;
306 time_t ctime;
307
308 mtx_lock(&sc->gen_lock);
309 ubuf->driver_pkt.status = 0;
310 switch(cmd) {
311 case TWS_IOCTL_GET_FIRST_EVENT :
312 case TWS_IOCTL_GET_LAST_EVENT :
313 case TWS_IOCTL_GET_NEXT_EVENT :
314 case TWS_IOCTL_GET_PREVIOUS_EVENT :
315 tws_retrive_aen(sc,cmd,ubuf);
316 break;
317 case TWS_IOCTL_GET_LOCK :
318 ctime = TWS_LOCAL_TIME;
319 memcpy(&lpkt, ubuf->data_buf, sizeof(struct tws_lock_packet));
320 if ( (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) ||
321 (lpkt.force_flag) ||
322 (ctime >= sc->ioctl_lock.timeout) ) {
323 sc->ioctl_lock.lock = TWS_IOCTL_LOCK_HELD;
324 sc->ioctl_lock.timeout = ctime + (lpkt.timeout_msec / 1000);
325 lpkt.time_remaining_msec = lpkt.timeout_msec;
326 } else {
327 lpkt.time_remaining_msec = (u_int32_t)
328 ((sc->ioctl_lock.timeout - ctime) * 1000);
329 ubuf->driver_pkt.status = TWS_IOCTL_LOCK_ALREADY_HELD;
330
331 }
332 break;
333 case TWS_IOCTL_RELEASE_LOCK :
334 if (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) {
335 ubuf->driver_pkt.status = TWS_IOCTL_LOCK_NOT_HELD;
336 } else {
337 sc->ioctl_lock.lock = TWS_IOCTL_LOCK_FREE;
338 ubuf->driver_pkt.status = 0;
339 }
340 break;
341 case TWS_IOCTL_GET_COMPATIBILITY_INFO :
342 TWS_TRACE_DEBUG(sc, "get comp info", sc, cmd);
343
344 memcpy( cpkt.driver_version, TWS_DRIVER_VERSION_STRING,
345 sizeof(TWS_DRIVER_VERSION_STRING));
346 cpkt.working_srl = sc->cinfo.working_srl;
347 cpkt.working_branch = sc->cinfo.working_branch;
348 cpkt.working_build = sc->cinfo.working_build;
349 cpkt.driver_srl_high = TWS_CURRENT_FW_SRL;
350 cpkt.driver_branch_high = TWS_CURRENT_FW_BRANCH;
351 cpkt.driver_build_high = TWS_CURRENT_FW_BUILD;
352 cpkt.driver_srl_low = TWS_BASE_FW_SRL;
353 cpkt.driver_branch_low = TWS_BASE_FW_BRANCH;
354 cpkt.driver_build_low = TWS_BASE_FW_BUILD;
355 cpkt.fw_on_ctlr_srl = sc->cinfo.fw_on_ctlr_srl;
356 cpkt.fw_on_ctlr_branch = sc->cinfo.fw_on_ctlr_branch;
357 cpkt.fw_on_ctlr_build = sc->cinfo.fw_on_ctlr_build;
358 ubuf->driver_pkt.status = 0;
359 int len = sizeof(struct tws_compatibility_packet);
360 if ( ubuf->driver_pkt.buffer_length < len )
361 len = ubuf->driver_pkt.buffer_length;
362 memcpy(ubuf->data_buf, &cpkt, len);
363
364 break;
365 default :
366 TWS_TRACE_DEBUG(sc, "not valid cmd", cmd,
367 TWS_IOCTL_GET_COMPATIBILITY_INFO);
368 break;
369
370 }
371 mtx_unlock(&sc->gen_lock);
372 return(SUCCESS);
373
374 }
375
376 void
tws_circular_aenq_insert(struct tws_softc * sc,struct tws_circular_q * cq,struct tws_event_packet * aen)377 tws_circular_aenq_insert(struct tws_softc *sc, struct tws_circular_q *cq,
378 struct tws_event_packet *aen)
379 {
380
381 struct tws_event_packet *q = (struct tws_event_packet *)cq->q;
382 volatile u_int16_t head, tail;
383 u_int8_t retr;
384 mtx_assert(&sc->gen_lock, MA_OWNED);
385
386 head = cq->head;
387 tail = cq->tail;
388 retr = q[tail].retrieved;
389
390 memcpy(&q[tail], aen, sizeof(struct tws_event_packet));
391 tail = (tail+1) % cq->depth;
392
393 if ( head == tail ) { /* q is full */
394 if ( retr != TWS_AEN_RETRIEVED )
395 cq->overflow = 1;
396 cq->head = (head+1) % cq->depth;
397 }
398 cq->tail = tail;
399
400 }
401