• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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