1226026Sdelphij/*
2226026Sdelphij * Copyright (c) 2010, LSI Corp.
3226026Sdelphij * All rights reserved.
4226026Sdelphij * Author : Manjunath Ranganathaiah
5226026Sdelphij * Support: freebsdraid@lsi.com
6226026Sdelphij *
7226026Sdelphij * Redistribution and use in source and binary forms, with or without
8226026Sdelphij * modification, are permitted provided that the following conditions
9226026Sdelphij * are met:
10226026Sdelphij *
11226026Sdelphij * 1. Redistributions of source code must retain the above copyright
12226026Sdelphij *    notice, this list of conditions and the following disclaimer.
13226026Sdelphij * 2. Redistributions in binary form must reproduce the above copyright
14226026Sdelphij *    notice, this list of conditions and the following disclaimer in
15226026Sdelphij *    the documentation and/or other materials provided with the
16226026Sdelphij *    distribution.
17226026Sdelphij * 3. Neither the name of the <ORGANIZATION> nor the names of its
18226026Sdelphij *    contributors may be used to endorse or promote products derived
19226026Sdelphij *    from this software without specific prior written permission.
20226026Sdelphij *
21226026Sdelphij * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22226026Sdelphij * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23226026Sdelphij * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24226026Sdelphij * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25226026Sdelphij * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26226026Sdelphij * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27226026Sdelphij * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28226026Sdelphij * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29226026Sdelphij * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30226026Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31226026Sdelphij * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32226026Sdelphij * POSSIBILITY OF SUCH DAMAGE.
33226026Sdelphij *
34226026Sdelphij * $FreeBSD$
35226026Sdelphij */
36226026Sdelphij
37226026Sdelphij#include <dev/tws/tws.h>
38226026Sdelphij#include <dev/tws/tws_services.h>
39226026Sdelphij#include <dev/tws/tws_hdm.h>
40226026Sdelphij#include <dev/tws/tws_user.h>
41226026Sdelphij
42226026Sdelphij
43226026Sdelphijint tws_ioctl(struct cdev *dev, long unsigned int cmd, caddr_t buf, int flags,
44226026Sdelphij                                                    d_thread_t *proc);
45226026Sdelphijvoid tws_passthru_complete(struct tws_request *req);
46226026Sdelphijextern void tws_circular_aenq_insert(struct tws_softc *sc,
47226026Sdelphij                    struct tws_circular_q *cq, struct tws_event_packet *aen);
48226026Sdelphij
49226026Sdelphij
50226026Sdelphijstatic int tws_passthru(struct tws_softc *sc, void *buf);
51226026Sdelphijstatic int tws_ioctl_aen(struct tws_softc *sc, u_long cmd, void *buf);
52226026Sdelphij
53226026Sdelphijextern int tws_bus_scan(struct tws_softc *sc);
54226026Sdelphijextern struct tws_request *tws_get_request(struct tws_softc *sc,
55226026Sdelphij                                           u_int16_t type);
56226026Sdelphijextern int32_t tws_map_request(struct tws_softc *sc, struct tws_request *req);
57226026Sdelphijextern void tws_unmap_request(struct tws_softc *sc, struct tws_request *req);
58226026Sdelphijextern uint8_t tws_get_state(struct tws_softc *sc);
59226026Sdelphijextern void tws_timeout(void *arg);
60226026Sdelphij
61226026Sdelphijint
62226026Sdelphijtws_ioctl(struct cdev *dev, u_long cmd, caddr_t buf, int flags,
63226026Sdelphij                                                    d_thread_t *proc)
64226026Sdelphij{
65226026Sdelphij    struct tws_softc *sc = (struct tws_softc *)(dev->si_drv1);
66226026Sdelphij    int error;
67226026Sdelphij
68226026Sdelphij    TWS_TRACE_DEBUG(sc, "entry", sc, cmd);
69226026Sdelphij    sc->stats.ioctls++;
70226026Sdelphij    switch(cmd) {
71226026Sdelphij        case TWS_IOCTL_FIRMWARE_PASS_THROUGH :
72226026Sdelphij            error = tws_passthru(sc, (void *)buf);
73226026Sdelphij            break;
74226026Sdelphij        case TWS_IOCTL_SCAN_BUS :
75226026Sdelphij            TWS_TRACE_DEBUG(sc, "scan-bus", 0, 0);
76226026Sdelphij            mtx_lock(&sc->sim_lock);
77226026Sdelphij            error = tws_bus_scan(sc);
78226026Sdelphij            mtx_unlock(&sc->sim_lock);
79226026Sdelphij            break;
80226026Sdelphij        default :
81226026Sdelphij            TWS_TRACE_DEBUG(sc, "ioctl-aen", cmd, buf);
82226026Sdelphij            error = tws_ioctl_aen(sc, cmd, (void *)buf);
83226026Sdelphij            break;
84226026Sdelphij
85226026Sdelphij    }
86226026Sdelphij    return(error);
87226026Sdelphij}
88226026Sdelphij
89226026Sdelphijstatic int
90226026Sdelphijtws_passthru(struct tws_softc *sc, void *buf)
91226026Sdelphij{
92226026Sdelphij    struct tws_request *req;
93226026Sdelphij    struct tws_ioctl_no_data_buf *ubuf = (struct tws_ioctl_no_data_buf *)buf;
94226026Sdelphij    int error;
95226026Sdelphij    u_int16_t lun4;
96226026Sdelphij
97241762Sdelphij
98226026Sdelphij    if ( tws_get_state(sc) != TWS_ONLINE) {
99226026Sdelphij        return(EBUSY);
100226026Sdelphij    }
101226026Sdelphij
102241762Sdelphij    //==============================================================================================
103241762Sdelphij    // Get a command
104241762Sdelphij    //
105226026Sdelphij    do {
106226026Sdelphij        req = tws_get_request(sc, TWS_REQ_TYPE_PASSTHRU);
107226026Sdelphij        if ( !req ) {
108263126Sdelphij            error = tsleep(sc,  0, "tws_sleep", TWS_IOCTL_TIMEOUT*hz);
109226026Sdelphij            if ( error == EWOULDBLOCK ) {
110226026Sdelphij                return(ETIMEDOUT);
111226026Sdelphij            }
112226026Sdelphij        } else {
113241762Sdelphij            // Make sure we are still ready for new commands...
114241762Sdelphij            if ( tws_get_state(sc) != TWS_ONLINE) {
115241762Sdelphij                return(EBUSY);
116241762Sdelphij            }
117226026Sdelphij            break;
118226026Sdelphij        }
119241762Sdelphij    } while(1);
120226026Sdelphij
121241762Sdelphij    req->length = (ubuf->driver_pkt.buffer_length + 511) & ~511;
122226026Sdelphij    TWS_TRACE_DEBUG(sc, "datal,rid", req->length, req->request_id);
123226026Sdelphij    if ( req->length ) {
124241762Sdelphij        req->data = sc->ioctl_data_mem;
125241762Sdelphij        req->dma_map = sc->ioctl_data_map;
126241762Sdelphij
127241762Sdelphij        //==========================================================================================
128241762Sdelphij        // Copy data in from user space
129241762Sdelphij        //
130226026Sdelphij        error = copyin(ubuf->pdata, req->data, req->length);
131226026Sdelphij    }
132241762Sdelphij
133241762Sdelphij    //==============================================================================================
134241762Sdelphij    // Set command fields
135241762Sdelphij    //
136226026Sdelphij    req->flags = TWS_DIR_IN | TWS_DIR_OUT;
137226026Sdelphij    req->cb = tws_passthru_complete;
138226026Sdelphij
139226026Sdelphij    memcpy(&req->cmd_pkt->cmd, &ubuf->cmd_pkt.cmd,
140226026Sdelphij                              sizeof(struct tws_command_apache));
141226026Sdelphij
142226026Sdelphij    if ( GET_OPCODE(req->cmd_pkt->cmd.pkt_a.res__opcode) ==
143226026Sdelphij                                               TWS_FW_CMD_EXECUTE_SCSI ) {
144226026Sdelphij        lun4 = req->cmd_pkt->cmd.pkt_a.lun_l4__req_id & 0xF000;
145226026Sdelphij        req->cmd_pkt->cmd.pkt_a.lun_l4__req_id = lun4 | req->request_id;
146226026Sdelphij    } else {
147226026Sdelphij        req->cmd_pkt->cmd.pkt_g.generic.request_id = (u_int8_t) req->request_id;
148226026Sdelphij    }
149226026Sdelphij
150241762Sdelphij    //==============================================================================================
151241762Sdelphij    // Send command to controller
152241762Sdelphij    //
153226026Sdelphij    error = tws_map_request(sc, req);
154226026Sdelphij    if (error) {
155226026Sdelphij        ubuf->driver_pkt.os_status = error;
156241762Sdelphij        goto out_data;
157226026Sdelphij    }
158226026Sdelphij
159241762Sdelphij    if ( req->state == TWS_REQ_STATE_COMPLETE ) {
160241762Sdelphij        ubuf->driver_pkt.os_status = req->error_code;
161241762Sdelphij        goto out_unmap;
162241762Sdelphij    }
163241762Sdelphij
164226026Sdelphij    mtx_lock(&sc->gen_lock);
165226026Sdelphij    error = mtx_sleep(req, &sc->gen_lock, 0, "tws_passthru", TWS_IOCTL_TIMEOUT*hz);
166226026Sdelphij    mtx_unlock(&sc->gen_lock);
167226026Sdelphij    if (( req->state != TWS_REQ_STATE_COMPLETE ) && ( error == EWOULDBLOCK )) {
168226026Sdelphij            TWS_TRACE_DEBUG(sc, "msleep timeout", error, req->request_id);
169226026Sdelphij            tws_timeout((void*) req);
170226026Sdelphij    }
171226026Sdelphij
172241762Sdelphijout_unmap:
173226026Sdelphij    if ( req->error_code == TWS_REQ_RET_RESET ) {
174226026Sdelphij        error = EBUSY;
175226026Sdelphij        req->error_code = EBUSY;
176226026Sdelphij        TWS_TRACE_DEBUG(sc, "ioctl reset", error, req->request_id);
177226026Sdelphij    }
178226026Sdelphij
179226026Sdelphij    tws_unmap_request(sc, req);
180226026Sdelphij
181241762Sdelphij    //==============================================================================================
182241762Sdelphij    // Return command status to user space
183241762Sdelphij    //
184226026Sdelphij    memcpy(&ubuf->cmd_pkt.hdr, &req->cmd_pkt->hdr, sizeof(struct tws_command_apache));
185226026Sdelphij    memcpy(&ubuf->cmd_pkt.cmd, &req->cmd_pkt->cmd, sizeof(struct tws_command_apache));
186241762Sdelphij
187241762Sdelphijout_data:
188241762Sdelphij    if ( req->length ) {
189241762Sdelphij        //==========================================================================================
190241762Sdelphij        // Copy data out to user space
191241762Sdelphij        //
192241762Sdelphij        if ( !error )
193241762Sdelphij            error = copyout(req->data, ubuf->pdata, ubuf->driver_pkt.buffer_length);
194226026Sdelphij    }
195226026Sdelphij
196226026Sdelphij    if ( error )
197226026Sdelphij        TWS_TRACE_DEBUG(sc, "errored", error, 0);
198226026Sdelphij
199226026Sdelphij    if ( req->error_code != TWS_REQ_RET_SUBMIT_SUCCESS )
200226026Sdelphij        ubuf->driver_pkt.os_status = error;
201226026Sdelphij
202241762Sdelphij    //==============================================================================================
203241762Sdelphij    // Free command
204241762Sdelphij    //
205226026Sdelphij    req->state = TWS_REQ_STATE_FREE;
206226026Sdelphij
207263126Sdelphij    wakeup_one(sc);
208241762Sdelphij
209226026Sdelphij    return(error);
210226026Sdelphij}
211226026Sdelphij
212226026Sdelphijvoid
213226026Sdelphijtws_passthru_complete(struct tws_request *req)
214226026Sdelphij{
215226026Sdelphij    req->state = TWS_REQ_STATE_COMPLETE;
216226026Sdelphij    wakeup_one(req);
217226026Sdelphij
218226026Sdelphij}
219226026Sdelphij
220226026Sdelphijstatic void
221226026Sdelphijtws_retrive_aen(struct tws_softc *sc, u_long cmd,
222226026Sdelphij                            struct tws_ioctl_packet *ubuf)
223226026Sdelphij{
224226026Sdelphij    u_int16_t index=0;
225226026Sdelphij    struct tws_event_packet eventp, *qp;
226226026Sdelphij
227226026Sdelphij    if ( sc->aen_q.head == sc->aen_q.tail ) {
228226026Sdelphij        ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS;
229226026Sdelphij        return;
230226026Sdelphij    }
231226026Sdelphij
232226026Sdelphij    ubuf->driver_pkt.status = 0;
233226026Sdelphij
234226026Sdelphij    /*
235226026Sdelphij     * once this flag is set cli will not display alarms
236226026Sdelphij     * needs a revisit from tools?
237226026Sdelphij     */
238226026Sdelphij    if ( sc->aen_q.overflow ) {
239226026Sdelphij        ubuf->driver_pkt.status = TWS_AEN_OVERFLOW;
240226026Sdelphij        sc->aen_q.overflow = 0; /* reset */
241226026Sdelphij    }
242226026Sdelphij
243226026Sdelphij    qp = (struct tws_event_packet *)sc->aen_q.q;
244226026Sdelphij
245226026Sdelphij    switch (cmd) {
246226026Sdelphij        case TWS_IOCTL_GET_FIRST_EVENT :
247226026Sdelphij            index = sc->aen_q.head;
248226026Sdelphij            break;
249226026Sdelphij        case TWS_IOCTL_GET_LAST_EVENT :
250226026Sdelphij            /* index = tail-1 */
251226026Sdelphij            index = (sc->aen_q.depth + sc->aen_q.tail - 1) % sc->aen_q.depth;
252226026Sdelphij            break;
253226026Sdelphij        case TWS_IOCTL_GET_NEXT_EVENT :
254226026Sdelphij            memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet));
255226026Sdelphij            index = sc->aen_q.head;
256226026Sdelphij            do {
257226026Sdelphij                if ( qp[index].sequence_id ==
258226026Sdelphij                           (eventp.sequence_id + 1) )
259226026Sdelphij                    break;
260226026Sdelphij                index  = (index+1) % sc->aen_q.depth;
261226026Sdelphij            }while ( index != sc->aen_q.tail );
262226026Sdelphij            if ( index == sc->aen_q.tail ) {
263226026Sdelphij                ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS;
264226026Sdelphij                return;
265226026Sdelphij            }
266226026Sdelphij            break;
267226026Sdelphij        case TWS_IOCTL_GET_PREVIOUS_EVENT :
268226026Sdelphij            memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet));
269226026Sdelphij            index = sc->aen_q.head;
270226026Sdelphij            do {
271226026Sdelphij                if ( qp[index].sequence_id ==
272226026Sdelphij                           (eventp.sequence_id - 1) )
273226026Sdelphij                    break;
274226026Sdelphij                index  = (index+1) % sc->aen_q.depth;
275226026Sdelphij            }while ( index != sc->aen_q.tail );
276226026Sdelphij            if ( index == sc->aen_q.tail ) {
277226026Sdelphij                ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS;
278226026Sdelphij                return;
279226026Sdelphij            }
280226026Sdelphij            break;
281226026Sdelphij        default :
282226026Sdelphij            TWS_TRACE_DEBUG(sc, "not a valid event", sc, cmd);
283226026Sdelphij            ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS;
284226026Sdelphij            return;
285226026Sdelphij    }
286226026Sdelphij
287226026Sdelphij    memcpy(ubuf->data_buf, &qp[index],
288226026Sdelphij                           sizeof(struct tws_event_packet));
289226026Sdelphij    qp[index].retrieved = TWS_AEN_RETRIEVED;
290226026Sdelphij
291226026Sdelphij    return;
292226026Sdelphij
293226026Sdelphij}
294226026Sdelphij
295226026Sdelphijstatic int
296226026Sdelphijtws_ioctl_aen(struct tws_softc *sc, u_long cmd, void *buf)
297226026Sdelphij{
298226026Sdelphij
299226026Sdelphij    struct tws_ioctl_packet *ubuf = (struct tws_ioctl_packet *)buf;
300226026Sdelphij    struct tws_compatibility_packet cpkt;
301226026Sdelphij    struct tws_lock_packet lpkt;
302226026Sdelphij    time_t ctime;
303226026Sdelphij
304226026Sdelphij    mtx_lock(&sc->gen_lock);
305226026Sdelphij    ubuf->driver_pkt.status = 0;
306226026Sdelphij    switch(cmd) {
307226026Sdelphij        case TWS_IOCTL_GET_FIRST_EVENT :
308226026Sdelphij        case TWS_IOCTL_GET_LAST_EVENT :
309226026Sdelphij        case TWS_IOCTL_GET_NEXT_EVENT :
310226026Sdelphij        case TWS_IOCTL_GET_PREVIOUS_EVENT :
311226026Sdelphij            tws_retrive_aen(sc,cmd,ubuf);
312226026Sdelphij            break;
313226026Sdelphij        case TWS_IOCTL_GET_LOCK :
314226026Sdelphij            ctime = TWS_LOCAL_TIME;
315226026Sdelphij            memcpy(&lpkt, ubuf->data_buf, sizeof(struct tws_lock_packet));
316226026Sdelphij            if ( (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) ||
317226026Sdelphij                 (lpkt.force_flag) ||
318226026Sdelphij                 (ctime >= sc->ioctl_lock.timeout) ) {
319226026Sdelphij                sc->ioctl_lock.lock = TWS_IOCTL_LOCK_HELD;
320226026Sdelphij                sc->ioctl_lock.timeout = ctime + (lpkt.timeout_msec / 1000);
321226026Sdelphij                lpkt.time_remaining_msec = lpkt.timeout_msec;
322226026Sdelphij            }  else {
323226026Sdelphij                lpkt.time_remaining_msec = (u_int32_t)
324226026Sdelphij                          ((sc->ioctl_lock.timeout - ctime) * 1000);
325226026Sdelphij                ubuf->driver_pkt.status = TWS_IOCTL_LOCK_ALREADY_HELD;
326226026Sdelphij
327226026Sdelphij            }
328226026Sdelphij            break;
329226026Sdelphij        case TWS_IOCTL_RELEASE_LOCK :
330226026Sdelphij            if (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) {
331226026Sdelphij                ubuf->driver_pkt.status = TWS_IOCTL_LOCK_NOT_HELD;
332226026Sdelphij            } else {
333226026Sdelphij                sc->ioctl_lock.lock = TWS_IOCTL_LOCK_FREE;
334226026Sdelphij                ubuf->driver_pkt.status = 0;
335226026Sdelphij            }
336226026Sdelphij            break;
337226026Sdelphij        case TWS_IOCTL_GET_COMPATIBILITY_INFO :
338226026Sdelphij            TWS_TRACE_DEBUG(sc, "get comp info", sc, cmd);
339226026Sdelphij
340226026Sdelphij            memcpy( cpkt.driver_version, TWS_DRIVER_VERSION_STRING,
341226026Sdelphij                                         sizeof(TWS_DRIVER_VERSION_STRING));
342226026Sdelphij            cpkt.working_srl = sc->cinfo.working_srl;
343226026Sdelphij            cpkt.working_branch = sc->cinfo.working_branch;
344226026Sdelphij            cpkt.working_build = sc->cinfo.working_build;
345226026Sdelphij            cpkt.driver_srl_high = TWS_CURRENT_FW_SRL;
346226026Sdelphij            cpkt.driver_branch_high = TWS_CURRENT_FW_BRANCH;
347226026Sdelphij            cpkt.driver_build_high = TWS_CURRENT_FW_BUILD;
348226026Sdelphij            cpkt.driver_srl_low = TWS_BASE_FW_SRL;
349226026Sdelphij            cpkt.driver_branch_low = TWS_BASE_FW_BRANCH;
350226026Sdelphij            cpkt.driver_build_low = TWS_BASE_FW_BUILD;
351226026Sdelphij            cpkt.fw_on_ctlr_srl = sc->cinfo.fw_on_ctlr_srl;
352226026Sdelphij            cpkt.fw_on_ctlr_branch = sc->cinfo.fw_on_ctlr_branch;
353226026Sdelphij            cpkt.fw_on_ctlr_build = sc->cinfo.fw_on_ctlr_build;
354226026Sdelphij            ubuf->driver_pkt.status = 0;
355226026Sdelphij            int len = sizeof(struct tws_compatibility_packet);
356226026Sdelphij            if ( ubuf->driver_pkt.buffer_length < len )
357226026Sdelphij                len = ubuf->driver_pkt.buffer_length;
358226026Sdelphij            memcpy(ubuf->data_buf, &cpkt, len);
359226026Sdelphij
360226026Sdelphij            break;
361226026Sdelphij        default :
362226026Sdelphij            TWS_TRACE_DEBUG(sc, "not valid cmd", cmd,
363226026Sdelphij                           TWS_IOCTL_GET_COMPATIBILITY_INFO);
364226026Sdelphij            break;
365226026Sdelphij
366226026Sdelphij    }
367226026Sdelphij    mtx_unlock(&sc->gen_lock);
368226026Sdelphij    return(SUCCESS);
369226026Sdelphij
370226026Sdelphij}
371226026Sdelphij
372226026Sdelphijvoid
373226026Sdelphijtws_circular_aenq_insert(struct tws_softc *sc, struct tws_circular_q *cq,
374226026Sdelphijstruct tws_event_packet *aen)
375226026Sdelphij{
376226026Sdelphij
377226026Sdelphij    struct tws_event_packet *q = (struct tws_event_packet *)cq->q;
378226026Sdelphij    volatile u_int16_t head, tail;
379226026Sdelphij    u_int8_t retr;
380226026Sdelphij    mtx_assert(&sc->gen_lock, MA_OWNED);
381226026Sdelphij
382226026Sdelphij    head = cq->head;
383226026Sdelphij    tail = cq->tail;
384226026Sdelphij    retr = q[tail].retrieved;
385226026Sdelphij
386226026Sdelphij    memcpy(&q[tail], aen, sizeof(struct tws_event_packet));
387226026Sdelphij    tail = (tail+1) % cq->depth;
388226026Sdelphij
389226026Sdelphij    if ( head == tail ) { /* q is full */
390226026Sdelphij        if ( retr != TWS_AEN_RETRIEVED )
391226026Sdelphij            cq->overflow = 1;
392226026Sdelphij        cq->head = (head+1) % cq->depth;
393226026Sdelphij    }
394226026Sdelphij    cq->tail = tail;
395226026Sdelphij
396226026Sdelphij}
397