1 /*
2 * Copyright (c) 2018 Intel Corporation.
3 * Copyright 2024 NXP
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <string.h>
9 #include <zephyr/types.h>
10 #include <zephyr/sys/__assert.h>
11 #include <zephyr/sys/util.h>
12 #include <zephyr/init.h>
13 #include <zephyr/storage/disk_access.h>
14 #include <errno.h>
15 #include <zephyr/device.h>
16
17 #define LOG_LEVEL CONFIG_DISK_LOG_LEVEL
18 #include <zephyr/logging/log.h>
19 LOG_MODULE_REGISTER(disk);
20
21 /* list of mounted file systems */
22 static sys_dlist_t disk_access_list = SYS_DLIST_STATIC_INIT(&disk_access_list);
23
24 /* lock to protect storage layer registration */
25 static struct k_spinlock lock;
26
disk_access_get_di(const char * name)27 struct disk_info *disk_access_get_di(const char *name)
28 {
29 struct disk_info *disk = NULL, *itr;
30 size_t name_len = strlen(name);
31 sys_dnode_t *node;
32 k_spinlock_key_t spinlock_key = k_spin_lock(&lock);
33
34 SYS_DLIST_FOR_EACH_NODE(&disk_access_list, node) {
35 itr = CONTAINER_OF(node, struct disk_info, node);
36
37 /*
38 * Move to next node if mount point length is
39 * shorter than longest_match match or if path
40 * name is shorter than the mount point name.
41 */
42 if (strlen(itr->name) != name_len) {
43 continue;
44 }
45
46 /* Check for disk name match */
47 if (strncmp(name, itr->name, name_len) == 0) {
48 disk = itr;
49 break;
50 }
51 }
52 k_spin_unlock(&lock, spinlock_key);
53
54 return disk;
55 }
56
disk_access_init(const char * pdrv)57 int disk_access_init(const char *pdrv)
58 {
59 struct disk_info *disk = disk_access_get_di(pdrv);
60 int rc = -EINVAL;
61
62 if ((disk != NULL) && (disk->refcnt == 0U)) {
63 /* Disk has not been initialized, start it */
64 if ((disk->ops != NULL) && (disk->ops->init != NULL)) {
65 rc = disk->ops->init(disk);
66 if (rc == 0) {
67 /* Increment reference count */
68 disk->refcnt++;
69 }
70 }
71 } else if ((disk != NULL) && (disk->refcnt < UINT16_MAX)) {
72 /* Disk reference count is nonzero, simply increment it */
73 disk->refcnt++;
74 rc = 0;
75 }
76
77 return rc;
78 }
79
disk_access_status(const char * pdrv)80 int disk_access_status(const char *pdrv)
81 {
82 struct disk_info *disk = disk_access_get_di(pdrv);
83 int rc = -EINVAL;
84
85 if ((disk != NULL) && (disk->ops != NULL) &&
86 (disk->ops->status != NULL)) {
87 rc = disk->ops->status(disk);
88 }
89
90 return rc;
91 }
92
disk_access_read(const char * pdrv,uint8_t * data_buf,uint32_t start_sector,uint32_t num_sector)93 int disk_access_read(const char *pdrv, uint8_t *data_buf,
94 uint32_t start_sector, uint32_t num_sector)
95 {
96 struct disk_info *disk = disk_access_get_di(pdrv);
97 int rc = -EINVAL;
98
99 if ((disk != NULL) && (disk->ops != NULL) &&
100 (disk->ops->read != NULL)) {
101 rc = disk->ops->read(disk, data_buf, start_sector, num_sector);
102 }
103
104 return rc;
105 }
106
disk_access_write(const char * pdrv,const uint8_t * data_buf,uint32_t start_sector,uint32_t num_sector)107 int disk_access_write(const char *pdrv, const uint8_t *data_buf,
108 uint32_t start_sector, uint32_t num_sector)
109 {
110 struct disk_info *disk = disk_access_get_di(pdrv);
111 int rc = -EINVAL;
112
113 if ((disk != NULL) && (disk->ops != NULL) &&
114 (disk->ops->write != NULL)) {
115 rc = disk->ops->write(disk, data_buf, start_sector, num_sector);
116 }
117
118 return rc;
119 }
120
disk_access_erase(const char * pdrv,uint32_t start_sector,uint32_t num_sector,enum disk_access_erase_type erase_type)121 int disk_access_erase(const char *pdrv, uint32_t start_sector, uint32_t num_sector,
122 enum disk_access_erase_type erase_type)
123 {
124 struct disk_info *disk = disk_access_get_di(pdrv);
125 uint32_t erase_sector_size;
126 int rc = -EINVAL;
127
128 /* Only support physical erase for now.
129 * This parameter is not passed through to the underlying disk to leave the design
130 * space open for future erase types (Other erase types may be dedicated functions).
131 */
132 if (erase_type != DISK_ACCESS_ERASE_PHYSICAL) {
133 return -EINVAL;
134 }
135
136 /* Validate sector sizes, if underlying driver exposes a way to query it */
137 if (disk_access_ioctl(pdrv, DISK_IOCTL_GET_ERASE_BLOCK_SZ, &erase_sector_size) == 0) {
138 /* Alignment check on both start and range of erase request */
139 if ((start_sector % erase_sector_size) || (num_sector % erase_sector_size)) {
140 return -EINVAL;
141 }
142 }
143
144 if ((disk != NULL) && (disk->ops != NULL) && (disk->ops->erase != NULL)) {
145 rc = disk->ops->erase(disk, start_sector, num_sector);
146 }
147
148 return rc;
149 }
150
disk_access_ioctl(const char * pdrv,uint8_t cmd,void * buf)151 int disk_access_ioctl(const char *pdrv, uint8_t cmd, void *buf)
152 {
153 struct disk_info *disk = disk_access_get_di(pdrv);
154 int rc = -EINVAL;
155
156 if ((disk != NULL) && (disk->ops != NULL) &&
157 (disk->ops->ioctl != NULL)) {
158 switch (cmd) {
159 case DISK_IOCTL_CTRL_INIT:
160 if (disk->refcnt == 0U) {
161 rc = disk->ops->ioctl(disk, cmd, buf);
162 if (rc == 0) {
163 disk->refcnt++;
164 }
165 } else if (disk->refcnt < UINT16_MAX) {
166 disk->refcnt++;
167 rc = 0;
168 } else {
169 LOG_ERR("Disk reference count at max value");
170 }
171 break;
172 case DISK_IOCTL_CTRL_DEINIT:
173 if ((buf != NULL) && (*((bool *)buf))) {
174 /* Force deinit disk */
175 disk->refcnt = 0U;
176 disk->ops->ioctl(disk, cmd, buf);
177 rc = 0;
178 } else if (disk->refcnt == 1U) {
179 rc = disk->ops->ioctl(disk, cmd, buf);
180 if (rc == 0) {
181 disk->refcnt--;
182 }
183 } else if (disk->refcnt > 0) {
184 disk->refcnt--;
185 rc = 0;
186 } else {
187 LOG_WRN("Disk is already deinitialized");
188 }
189 break;
190 default:
191 rc = disk->ops->ioctl(disk, cmd, buf);
192 }
193 }
194
195 return rc;
196 }
197
disk_access_register(struct disk_info * disk)198 int disk_access_register(struct disk_info *disk)
199 {
200 k_spinlock_key_t spinlock_key;
201
202 if ((disk == NULL) || (disk->name == NULL)) {
203 LOG_ERR("invalid disk interface!!");
204 return -EINVAL;
205 }
206
207 if (disk_access_get_di(disk->name) != NULL) {
208 LOG_ERR("disk interface already registered!!");
209 return -EINVAL;
210 }
211
212 /* Initialize reference count to zero */
213 disk->refcnt = 0U;
214
215 spinlock_key = k_spin_lock(&lock);
216 /* append to the disk list */
217 sys_dlist_append(&disk_access_list, &disk->node);
218 LOG_DBG("disk interface(%s) registered", disk->name);
219 k_spin_unlock(&lock, spinlock_key);
220 return 0;
221 }
222
disk_access_unregister(struct disk_info * disk)223 int disk_access_unregister(struct disk_info *disk)
224 {
225 k_spinlock_key_t spinlock_key;
226
227 if ((disk == NULL) || (disk->name == NULL)) {
228 LOG_ERR("invalid disk interface!!");
229 return -EINVAL;
230 }
231
232 if (disk_access_get_di(disk->name) == NULL) {
233 LOG_ERR("disk interface not registered!!");
234 return -EINVAL;
235 }
236
237 spinlock_key = k_spin_lock(&lock);
238 /* remove disk node from the list */
239 sys_dlist_remove(&disk->node);
240 k_spin_unlock(&lock, spinlock_key);
241 LOG_DBG("disk interface(%s) unregistered", disk->name);
242 return 0;
243 }
244