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
42int tws_ioctl(struct cdev *dev, long unsigned int cmd, caddr_t buf, int flags,
43                                                    struct thread *td);
44void tws_passthru_complete(struct tws_request *req);
45extern void tws_circular_aenq_insert(struct tws_softc *sc,
46                    struct tws_circular_q *cq, struct tws_event_packet *aen);
47
48static int tws_passthru(struct tws_softc *sc, void *buf);
49static int tws_ioctl_aen(struct tws_softc *sc, u_long cmd, void *buf);
50
51extern int tws_bus_scan(struct tws_softc *sc);
52extern struct tws_request *tws_get_request(struct tws_softc *sc,
53                                           u_int16_t type);
54extern int32_t tws_map_request(struct tws_softc *sc, struct tws_request *req);
55extern void tws_unmap_request(struct tws_softc *sc, struct tws_request *req);
56extern uint8_t tws_get_state(struct tws_softc *sc);
57extern void tws_timeout(void *arg);
58
59int
60tws_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
84static int
85tws_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
171out_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
186out_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
211void
212tws_passthru_complete(struct tws_request *req)
213{
214    req->state = TWS_REQ_STATE_COMPLETE;
215    wakeup_one(req);
216
217}
218
219static void
220tws_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
294static int
295tws_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
369void
370tws_circular_aenq_insert(struct tws_softc *sc, struct tws_circular_q *cq,
371struct 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