• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * linux-5.4/drivers/media/platform/sunxi-vin/vin-stat/vin_h3a.c
3  *
4  * Copyright (c) 2007-2017 Allwinnertech Co., Ltd.
5  *
6  * This software is licensed under the terms of the GNU General Public
7  * License version 2, as published by the Free Software Foundation, and
8  * may be copied, distributed, and modified under those terms.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  */
16 
17 #include <linux/slab.h>
18 #include <linux/uaccess.h>
19 
20 #include "vin_h3a.h"
21 
22 #include "../vin-isp/sunxi_isp.h"
23 #include "../vin-video/vin_video.h"
24 
__isp_stat_buf_find(struct isp_stat * stat,int look_empty)25 static struct ispstat_buffer *__isp_stat_buf_find(struct isp_stat *stat, int look_empty)
26 {
27 	struct ispstat_buffer *found = NULL;
28 	int i;
29 
30 	for (i = 0; i < stat->buf_cnt; i++) {
31 		struct ispstat_buffer *curr = &stat->buf[i];
32 
33 		/*
34 		 * Don't select the buffer which is being copied to
35 		 * userspace or used by the module.
36 		 */
37 		if (curr == stat->locked_buf || curr == stat->active_buf)
38 			continue;
39 
40 		/* Don't select uninitialised buffers if it's not required */
41 		if (!look_empty && curr->empty)
42 			continue;
43 
44 		if (curr->empty) {
45 			found = curr;
46 			break;
47 		}
48 
49 		if (!found ||
50 		    (s32)curr->frame_number - (s32)found->frame_number < 0)
51 			found = curr;
52 	}
53 
54 	return found;
55 }
56 
isp_stat_buf_find_oldest(struct isp_stat * stat)57 static inline struct ispstat_buffer *isp_stat_buf_find_oldest(struct isp_stat *stat)
58 {
59 	return __isp_stat_buf_find(stat, 0);
60 }
61 
isp_stat_buf_find_oldest_or_empty(struct isp_stat * stat)62 static inline struct ispstat_buffer *isp_stat_buf_find_oldest_or_empty(struct isp_stat *stat)
63 {
64 	return __isp_stat_buf_find(stat, 1);
65 }
66 
isp_stat_buf_queue(struct isp_stat * stat)67 static int isp_stat_buf_queue(struct isp_stat *stat)
68 {
69 	if (!stat->active_buf)
70 		return STAT_NO_BUF;
71 
72 	stat->active_buf->buf_size = stat->buf_size;
73 
74 	stat->active_buf->frame_number = stat->frame_number;
75 	stat->active_buf->empty = 0;
76 	stat->active_buf = NULL;
77 
78 	return STAT_BUF_DONE;
79 }
80 
81 /* Get next free buffer to write the statistics to and mark it active. */
isp_stat_buf_next(struct isp_stat * stat)82 static void isp_stat_buf_next(struct isp_stat *stat)
83 {
84 	if (unlikely(stat->active_buf)) {
85 		/* Overwriting unused active buffer */
86 		vin_log(VIN_LOG_STAT, "%s: new buffer requested without queuing active one.\n",	stat->sd.name);
87 	} else {
88 		stat->active_buf = isp_stat_buf_find_oldest_or_empty(stat);
89 	}
90 }
91 
isp_stat_buf_release(struct isp_stat * stat)92 static void isp_stat_buf_release(struct isp_stat *stat)
93 {
94 	unsigned long flags;
95 
96 	spin_lock_irqsave(&stat->isp->slock, flags);
97 	stat->locked_buf = NULL;
98 	spin_unlock_irqrestore(&stat->isp->slock, flags);
99 }
100 
101 /* Get buffer to userspace. */
isp_stat_buf_get(struct isp_stat * stat,struct vin_isp_stat_data * data)102 static struct ispstat_buffer *isp_stat_buf_get(struct isp_stat *stat, struct vin_isp_stat_data *data)
103 {
104 	int rval = 0;
105 	unsigned long flags;
106 	struct ispstat_buffer *buf;
107 
108 	spin_lock_irqsave(&stat->isp->slock, flags);
109 
110 	while (1) {
111 		buf = isp_stat_buf_find_oldest(stat);
112 		if (!buf) {
113 			spin_unlock_irqrestore(&stat->isp->slock, flags);
114 			vin_log(VIN_LOG_STAT, "%s: cannot find a buffer.\n", stat->sd.name);
115 			return ERR_PTR(-EBUSY);
116 		}
117 		break;
118 	}
119 
120 	stat->locked_buf = buf;
121 
122 	spin_unlock_irqrestore(&stat->isp->slock, flags);
123 	if (NULL != data) {
124 		if (buf->buf_size > data->buf_size) {
125 			vin_warn("%s: userspace's buffer size is not enough.\n", stat->sd.name);
126 			isp_stat_buf_release(stat);
127 			return ERR_PTR(-EINVAL);
128 		}
129 		rval = copy_to_user(data->buf, buf->virt_addr, buf->buf_size);
130 		if (rval) {
131 			vin_warn("%s: failed copying %d bytes of stat data\n", stat->sd.name, rval);
132 			buf = ERR_PTR(-EFAULT);
133 			isp_stat_buf_release(stat);
134 		}
135 	}
136 	return buf;
137 }
138 
isp_stat_bufs_free(struct isp_stat * stat)139 static void isp_stat_bufs_free(struct isp_stat *stat)
140 {
141 	int i;
142 
143 	for (i = 1; i < stat->buf_cnt; i++) {
144 		struct ispstat_buffer *buf = &stat->buf[i];
145 		struct vin_mm *mm = &stat->ion_man[i];
146 		mm->size = stat->buf_size;
147 
148 		if (!buf->virt_addr)
149 			continue;
150 
151 		mm->vir_addr = buf->virt_addr;
152 		mm->dma_addr = buf->dma_addr;
153 		os_mem_free(&stat->isp->pdev->dev, mm);
154 
155 		buf->dma_addr = NULL;
156 		buf->virt_addr = NULL;
157 		buf->empty = 1;
158 	}
159 
160 	vin_log(VIN_LOG_STAT, "%s: all buffers were freed.\n", stat->sd.name);
161 
162 	stat->buf_size = 0;
163 	stat->active_buf = NULL;
164 }
165 
isp_stat_bufs_alloc(struct isp_stat * stat,u32 size,u32 count)166 static int isp_stat_bufs_alloc(struct isp_stat *stat, u32 size, u32 count)
167 {
168 	unsigned long flags;
169 	int i;
170 
171 	spin_lock_irqsave(&stat->isp->slock, flags);
172 
173 	BUG_ON(stat->locked_buf != NULL);
174 
175 	for (i = 1; i < stat->buf_cnt; i++)
176 		stat->buf[i].empty = 1;
177 
178 	/* Are the old buffers big enough? */
179 	if ((stat->buf_size >= size) && (stat->buf_cnt == count)) {
180 		spin_unlock_irqrestore(&stat->isp->slock, flags);
181 		vin_log(VIN_LOG_STAT, "%s: old stat buffers are enough.\n", stat->sd.name);
182 		return 0;
183 	}
184 
185 	spin_unlock_irqrestore(&stat->isp->slock, flags);
186 
187 	isp_stat_bufs_free(stat);
188 
189 	stat->buf_size = size;
190 	stat->buf_cnt = count;
191 
192 	for (i = 1; i < stat->buf_cnt; i++) {
193 		struct ispstat_buffer *buf = &stat->buf[i];
194 		struct vin_mm *mm = &stat->ion_man[i];
195 		mm->size = size;
196 		if (!os_mem_alloc(&stat->isp->pdev->dev, mm)) {
197 			buf->virt_addr = mm->vir_addr;
198 			buf->dma_addr = mm->dma_addr;
199 		}
200 		if (!buf->virt_addr || !buf->dma_addr) {
201 			vin_err("%s: Can't acquire memory for DMA buffer %d\n",	stat->sd.name, i);
202 			isp_stat_bufs_free(stat);
203 			return -ENOMEM;
204 		}
205 		buf->empty = 1;
206 	}
207 	return 0;
208 }
209 
isp_stat_queue_event(struct isp_stat * stat,int err)210 static void isp_stat_queue_event(struct isp_stat *stat, int err)
211 {
212 	struct video_device *vdev = stat->sd.devnode;
213 	struct v4l2_event event;
214 	struct vin_isp_stat_event_status *status = (void *)event.u.data;
215 
216 	memset(&event, 0, sizeof(event));
217 	if (!err)
218 		status->frame_number = stat->frame_number;
219 	else
220 		status->buf_err = 1;
221 
222 	event.type = stat->event_type;
223 	v4l2_event_queue(vdev, &event);
224 }
225 
isp_stat_request_statistics(struct isp_stat * stat,struct vin_isp_stat_data * data)226 int isp_stat_request_statistics(struct isp_stat *stat,
227 				     struct vin_isp_stat_data *data)
228 {
229 	struct ispstat_buffer *buf;
230 
231 	if (stat->state != ISPSTAT_ENABLED) {
232 		vin_log(VIN_LOG_STAT, "%s: engine not enabled.\n", stat->sd.name);
233 		return -EINVAL;
234 	}
235 	vin_log(VIN_LOG_STAT, "user wants to request statistics.\n");
236 
237 	mutex_lock(&stat->ioctl_lock);
238 	buf = isp_stat_buf_get(stat, data);
239 	if (IS_ERR(buf)) {
240 		mutex_unlock(&stat->ioctl_lock);
241 		return PTR_ERR(buf);
242 	}
243 
244 	data->frame_number = buf->frame_number;
245 	data->buf_size = buf->buf_size;
246 
247 	buf->empty = 1;
248 	isp_stat_buf_release(stat);
249 	mutex_unlock(&stat->ioctl_lock);
250 
251 	return 0;
252 }
253 
isp_stat_config(struct isp_stat * stat,void * new_conf)254 int isp_stat_config(struct isp_stat *stat, void *new_conf)
255 {
256 	int ret;
257 	u32 count;
258 	struct vin_isp_h3a_config *user_cfg = new_conf;
259 
260 	if (!new_conf) {
261 		vin_log(VIN_LOG_STAT, "%s: configuration is NULL\n", stat->sd.name);
262 		return -EINVAL;
263 	}
264 
265 	mutex_lock(&stat->ioctl_lock);
266 
267 	user_cfg->buf_size = ISP_STAT_TOTAL_SIZE;
268 
269 	if (stat->sensor_fps <= 30)
270 		count = 2;
271 	else if (stat->sensor_fps <= 60)
272 		count = 3;
273 	else if (stat->sensor_fps <= 120)
274 		count = 4;
275 	else
276 		count = 5;
277 
278 	ret = isp_stat_bufs_alloc(stat, user_cfg->buf_size, count);
279 	if (ret) {
280 		mutex_unlock(&stat->ioctl_lock);
281 		return ret;
282 	}
283 
284 	/* Module has a valid configuration. */
285 	stat->configured = 1;
286 
287 	mutex_unlock(&stat->ioctl_lock);
288 
289 	return 0;
290 }
291 
isp_stat_buf_process(struct isp_stat * stat,int buf_state)292 static int isp_stat_buf_process(struct isp_stat *stat, int buf_state)
293 {
294 	int ret = STAT_NO_BUF;
295 	dma_addr_t dma_addr;
296 
297 	if (buf_state == STAT_BUF_DONE && stat->state == ISPSTAT_ENABLED) {
298 		ret = isp_stat_buf_queue(stat);
299 		isp_stat_buf_next(stat);
300 
301 		if (!stat->active_buf)
302 			return STAT_NO_BUF;
303 
304 		dma_addr = (dma_addr_t)(stat->active_buf->dma_addr);
305 		bsp_isp_set_statistics_addr(stat->isp->id, dma_addr);
306 	}
307 
308 	return ret;
309 }
310 
isp_stat_enable(struct isp_stat * stat,u8 enable)311 int isp_stat_enable(struct isp_stat *stat, u8 enable)
312 {
313 	unsigned long irqflags;
314 
315 	vin_log(VIN_LOG_STAT, "%s: user wants to %s module.\n", stat->sd.name, enable ? "enable" : "disable");
316 
317 	/* Prevent enabling while configuring */
318 	mutex_lock(&stat->ioctl_lock);
319 
320 	spin_lock_irqsave(&stat->isp->slock, irqflags);
321 
322 	if (!stat->configured && enable) {
323 		spin_unlock_irqrestore(&stat->isp->slock, irqflags);
324 		mutex_unlock(&stat->ioctl_lock);
325 		vin_log(VIN_LOG_STAT, "%s: cannot enable module as it's never been successfully configured so far.\n", stat->sd.name);
326 		return -EINVAL;
327 	}
328 	stat->stat_en_flag = enable;
329 	stat->isp->f1_after_librun = 0;
330 
331 	if (enable)
332 		stat->state = ISPSTAT_ENABLED;
333 	else
334 		stat->state = ISPSTAT_DISABLED;
335 
336 	isp_stat_buf_next(stat);
337 
338 	spin_unlock_irqrestore(&stat->isp->slock, irqflags);
339 	mutex_unlock(&stat->ioctl_lock);
340 
341 	return 0;
342 }
343 
isp_stat_isr(struct isp_stat * stat)344 void isp_stat_isr(struct isp_stat *stat)
345 {
346 	int ret = STAT_BUF_DONE;
347 	unsigned long irqflags;
348 
349 	vin_log(VIN_LOG_STAT, "buf state is %d, frame number is %d 0x%x %d\n",
350 		stat->state, stat->frame_number, stat->buf_size, stat->configured);
351 
352 	spin_lock_irqsave(&stat->isp->slock, irqflags);
353 	if (stat->state == ISPSTAT_DISABLED) {
354 		spin_unlock_irqrestore(&stat->isp->slock, irqflags);
355 		return;
356 	}
357 	stat->isp->h3a_stat.frame_number++;
358 
359 	ret = isp_stat_buf_process(stat, ret);
360 
361 	spin_unlock_irqrestore(&stat->isp->slock, irqflags);
362 
363 	isp_stat_queue_event(stat, ret != STAT_BUF_DONE);
364 }
365 
h3a_ioctl(struct v4l2_subdev * sd,unsigned int cmd,void * arg)366 static long h3a_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
367 {
368 	struct isp_stat *stat = v4l2_get_subdevdata(sd);
369 	vin_log(VIN_LOG_STAT, "%s cmd is 0x%x\n", __func__, cmd);
370 	switch (cmd) {
371 	case VIDIOC_VIN_ISP_H3A_CFG:
372 		return isp_stat_config(stat, arg);
373 	case VIDIOC_VIN_ISP_STAT_REQ:
374 		return isp_stat_request_statistics(stat, arg);
375 	case VIDIOC_VIN_ISP_STAT_EN:
376 		return isp_stat_enable(stat, *(u8 *)arg);
377 	}
378 
379 	return -ENOIOCTLCMD;
380 }
381 
382 #ifdef CONFIG_COMPAT
383 struct vin_isp_stat_data32 {
384 	compat_caddr_t buf;
385 	__u32 buf_size;
386 	__u32 frame_number;
387 	__u32 config_counter;
388 };
389 struct vin_isp_h3a_config32 {
390 	__u32 buf_size;
391 	__u32 config_counter;
392 };
393 
get_isp_statistics_buf32(struct vin_isp_stat_data * kp,struct vin_isp_stat_data32 __user * up)394 static int get_isp_statistics_buf32(struct vin_isp_stat_data *kp,
395 			      struct vin_isp_stat_data32 __user *up)
396 {
397 	u32 tmp;
398 
399 	if (!access_ok(up, sizeof(struct vin_isp_stat_data32)) ||
400 	    get_user(kp->buf_size, &up->buf_size) ||
401 	    get_user(kp->frame_number, &up->frame_number) ||
402 	    get_user(kp->config_counter, &up->config_counter) ||
403 	    get_user(tmp, &up->buf))
404 		return -EFAULT;
405 	kp->buf = compat_ptr(tmp);
406 	return 0;
407 }
408 
put_isp_statistics_buf32(struct vin_isp_stat_data * kp,struct vin_isp_stat_data32 __user * up)409 static int put_isp_statistics_buf32(struct vin_isp_stat_data *kp,
410 			      struct vin_isp_stat_data32 __user *up)
411 {
412 	u32 tmp = (u32) ((unsigned long)kp->buf);
413 
414 	if (!access_ok(up, sizeof(struct vin_isp_stat_data32)) ||
415 	    put_user(kp->buf_size, &up->buf_size) ||
416 	    put_user(kp->frame_number, &up->frame_number) ||
417 	    put_user(kp->config_counter, &up->config_counter) ||
418 	    put_user(tmp, &up->buf))
419 		return -EFAULT;
420 	return 0;
421 }
422 
get_isp_statistics_config32(struct vin_isp_h3a_config * kp,struct vin_isp_h3a_config32 __user * up)423 static int get_isp_statistics_config32(struct vin_isp_h3a_config *kp,
424 			      struct vin_isp_h3a_config32 __user *up)
425 {
426 	if (!access_ok(up, sizeof(struct vin_isp_h3a_config32)) ||
427 	    get_user(kp->buf_size, &up->buf_size) ||
428 	    get_user(kp->config_counter, &up->config_counter))
429 		return -EFAULT;
430 	return 0;
431 }
432 
put_isp_statistics_config32(struct vin_isp_h3a_config * kp,struct vin_isp_h3a_config32 __user * up)433 static int put_isp_statistics_config32(struct vin_isp_h3a_config *kp,
434 			      struct vin_isp_h3a_config32 __user *up)
435 {
436 	if (!access_ok(up, sizeof(struct vin_isp_h3a_config32)) ||
437 	    put_user(kp->buf_size, &up->buf_size) ||
438 	    put_user(kp->config_counter, &up->config_counter))
439 		return -EFAULT;
440 	return 0;
441 }
442 
get_isp_statistics_enable32(unsigned int * kp,unsigned int __user * up)443 static int get_isp_statistics_enable32(unsigned int *kp,
444 			      unsigned int __user *up)
445 {
446 	if (!access_ok(up, sizeof(struct vin_isp_h3a_config32)) ||
447 	    get_user(*kp, up))
448 		return -EFAULT;
449 	return 0;
450 }
451 
put_isp_statistics_enable32(unsigned int * kp,unsigned int __user * up)452 static int put_isp_statistics_enable32(unsigned int *kp,
453 			      unsigned int __user *up)
454 {
455 	if (!access_ok(up, sizeof(struct vin_isp_h3a_config32)) ||
456 	    put_user(*kp, up))
457 		return -EFAULT;
458 	return 0;
459 }
460 
461 #define VIDIOC_VIN_ISP_H3A_CFG32 _IOWR('V', BASE_VIDIOC_PRIVATE + 31, struct vin_isp_h3a_config32)
462 #define VIDIOC_VIN_ISP_STAT_REQ32 _IOWR('V', BASE_VIDIOC_PRIVATE + 32, struct vin_isp_stat_data32)
463 #define VIDIOC_VIN_ISP_STAT_EN32 _IOWR('V', BASE_VIDIOC_PRIVATE + 33, unsigned int)
464 
h3a_compat_ioctl32(struct v4l2_subdev * sd,unsigned int cmd,unsigned long arg)465 static long h3a_compat_ioctl32(struct v4l2_subdev *sd,
466 		unsigned int cmd, unsigned long arg)
467 {
468 	union {
469 		struct vin_isp_h3a_config isb;
470 		struct vin_isp_stat_data isd;
471 		unsigned int isu;
472 	} karg;
473 	void __user *up = compat_ptr(arg);
474 	int compatible_arg = 1;
475 	long err = 0;
476 
477 	vin_log(VIN_LOG_STAT, "%s cmd is 0x%x\n", __func__, cmd);
478 
479 	switch (cmd) {
480 	case VIDIOC_VIN_ISP_STAT_REQ32:
481 		cmd = VIDIOC_VIN_ISP_STAT_REQ;
482 		break;
483 	case VIDIOC_VIN_ISP_H3A_CFG32:
484 		cmd = VIDIOC_VIN_ISP_H3A_CFG;
485 		break;
486 	case VIDIOC_VIN_ISP_STAT_EN32:
487 		cmd = VIDIOC_VIN_ISP_STAT_EN;
488 		break;
489 	}
490 
491 	switch (cmd) {
492 	case VIDIOC_VIN_ISP_STAT_REQ:
493 		err = get_isp_statistics_buf32(&karg.isd, up);
494 		compatible_arg = 0;
495 		break;
496 	case VIDIOC_VIN_ISP_H3A_CFG:
497 		err = get_isp_statistics_config32(&karg.isb, up);
498 		compatible_arg = 0;
499 		break;
500 	case VIDIOC_VIN_ISP_STAT_EN:
501 		err = get_isp_statistics_enable32(&karg.isu, up);
502 		compatible_arg = 0;
503 		break;
504 	}
505 
506 	if (err)
507 		return err;
508 
509 	if (compatible_arg)
510 		err = h3a_ioctl(sd, cmd, up);
511 	else {
512 		err = h3a_ioctl(sd, cmd, &karg);
513 	}
514 
515 	switch (cmd) {
516 	case VIDIOC_VIN_ISP_STAT_REQ:
517 		err = put_isp_statistics_buf32(&karg.isd, up);
518 		break;
519 	case VIDIOC_VIN_ISP_H3A_CFG:
520 		err = put_isp_statistics_config32(&karg.isb, up);
521 		break;
522 	case VIDIOC_VIN_ISP_STAT_EN:
523 		err = put_isp_statistics_enable32(&karg.isu, up);
524 		compatible_arg = 0;
525 		break;
526 	}
527 
528 	return err;
529 }
530 #endif
531 
isp_stat_subscribe_event(struct v4l2_subdev * subdev,struct v4l2_fh * fh,struct v4l2_event_subscription * sub)532 int isp_stat_subscribe_event(struct v4l2_subdev *subdev,
533 				  struct v4l2_fh *fh,
534 				  struct v4l2_event_subscription *sub)
535 {
536 	struct isp_stat *stat = v4l2_get_subdevdata(subdev);
537 
538 	if (sub->type != stat->event_type)
539 		return -EINVAL;
540 
541 	return v4l2_event_subscribe(fh, sub, STAT_NEVENTS, NULL);
542 }
543 
544 static const struct v4l2_subdev_core_ops h3a_subdev_core_ops = {
545 	.ioctl = h3a_ioctl,
546 #ifdef CONFIG_COMPAT
547 	.compat_ioctl32 = h3a_compat_ioctl32,
548 #endif
549 	.subscribe_event = isp_stat_subscribe_event,
550 	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
551 };
552 
553 static const struct v4l2_subdev_ops h3a_subdev_ops = {
554 	.core = &h3a_subdev_core_ops,
555 };
556 
vin_isp_h3a_init(struct isp_dev * isp)557 int vin_isp_h3a_init(struct isp_dev *isp)
558 {
559 	struct isp_stat *stat = &isp->h3a_stat;
560 
561 	vin_log(VIN_LOG_STAT, "%s\n", __func__);
562 
563 	stat->isp = isp;
564 	stat->event_type = V4L2_EVENT_VIN_H3A;
565 
566 	mutex_init(&stat->ioctl_lock);
567 
568 	v4l2_subdev_init(&stat->sd, &h3a_subdev_ops);
569 	snprintf(stat->sd.name, V4L2_SUBDEV_NAME_SIZE, "sunxi_h3a.%u", isp->id);
570 	stat->sd.grp_id = VIN_GRP_ID_STAT;
571 	stat->sd.flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
572 	v4l2_set_subdevdata(&stat->sd, stat);
573 
574 	stat->pad.flags = MEDIA_PAD_FL_SINK;
575 	stat->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_STATISTICS;
576 
577 	return media_entity_pads_init(&stat->sd.entity, 1, &stat->pad);
578 }
579 
vin_isp_h3a_cleanup(struct isp_dev * isp)580 void vin_isp_h3a_cleanup(struct isp_dev *isp)
581 {
582 	struct isp_stat *stat = &isp->h3a_stat;
583 
584 	vin_log(VIN_LOG_STAT, "%s\n", __func__);
585 
586 	media_entity_cleanup(&stat->sd.entity);
587 	mutex_destroy(&stat->ioctl_lock);
588 	isp_stat_bufs_free(stat);
589 }
590