buffer structure moved to flashlight structure.
[flashlight-appl] / src / flashlight_lib.c
1 /*
2  *  Flashlight applet (widget) for Maemo.
3  *  Copyright (C) 2009 Roman Moravcik
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
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  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <sys/stat.h>
28 #include <sys/ioctl.h>
29 #include <sys/mman.h>
30
31 #include <asm/types.h>
32 #include <linux/videodev2.h>
33
34 #include "flashlight_lib.h"
35
36
37 int flashlight_get_status (FlashlightContext_t *flashlight, int *status)
38 {
39         struct v4l2_control ctrl;
40
41         printf ("flashlight_get_status()\n");
42
43         if (flashlight == NULL) {
44                 printf ("flashlight_get_status: flashlight context is not valid\n");
45                 return ENOCONTEXT;
46         }
47
48         if (flashlight->fd == -1) {
49                 printf ("flashlight_get_status: device not openned\n");
50                 return ENODEVICE;
51         }
52
53         *status = 0;
54
55         /* check short circuit fault */
56         ctrl.id = V4L2_CID_FLASH_ADP1653_FAULT_SCP;
57         if (ioctl (flashlight->fd, VIDIOC_G_CTRL, &ctrl) == -1) {
58                 printf ("flashlight_set_intensity: cannot get circuit fault status (%s)\n", strerror (errno));
59                 return EGENERROR;
60         }
61
62         if (ctrl.value)
63                 *status |= FLASHLIGHT_STATUS_SHORT_CIRCUT_FAULT;
64         else
65                 *status &= ~FLASHLIGHT_STATUS_SHORT_CIRCUT_FAULT;
66
67         /* check overtemperature fault */
68         ctrl.id = V4L2_CID_FLASH_ADP1653_FAULT_OT;
69         if (ioctl (flashlight->fd, VIDIOC_G_CTRL, &ctrl) == -1) {
70                 printf ("flashlight_set_intensity: cannot get overtemperature fault status (%s)\n", strerror (errno));
71                 return EGENERROR;
72         }
73
74         if (ctrl.value)
75                 *status |= FLASHLIGHT_STATUS_OVERTEMPERATURE_FAULT;
76         else
77                 *status &= ~FLASHLIGHT_STATUS_OVERTEMPERATURE_FAULT;
78
79         /* check timeout fault */
80         ctrl.id = V4L2_CID_FLASH_ADP1653_FAULT_TMR;
81         if (ioctl (flashlight->fd, VIDIOC_G_CTRL, &ctrl) == -1) {
82                 printf ("flashlight_set_intensity: cannot get timeout fault status (%s)\n", strerror (errno));
83                 return EGENERROR;
84         }
85
86         if (ctrl.value)
87                 *status |= FLASHLIGHT_STATUS_TIMEOUT_FAULT;
88         else
89                 *status &= ~FLASHLIGHT_STATUS_TIMEOUT_FAULT;
90
91         /* check overtemperature fault */
92         ctrl.id = V4L2_CID_FLASH_ADP1653_FAULT_OV;
93         if (ioctl (flashlight->fd, VIDIOC_G_CTRL, &ctrl) == -1) {
94                 printf ("flashlight_set_intensity: cannot get overvoltage fault status (%s)\n", strerror (errno));
95                 return EGENERROR;
96         }
97
98         if (ctrl.value)
99                 *status |= FLASHLIGHT_STATUS_OVERVOLTAGE_FAULT;
100         else
101                 *status &= ~FLASHLIGHT_STATUS_OVERVOLTAGE_FAULT;
102
103         return ENOERROR;
104 }
105
106 int flashlight_set_intensity (FlashlightContext_t *flashlight, int intensity)
107 {
108         struct v4l2_control ctrl;
109         enum v4l2_buf_type type;
110         unsigned int i;
111
112         printf ("flashlight_set_intensity(%d)\n", intensity);
113
114         if (flashlight == NULL) {
115                 printf ("flashlight_set_intensity: flashlight context is not valid\n");
116                 return ENOCONTEXT;
117         }
118
119         if (flashlight->fd == -1) {
120                 printf ("flashlight_set_intensity: device not openned\n");
121                 return ENODEVICE;
122         }
123
124         if (intensity > flashlight->max_intensity)
125                 intensity = flashlight->max_intensity;
126
127         ctrl.id = V4L2_CID_TORCH_INTENSITY;
128         ctrl.value = intensity;
129
130         if (ioctl (flashlight->fd, VIDIOC_S_CTRL, &ctrl) == -1) {
131                 printf ("flashlight_set_intensity: cannot set intensity (%s)\n", strerror (errno));
132                 return EGENERROR;
133         }
134
135         /*
136            WORKAROUND: start/stop i/o streaming to block camera application
137          */
138         if (intensity > 0) {
139                 for (i = 0; i < flashlight->n_buffers; ++i) {
140                         struct v4l2_buffer buf;
141
142                         buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
143                         buf.memory      = V4L2_MEMORY_MMAP;
144                         buf.index       = i;
145                         if (ioctl (flashlight->fd, VIDIOC_QBUF, &buf) == -1) {
146                                 printf ("flashlight_set_intensity: unable to exchange a buffer %d with driver (%s)\n", i, strerror (errno));
147                                 return EGENERROR;
148                         }
149                 }
150
151                 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
152                 if (ioctl (flashlight->fd, VIDIOC_STREAMON, &type)) {
153                         printf ("flashlight_set_intensity: unable to start i/o streaming (%s)\n", strerror (errno));
154                         return EGENERROR;
155                 }
156         } else {
157                 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
158                 if (ioctl (flashlight->fd, VIDIOC_STREAMOFF, &type) == -1) {
159                         printf ("flashlight_set_intensity: unable to stop i/o streaming (%s)\n", strerror (errno));
160                         return EGENERROR;
161                 }
162         }
163
164         return ENOERROR;
165 }
166
167 int flashlight_get_intensity (FlashlightContext_t *flashlight, int *intensity)
168 {
169         struct v4l2_control ctrl;
170
171         printf ("flashlight_get_intensity()\n");
172
173         if (flashlight == NULL) {
174                 printf ("flashlight_get_intensity: flashlight context is not valid\n");
175                 return ENOCONTEXT;
176         }
177
178         if (flashlight->fd == -1) {
179                 printf ("flashlight_get_intensity: device not openned\n");
180                 return ENODEVICE;
181         }
182
183         ctrl.id = V4L2_CID_TORCH_INTENSITY;
184
185         if (ioctl (flashlight->fd, VIDIOC_G_CTRL, &ctrl) == -1) {
186                 printf ("flashlight_get_intensity: cannot get intensity (%s)\n", strerror (errno));
187                 return EGENERROR;
188         }
189
190         *intensity = ctrl.value;
191         return ENOERROR;
192 }
193
194 int flashlight_open (FlashlightContext_t *flashlight, const char *device_name)
195 {
196         struct v4l2_queryctrl ctrl;
197         struct v4l2_cropcap cropcap;
198         struct v4l2_crop crop;
199         struct v4l2_format fmt;
200         struct v4l2_requestbuffers req;
201         struct stat st;
202
203         printf ("flashlight_open(%s)\n", device_name);
204
205         if (flashlight == NULL) {
206                 printf ("flashlight_open: flashlight context is not valid\n");
207                 return ENOCONTEXT;
208         }
209
210         if (device_name == NULL) {
211                 printf ("flashlight_open: device name not specified\n");
212                 return EGENERROR;
213         }
214
215         memcpy (flashlight->device_name, device_name, sizeof(flashlight->device_name));
216
217         if (stat (flashlight->device_name, &st) == -1) {
218                 printf ("flashlight_open: cannot identify '%s' (%s)\n", flashlight->device_name, strerror (errno));
219                 return EGENERROR;
220         }
221
222         /* check it device_name is real device */
223         if (!S_ISCHR (st.st_mode)) {
224                 printf ("flashlight_open: %s is no device\n", flashlight->device_name);
225                 return EGENERROR;
226         }
227
228         flashlight->fd = open (flashlight->device_name, O_RDWR /* required */ | O_NONBLOCK, 0);
229
230         if (flashlight->fd == -1) {
231                 printf ("flashlight_open: cannot open '%s' (%s)\n", flashlight->device_name, strerror (errno));
232                 return ENODEVICE;
233         }
234
235         /* query from driver minimal and maximal flashlight intensity */
236         ctrl.id = V4L2_CID_TORCH_INTENSITY;
237         if (ioctl (flashlight->fd, VIDIOC_QUERYCTRL, &ctrl) == -1) {
238                 printf ("flashlight_open: cannot get minimal and maximal flashlight intensity (%s)\n", strerror (errno));
239                 return EGENERROR;
240         }
241
242         flashlight->min_intensity = ctrl.minimum;
243         flashlight->max_intensity = ctrl.maximum;
244
245         /*
246            WORKAROUND: Initialization of camera extracted from v4l2_example.
247            http://v4l2spec.bytesex.org/spec/capture-example.html
248
249            We need to initialize camera in other to block camera application.
250            (bug 4949: Applet breaks the camera application)
251          */
252         /* get crop capabilities */
253         cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
254         if (ioctl (flashlight->fd, VIDIOC_CROPCAP, &cropcap) == -1) {
255                 printf ("flashlight_open: unable to get crop capabilities (%s)\n", strerror (errno));
256                 return EGENERROR;
257         }
258
259         /* set crop capabilities */
260         crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
261         crop.c = cropcap.defrect; /* reset to default */
262         if (ioctl (flashlight->fd, VIDIOC_S_CROP, &crop) == -1) {
263                 printf ("flashlight_open: unable to set cropping rectangle (%s)\n", strerror (errno));
264                 return EGENERROR;
265         }
266
267         /* set data format */
268         fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
269         fmt.fmt.pix.width       = 640;
270         fmt.fmt.pix.height      = 480;
271         fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
272         fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;
273         if (ioctl (flashlight->fd, VIDIOC_S_FMT, &fmt) == -1) {
274                 printf ("flashlight_open: unable to set data format (%s)\n", strerror (errno));
275                 return EGENERROR;
276         }
277
278         req.count       = 4;
279         req.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
280         req.memory      = V4L2_MEMORY_MMAP;
281         if (ioctl (flashlight->fd, VIDIOC_REQBUFS, &req) == -1) {
282                 printf ("flashlight_open: unable to initiate memory mapping (%s)\n", strerror (errno));
283                 return EGENERROR;
284         }
285
286         if (req.count < 2) {
287                 printf ("flashlight_open: insufficient buffer memory on %s\n", device_name);
288                 return EGENERROR;
289         }
290
291         flashlight->buffers = calloc (req.count, sizeof (*flashlight->buffers));
292         if (!flashlight->buffers) {
293                 printf ("flashlight_open: unable to allocate memory\n");
294                 return EGENERROR;
295         }
296
297         for (flashlight->n_buffers = 0; flashlight->n_buffers < req.count; ++flashlight->n_buffers) {
298                 struct v4l2_buffer buf;
299
300                 buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
301                 buf.memory      = V4L2_MEMORY_MMAP;
302                 buf.index       = flashlight->n_buffers;
303
304                 if (ioctl (flashlight->fd, VIDIOC_QUERYBUF, &buf) == -1) {
305                         printf ("flashlight_open: unable to query the status of a buffer %d (%s)\n",
306                                 flashlight->n_buffers, strerror (errno));
307                         return EGENERROR;
308                 }
309
310                 flashlight->buffers[flashlight->n_buffers].length = buf.length;
311                 flashlight->buffers[flashlight->n_buffers].start = mmap (NULL /* start anywhere */,
312                                                                          buf.length,
313                                                                          PROT_READ | PROT_WRITE /* required */,
314                                                                          MAP_SHARED /* recommended */,
315                                                                          flashlight->fd,
316                                                                          buf.m.offset);
317
318                 if (flashlight->buffers[flashlight->n_buffers].start == MAP_FAILED) {
319                         printf ("flashlight_open: unable to map memory (%s)\n", strerror (errno));
320                         return EGENERROR;
321                 }
322         }
323
324         return ENOERROR;
325 }
326
327 int flashlight_close (FlashlightContext_t *flashlight)
328 {
329         unsigned int i;
330
331         printf ("flashlight_close()\n");
332
333         if (flashlight == NULL) {
334                 printf ("flashlight_close: flashlight context is not valid\n");
335                 return ENOCONTEXT;
336         }
337
338         /* unmap memory mapped buffers */
339         for (i = 0; i < flashlight->n_buffers; ++i) {
340                 if (flashlight->buffers[flashlight->n_buffers].start != MAP_FAILED) {
341                         if (munmap (flashlight->buffers[i].start, flashlight->buffers[i].length) == -1) {
342                                 printf ("flashlight_close: unable to unmap memory (%s)\n", strerror (errno));
343                                 return EGENERROR;
344                         }
345                 }
346         }
347         flashlight->n_buffers = 0;
348
349         /* free buffers */
350         if (flashlight->buffers)
351                 free (flashlight->buffers);
352         flashlight->buffers = NULL;
353
354         /* close camera device */
355         if (flashlight->fd != -1) {
356                 if (close (flashlight->fd) == -1) {
357                         printf ("flashlight_close: cannot close device '%s' (%s)\n", flashlight->device_name, strerror (errno));
358                         return ENODEVICE;
359                 }
360         }
361         flashlight->fd = -1;
362
363         return ENOERROR;
364 }
365
366 int flashlight_init (FlashlightContext_t **pRefContext)
367 {
368         FlashlightContext_t *flashlight = NULL;
369
370         printf ("flashlight_init()\n");
371
372         if (*pRefContext != NULL) {
373                 printf("flashlight_init: expecting zero pointer context '*pRefContext'\n");
374                 return EGENERROR;
375         }
376
377         /* allocate memory for context structure */
378         flashlight = malloc (sizeof (FlashlightContext_t));
379         if (flashlight == NULL) {
380                 printf ("flashlight_init: unable to allocate memory for context\n");
381                 return ENOCONTEXT;
382         }
383
384         *pRefContext = flashlight;
385
386         /* initialize default values */
387         memset (flashlight, 0x00, sizeof (FlashlightContext_t));
388         flashlight->fd = -1;
389
390         flashlight->n_buffers = 0;
391         flashlight->buffers = NULL;
392
393         /* from adp1653.c */
394         flashlight->min_intensity = 0;
395         flashlight->max_intensity = 11;
396
397         return ENOERROR;
398 }
399
400 int flashlight_deinit (FlashlightContext_t *flashlight)
401 {
402         int intensity = 0;
403
404         printf ("flashlight_deinit()\n");
405
406         if (flashlight == NULL) {
407                 printf ("flashlight_deinit: flashlight context is not valid\n");
408                 return ENOCONTEXT;
409         }
410
411         if (flashlight->fd != -1) {
412                 /* check if flashlight isn't enabled before closing device */
413                 if (flashlight_get_intensity (flashlight, &intensity) == -1)
414                         return EGENERROR;
415
416                 if (intensity > 0) {
417                         if (flashlight_set_intensity (flashlight, 0) == -1)
418                                 return EGENERROR;
419                 }
420
421                 if (flashlight_close(flashlight))
422                         return EGENERROR;
423         }
424
425         /* free allocated memory */
426         free (flashlight);
427
428         return ENOERROR;
429 }