1 /****************************************************************************
2 * drivers/bch/bchdev_driver.c
3 *
4 * Copyright (C) 2008-2009, 2014-2017 Gregory Nutt. All rights reserved.
5 * Author: Gregory Nutt <gnutt@nuttx.org>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name NuttX nor the names of its contributors may be
18 * used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 ****************************************************************************/
35
36 /****************************************************************************
37 * Included Files
38 ****************************************************************************/
39
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <sys/ioctl.h>
43 #include <unistd.h>
44 #include <string.h>
45 #include <fcntl.h>
46 #include <sched.h>
47 #include <errno.h>
48 #include <poll.h>
49 #include <assert.h>
50 #include "bch.h"
51
52 /****************************************************************************
53 * Pre-processor Definitions
54 ****************************************************************************/
55
56 /****************************************************************************
57 * Private Function Prototypes
58 ****************************************************************************/
59
60 static int bch_open(struct file *filep);
61 static int bch_close(struct file *filep);
62 static off_t bch_seek(struct file *filep, off_t offset, int whence);
63 static ssize_t bch_read(struct file *filep, char *buffer,
64 size_t buflen);
65 static ssize_t bch_write(struct file *filep, const char *buffer,
66 size_t buflen);
67 static int bch_ioctl(struct file *filep, int cmd,
68 unsigned long arg);
69 #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
70 static int bch_unlink(struct Vnode *vnode);
71 #endif
72
73 /****************************************************************************
74 * Public Data
75 ****************************************************************************/
76
77 const struct file_operations_vfs bch_fops =
78 {
79 .open = bch_open, /* open */
80 .close = bch_close, /* close */
81 .read = bch_read, /* read */
82 .write = bch_write, /* write */
83 .seek = bch_seek, /* seek */
84 .ioctl = bch_ioctl, /* ioctl */
85 .unlink = bch_unlink, /* unlink */
86 };
87
88 /****************************************************************************
89 * Private Functions
90 ****************************************************************************/
91
92 /****************************************************************************
93 * Name: bch_open
94 *
95 * Description: Open the block device
96 *
97 ****************************************************************************/
98
bch_open(struct file * filep)99 static int bch_open(struct file *filep)
100 {
101 struct Vnode *vnode = filep->f_vnode;
102 struct bchlib_s *bch;
103 int ret = OK;
104
105 bch = (struct bchlib_s *)((struct drv_data *)vnode->data)->priv;
106
107 /* Increment the reference count */
108
109 bchlib_semtake(bch);
110 if (bch->refs == MAX_OPENCNT)
111 {
112 ret = -EMFILE;
113 }
114 else
115 {
116 bch->refs++;
117 }
118
119 bchlib_semgive(bch);
120 return ret;
121 }
122
123 /****************************************************************************
124 * Name: bch_close
125 *
126 * Description: close the block device
127 *
128 ****************************************************************************/
129
bch_close(struct file * filep)130 static int bch_close(struct file *filep)
131 {
132 struct Vnode *vnode = filep->f_vnode;
133 struct bchlib_s *bch;
134 int ret = OK;
135
136 bch = (struct bchlib_s *)((struct drv_data *)vnode->data)->priv;
137
138 /* Flush any dirty pages remaining in the cache */
139
140 bchlib_semtake(bch);
141 (void)bchlib_flushsector(bch);
142
143 /* Decrement the reference count (I don't use bchlib_decref() because I
144 * want the entire close operation to be atomic wrt other driver
145 * operations.
146 */
147
148 if (bch->refs == 0)
149 {
150 ret = -EIO;
151 }
152 else
153 {
154 bch->refs--;
155
156 /* If the reference count decremented to zero AND if the character
157 * driver has been unlinked, then teardown the BCH device now.
158 */
159
160 if (bch->refs == 0 && bch->unlinked)
161 {
162 /* Tear the driver down now. */
163
164 ret = bchlib_teardown((void *)bch);
165
166 /* bchlib_teardown() would only fail if there are outstanding
167 * references on the device. Since we know that is not true, it
168 * should not fail at all.
169 */
170
171 DEBUGASSERT(ret >= 0);
172 if (ret >= 0)
173 {
174 /* Return without releasing the stale semaphore */
175
176 return OK;
177 }
178 }
179 }
180
181 bchlib_semgive(bch);
182 return ret;
183 }
184
185 /****************************************************************************
186 * Name: bch_seek
187 ****************************************************************************/
188
bch_seek(struct file * filep,off_t offset,int whence)189 static off_t bch_seek(struct file *filep, off_t offset, int whence)
190 {
191 struct Vnode *vnode = filep->f_vnode;
192 struct bchlib_s *bch;
193 loff_t newpos;
194 int ret;
195
196 bch = (struct bchlib_s *)((struct drv_data *)vnode->data)->priv;
197 bchlib_semtake(bch);
198
199 /* Determine the new, requested file position */
200
201 switch (whence)
202 {
203 case SEEK_CUR:
204 newpos = filep->f_pos + offset;
205 break;
206
207 case SEEK_SET:
208 newpos = offset;
209 break;
210
211 case SEEK_END:
212 newpos = (loff_t)bch->sectsize * bch->nsectors + offset;
213 break;
214
215 default:
216 /* Return EINVAL if the whence argument is invalid */
217
218 bchlib_semgive(bch);
219 return -EINVAL;
220 }
221
222 /* Opengroup.org:
223 *
224 * "The lseek() function shall allow the file offset to be set beyond the end
225 * of the existing data in the file. If data is later written at this point,
226 * subsequent reads of data in the gap shall return bytes with the value 0
227 * until data is actually written into the gap."
228 *
229 * We can conform to the first part, but not the second. But return EINVAL if
230 *
231 * "...the resulting file offset would be negative for a regular file, block
232 * special file, or directory."
233 */
234
235 if (newpos >= 0)
236 {
237 filep->f_pos = newpos;
238 ret = newpos;
239 }
240 else
241 {
242 ret = -EINVAL;
243 }
244
245 bchlib_semgive(bch);
246 return ret;
247 }
248
249 /****************************************************************************
250 * Name: bch_read
251 ****************************************************************************/
252
bch_read(struct file * filep,char * buffer,size_t len)253 static ssize_t bch_read(struct file *filep, char *buffer, size_t len)
254 {
255 struct Vnode *vnode = filep->f_vnode;
256 struct bchlib_s *bch;
257 int ret;
258
259 bch = (struct bchlib_s *)((struct drv_data *)vnode->data)->priv;
260
261 bchlib_semtake(bch);
262 ret = bchlib_read(bch, buffer, filep->f_pos, len);
263 if (ret > 0)
264 {
265 filep->f_pos += len;
266 }
267
268 bchlib_semgive(bch);
269 return ret;
270 }
271
272 /****************************************************************************
273 * Name: bch_write
274 ****************************************************************************/
275
bch_write(struct file * filep,const char * buffer,size_t len)276 static ssize_t bch_write(struct file *filep, const char *buffer, size_t len)
277 {
278 struct Vnode *vnode = filep->f_vnode;
279 struct bchlib_s *bch;
280 int ret = -EACCES;
281
282 bch = (struct bchlib_s *)((struct drv_data *)vnode->data)->priv;
283
284 if (!bch->readonly)
285 {
286 bchlib_semtake(bch);
287 ret = bchlib_write(bch, buffer, filep->f_pos, len);
288 if (ret > 0)
289 {
290 filep->f_pos += len;
291 }
292
293 bchlib_semgive(bch);
294 }
295
296 return ret;
297 }
298
299 /****************************************************************************
300 * Name: bch_ioctl
301 *
302 * Description:
303 * Handle IOCTL commands
304 *
305 ****************************************************************************/
306
bch_ioctl(struct file * filep,int cmd,unsigned long arg)307 static int bch_ioctl(struct file *filep, int cmd, unsigned long arg)
308 {
309 struct Vnode *vnode = filep->f_vnode;
310 struct bchlib_s *bch;
311 struct block_operations *bop = NULL;
312 int ret = -ENOTTY;
313
314 bch = (struct bchlib_s *)((struct drv_data *)vnode->data)->priv;
315
316 /* Process the call according to the command */
317
318 switch (cmd)
319 {
320 /* This isa request to get the private data structure */
321
322 case DIOC_GETPRIV:
323 {
324 struct bchlib_s **bchr =
325 (struct bchlib_s **)((uintptr_t)arg);
326
327 bchlib_semtake(bch);
328 if (!bchr || bch->refs == MAX_OPENCNT)
329 {
330 ret = -EINVAL;
331 }
332 else
333 {
334 bch->refs++;
335 ret = LOS_CopyFromKernel(bchr, sizeof(char *), &bch, sizeof(char *));
336 if (ret)
337 {
338 ret = -EFAULT;
339 }
340 else
341 {
342 ret = OK;
343 }
344 }
345
346 bchlib_semgive(bch);
347 }
348 break;
349
350 /* Otherwise, pass the IOCTL command on to the contained block driver. */
351
352 default:
353 {
354 /* Does the block driver support the ioctl method? */
355
356 los_disk *disk = bch->disk;
357 if (disk == NULL)
358 {
359 ret = -1;
360 break;
361 }
362
363 if (pthread_mutex_lock(&disk->disk_mutex) != ENOERR)
364 {
365 PRINT_ERR("%s %d, mutex lock fail!\n", __FUNCTION__, __LINE__);
366 return -1;
367 }
368 if (disk->disk_status == STAT_INUSED)
369 {
370 bop = (struct block_operations *)(((struct drv_data *)vnode->data)->ops);
371 if (bop != NULL && bop->ioctl != NULL)
372 {
373 ret = bop->ioctl(bch->vnode, cmd, arg);
374 }
375 }
376
377 if (pthread_mutex_unlock(&disk->disk_mutex) != ENOERR)
378 {
379 PRINT_ERR("%s %d, mutex unlock fail!\n", __FUNCTION__, __LINE__);
380 return -1;
381 }
382 }
383 break;
384 }
385
386 return ret;
387 }
388
389 /****************************************************************************
390 * Name: bch_unlink
391 *
392 * Handle unlinking of the BCH device
393 *
394 ****************************************************************************/
395
bch_unlink(struct Vnode * vnode)396 int bch_unlink(struct Vnode *vnode)
397 {
398 struct bchlib_s *bch;
399 int ret = OK;
400
401 bch = (struct bchlib_s *)((struct drv_data *)vnode->data)->priv;
402
403 /* Get exclusive access to the BCH device */
404
405 bchlib_semtake(bch);
406
407 /* Indicate that the driver has been unlinked */
408
409 bch->unlinked = true;
410
411 /* If there are no open references to the drvier then teardown the BCH
412 * device now.
413 */
414
415 if (bch->refs == 0)
416 {
417 /* Tear the driver down now. */
418
419 ret = bchlib_teardown((void *)bch);
420
421 /* bchlib_teardown() would only fail if there are outstanding
422 * references on the device. Since we know that is not true, it
423 * should not fail at all.
424 */
425
426 DEBUGASSERT(ret >= 0);
427 if (ret >= 0)
428 {
429 /* Return without releasing the stale semaphore */
430
431 return OK;
432 }
433 }
434
435 bchlib_semgive(bch);
436 return ret;
437 }
438
439 /****************************************************************************
440 * Public Functions
441 ****************************************************************************/
442