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