source: rtems-libbsd/rtemsbsd/rtems/rtems-kernel-cam.c

6-freebsd-12
Last change on this file was 3c967ca, checked in by Sebastian Huber <sebastian.huber@…>, on 06/08/17 at 11:15:12

Use <sys/lock.h> provided by Newlib

  • Property mode set to 100644
File size: 11.2 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup rtems_bsd_rtems
5 *
6 * @brief TODO.
7 */
8
9/*
10 * Copyright (c) 2009-2012 embedded brains GmbH. 
11 * All rights reserved.
12 *
13 *  embedded brains GmbH
14 *  Obere Lagerstr. 30
15 *  82178 Puchheim
16 *  Germany
17 *  <rtems@embedded-brains.de>
18 *
19 * Redistribution and use in source and binary forms, with or without
20 * modification, are permitted provided that the following conditions
21 * are met:
22 * 1. Redistributions of source code must retain the above copyright
23 *    notice, this list of conditions and the following disclaimer.
24 * 2. Redistributions in binary form must reproduce the above copyright
25 *    notice, this list of conditions and the following disclaimer in the
26 *    documentation and/or other materials provided with the distribution.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 */
40
41#include <machine/rtems-bsd-kernel-space.h>
42#include <machine/rtems-bsd-support.h>
43
44#include <sys/param.h>
45#include <sys/systm.h>
46#include <sys/malloc.h>
47#include <sys/kernel.h>
48#include <sys/lock.h>
49#include <sys/mutex.h>
50#include <sys/condvar.h>
51
52#include <cam/cam.h>
53#include <cam/cam_ccb.h>
54#include <cam/cam_sim.h>
55#include <cam/cam_xpt.h>
56#include <cam/cam_xpt_sim.h>
57#include <cam/cam_debug.h>
58
59#include <cam/scsi/scsi_all.h>
60
61#include <rtems/media.h>
62#include <rtems/libio.h>
63#include <rtems/diskdevs.h>
64
65#define BSD_CAM_DEVQ_DUMMY ((struct cam_devq *) 0xdeadbeef)
66
67#define BSD_SCSI_TAG 0
68
69#define BSD_SCSI_RETRIES 4
70
71#define BSD_SCSI_TIMEOUT (60 * 1000)
72
73#define BSD_SCSI_MIN_COMMAND_SIZE 10
74
75MALLOC_DEFINE(M_CAMSIM, "CAM SIM", "CAM SIM buffers");
76
77static void
78rtems_bsd_sim_set_state(struct cam_sim *sim, enum bsd_sim_state state)
79{
80        sim->state = state;
81}
82
83static void
84rtems_bsd_sim_set_state_and_notify(struct cam_sim *sim, enum bsd_sim_state state)
85{
86        sim->state = state;
87        cv_broadcast(&sim->state_changed);
88}
89
90static void
91rtems_bsd_sim_wait_for_state(struct cam_sim *sim, enum bsd_sim_state state)
92{
93        while (sim->state != state) {
94                cv_wait(&sim->state_changed, sim->mtx);
95        }
96}
97
98static void
99rtems_bsd_sim_wait_for_state_and_cancel_ccb(struct cam_sim *sim, enum bsd_sim_state state)
100{
101        while (sim->state != state) {
102                if (sim->state != BSD_SIM_BUSY) {
103                        cv_wait(&sim->state_changed, sim->mtx);
104                } else {
105                        sim->ccb.ccb_h.status = CAM_SEL_TIMEOUT;
106                        (*sim->ccb.ccb_h.cbfcnp)(NULL, &sim->ccb);
107                }
108        }
109}
110
111static void
112rtems_bsd_ccb_callback(struct cam_periph *periph, union ccb *ccb)
113{
114        struct cam_sim *sim = ccb->ccb_h.sim;
115
116        BSD_ASSERT(periph == NULL && sim->state == BSD_SIM_INIT_BUSY);
117
118        rtems_bsd_sim_set_state_and_notify(sim, BSD_SIM_INIT_READY);
119}
120
121static rtems_status_code
122rtems_bsd_ccb_action(union ccb *ccb)
123{
124        rtems_status_code sc = RTEMS_SUCCESSFUL;
125        struct cam_sim *sim = ccb->ccb_h.sim;
126
127        mtx_lock(sim->mtx);
128
129        BSD_ASSERT(sim->state == BSD_SIM_INIT);
130        rtems_bsd_sim_set_state(sim, BSD_SIM_INIT_BUSY);
131        (*sim->sim_action)(sim, ccb);
132        rtems_bsd_sim_wait_for_state(sim, BSD_SIM_INIT_READY);
133        if (ccb->ccb_h.status != CAM_REQ_CMP) {
134                sc = RTEMS_IO_ERROR;
135        }
136        rtems_bsd_sim_set_state(sim, BSD_SIM_INIT);
137
138        mtx_unlock(sim->mtx);
139
140        return sc;
141}
142
143static rtems_status_code
144rtems_bsd_scsi_inquiry(union ccb *ccb, struct scsi_inquiry_data *inq_data)
145{
146        memset(inq_data, 0, sizeof(*inq_data));
147
148        scsi_inquiry(
149                &ccb->csio,
150                BSD_SCSI_RETRIES,
151                rtems_bsd_ccb_callback,
152                BSD_SCSI_TAG,
153                (u_int8_t *) inq_data,
154                SHORT_INQUIRY_LENGTH,
155                FALSE,
156                0,
157                SSD_MIN_SIZE,
158                BSD_SCSI_TIMEOUT
159        );
160
161        return rtems_bsd_ccb_action(ccb);
162}
163
164static rtems_status_code
165rtems_bsd_scsi_test_unit_ready(union ccb *ccb)
166{
167        scsi_test_unit_ready(
168                &ccb->csio,
169                BSD_SCSI_RETRIES,
170                rtems_bsd_ccb_callback,
171                BSD_SCSI_TAG,
172                SSD_FULL_SIZE,
173                BSD_SCSI_TIMEOUT
174        );
175
176        return rtems_bsd_ccb_action(ccb);
177}
178
179static rtems_status_code
180rtems_bsd_scsi_read_capacity(union ccb *ccb, uint32_t *block_count, uint32_t *block_size)
181{
182        rtems_status_code sc = RTEMS_SUCCESSFUL;
183        struct scsi_read_capacity_data rdcap;
184
185        memset(&rdcap, 0, sizeof(rdcap));
186
187        scsi_read_capacity(
188                &ccb->csio,
189                BSD_SCSI_RETRIES,
190                rtems_bsd_ccb_callback,
191                BSD_SCSI_TAG,
192                &rdcap,
193                SSD_FULL_SIZE,
194                BSD_SCSI_TIMEOUT
195        );
196
197        sc = rtems_bsd_ccb_action(ccb);
198        if (sc != RTEMS_SUCCESSFUL) {
199                return RTEMS_IO_ERROR;
200        }
201
202        *block_size = scsi_4btoul(rdcap.length);
203        *block_count = scsi_4btoul(rdcap.addr) + 1;
204
205        return RTEMS_SUCCESSFUL;
206}
207
208static void
209rtems_bsd_csio_callback(struct cam_periph *periph, union ccb *ccb)
210{
211        rtems_status_code sc = RTEMS_SUCCESSFUL;
212        bool done = false;
213        struct cam_sim *sim = ccb->ccb_h.sim;
214
215        BSD_ASSERT(periph == NULL && sim->state == BSD_SIM_BUSY);
216
217        if (ccb->ccb_h.status == CAM_REQ_CMP) {
218                rtems_blkdev_sg_buffer *sg = ccb->csio.sg_current;
219
220                if (sg != ccb->csio.sg_end) {
221                        scsi_read_write(
222                                &ccb->csio,
223                                BSD_SCSI_RETRIES,
224                                rtems_bsd_csio_callback,
225                                BSD_SCSI_TAG,
226                                ccb->csio.readop,
227                                0,
228                                BSD_SCSI_MIN_COMMAND_SIZE,
229                                sg->block,
230                                sg->length / 512, /* FIXME */
231                                sg->buffer,
232                                sg->length,
233                                SSD_FULL_SIZE,
234                                BSD_SCSI_TIMEOUT
235                        );
236                        ccb->csio.sg_current = sg + 1;
237                        (*sim->sim_action)(sim, ccb);
238                } else {
239                        done = true;
240                }
241        } else if (ccb->ccb_h.status == CAM_SEL_TIMEOUT) {
242                sc = RTEMS_UNSATISFIED;
243                done = true;
244        } else {
245                sc = RTEMS_IO_ERROR;
246                done = true;
247        }
248
249        if (done) {
250                rtems_blkdev_request_done(ccb->csio.req, sc);
251                rtems_bsd_sim_set_state_and_notify(sim, BSD_SIM_IDLE);
252        }
253}
254
255static int rtems_bsd_sim_disk_read_write(struct cam_sim *sim, rtems_blkdev_request *req)
256{
257        mtx_lock(sim->mtx);
258
259        rtems_bsd_sim_wait_for_state(sim, BSD_SIM_IDLE);
260        rtems_bsd_sim_set_state(sim, BSD_SIM_BUSY);
261
262        switch (req->req) {
263                case RTEMS_BLKDEV_REQ_READ:
264                        sim->ccb.csio.readop = TRUE;
265                        break;
266                case RTEMS_BLKDEV_REQ_WRITE:
267                        sim->ccb.csio.readop = FALSE;
268                        break;
269                default:
270                        mtx_unlock(sim->mtx);
271                        return -1;
272        }
273
274        sim->ccb.csio.sg_current = req->bufs;
275        sim->ccb.csio.sg_end = req->bufs + req->bufnum;
276        sim->ccb.csio.req = req;
277
278        sim->ccb.ccb_h.status = CAM_REQ_CMP;
279
280        rtems_bsd_csio_callback(NULL, &sim->ccb);
281
282        mtx_unlock(sim->mtx);
283
284        return 0;
285}
286
287static int rtems_bsd_sim_disk_ioctl(rtems_disk_device *dd, uint32_t req, void *arg)
288{
289        struct cam_sim *sim = rtems_disk_get_driver_data(dd);
290
291        if (req == RTEMS_BLKIO_REQUEST) {
292                rtems_blkdev_request *r = arg;
293
294                return rtems_bsd_sim_disk_read_write(sim, r);
295        } else if (req == RTEMS_BLKIO_DELETED) {
296                mtx_lock(sim->mtx);
297
298                free(sim->disk, M_RTEMS_HEAP);
299                sim->disk = NULL;
300                rtems_bsd_sim_set_state_and_notify(sim, BSD_SIM_DELETED);
301
302                mtx_unlock(sim->mtx);
303
304                return 0;
305        } else {
306                return rtems_blkdev_ioctl(dd, req, arg);
307        }
308}
309
310static void
311rtems_bsd_sim_disk_initialized(struct cam_sim *sim, char *disk)
312{
313        mtx_lock(sim->mtx);
314
315        sim->disk = disk;
316        rtems_bsd_sim_set_state_and_notify(sim, BSD_SIM_IDLE);
317
318        mtx_unlock(sim->mtx);
319}
320
321static rtems_status_code
322rtems_bsd_sim_attach_worker(rtems_media_state state, const char *src, char **dest, void *arg)
323{
324        rtems_status_code sc = RTEMS_SUCCESSFUL;
325        struct cam_sim *sim = arg;
326        char *disk = NULL;
327
328        if (state == RTEMS_MEDIA_STATE_READY) {
329                unsigned retries = 0;
330
331                struct scsi_inquiry_data inq_data;
332                uint32_t block_count = 0;
333                uint32_t block_size = 0;
334
335                disk = rtems_media_create_path("/dev", src, cam_sim_unit(sim));
336                if (disk == NULL) {
337                        BSD_PRINTF("OOPS: create path failed\n");
338                        goto error;
339                }
340
341                sc = rtems_bsd_scsi_inquiry(&sim->ccb, &inq_data);
342                if (sc == RTEMS_SUCCESSFUL) {
343                        scsi_print_inquiry(&inq_data);
344                } else {
345                        BSD_PRINTF("OOPS: inquiry failed\n");
346                }
347
348                for (retries = 0; retries <= 3; ++retries) {
349                        sc = rtems_bsd_scsi_test_unit_ready(&sim->ccb);
350                        if (sc == RTEMS_SUCCESSFUL) {
351                                break;
352                        }
353                }
354                if (sc != RTEMS_SUCCESSFUL) {
355                        BSD_PRINTF("OOPS: test unit ready failed\n");
356                }
357
358                for (retries = 0; retries <= 3; ++retries) {
359                        sc = rtems_bsd_scsi_read_capacity(&sim->ccb, &block_count, &block_size);
360                        if (sc == RTEMS_SUCCESSFUL) {
361                                break;
362                        }
363                }
364                if (sc != RTEMS_SUCCESSFUL) {
365                        BSD_PRINTF("OOPS: read capacity failed\n");
366                        goto error;
367                }
368
369                BSD_PRINTF("read capacity: block count %u, block size %u\n", block_count, block_size);
370
371                sc = rtems_blkdev_create(disk, block_size, block_count, rtems_bsd_sim_disk_ioctl, sim);
372                if (sc != RTEMS_SUCCESSFUL) {
373                        goto error;
374                }
375
376                /* FIXME */
377#if 0
378                rtems_disk_device *dd = rtems_disk_obtain(dev);
379                dd->block_size *= 64;
380                rtems_disk_release(dd);
381#endif
382
383                rtems_bsd_sim_disk_initialized(sim, disk);
384
385                *dest = strdup(disk, M_RTEMS_HEAP);
386        }
387
388        return RTEMS_SUCCESSFUL;
389
390error:
391
392        free(disk, M_RTEMS_HEAP);
393
394        rtems_bsd_sim_disk_initialized(sim, NULL);
395
396        return RTEMS_IO_ERROR;
397}
398
399struct cam_sim *
400cam_sim_alloc(
401        sim_action_func sim_action,
402        sim_poll_func sim_poll,
403        const char *sim_name,
404        void *softc,
405        u_int32_t unit,
406        struct mtx *mtx,
407        int max_dev_transactions,
408        int max_tagged_dev_transactions,
409        struct cam_devq *queue
410)
411{
412        rtems_status_code sc = RTEMS_SUCCESSFUL;
413        struct cam_sim *sim = NULL;
414
415        if (mtx == NULL) {
416                return NULL;
417        }
418
419        sim = malloc(sizeof(*sim), M_CAMSIM, M_NOWAIT | M_ZERO);
420        if (sim == NULL) {
421                return NULL;
422        }
423
424        sim->sim_action = sim_action;
425        sim->sim_poll = sim_poll;
426        sim->sim_name = sim_name;
427        sim->softc = softc;
428        sim->mtx = mtx;
429        sim->unit_number = unit;
430        sim->ccb.ccb_h.sim = sim;
431
432        cv_init(&sim->state_changed, "SIM state changed");
433
434        sc = rtems_media_server_disk_attach(sim_name, rtems_bsd_sim_attach_worker, sim);
435        BSD_ASSERT_SC(sc);
436
437        return sim;
438}
439
440void
441cam_sim_free(struct cam_sim *sim, int free_devq)
442{
443        rtems_status_code sc = RTEMS_SUCCESSFUL;
444
445        /*
446         * The umass_detach() cancels all transfers via
447         * usbd_transfer_unsetup().  This prevents also the start of new
448         * transfers since the transfer descriptors will be removed.  Started
449         * transfers that are not in the transferring state will be canceled
450         * and the callbacks will be not called.  Thus it is necessary to do
451         * this here if we are in the BUSY state.
452         */
453        rtems_bsd_sim_wait_for_state_and_cancel_ccb(sim, BSD_SIM_IDLE);
454
455        if (sim->disk != NULL) {
456                sc = rtems_media_server_disk_detach(sim->disk);
457                BSD_ASSERT_SC(sc);
458
459                rtems_bsd_sim_wait_for_state(sim, BSD_SIM_DELETED);
460        }
461
462        cv_destroy(&sim->state_changed);
463        free(sim, M_CAMSIM);
464}
465
466struct cam_devq *
467cam_simq_alloc(u_int32_t max_sim_transactions)
468{
469        return BSD_CAM_DEVQ_DUMMY;
470}
471
472void
473cam_simq_free(struct cam_devq *devq)
474{
475        BSD_ASSERT(devq == BSD_CAM_DEVQ_DUMMY);
476}
477
478void
479xpt_done(union ccb *done_ccb)
480{
481        (*done_ccb->ccb_h.cbfcnp)(NULL, done_ccb);
482}
483
484int32_t
485xpt_bus_register(struct cam_sim *sim, device_t parent, u_int32_t bus)
486{
487        /*
488         * We ignore this bus stuff completely.  This is easier than removing
489         * the calls from "umass.c".
490         */
491
492        return CAM_SUCCESS;
493}
494
495int32_t
496xpt_bus_deregister(path_id_t pathid)
497{
498        /*
499         * We ignore this bus stuff completely.  This is easier than removing
500         * the calls from "umass.c".
501         */
502
503        return CAM_REQ_CMP;
504}
Note: See TracBrowser for help on using the repository browser.