1 /*
2 *
3 * SPDX-License-Identifier: GPL-2.0
4 *
5 * Copyright (C) 2011-2018 ARM or its affiliates
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20 #include <linux/kernel.h>
21 #include <linux/slab.h>
22 #include <linux/mm.h>
23 #include <linux/fs.h>
24 #include <linux/miscdevice.h>
25 #include <linux/kfifo.h>
26 #include <linux/sched.h>
27 #include <linux/wait.h>
28 #include "system_chardev.h"
29 #include "acamera_logger.h"
30
31 #define SYSTEM_CHARDEV_FIFO_SIZE 4096
32 #define SYSTEM_CHARDEV_NAME "ac_isp"
33
34 struct isp_dev_context {
35 uint8_t dev_inited;
36 int dev_minor_id;
37 char *dev_name;
38 int dev_opened;
39
40 struct miscdevice isp_dev;
41 struct mutex fops_lock;
42
43 struct kfifo isp_kfifo_in;
44 struct kfifo isp_kfifo_out;
45
46 wait_queue_head_t kfifo_in_queue;
47 wait_queue_head_t kfifo_out_queue;
48 };
49
50 /* First item for ACT Control, second for user-FW */
51 static struct isp_dev_context isp_dev_ctx;
52
isp_fops_open(struct inode * inode,struct file * f)53 static int isp_fops_open( struct inode *inode, struct file *f )
54 {
55 int rc;
56 struct isp_dev_context *p_ctx = NULL;
57 int minor = iminor( inode );
58
59 if ( !isp_dev_ctx.dev_inited ) {
60 LOG( LOG_ERR, "dev is not inited, failed to open." );
61 return -ERESTARTSYS;
62 }
63
64 if ( minor == isp_dev_ctx.dev_minor_id )
65 p_ctx = &isp_dev_ctx;
66
67 if ( !p_ctx ) {
68 LOG( LOG_CRIT, "Fatal error, isp dev contexts is wrong, contents dump:" );
69 LOG( LOG_CRIT, " minor_id: %d, name: %s.", isp_dev_ctx.dev_minor_id, isp_dev_ctx.dev_name );
70
71 return -ERESTARTSYS;
72 }
73
74 rc = mutex_lock_interruptible( &p_ctx->fops_lock );
75 if ( rc ) {
76 LOG( LOG_ERR, "Error: lock failed of dev: %s.", p_ctx->dev_name );
77 goto lock_failure;
78 }
79
80 if ( p_ctx->dev_opened ) {
81 LOG( LOG_ERR, "open(%s) failed, already opened.", p_ctx->dev_name );
82 rc = -EBUSY;
83 } else {
84 p_ctx->dev_opened = 1;
85 rc = 0;
86 f->private_data = p_ctx;
87 }
88
89 mutex_unlock( &p_ctx->fops_lock );
90
91 lock_failure:
92 return rc;
93 }
94
isp_fops_release(struct inode * inode,struct file * f)95 static int isp_fops_release( struct inode *inode, struct file *f )
96 {
97 int rc;
98 struct isp_dev_context *p_ctx = (struct isp_dev_context *)f->private_data;
99
100 if ( p_ctx != &isp_dev_ctx ) {
101 LOG( LOG_ERR, "Inalid paramter: %p.", p_ctx );
102 return -EINVAL;
103 }
104
105 rc = mutex_lock_interruptible( &p_ctx->fops_lock );
106 if ( rc ) {
107 LOG( LOG_ERR, "Error: lock failed of dev: %s.", p_ctx->dev_name );
108 return rc;
109 }
110
111 if ( p_ctx->dev_opened ) {
112 p_ctx->dev_opened = 0;
113 f->private_data = NULL;
114 kfifo_reset( &p_ctx->isp_kfifo_in );
115 kfifo_reset( &p_ctx->isp_kfifo_out );
116 } else {
117 LOG( LOG_CRIT, "Fatal error: wrong state of dev: %s, dev_opened: %d.", p_ctx->dev_name, p_ctx->dev_opened );
118 rc = -EINVAL;
119 }
120
121 mutex_unlock( &p_ctx->fops_lock );
122
123 return 0;
124 }
125
isp_fops_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)126 static ssize_t isp_fops_write( struct file *file, const char __user *buf, size_t count, loff_t *ppos )
127 {
128 int rc;
129 unsigned int copied;
130 struct isp_dev_context *p_ctx = (struct isp_dev_context *)file->private_data;
131
132 /* isp_dev_uf device only support read at the moment */
133 if ( p_ctx != &isp_dev_ctx ) {
134 LOG( LOG_ERR, "Inalid paramter: %p.", p_ctx );
135 return -EINVAL;
136 }
137
138 if ( mutex_lock_interruptible( &p_ctx->fops_lock ) ) {
139 LOG( LOG_CRIT, "Fatal error: access lock failed." );
140 return -ERESTARTSYS;
141 }
142
143 rc = kfifo_from_user( &p_ctx->isp_kfifo_in, buf, count, &copied );
144
145 /* awake any reader */
146 wake_up_interruptible( &p_ctx->kfifo_in_queue );
147
148 LOG( LOG_DEBUG, "wake up reader." );
149
150 mutex_unlock( &p_ctx->fops_lock );
151
152 return rc ? rc : copied;
153 }
154
isp_fops_read(struct file * file,char __user * buf,size_t count,loff_t * ppos)155 static ssize_t isp_fops_read( struct file *file, char __user *buf, size_t count, loff_t *ppos )
156 {
157 int rc;
158 unsigned int copied;
159 struct isp_dev_context *p_ctx = (struct isp_dev_context *)file->private_data;
160
161 if ( p_ctx != &isp_dev_ctx ) {
162 LOG( LOG_ERR, "Inalid paramter: %p.", p_ctx );
163 return -EINVAL;
164 }
165
166 if ( mutex_lock_interruptible( &p_ctx->fops_lock ) ) {
167 LOG( LOG_CRIT, "Fatal error: access lock failed." );
168 return -ERESTARTSYS;
169 }
170
171 if ( kfifo_is_empty( &p_ctx->isp_kfifo_out ) ) {
172 long time_out_in_jiffies = 1; /* jiffies is depend on HW, in x86 Ubuntu, it's 4 ms, 1 is 4ms. */
173 mutex_unlock( &p_ctx->fops_lock ); /* unlock before we return or go sleeping */
174
175 /* return if it's a non-blocking reading */
176 if ( file->f_flags & O_NONBLOCK )
177 return -EAGAIN;
178
179 /* wait for the event */
180 //LOG( LOG_DEBUG, "input FIFO is empty, wait for data, timeout_in_jiffies: %ld, HZ: %d.", time_out_in_jiffies, HZ );
181 rc = wait_event_interruptible_timeout( p_ctx->kfifo_out_queue, !kfifo_is_empty( &p_ctx->isp_kfifo_out ), time_out_in_jiffies );
182
183 //LOG( LOG_DEBUG, "data is coming or timeout, kfifo_out size: %u, rc: %d.", kfifo_len( &p_ctx->isp_kfifo_out ), rc );
184
185 /* after wake up, we need to re-gain the mutex */
186 if ( mutex_lock_interruptible( &p_ctx->fops_lock ) ) {
187 LOG( LOG_CRIT, "Fatal error: access lock failed." );
188 return -ERESTARTSYS;
189 }
190 }
191
192 rc = kfifo_to_user( &p_ctx->isp_kfifo_out, buf, count, &copied );
193 mutex_unlock( &p_ctx->fops_lock );
194
195 return rc ? rc : copied;
196 }
197
198 static struct file_operations isp_fops = {
199 .owner = THIS_MODULE,
200 .open = isp_fops_open,
201 .release = isp_fops_release,
202 .read = isp_fops_read,
203 .write = isp_fops_write,
204 .llseek = noop_llseek,
205 };
206
isp_dev_context_init(struct isp_dev_context * p_ctx)207 static int isp_dev_context_init( struct isp_dev_context *p_ctx )
208 {
209 int rc;
210
211 p_ctx->isp_dev.minor = MISC_DYNAMIC_MINOR;
212 p_ctx->isp_dev.fops = &isp_fops;
213
214 rc = misc_register( &p_ctx->isp_dev );
215 if ( rc ) {
216 LOG( LOG_ERR, "Error: register ISP device failed, ret: %d.", rc );
217 return rc;
218 }
219
220 p_ctx->dev_minor_id = p_ctx->isp_dev.minor;
221
222 rc = kfifo_alloc( &p_ctx->isp_kfifo_in, SYSTEM_CHARDEV_FIFO_SIZE, GFP_KERNEL );
223 if ( rc ) {
224 LOG( LOG_ERR, "Error: kfifo_in alloc failed, ret: %d.", rc );
225 goto failed_kfifo_in_alloc;
226 }
227
228 rc = kfifo_alloc( &p_ctx->isp_kfifo_out, SYSTEM_CHARDEV_FIFO_SIZE, GFP_KERNEL );
229 if ( rc ) {
230 LOG( LOG_ERR, "Error: kfifo_out alloc failed, ret: %d.", rc );
231 goto failed_kfifo_out_alloc;
232 }
233
234 mutex_init( &p_ctx->fops_lock );
235 init_waitqueue_head( &p_ctx->kfifo_in_queue );
236 init_waitqueue_head( &p_ctx->kfifo_out_queue );
237
238 p_ctx->dev_inited = 1;
239
240 LOG( LOG_INFO, "isp_dev_context(%s) init OK.", p_ctx->dev_name );
241
242 return 0;
243
244 failed_kfifo_out_alloc:
245 kfifo_free( &p_ctx->isp_kfifo_in );
246 failed_kfifo_in_alloc:
247 misc_deregister( &p_ctx->isp_dev );
248
249 return rc;
250 }
251
system_chardev_init(void)252 int system_chardev_init( void )
253 {
254 int rc;
255
256 struct isp_dev_context *p_ctx0 = NULL;
257
258 LOG( LOG_INFO, "system init" );
259
260 p_ctx0 = &isp_dev_ctx;
261 memset( p_ctx0, 0, sizeof( *p_ctx0 ) );
262 p_ctx0->isp_dev.name = SYSTEM_CHARDEV_NAME;
263 p_ctx0->dev_name = SYSTEM_CHARDEV_NAME;
264 rc = isp_dev_context_init( p_ctx0 );
265 if ( rc ) {
266 LOG( LOG_ERR, "Error: isp_dev_context_init failed for dev: %s.", p_ctx0->isp_dev.name );
267 return rc;
268 }
269
270 return 0;
271 }
272
273
system_chardev_read(char * data,int size)274 int system_chardev_read( char *data, int size )
275 {
276 int rc;
277
278 if ( !isp_dev_ctx.dev_inited ) {
279 LOG( LOG_ERR, "dev is not inited, failed to read." );
280 return -1;
281 }
282
283 mutex_lock( &isp_dev_ctx.fops_lock );
284
285 if ( kfifo_is_empty( &isp_dev_ctx.isp_kfifo_in ) ) {
286 long time_out_in_jiffies = 2; /* jiffies is depend on HW, in x86 Ubuntu, it's 4 ms, 2 is 8ms. */
287 mutex_unlock( &isp_dev_ctx.fops_lock ); /* unlock before we return or go sleeping */
288
289 /* wait for the event */
290 //LOG( LOG_DEBUG, "input FIFO is empty, wait for data, timeout_in_jiffies: %ld, HZ: %d.", time_out_in_jiffies, HZ );
291 rc = wait_event_interruptible_timeout( isp_dev_ctx.kfifo_in_queue, !kfifo_is_empty( &isp_dev_ctx.isp_kfifo_in ), time_out_in_jiffies );
292
293 //LOG( LOG_DEBUG, "data is coming or timeout, kfifo_in size: %u, rc: %d.", kfifo_len( &isp_dev_ctx.isp_kfifo_in ), rc );
294
295 /* after wake up, we need to re-gain the mutex */
296 mutex_lock( &isp_dev_ctx.fops_lock );
297 }
298
299 rc = kfifo_out( &isp_dev_ctx.isp_kfifo_in, data, size );
300
301 mutex_unlock( &isp_dev_ctx.fops_lock );
302 return rc;
303 }
304
system_chardev_write(const char * data,int size)305 int system_chardev_write( const char *data, int size )
306 {
307 int rc;
308
309 if ( !isp_dev_ctx.dev_inited ) {
310 LOG( LOG_ERR, "dev is not inited, failed to write." );
311 return -1;
312 }
313
314 mutex_lock( &isp_dev_ctx.fops_lock );
315
316 rc = kfifo_in( &isp_dev_ctx.isp_kfifo_out, data, size );
317
318 /* awake any reader */
319 wake_up_interruptible( &isp_dev_ctx.kfifo_out_queue );
320 LOG( LOG_DEBUG, "wake up reader who wait on kfifo out." );
321
322 mutex_unlock( &isp_dev_ctx.fops_lock );
323 return rc;
324 }
325
system_chardev_destroy(void)326 int system_chardev_destroy( void )
327 {
328 if ( isp_dev_ctx.dev_inited ) {
329 kfifo_free( &isp_dev_ctx.isp_kfifo_in );
330
331 kfifo_free( &isp_dev_ctx.isp_kfifo_out );
332
333 misc_deregister( &isp_dev_ctx.isp_dev );
334
335 LOG( LOG_INFO, "misc_deregister dev: %s.", isp_dev_ctx.dev_name );
336 } else {
337 LOG( LOG_INFO, "dev not inited, do nothing." );
338 }
339
340 return 0;
341 }
342