1 /****************************************************************************
2 * drivers/bch/bchdev_driver.c
3 *
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements. See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership. The
7 * ASF licenses this file to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance with the
9 * License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16 * License for the specific language governing permissions and limitations
17 * under the License.
18 *
19 ****************************************************************************/
20
21 /****************************************************************************
22 * Included Files
23 ****************************************************************************/
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/ioctl.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <fcntl.h>
31 #include <sched.h>
32 #include <errno.h>
33 #include <poll.h>
34 #include <assert.h>
35 #include "bch.h"
36
37 /****************************************************************************
38 * Pre-processor Definitions
39 ****************************************************************************/
40
41 /****************************************************************************
42 * Private Function Prototypes
43 ****************************************************************************/
44
45 static int bch_open(struct file *filep);
46 static int bch_close(struct file *filep);
47 static off_t bch_seek(struct file *filep, off_t offset, int whence);
48 static ssize_t bch_read(struct file *filep, char *buffer,
49 size_t buflen);
50 static ssize_t bch_write(struct file *filep, const char *buffer,
51 size_t buflen);
52 static int bch_ioctl(struct file *filep, int cmd,
53 unsigned long arg);
54 #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
55 static int bch_unlink(struct Vnode *vnode);
56 #endif
57
58 /****************************************************************************
59 * Public Data
60 ****************************************************************************/
61
62 const struct file_operations_vfs bch_fops =
63 {
64 .open = bch_open, /* open */
65 .close = bch_close, /* close */
66 .read = bch_read, /* read */
67 .write = bch_write, /* write */
68 .seek = bch_seek, /* seek */
69 .ioctl = bch_ioctl, /* ioctl */
70 .unlink = bch_unlink, /* unlink */
71 };
72
73 /****************************************************************************
74 * Private Functions
75 ****************************************************************************/
76
77 /****************************************************************************
78 * Name: bch_open
79 *
80 * Description: Open the block device
81 *
82 ****************************************************************************/
83
bch_open(struct file * filep)84 static int bch_open(struct file *filep)
85 {
86 struct Vnode *vnode = filep->f_vnode;
87 struct bchlib_s *bch;
88 int ret = OK;
89
90 bch = (struct bchlib_s *)((struct drv_data *)vnode->data)->priv;
91
92 /* Increment the reference count */
93
94 bchlib_semtake(bch);
95 if (bch->refs == MAX_OPENCNT)
96 {
97 ret = -EMFILE;
98 }
99 else
100 {
101 bch->refs++;
102 }
103
104 bchlib_semgive(bch);
105 return ret;
106 }
107
108 /****************************************************************************
109 * Name: bch_close
110 *
111 * Description: close the block device
112 *
113 ****************************************************************************/
114
bch_close(struct file * filep)115 static int bch_close(struct file *filep)
116 {
117 struct Vnode *vnode = filep->f_vnode;
118 struct bchlib_s *bch;
119 int ret = OK;
120
121 bch = (struct bchlib_s *)((struct drv_data *)vnode->data)->priv;
122
123 /* Flush any dirty pages remaining in the cache */
124
125 bchlib_semtake(bch);
126 (void)bchlib_flushsector(bch);
127
128 /* Decrement the reference count (I don't use bchlib_decref() because I
129 * want the entire close operation to be atomic wrt other driver
130 * operations.
131 */
132
133 if (bch->refs == 0)
134 {
135 ret = -EIO;
136 }
137 else
138 {
139 bch->refs--;
140
141 /* If the reference count decremented to zero AND if the character
142 * driver has been unlinked, then teardown the BCH device now.
143 */
144
145 if (bch->refs == 0 && bch->unlinked)
146 {
147 /* Tear the driver down now. */
148
149 ret = bchlib_teardown((void *)bch);
150
151 /* bchlib_teardown() would only fail if there are outstanding
152 * references on the device. Since we know that is not true, it
153 * should not fail at all.
154 */
155
156 DEBUGASSERT(ret >= 0);
157 if (ret >= 0)
158 {
159 /* Return without releasing the stale semaphore */
160
161 return OK;
162 }
163 }
164 }
165
166 bchlib_semgive(bch);
167 return ret;
168 }
169
170 /****************************************************************************
171 * Name: bch_seek
172 ****************************************************************************/
173
bch_seek(struct file * filep,off_t offset,int whence)174 static off_t bch_seek(struct file *filep, off_t offset, int whence)
175 {
176 struct Vnode *vnode = filep->f_vnode;
177 struct bchlib_s *bch;
178 loff_t newpos;
179 int ret;
180
181 bch = (struct bchlib_s *)((struct drv_data *)vnode->data)->priv;
182 bchlib_semtake(bch);
183
184 /* Determine the new, requested file position */
185
186 switch (whence)
187 {
188 case SEEK_CUR:
189 newpos = filep->f_pos + offset;
190 break;
191
192 case SEEK_SET:
193 newpos = offset;
194 break;
195
196 case SEEK_END:
197 newpos = (loff_t)bch->sectsize * bch->nsectors + offset;
198 break;
199
200 default:
201 /* Return EINVAL if the whence argument is invalid */
202
203 bchlib_semgive(bch);
204 return -EINVAL;
205 }
206
207 /* Opengroup.org:
208 *
209 * "The lseek() function shall allow the file offset to be set beyond the end
210 * of the existing data in the file. If data is later written at this point,
211 * subsequent reads of data in the gap shall return bytes with the value 0
212 * until data is actually written into the gap."
213 *
214 * We can conform to the first part, but not the second. But return EINVAL if
215 *
216 * "...the resulting file offset would be negative for a regular file, block
217 * special file, or directory."
218 */
219
220 if (newpos >= 0)
221 {
222 filep->f_pos = newpos;
223 ret = newpos;
224 }
225 else
226 {
227 ret = -EINVAL;
228 }
229
230 bchlib_semgive(bch);
231 return ret;
232 }
233
234 /****************************************************************************
235 * Name: bch_read
236 ****************************************************************************/
237
bch_read(struct file * filep,char * buffer,size_t len)238 static ssize_t bch_read(struct file *filep, char *buffer, size_t len)
239 {
240 struct Vnode *vnode = filep->f_vnode;
241 struct bchlib_s *bch;
242 int ret;
243
244 bch = (struct bchlib_s *)((struct drv_data *)vnode->data)->priv;
245
246 bchlib_semtake(bch);
247 ret = bchlib_read(bch, buffer, filep->f_pos, len);
248 if (ret > 0)
249 {
250 filep->f_pos += len;
251 }
252
253 bchlib_semgive(bch);
254 return ret;
255 }
256
257 /****************************************************************************
258 * Name: bch_write
259 ****************************************************************************/
260
bch_write(struct file * filep,const char * buffer,size_t len)261 static ssize_t bch_write(struct file *filep, const char *buffer, size_t len)
262 {
263 struct Vnode *vnode = filep->f_vnode;
264 struct bchlib_s *bch;
265 int ret = -EACCES;
266
267 bch = (struct bchlib_s *)((struct drv_data *)vnode->data)->priv;
268
269 if (!bch->readonly)
270 {
271 bchlib_semtake(bch);
272 ret = bchlib_write(bch, buffer, filep->f_pos, len);
273 if (ret > 0)
274 {
275 filep->f_pos += len;
276 }
277
278 bchlib_semgive(bch);
279 }
280
281 return ret;
282 }
283
284 /****************************************************************************
285 * Name: bch_ioctl
286 *
287 * Description:
288 * Handle IOCTL commands
289 *
290 ****************************************************************************/
291
bch_ioctl(struct file * filep,int cmd,unsigned long arg)292 static int bch_ioctl(struct file *filep, int cmd, unsigned long arg)
293 {
294 struct Vnode *vnode = filep->f_vnode;
295 struct bchlib_s *bch;
296 struct block_operations *bop = NULL;
297 int ret = -ENOTTY;
298
299 bch = (struct bchlib_s *)((struct drv_data *)vnode->data)->priv;
300
301 /* Process the call according to the command */
302
303 switch (cmd)
304 {
305 /* This isa request to get the private data structure */
306
307 case DIOC_GETPRIV:
308 {
309 struct bchlib_s **bchr =
310 (struct bchlib_s **)((uintptr_t)arg);
311
312 bchlib_semtake(bch);
313 if (!bchr || bch->refs == MAX_OPENCNT)
314 {
315 ret = -EINVAL;
316 }
317 else
318 {
319 bch->refs++;
320 ret = LOS_CopyFromKernel(bchr, sizeof(char *), &bch, sizeof(char *));
321 if (ret)
322 {
323 ret = -EFAULT;
324 }
325 else
326 {
327 ret = OK;
328 }
329 }
330
331 bchlib_semgive(bch);
332 }
333 break;
334
335 /* Otherwise, pass the IOCTL command on to the contained block driver. */
336
337 default:
338 {
339 /* Does the block driver support the ioctl method? */
340
341 los_disk *disk = bch->disk;
342 if (disk == NULL)
343 {
344 ret = -1;
345 break;
346 }
347
348 if (pthread_mutex_lock(&disk->disk_mutex) != ENOERR)
349 {
350 PRINT_ERR("%s %d, mutex lock fail!\n", __FUNCTION__, __LINE__);
351 return -1;
352 }
353 if (disk->disk_status == STAT_INUSED)
354 {
355 bop = (struct block_operations *)(((struct drv_data *)vnode->data)->ops);
356 if (bop != NULL && bop->ioctl != NULL)
357 {
358 ret = bop->ioctl(bch->vnode, cmd, arg);
359 }
360 }
361
362 if (pthread_mutex_unlock(&disk->disk_mutex) != ENOERR)
363 {
364 PRINT_ERR("%s %d, mutex unlock fail!\n", __FUNCTION__, __LINE__);
365 return -1;
366 }
367 }
368 break;
369 }
370
371 return ret;
372 }
373
374 /****************************************************************************
375 * Name: bch_unlink
376 *
377 * Handle unlinking of the BCH device
378 *
379 ****************************************************************************/
380
bch_unlink(struct Vnode * vnode)381 int bch_unlink(struct Vnode *vnode)
382 {
383 struct bchlib_s *bch;
384 int ret = OK;
385
386 bch = (struct bchlib_s *)((struct drv_data *)vnode->data)->priv;
387
388 /* Get exclusive access to the BCH device */
389
390 bchlib_semtake(bch);
391
392 /* Indicate that the driver has been unlinked */
393
394 bch->unlinked = true;
395
396 /* If there are no open references to the drvier then teardown the BCH
397 * device now.
398 */
399
400 if (bch->refs == 0)
401 {
402 /* Tear the driver down now. */
403
404 ret = bchlib_teardown((void *)bch);
405
406 /* bchlib_teardown() would only fail if there are outstanding
407 * references on the device. Since we know that is not true, it
408 * should not fail at all.
409 */
410
411 DEBUGASSERT(ret >= 0);
412 if (ret >= 0)
413 {
414 /* Return without releasing the stale semaphore */
415
416 return OK;
417 }
418 }
419
420 bchlib_semgive(bch);
421 return ret;
422 }
423
424 /****************************************************************************
425 * Public Functions
426 ****************************************************************************/
427