source: rtems-libbsd/rtemsbsd/src/rtems-bsd-cam.c @ 8dce372

4.115-freebsd-12freebsd-9.3
Last change on this file since 8dce372 was 8dce372, checked in by Sebastian Huber <sebastian.huber@…>, on Mar 26, 2012 at 8:19:30 AM

Use new block device API

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