blob: 3c354002457a0d0a1e36fe353627a2be1b058740 [file] [log] [blame]
Travis Geiselbrecht52486732010-05-06 14:04:14 -07001/*
2 * Copyright (c) 2009 Travis Geiselbrecht
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23#include <stdlib.h>
24#include <debug.h>
25#include <err.h>
26#include <string.h>
27#include <list.h>
28#include <lib/bio.h>
29#include <kernel/mutex.h>
30
31#define LOCAL_TRACE 0
32
33struct bdev_struct {
34 struct list_node list;
35 mutex_t lock;
36};
37
38static struct bdev_struct *bdevs;
39
40/* default implementation is to use the read_block hook to 'deblock' the device */
41static ssize_t bio_default_read(struct bdev *dev, void *_buf, off_t offset, size_t len)
42{
43 uint8_t *buf = (uint8_t *)_buf;
44 ssize_t bytes_read = 0;
45 bnum_t block;
46 int err = 0;
47 STACKBUF_DMA_ALIGN(temp, dev->block_size); // temporary buffer for partial block transfers
48
49 /* find the starting block */
50 block = offset / dev->block_size;
51
52 LTRACEF("buf %p, offset %lld, block %u, len %zd\n", buf, offset, block, len);
53 /* handle partial first block */
54 if ((offset % dev->block_size) != 0) {
55 /* read in the block */
56 err = bio_read_block(dev, temp, block, 1);
57 if (err < 0)
58 goto err;
59
60 /* copy what we need */
61 size_t block_offset = offset % dev->block_size;
62 size_t tocopy = MIN(dev->block_size - block_offset, len);
63 memcpy(buf, temp + block_offset, tocopy);
64
65 /* increment our buffers */
66 buf += tocopy;
67 len -= tocopy;
68 bytes_read += tocopy;
69 block++;
70 }
71
72 LTRACEF("buf %p, block %u, len %zd\n", buf, block, len);
73 /* handle middle blocks */
74 if (len >= dev->block_size) {
75 /* do the middle reads */
76 size_t block_count = len / dev->block_size;
77 err = bio_read_block(dev, buf, block, block_count);
78 if (err < 0)
79 goto err;
80
81 /* increment our buffers */
82 size_t bytes = block_count * dev->block_size;
83 DEBUG_ASSERT(bytes <= len);
84
85 buf += bytes;
86 len -= bytes;
87 bytes_read += bytes;
88 block += block_count;
89 }
90
91 LTRACEF("buf %p, block %u, len %zd\n", buf, block, len);
92 /* handle partial last block */
93 if (len > 0) {
94 /* read the block */
95 err = bio_read_block(dev, temp, block, 1);
96 if (err < 0)
97 goto err;
98
99 /* copy the partial block from our temp buffer */
100 memcpy(buf, temp, len);
101
102 bytes_read += len;
103 }
104
105err:
106 /* return error or bytes read */
107 return (err >= 0) ? bytes_read : err;
108}
109
110static ssize_t bio_default_write(struct bdev *dev, const void *_buf, off_t offset, size_t len)
111{
112 const uint8_t *buf = (const uint8_t *)_buf;
113 ssize_t bytes_written = 0;
114 bnum_t block;
115 int err = 0;
116 STACKBUF_DMA_ALIGN(temp, dev->block_size); // temporary buffer for partial block transfers
117
118 /* find the starting block */
119 block = offset / dev->block_size;
120
121 LTRACEF("buf %p, offset %lld, block %u, len %zd\n", buf, offset, block, len);
122 /* handle partial first block */
123 if ((offset % dev->block_size) != 0) {
124 /* read in the block */
125 err = bio_read_block(dev, temp, block, 1);
126 if (err < 0)
127 goto err;
128
129 /* copy what we need */
130 size_t block_offset = offset % dev->block_size;
131 size_t tocopy = MIN(dev->block_size - block_offset, len);
132 memcpy(temp + block_offset, buf, tocopy);
133
134 /* write it back out */
135 err = bio_write_block(dev, temp, block, 1);
136 if (err < 0)
137 goto err;
138
139 /* increment our buffers */
140 buf += tocopy;
141 len -= tocopy;
142 bytes_written += tocopy;
143 block++;
144 }
145
146 LTRACEF("buf %p, block %u, len %zd\n", buf, block, len);
147 /* handle middle blocks */
148 if (len >= dev->block_size) {
149 /* do the middle writes */
150 size_t block_count = len / dev->block_size;
151 err = bio_write_block(dev, buf, block, block_count);
152 if (err < 0)
153 goto err;
154
155 /* increment our buffers */
156 size_t bytes = block_count * dev->block_size;
157 DEBUG_ASSERT(bytes <= len);
158
159 buf += bytes;
160 len -= bytes;
161 bytes_written += bytes;
162 block += block_count;
163 }
164
165 LTRACEF("buf %p, block %u, len %zd\n", buf, block, len);
166 /* handle partial last block */
167 if (len > 0) {
168 /* read the block */
169 err = bio_read_block(dev, temp, block, 1);
170 if (err < 0)
171 goto err;
172
173 /* copy the partial block from our temp buffer */
174 memcpy(temp, buf, len);
175
176 /* write it back out */
177 err = bio_write_block(dev, temp, block, 1);
178 if (err < 0)
179 goto err;
180
181 bytes_written += len;
182 }
183
184err:
185 /* return error or bytes written */
186 return (err >= 0) ? bytes_written : err;
187}
188
189static ssize_t bio_default_erase(struct bdev *dev, off_t offset, size_t len)
190{
191 /* default erase operation is to just write zeros over the device */
192#define ERASE_BUF_SIZE 4096
193 uint8_t *zero_buf;
194
195 zero_buf = calloc(1, ERASE_BUF_SIZE);
196
197 size_t remaining = len;
198 off_t pos = offset;
199 while (remaining > 0) {
200 ssize_t towrite = MIN(remaining, ERASE_BUF_SIZE);
201
202 ssize_t written = bio_write(dev, zero_buf, pos, towrite);
203 if (written < 0)
204 return pos;
205
206 pos += written;
207 remaining -= written;
208
209 if (written < towrite)
210 return pos;
211 }
212
213 return len;
214}
215
216static ssize_t bio_default_read_block(struct bdev *dev, void *buf, bnum_t block, uint count)
217{
218 panic("%s no reasonable default operation\n", __PRETTY_FUNCTION__);
219}
220
221static ssize_t bio_default_write_block(struct bdev *dev, const void *buf, bnum_t block, uint count)
222{
223 panic("%s no reasonable default operation\n", __PRETTY_FUNCTION__);
224}
225
226static void bdev_inc_ref(bdev_t *dev)
227{
228 atomic_add(&dev->ref, 1);
229}
230
231static void bdev_dec_ref(bdev_t *dev)
232{
233 int oldval = atomic_add(&dev->ref, -1);
234 if (oldval == 1) {
235 // last ref, remove it
236 DEBUG_ASSERT(!list_in_list(&dev->node));
237
238 TRACEF("last ref, removing (%s)\n", dev->name);
239
240 // call the close hook if it exists
241 if (dev->close)
242 dev->close(dev);
243
244 free(dev->name);
245 free(dev);
246 }
247}
248
249bdev_t *bio_open(const char *name)
250{
251 bdev_t *bdev = NULL;
252
253 /* see if it's in our list */
254 bdev_t *entry;
255 mutex_acquire(&bdevs->lock);
256 list_for_every_entry(&bdevs->list, entry, bdev_t, node) {
257 DEBUG_ASSERT(entry->ref > 0);
258 if (!strcmp(entry->name, name)) {
259 bdev = entry;
260 bdev_inc_ref(bdev);
261 break;
262 }
263 }
264 mutex_release(&bdevs->lock);
265
266 return bdev;
267}
268
269void bio_close(bdev_t *dev)
270{
271 DEBUG_ASSERT(dev);
272
273 bdev_dec_ref(dev);
274}
275
276ssize_t bio_read(bdev_t *dev, void *buf, off_t offset, size_t len)
277{
278 LTRACEF("dev '%s', buf %p, offset %lld, len %zd\n", dev->name, buf, offset, len);
279
280 DEBUG_ASSERT(dev->ref > 0);
281
282 /* range check */
283 if (offset < 0)
284 return -1;
285 if (offset >= dev->size)
286 return 0;
287 if (len == 0)
288 return 0;
289 if (offset + len > dev->size)
290 len = dev->size - offset;
291
292 return dev->read(dev, buf, offset, len);
293}
294
295ssize_t bio_read_block(bdev_t *dev, void *buf, bnum_t block, uint count)
296{
297 LTRACEF("dev '%s', buf %p, block %d, count %u\n", dev->name, buf, block, count);
298
299 DEBUG_ASSERT(dev->ref > 0);
300
301 /* range check */
302 if (block > dev->block_count)
303 return 0;
304 if (count == 0)
305 return 0;
306 if (block + count > dev->block_count)
307 count = dev->block_count - block;
308
309 return dev->read_block(dev, buf, block, count);
310}
311
312ssize_t bio_write(bdev_t *dev, const void *buf, off_t offset, size_t len)
313{
314 LTRACEF("dev '%s', buf %p, offset %lld, len %zd\n", dev->name, buf, offset, len);
315
316 DEBUG_ASSERT(dev->ref > 0);
317
318 /* range check */
319 if (offset < 0)
320 return -1;
321 if (offset >= dev->size)
322 return 0;
323 if (len == 0)
324 return 0;
325 if (offset + len > dev->size)
326 len = dev->size - offset;
327
328 return dev->write(dev, buf, offset, len);
329}
330
331ssize_t bio_write_block(bdev_t *dev, const void *buf, bnum_t block, uint count)
332{
333 LTRACEF("dev '%s', buf %p, block %d, count %u\n", dev->name, buf, block, count);
334
335 DEBUG_ASSERT(dev->ref > 0);
336
337 /* range check */
338 if (block > dev->block_count)
339 return 0;
340 if (count == 0)
341 return 0;
342 if (block + count > dev->block_count)
343 count = dev->block_count - block;
344
345 return dev->write_block(dev, buf, block, count);
346}
347
348ssize_t bio_erase(bdev_t *dev, off_t offset, size_t len)
349{
350 LTRACEF("dev '%s', offset %lld, len %zd\n", dev->name, offset, len);
351
352 DEBUG_ASSERT(dev->ref > 0);
353
354 /* range check */
355 if (offset < 0)
356 return -1;
357 if (offset >= dev->size)
358 return 0;
359 if (len == 0)
360 return 0;
361 if (offset + len > dev->size)
362 len = dev->size - offset;
363
364 return dev->erase(dev, offset, len);
365}
366
367int bio_ioctl(bdev_t *dev, int request, void *argp)
368{
369 LTRACEF("dev '%s', request %08x, argp %p\n", dev->name, request, argp);
370
371 if (dev->ioctl == NULL) {
372 return ERR_NOT_SUPPORTED;
373 } else {
374 return dev->ioctl(dev, request, argp);
375 }
376}
377
378void bio_initialize_bdev(bdev_t *dev, const char *name, size_t block_size, bnum_t block_count)
379{
380 DEBUG_ASSERT(dev);
381 DEBUG_ASSERT(name);
382 DEBUG_ASSERT(block_size == 512); // XXX can only deal with 512 for now
383
384 list_clear_node(&dev->node);
385 dev->name = strdup(name);
386 dev->block_size = block_size;
387 dev->block_count = block_count;
388 dev->size = (off_t)block_count * block_size;
389 dev->ref = 0;
390
391 /* set up the default hooks, the sub driver should override the block operations at least */
392 dev->read = bio_default_read;
393 dev->read_block = bio_default_read_block;
394 dev->write = bio_default_write;
395 dev->write_block = bio_default_write_block;
396 dev->erase = bio_default_erase;
397 dev->close = NULL;
398}
399
400void bio_register_device(bdev_t *dev)
401{
402 DEBUG_ASSERT(dev);
403
404 LTRACEF(" '%s'\n", dev->name);
405
406 bdev_inc_ref(dev);
407
408 mutex_acquire(&bdevs->lock);
409 list_add_head(&bdevs->list, &dev->node);
410 mutex_release(&bdevs->lock);
411}
412
413void bio_unregister_device(bdev_t *dev)
414{
415 DEBUG_ASSERT(dev);
416
417 LTRACEF(" '%s'\n", dev->name);
418
419 // remove it from the list
420 mutex_acquire(&bdevs->lock);
421 list_delete(&dev->node);
422 mutex_release(&bdevs->lock);
423
424 bdev_dec_ref(dev); // remove the ref the list used to have
425}
426
427void bio_dump_devices(void)
428{
429 printf("block devices:\n");
430 bdev_t *entry;
431 mutex_acquire(&bdevs->lock);
432 list_for_every_entry(&bdevs->list, entry, bdev_t, node) {
433 printf("\t%s, size %lld, bsize %zd, ref %d\n", entry->name, entry->size, entry->block_size, entry->ref);
434 }
435 mutex_release(&bdevs->lock);
436}
437
438void bio_init(void)
439{
440 bdevs = malloc(sizeof(*bdevs));
441
442 list_initialize(&bdevs->list);
443 mutex_init(&bdevs->lock);
444}
445