xref: /freebsd/sys/dev/tws/tws_user.c (revision 95ee2897e98f5d444f26ed2334cc7c439f9c16c6)
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 
37 #include <dev/tws/tws.h>
38 #include <dev/tws/tws_services.h>
39 #include <dev/tws/tws_hdm.h>
40 #include <dev/tws/tws_user.h>
41 
42 int tws_ioctl(struct cdev *dev, long unsigned int cmd, caddr_t buf, int flags,
43                                                     struct thread *td);
44 void tws_passthru_complete(struct tws_request *req);
45 extern void tws_circular_aenq_insert(struct tws_softc *sc,
46                     struct tws_circular_q *cq, struct tws_event_packet *aen);
47 
48 static int tws_passthru(struct tws_softc *sc, void *buf);
49 static int tws_ioctl_aen(struct tws_softc *sc, u_long cmd, void *buf);
50 
51 extern int tws_bus_scan(struct tws_softc *sc);
52 extern struct tws_request *tws_get_request(struct tws_softc *sc,
53                                            u_int16_t type);
54 extern int32_t tws_map_request(struct tws_softc *sc, struct tws_request *req);
55 extern void tws_unmap_request(struct tws_softc *sc, struct tws_request *req);
56 extern uint8_t tws_get_state(struct tws_softc *sc);
57 extern void tws_timeout(void *arg);
58 
59 int
tws_ioctl(struct cdev * dev,u_long cmd,caddr_t buf,int flags,struct thread * td)60 tws_ioctl(struct cdev *dev, u_long cmd, caddr_t buf, int flags,
61                                                     struct thread *td)
62 {
63     struct tws_softc *sc = (struct tws_softc *)(dev->si_drv1);
64     int error;
65 
66     TWS_TRACE_DEBUG(sc, "entry", sc, cmd);
67     sc->stats.ioctls++;
68     switch(cmd) {
69         case TWS_IOCTL_FIRMWARE_PASS_THROUGH :
70             error = tws_passthru(sc, (void *)buf);
71             break;
72         case TWS_IOCTL_SCAN_BUS :
73             TWS_TRACE_DEBUG(sc, "scan-bus", 0, 0);
74             error = tws_bus_scan(sc);
75             break;
76         default :
77             TWS_TRACE_DEBUG(sc, "ioctl-aen", cmd, buf);
78             error = tws_ioctl_aen(sc, cmd, (void *)buf);
79             break;
80     }
81     return(error);
82 }
83 
84 static int
tws_passthru(struct tws_softc * sc,void * buf)85 tws_passthru(struct tws_softc *sc, void *buf)
86 {
87     struct tws_request *req;
88     struct tws_ioctl_no_data_buf *ubuf = (struct tws_ioctl_no_data_buf *)buf;
89     int error;
90     u_int32_t buffer_length;
91     u_int16_t lun4;
92 
93     buffer_length = roundup2(ubuf->driver_pkt.buffer_length, 512);
94     if ( buffer_length > TWS_MAX_IO_SIZE ) {
95         return(EINVAL);
96     }
97     if ( tws_get_state(sc) != TWS_ONLINE) {
98         return(EBUSY);
99     }
100 
101     //==============================================================================================
102     // Get a command
103     //
104     do {
105         req = tws_get_request(sc, TWS_REQ_TYPE_PASSTHRU);
106         if ( !req ) {
107             error = tsleep(sc,  0, "tws_sleep", TWS_IOCTL_TIMEOUT*hz);
108             if ( error == EWOULDBLOCK ) {
109                 return(ETIMEDOUT);
110             }
111         } else {
112             // Make sure we are still ready for new commands...
113             if ( tws_get_state(sc) != TWS_ONLINE) {
114                 return(EBUSY);
115             }
116             break;
117         }
118     } while(1);
119 
120     req->length = buffer_length;
121     TWS_TRACE_DEBUG(sc, "datal,rid", req->length, req->request_id);
122     if ( req->length ) {
123         req->data = sc->ioctl_data_mem;
124         req->dma_map = sc->ioctl_data_map;
125 
126         //==========================================================================================
127         // Copy data in from user space
128         //
129         error = copyin(ubuf->pdata, req->data, req->length);
130     }
131 
132     //==============================================================================================
133     // Set command fields
134     //
135     req->flags = TWS_DIR_IN | TWS_DIR_OUT;
136     req->cb = tws_passthru_complete;
137 
138     memcpy(&req->cmd_pkt->cmd, &ubuf->cmd_pkt.cmd,
139                               sizeof(struct tws_command_apache));
140 
141     if ( GET_OPCODE(req->cmd_pkt->cmd.pkt_a.res__opcode) ==
142                                                TWS_FW_CMD_EXECUTE_SCSI ) {
143         lun4 = req->cmd_pkt->cmd.pkt_a.lun_l4__req_id & 0xF000;
144         req->cmd_pkt->cmd.pkt_a.lun_l4__req_id = lun4 | req->request_id;
145     } else {
146         req->cmd_pkt->cmd.pkt_g.generic.request_id = (u_int8_t) req->request_id;
147     }
148 
149     //==============================================================================================
150     // Send command to controller
151     //
152     error = tws_map_request(sc, req);
153     if (error) {
154         ubuf->driver_pkt.os_status = error;
155         goto out_data;
156     }
157 
158     if ( req->state == TWS_REQ_STATE_COMPLETE ) {
159         ubuf->driver_pkt.os_status = req->error_code;
160         goto out_unmap;
161     }
162 
163     mtx_lock(&sc->gen_lock);
164     error = mtx_sleep(req, &sc->gen_lock, 0, "tws_passthru", TWS_IOCTL_TIMEOUT*hz);
165     mtx_unlock(&sc->gen_lock);
166     if (( req->state != TWS_REQ_STATE_COMPLETE ) && ( error == EWOULDBLOCK )) {
167             TWS_TRACE_DEBUG(sc, "msleep timeout", error, req->request_id);
168             tws_timeout((void*) req);
169     }
170 
171 out_unmap:
172     if ( req->error_code == TWS_REQ_RET_RESET ) {
173         error = EBUSY;
174         req->error_code = EBUSY;
175         TWS_TRACE_DEBUG(sc, "ioctl reset", error, req->request_id);
176     }
177 
178     tws_unmap_request(sc, req);
179 
180     //==============================================================================================
181     // Return command status to user space
182     //
183     memcpy(&ubuf->cmd_pkt.hdr, &req->cmd_pkt->hdr, sizeof(struct tws_command_apache));
184     memcpy(&ubuf->cmd_pkt.cmd, &req->cmd_pkt->cmd, sizeof(struct tws_command_apache));
185 
186 out_data:
187     if ( req->length ) {
188         //==========================================================================================
189         // Copy data out to user space
190         //
191         if ( !error )
192             error = copyout(req->data, ubuf->pdata, ubuf->driver_pkt.buffer_length);
193     }
194 
195     if ( error )
196         TWS_TRACE_DEBUG(sc, "errored", error, 0);
197 
198     if ( req->error_code != TWS_REQ_RET_SUBMIT_SUCCESS )
199         ubuf->driver_pkt.os_status = error;
200 
201     //==============================================================================================
202     // Free command
203     //
204     req->state = TWS_REQ_STATE_FREE;
205 
206     wakeup_one(sc);
207 
208     return(error);
209 }
210 
211 void
tws_passthru_complete(struct tws_request * req)212 tws_passthru_complete(struct tws_request *req)
213 {
214     req->state = TWS_REQ_STATE_COMPLETE;
215     wakeup_one(req);
216 
217 }
218 
219 static void
tws_retrive_aen(struct tws_softc * sc,u_long cmd,struct tws_ioctl_packet * ubuf)220 tws_retrive_aen(struct tws_softc *sc, u_long cmd,
221                             struct tws_ioctl_packet *ubuf)
222 {
223     u_int16_t index=0;
224     struct tws_event_packet eventp, *qp;
225 
226     if ( sc->aen_q.head == sc->aen_q.tail ) {
227         ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS;
228         return;
229     }
230 
231     ubuf->driver_pkt.status = 0;
232 
233     /*
234      * once this flag is set cli will not display alarms
235      * needs a revisit from tools?
236      */
237     if ( sc->aen_q.overflow ) {
238         ubuf->driver_pkt.status = TWS_AEN_OVERFLOW;
239         sc->aen_q.overflow = 0; /* reset */
240     }
241 
242     qp = (struct tws_event_packet *)sc->aen_q.q;
243 
244     switch (cmd) {
245         case TWS_IOCTL_GET_FIRST_EVENT :
246             index = sc->aen_q.head;
247             break;
248         case TWS_IOCTL_GET_LAST_EVENT :
249             /* index = tail-1 */
250             index = (sc->aen_q.depth + sc->aen_q.tail - 1) % sc->aen_q.depth;
251             break;
252         case TWS_IOCTL_GET_NEXT_EVENT :
253             memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet));
254             index = sc->aen_q.head;
255             do {
256                 if ( qp[index].sequence_id ==
257                            (eventp.sequence_id + 1) )
258                     break;
259                 index  = (index+1) % sc->aen_q.depth;
260             }while ( index != sc->aen_q.tail );
261             if ( index == sc->aen_q.tail ) {
262                 ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS;
263                 return;
264             }
265             break;
266         case TWS_IOCTL_GET_PREVIOUS_EVENT :
267             memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet));
268             index = sc->aen_q.head;
269             do {
270                 if ( qp[index].sequence_id ==
271                            (eventp.sequence_id - 1) )
272                     break;
273                 index  = (index+1) % sc->aen_q.depth;
274             }while ( index != sc->aen_q.tail );
275             if ( index == sc->aen_q.tail ) {
276                 ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS;
277                 return;
278             }
279             break;
280         default :
281             TWS_TRACE_DEBUG(sc, "not a valid event", sc, cmd);
282             ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS;
283             return;
284     }
285 
286     memcpy(ubuf->data_buf, &qp[index],
287                            sizeof(struct tws_event_packet));
288     qp[index].retrieved = TWS_AEN_RETRIEVED;
289 
290     return;
291 
292 }
293 
294 static int
tws_ioctl_aen(struct tws_softc * sc,u_long cmd,void * buf)295 tws_ioctl_aen(struct tws_softc *sc, u_long cmd, void *buf)
296 {
297 
298     struct tws_ioctl_packet *ubuf = (struct tws_ioctl_packet *)buf;
299     struct tws_compatibility_packet cpkt;
300     struct tws_lock_packet lpkt;
301     time_t ctime;
302 
303     mtx_lock(&sc->gen_lock);
304     ubuf->driver_pkt.status = 0;
305     switch(cmd) {
306         case TWS_IOCTL_GET_FIRST_EVENT :
307         case TWS_IOCTL_GET_LAST_EVENT :
308         case TWS_IOCTL_GET_NEXT_EVENT :
309         case TWS_IOCTL_GET_PREVIOUS_EVENT :
310             tws_retrive_aen(sc,cmd,ubuf);
311             break;
312         case TWS_IOCTL_GET_LOCK :
313             ctime = TWS_LOCAL_TIME;
314             memcpy(&lpkt, ubuf->data_buf, sizeof(struct tws_lock_packet));
315             if ( (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) ||
316                  (lpkt.force_flag) ||
317                  (ctime >= sc->ioctl_lock.timeout) ) {
318                 sc->ioctl_lock.lock = TWS_IOCTL_LOCK_HELD;
319                 sc->ioctl_lock.timeout = ctime + (lpkt.timeout_msec / 1000);
320                 lpkt.time_remaining_msec = lpkt.timeout_msec;
321             }  else {
322                 lpkt.time_remaining_msec = (u_int32_t)
323                           ((sc->ioctl_lock.timeout - ctime) * 1000);
324                 ubuf->driver_pkt.status = TWS_IOCTL_LOCK_ALREADY_HELD;
325             }
326             break;
327         case TWS_IOCTL_RELEASE_LOCK :
328             if (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) {
329                 ubuf->driver_pkt.status = TWS_IOCTL_LOCK_NOT_HELD;
330             } else {
331                 sc->ioctl_lock.lock = TWS_IOCTL_LOCK_FREE;
332                 ubuf->driver_pkt.status = 0;
333             }
334             break;
335         case TWS_IOCTL_GET_COMPATIBILITY_INFO :
336             TWS_TRACE_DEBUG(sc, "get comp info", sc, cmd);
337 
338             memcpy( cpkt.driver_version, TWS_DRIVER_VERSION_STRING,
339                                          sizeof(TWS_DRIVER_VERSION_STRING));
340             cpkt.working_srl = sc->cinfo.working_srl;
341             cpkt.working_branch = sc->cinfo.working_branch;
342             cpkt.working_build = sc->cinfo.working_build;
343             cpkt.driver_srl_high = TWS_CURRENT_FW_SRL;
344             cpkt.driver_branch_high = TWS_CURRENT_FW_BRANCH;
345             cpkt.driver_build_high = TWS_CURRENT_FW_BUILD;
346             cpkt.driver_srl_low = TWS_BASE_FW_SRL;
347             cpkt.driver_branch_low = TWS_BASE_FW_BRANCH;
348             cpkt.driver_build_low = TWS_BASE_FW_BUILD;
349             cpkt.fw_on_ctlr_srl = sc->cinfo.fw_on_ctlr_srl;
350             cpkt.fw_on_ctlr_branch = sc->cinfo.fw_on_ctlr_branch;
351             cpkt.fw_on_ctlr_build = sc->cinfo.fw_on_ctlr_build;
352             ubuf->driver_pkt.status = 0;
353             int len = sizeof(struct tws_compatibility_packet);
354             if ( ubuf->driver_pkt.buffer_length < len )
355                 len = ubuf->driver_pkt.buffer_length;
356             memcpy(ubuf->data_buf, &cpkt, len);
357 
358             break;
359         default :
360             TWS_TRACE_DEBUG(sc, "not valid cmd", cmd,
361                            TWS_IOCTL_GET_COMPATIBILITY_INFO);
362             break;
363     }
364     mtx_unlock(&sc->gen_lock);
365     return(SUCCESS);
366 
367 }
368 
369 void
tws_circular_aenq_insert(struct tws_softc * sc,struct tws_circular_q * cq,struct tws_event_packet * aen)370 tws_circular_aenq_insert(struct tws_softc *sc, struct tws_circular_q *cq,
371 struct tws_event_packet *aen)
372 {
373 
374     struct tws_event_packet *q = (struct tws_event_packet *)cq->q;
375     volatile u_int16_t head, tail;
376     u_int8_t retr;
377     mtx_assert(&sc->gen_lock, MA_OWNED);
378 
379     head = cq->head;
380     tail = cq->tail;
381     retr = q[tail].retrieved;
382 
383     memcpy(&q[tail], aen, sizeof(struct tws_event_packet));
384     tail = (tail+1) % cq->depth;
385 
386     if ( head == tail ) { /* q is full */
387         if ( retr != TWS_AEN_RETRIEVED )
388             cq->overflow = 1;
389         cq->head = (head+1) % cq->depth;
390     }
391     cq->tail = tail;
392 
393 }
394