source: rtems-libbsd/rtemsbsd/rtems/rtems-bsd-cam.c @ 150d4d6

4.115-freebsd-12freebsd-9.3
Last change on this file since 150d4d6 was 150d4d6, checked in by Sebastian Huber <sebastian.huber@…>, on Oct 24, 2013 at 9:11:32 AM

Move content to new <machine/rtems-bsd-support.h>

  • 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-config.h>
42#include <machine/rtems-bsd-support.h>
43
44#include <rtems/bsd/sys/param.h>
45#include <sys/systm.h>
46#include <sys/malloc.h>
47#include <sys/kernel.h>
48#include <rtems/bsd/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                sizeof(*inq_data) - 1,
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                        BSD_PRINTF("OOPS: inquiry failed\n");
344                        goto error;
345                }
346                scsi_print_inquiry(&inq_data);
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                        goto error;
357                }
358
359                sc = rtems_bsd_scsi_read_capacity(&sim->ccb, &block_count, &block_size);
360                if (sc != RTEMS_SUCCESSFUL) {
361                        BSD_PRINTF("OOPS: read capacity failed\n");
362                        goto error;
363                }
364
365                BSD_PRINTF("read capacity: block count %u, block size %u\n", block_count, block_size);
366
367                sc = rtems_blkdev_create(disk, block_size, block_count, rtems_bsd_sim_disk_ioctl, sim);
368                if (sc != RTEMS_SUCCESSFUL) {
369                        goto error;
370                }
371
372                /* FIXME */
373#if 0
374                rtems_disk_device *dd = rtems_disk_obtain(dev);
375                dd->block_size *= 64;
376                rtems_disk_release(dd);
377#endif
378
379                rtems_bsd_sim_disk_initialized(sim, disk);
380
381                *dest = strdup(disk, M_RTEMS_HEAP);
382        }
383
384        return RTEMS_SUCCESSFUL;
385
386error:
387
388        free(disk, M_RTEMS_HEAP);
389
390        rtems_bsd_sim_disk_initialized(sim, NULL);
391
392        return RTEMS_IO_ERROR;
393}
394
395struct cam_sim *
396cam_sim_alloc(
397        sim_action_func sim_action,
398        sim_poll_func sim_poll,
399        const char *sim_name,
400        void *softc,
401        u_int32_t unit,
402        struct mtx *mtx,
403        int max_dev_transactions,
404        int max_tagged_dev_transactions,
405        struct cam_devq *queue
406)
407{
408        rtems_status_code sc = RTEMS_SUCCESSFUL;
409        struct cam_sim *sim = NULL;
410
411        if (mtx == NULL) {
412                return NULL;
413        }
414
415        sim = malloc(sizeof(*sim), M_CAMSIM, M_NOWAIT | M_ZERO);
416        if (sim == NULL) {
417                return NULL;
418        }
419
420        sim->sim_action = sim_action;
421        sim->sim_poll = sim_poll;
422        sim->sim_name = sim_name;
423        sim->softc = softc;
424        sim->mtx = mtx;
425        sim->unit_number = unit;
426        sim->ccb.ccb_h.sim = sim;
427
428        cv_init(&sim->state_changed, "SIM state changed");
429
430        sc = rtems_media_server_disk_attach(sim_name, rtems_bsd_sim_attach_worker, sim);
431        BSD_ASSERT_SC(sc);
432
433        return sim;
434}
435
436void
437cam_sim_free(struct cam_sim *sim, int free_devq)
438{
439        rtems_status_code sc = RTEMS_SUCCESSFUL;
440
441        /*
442         * The umass_detach() cancels all transfers via
443         * usbd_transfer_unsetup().  This prevents also the start of new
444         * transfers since the transfer descriptors will be removed.  Started
445         * transfers that are not in the transferring state will be canceled
446         * and the callbacks will be not called.  Thus it is necessary to do
447         * this here if we are in the BUSY state.
448         */
449        rtems_bsd_sim_wait_for_state_and_cancel_ccb(sim, BSD_SIM_IDLE);
450
451        if (sim->disk != NULL) {
452                sc = rtems_media_server_disk_detach(sim->disk);
453                BSD_ASSERT_SC(sc);
454
455                rtems_bsd_sim_wait_for_state(sim, BSD_SIM_DELETED);
456        }
457
458        cv_destroy(&sim->state_changed);
459        free(sim, M_CAMSIM);
460}
461
462struct cam_devq *
463cam_simq_alloc(u_int32_t max_sim_transactions)
464{
465        return BSD_CAM_DEVQ_DUMMY;
466}
467
468void
469cam_simq_free(struct cam_devq *devq)
470{
471        BSD_ASSERT(devq == BSD_CAM_DEVQ_DUMMY);
472}
473
474void
475xpt_done(union ccb *done_ccb)
476{
477        (*done_ccb->ccb_h.cbfcnp)(NULL, done_ccb);
478}
479
480int32_t
481xpt_bus_register(struct cam_sim *sim, device_t parent, u_int32_t bus)
482{
483        /*
484         * We ignore this bus stuff completely.  This is easier than removing
485         * the calls from "umass.c".
486         */
487
488        return CAM_SUCCESS;
489}
490
491int32_t
492xpt_bus_deregister(path_id_t pathid)
493{
494        /*
495         * We ignore this bus stuff completely.  This is easier than removing
496         * the calls from "umass.c".
497         */
498
499        return CAM_REQ_CMP;
500}
Note: See TracBrowser for help on using the repository browser.