1 /*
2  * Copyright (c) 2006-2022, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2016-09-20     Bernard      the first version
9  */
10 
11 #include <stdint.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 
15 #include <rtthread.h>
16 #include <rtdevice.h>
17 
18 #include "partition.h"
19 
20 // #define PARTITION_DEBUG
21 
22 #ifdef PARTITION_DEBUG
23 #define DEBUG_TRACE         rt_kprintf("[part] "); rt_kprintf
24 #else
25 #define DEBUG_TRACE(...)
26 #endif
27 
28 struct partition_device
29 {
30     struct rt_device parent;            /* parent block device */
31 
32     int sector_size;
33     int block_size;
34 
35     struct rt_device *block_device;
36     const struct rt_partition *partition;
37 };
38 
39 /* RT-Thread device interface */
partition_init(rt_device_t dev)40 static rt_err_t partition_init(rt_device_t dev)
41 {
42     return RT_EOK;
43 }
44 
partition_open(rt_device_t dev,rt_uint16_t oflag)45 static rt_err_t partition_open(rt_device_t dev, rt_uint16_t oflag)
46 {
47     return RT_EOK;
48 }
49 
partition_close(rt_device_t dev)50 static rt_err_t partition_close(rt_device_t dev)
51 {
52     return RT_EOK;
53 }
54 
55 #if RTTHREAD_VERSION >= 30000
partition_control(rt_device_t dev,int cmd,void * args)56 static rt_err_t partition_control(rt_device_t dev, int cmd, void *args)
57 #else
58 static rt_err_t partition_control(rt_device_t dev, rt_uint8_t cmd, void *args)
59 #endif
60 {
61     struct partition_device *part;
62 
63     part = (struct partition_device*) dev;
64     RT_ASSERT(part != RT_NULL);
65 
66     if (cmd == RT_DEVICE_CTRL_BLK_GETGEOME)
67     {
68         struct rt_device_blk_geometry *geometry;
69 
70         geometry = (struct rt_device_blk_geometry *)args;
71         if (geometry == RT_NULL) return -RT_ERROR;
72 
73         geometry->bytes_per_sector = part->sector_size;
74         geometry->sector_count = part->partition->size / part->sector_size;
75         geometry->block_size = part->block_size;
76     }
77 
78     return RT_EOK;
79 }
80 
partition_read(rt_device_t dev,rt_off_t pos,void * buffer,rt_size_t size)81 static rt_ssize_t partition_read(rt_device_t dev,
82                                    rt_off_t pos,
83                                    void* buffer,
84                                    rt_size_t size)
85 {
86     struct partition_device *part;
87 
88     part = (struct partition_device*) dev;
89     RT_ASSERT(part != RT_NULL);
90 
91     DEBUG_TRACE("read block: %d, size=%d\n", pos + part->partition->offset/part->sector_size, size);
92 
93     return rt_device_read(part->block_device, pos + part->partition->offset/part->sector_size, buffer, size);
94 }
95 
partition_write(rt_device_t dev,rt_off_t pos,const void * buffer,rt_size_t size)96 static rt_ssize_t partition_write(rt_device_t dev,
97                                     rt_off_t pos,
98                                     const void* buffer,
99                                     rt_size_t size)
100 {
101     struct partition_device *part;
102 
103     part = (struct partition_device*) dev;
104     RT_ASSERT(part != RT_NULL);
105 
106     if (part->partition->flags & PARTITION_WRITEABLE)
107     {
108         DEBUG_TRACE("write block: %d, size=%d\n", pos + part->partition->offset/part->sector_size, size);
109 
110         return rt_device_write(part->block_device, pos + part->partition->offset/part->sector_size,
111                 buffer, size);
112     }
113 
114     DEBUG_TRACE("read-only partition!\n");
115     return 0;
116 }
117 
118 #ifdef RT_USING_DEVICE_OPS
119 const static struct rt_device_ops _partition_ops =
120 {
121     .init = partition_init,
122     .open = partition_open,
123     .close = partition_close,
124     .read = partition_read,
125     .write = partition_write,
126     .control = partition_control,
127 };
128 #endif /* RT_USING_DEVICE_OPS */
129 
rt_partition_init(const char * flash_device,const struct rt_partition * parts,rt_size_t num)130 int rt_partition_init(const char* flash_device, const struct rt_partition* parts, rt_size_t num)
131 {
132     struct rt_device *device;
133     struct partition_device *part_dev;
134     const struct rt_partition* part;
135     struct rt_device_blk_geometry geometry;
136 
137     device = (struct rt_device *)rt_device_find(flash_device);
138     if(device == RT_NULL)
139     {
140         DEBUG_TRACE("block device %s not found!\r\n", flash_device);
141         return -RT_ENOSYS;
142     }
143 
144     /* open the block device */
145     rt_device_open(device, RT_DEVICE_OFLAG_RDWR);
146     /* get block device geometry */
147     rt_device_control(device, RT_DEVICE_CTRL_BLK_GETGEOME, &geometry);
148 
149     /* create each partition device */
150     while(num --)
151     {
152         part = parts ++;
153         part_dev = (struct partition_device*)rt_malloc (sizeof(struct partition_device));
154         if (part_dev)
155         {
156             part_dev->block_device = device;
157             part_dev->partition = part;
158             part_dev->sector_size = geometry.bytes_per_sector;
159             part_dev->block_size  = geometry.block_size;
160 
161             /* register device */
162             part_dev->parent.type    = RT_Device_Class_Block;
163 #ifndef RT_USING_DEVICE_OPS
164             part_dev->parent.init    = partition_init;
165             part_dev->parent.open    = partition_open;
166             part_dev->parent.close   = partition_close;
167             part_dev->parent.read    = partition_read;
168             part_dev->parent.write   = partition_write;
169             part_dev->parent.control = partition_control;
170 #else
171             part_dev->parent.ops     = &_partition_ops;
172 #endif /* RT_USING_DEVICE_OPS */
173             /* no private */
174             part_dev->parent.user_data = RT_NULL;
175 
176             DEBUG_TRACE("add partition: %s\n", part->name);
177             rt_device_register(RT_DEVICE(part_dev), part->name,
178                                RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE);
179         }
180     }
181 
182     return RT_EOK;
183 }
184