Merge branch 'next'
[pandora-u-boot.git] / drivers / block / sandbox.c
1 /*
2  * Copyright (C) 2013 Henrik Nordstrom <henrik@henriknordstrom.net>
3  *
4  * SPDX-License-Identifier:     GPL-2.0+
5  */
6
7 #include <common.h>
8 #include <blk.h>
9 #include <dm.h>
10 #include <fdtdec.h>
11 #include <part.h>
12 #include <os.h>
13 #include <malloc.h>
14 #include <sandboxblockdev.h>
15 #include <asm/errno.h>
16 #include <dm/device-internal.h>
17
18 DECLARE_GLOBAL_DATA_PTR;
19
20 static unsigned long host_block_read(struct udevice *dev,
21                                      unsigned long start, lbaint_t blkcnt,
22                                      void *buffer)
23 {
24         struct host_block_dev *host_dev = dev_get_priv(dev);
25         struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
26
27         if (os_lseek(host_dev->fd, start * block_dev->blksz, OS_SEEK_SET) ==
28                         -1) {
29                 printf("ERROR: Invalid block %lx\n", start);
30                 return -1;
31         }
32         ssize_t len = os_read(host_dev->fd, buffer, blkcnt * block_dev->blksz);
33         if (len >= 0)
34                 return len / block_dev->blksz;
35         return -1;
36 }
37
38 static unsigned long host_block_write(struct udevice *dev,
39                                       unsigned long start, lbaint_t blkcnt,
40                                       const void *buffer)
41 {
42         struct host_block_dev *host_dev = dev_get_priv(dev);
43         struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
44
45         if (os_lseek(host_dev->fd, start * block_dev->blksz, OS_SEEK_SET) ==
46                         -1) {
47                 printf("ERROR: Invalid block %lx\n", start);
48                 return -1;
49         }
50         ssize_t len = os_write(host_dev->fd, buffer, blkcnt * block_dev->blksz);
51         if (len >= 0)
52                 return len / block_dev->blksz;
53         return -1;
54 }
55
56 int host_dev_bind(int devnum, char *filename)
57 {
58         struct host_block_dev *host_dev;
59         struct udevice *dev;
60         char dev_name[20], *str, *fname;
61         int ret, fd;
62
63         /* Remove and unbind the old device, if any */
64         ret = blk_get_device(IF_TYPE_HOST, devnum, &dev);
65         if (ret == 0) {
66                 ret = device_remove(dev);
67                 if (ret)
68                         return ret;
69                 ret = device_unbind(dev);
70                 if (ret)
71                         return ret;
72         } else if (ret != -ENODEV) {
73                 return ret;
74         }
75
76         if (!filename)
77                 return 0;
78
79         snprintf(dev_name, sizeof(dev_name), "host%d", devnum);
80         str = strdup(dev_name);
81         if (!str)
82                 return -ENOMEM;
83         fname = strdup(filename);
84         if (!fname) {
85                 free(str);
86                 return -ENOMEM;
87         }
88
89         fd = os_open(filename, OS_O_RDWR);
90         if (fd == -1) {
91                 printf("Failed to access host backing file '%s'\n", filename);
92                 ret = -ENOENT;
93                 goto err;
94         }
95         ret = blk_create_device(gd->dm_root, "sandbox_host_blk", str,
96                                 IF_TYPE_HOST, devnum, 512,
97                                 os_lseek(fd, 0, OS_SEEK_END), &dev);
98         if (ret)
99                 goto err_file;
100         ret = device_probe(dev);
101         if (ret) {
102                 device_unbind(dev);
103                 goto err_file;
104         }
105
106         host_dev = dev_get_priv(dev);
107         host_dev->fd = fd;
108         host_dev->filename = fname;
109
110         return blk_prepare_device(dev);
111 err_file:
112         os_close(fd);
113 err:
114         free(fname);
115         free(str);
116         return ret;
117 }
118
119 int host_get_dev_err(int devnum, struct blk_desc **blk_devp)
120 {
121         struct udevice *dev;
122         int ret;
123
124         ret = blk_get_device(IF_TYPE_HOST, devnum, &dev);
125         if (ret)
126                 return ret;
127         *blk_devp = dev_get_uclass_platdata(dev);
128
129         return 0;
130 }
131
132 struct blk_desc *host_get_dev(int dev)
133 {
134         struct blk_desc *blk_dev;
135
136         if (host_get_dev_err(dev, &blk_dev))
137                 return NULL;
138
139         return blk_dev;
140 }
141
142 static const struct blk_ops sandbox_host_blk_ops = {
143         .read   = host_block_read,
144         .write  = host_block_write,
145 };
146
147 U_BOOT_DRIVER(sandbox_host_blk) = {
148         .name           = "sandbox_host_blk",
149         .id             = UCLASS_BLK,
150         .ops            = &sandbox_host_blk_ops,
151         .priv_auto_alloc_size   = sizeof(struct host_block_dev),
152 };