xref: /freebsd/sys/dev/tws/tws_user.c (revision 4bc52338)
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: freebsdraid@lsi.com
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
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
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
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
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
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
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