1 | /* qcam-Linux.c -- Linux-specific routines for accessing QuickCam */ |
---|
2 | |
---|
3 | /* Version 0.1, January 2, 1996 */ |
---|
4 | /* Version 0.5, August 24, 1996 */ |
---|
5 | |
---|
6 | #define LOCALSTATEDIR "" |
---|
7 | //#define LOCALSTATEDIR "/var/run" |
---|
8 | |
---|
9 | /****************************************************************** |
---|
10 | |
---|
11 | Copyright (C) 1996 by Scott Laird |
---|
12 | |
---|
13 | Permission is hereby granted, free of charge, to any person obtaining |
---|
14 | a copy of this software and associated documentation files (the |
---|
15 | "Software"), to deal in the Software without restriction, including |
---|
16 | without limitation the rights to use, copy, modify, merge, publish, |
---|
17 | distribute, sublicense, and/or sell copies of the Software, and to |
---|
18 | permit persons to whom the Software is furnished to do so, subject to |
---|
19 | the following conditions: |
---|
20 | |
---|
21 | The above copyright notice and this permission notice shall be |
---|
22 | included in all copies or substantial portions of the Software. |
---|
23 | |
---|
24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
---|
25 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
---|
26 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
---|
27 | IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY CLAIM, DAMAGES OR |
---|
28 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
---|
29 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
---|
30 | OTHER DEALINGS IN THE SOFTWARE. |
---|
31 | |
---|
32 | ******************************************************************/ |
---|
33 | |
---|
34 | #ifdef HAVE_CONFIG_H |
---|
35 | #include "config.h" |
---|
36 | #endif |
---|
37 | |
---|
38 | #include <stdio.h> |
---|
39 | #include <unistd.h> |
---|
40 | #ifdef TESTING |
---|
41 | #include <errno.h> |
---|
42 | #endif |
---|
43 | #include <sys/io.h> |
---|
44 | |
---|
45 | #include <sys/time.h> |
---|
46 | #include <sys/types.h> |
---|
47 | #include <sys/stat.h> |
---|
48 | #include <fcntl.h> |
---|
49 | |
---|
50 | #include "qcam.h" |
---|
51 | #include "qcam-Linux.h" |
---|
52 | |
---|
53 | int __inline__ |
---|
54 | read_lpstatus (const struct qcam *q) |
---|
55 | { |
---|
56 | return inb (q->port + 1); |
---|
57 | } |
---|
58 | |
---|
59 | int |
---|
60 | read_lpcontrol (const struct qcam *q) |
---|
61 | { |
---|
62 | return inb (q->port + 2); |
---|
63 | } |
---|
64 | |
---|
65 | int |
---|
66 | read_lpdata (const struct qcam *q) |
---|
67 | { |
---|
68 | return inb (q->port); |
---|
69 | } |
---|
70 | |
---|
71 | void |
---|
72 | write_lpdata (const struct qcam *q, int d) |
---|
73 | { |
---|
74 | outb (d, q->port); |
---|
75 | } |
---|
76 | |
---|
77 | void |
---|
78 | write_lpcontrol (const struct qcam *q, int d) |
---|
79 | { |
---|
80 | outb (d, q->port + 2); |
---|
81 | } |
---|
82 | |
---|
83 | int |
---|
84 | enable_ports (const struct qcam *q) |
---|
85 | { |
---|
86 | if (q->port < 0x278) |
---|
87 | return 1; /* Better safe than sorry */ |
---|
88 | if (q->port > 0x3bc) |
---|
89 | return 1; |
---|
90 | return (ioperm (q->port, 3, 1)); |
---|
91 | } |
---|
92 | |
---|
93 | int |
---|
94 | disable_ports (const struct qcam *q) |
---|
95 | { |
---|
96 | return (ioperm (q->port, 3, 0)); |
---|
97 | } |
---|
98 | |
---|
99 | /* Lock port. This is currently sub-optimal, and is begging to be |
---|
100 | fixed. It should check for dead locks. Any takers? */ |
---|
101 | |
---|
102 | /* qc_lock_wait |
---|
103 | * This function uses POSIX fcntl-style locking on a file created in the |
---|
104 | * /tmp directory. Because it uses the Unix record locking facility, locks |
---|
105 | * are relinquished automatically on process termination, so "dead locks" |
---|
106 | * are not a problem. (FYI, the lock file will remain after process |
---|
107 | * termination, but this is actually desired so that the next process need |
---|
108 | * not re-creat(2)e it... just lock it.) |
---|
109 | * The wait argument indicates whether or not this funciton should "block" |
---|
110 | * waiting for the previous lock to be relinquished. This is ideal so that |
---|
111 | * multiple processes (eg. qcam) taking "snapshots" can peacefully coexist. |
---|
112 | * - Dave Plonka (plonka@carroll1.cc.edu) |
---|
113 | */ |
---|
114 | int |
---|
115 | qc_lock_wait (struct qcam *q, int wait) |
---|
116 | { |
---|
117 | #if 1 |
---|
118 | static struct flock sfl; |
---|
119 | |
---|
120 | if (-1 == q->fd) { /* we've yet to open the lock file */ |
---|
121 | static char lockfile[128]; |
---|
122 | |
---|
123 | sprintf (lockfile, LOCALSTATEDIR "LOCK.qcam.0x%x", q->port); |
---|
124 | if (-1 == (q->fd = open (lockfile, O_WRONLY | O_CREAT, 0666))) { |
---|
125 | perror ("open"); |
---|
126 | return 1; |
---|
127 | } |
---|
128 | #ifdef TESTING |
---|
129 | fprintf (stderr, "%s - %d: %s open(2)ed\n", __FILE__, __LINE__, lockfile); |
---|
130 | #endif |
---|
131 | |
---|
132 | /* initialize the l_type memver to lock the file exclusively */ |
---|
133 | sfl.l_type = F_WRLCK; |
---|
134 | } |
---|
135 | #ifdef TESTING |
---|
136 | if (0 != fcntl (q->fd, F_SETLK, &sfl)) /* non-blocking set lock */ |
---|
137 | #else |
---|
138 | if (0 != fcntl (q->fd, wait ? F_SETLKW : F_SETLK, &sfl)) |
---|
139 | #endif |
---|
140 | { |
---|
141 | #ifdef TESTING |
---|
142 | perror ("fcntl"); |
---|
143 | if (EAGAIN != errno || !wait) |
---|
144 | return 1; |
---|
145 | |
---|
146 | fprintf (stderr, "%s - %d: waiting for exclusive lock on fd %d...\n", |
---|
147 | __FILE__, __LINE__, q->fd); |
---|
148 | |
---|
149 | if (0 != fcntl (q->fd, F_SETLKW, &sfl)) /* "blocking" set lock */ |
---|
150 | #endif |
---|
151 | { |
---|
152 | perror ("fcntl"); |
---|
153 | return 1; |
---|
154 | } |
---|
155 | } |
---|
156 | #ifdef TESTING |
---|
157 | fprintf (stderr, "%s - %d: fd %d locked exclusively\n", __FILE__, __LINE__, |
---|
158 | q->fd); |
---|
159 | #endif |
---|
160 | |
---|
161 | #else |
---|
162 | char lockfile[128], tmp[128]; |
---|
163 | struct stat statbuf; |
---|
164 | |
---|
165 | sprintf (lockfile, LOCALSTATEDIR "/LOCK.qcam.0x%x", q->port); |
---|
166 | sprintf (tmp, "%s-%d", lockfile, getpid ()); |
---|
167 | |
---|
168 | if ((creat (tmp, 0) == -1) || |
---|
169 | (link (tmp, lockfile) == -1) || |
---|
170 | (stat (tmp, &statbuf) == -1) || (statbuf.st_nlink == 1)) { |
---|
171 | #ifdef DEBUGQC |
---|
172 | perror ("QuickCam Locked"); |
---|
173 | if (unlink (tmp) == -1) |
---|
174 | perror ("Error unlinking temp file."); |
---|
175 | #else |
---|
176 | unlink (tmp); |
---|
177 | #endif |
---|
178 | return 1; |
---|
179 | } |
---|
180 | |
---|
181 | unlink (tmp); |
---|
182 | if (chown (lockfile, getuid (), getgid ()) == -1) |
---|
183 | perror ("Chown problems"); |
---|
184 | #endif |
---|
185 | |
---|
186 | return 0; |
---|
187 | } |
---|
188 | |
---|
189 | int |
---|
190 | qc_lock (struct qcam *q) |
---|
191 | { |
---|
192 | #if 1 |
---|
193 | return qc_lock_wait (q, 1 /*wait */ ); |
---|
194 | #else |
---|
195 | return qc_lock_wait (q, 0 /*don't wait */ ); |
---|
196 | #endif |
---|
197 | } |
---|
198 | |
---|
199 | /* Unlock port */ |
---|
200 | |
---|
201 | int |
---|
202 | qc_unlock (struct qcam *q) |
---|
203 | { |
---|
204 | static struct flock sfl; |
---|
205 | |
---|
206 | #if 1 |
---|
207 | if (-1 == q->fd) { /* port was not locked */ |
---|
208 | return 1; |
---|
209 | } |
---|
210 | |
---|
211 | /* clear the exclusive lock */ |
---|
212 | sfl.l_type = F_UNLCK; |
---|
213 | if (0 != fcntl (q->fd, F_SETLK, &sfl)) { |
---|
214 | perror ("fcntl"); |
---|
215 | return 1; |
---|
216 | } |
---|
217 | #ifdef TESTING |
---|
218 | fprintf (stderr, "%s - %d: fd %d unlocked\n", __FILE__, __LINE__, q->fd); |
---|
219 | #endif |
---|
220 | |
---|
221 | #else |
---|
222 | char lockfile[128]; |
---|
223 | |
---|
224 | sprintf (lockfile, LOCALSTATEDIR "/LOCK.qcam.0x%x", q->port); |
---|
225 | unlink (lockfile); /* What would I do with an error? */ |
---|
226 | #endif |
---|
227 | |
---|
228 | return 0; |
---|
229 | } |
---|
230 | |
---|
231 | |
---|
232 | /* Probe for camera. Returns 0 if found, 1 if not found, sets |
---|
233 | q->port.*/ |
---|
234 | |
---|
235 | int |
---|
236 | qc_probe (struct qcam *q) |
---|
237 | { |
---|
238 | int ioports[] = { 0x378, 0x278, 0x3bc, 0 }; |
---|
239 | int i = 0; |
---|
240 | |
---|
241 | /* Attempt to get permission to access IO ports. Must be root */ |
---|
242 | |
---|
243 | while (ioports[i] != 0) { |
---|
244 | q->port = ioports[i++]; |
---|
245 | |
---|
246 | if (qc_open (q)) { |
---|
247 | perror ("Can't get I/O permission"); |
---|
248 | exit (1); |
---|
249 | } |
---|
250 | |
---|
251 | if (qc_detect (q)) { |
---|
252 | fprintf (stderr, "QuickCam detected at 0x%x\n", q->port); |
---|
253 | qc_close (q); |
---|
254 | return (0); |
---|
255 | } else |
---|
256 | qc_close (q); |
---|
257 | } |
---|
258 | |
---|
259 | return 1; |
---|
260 | } |
---|
261 | |
---|
262 | |
---|
263 | /* THIS IS UGLY. I need a short delay loop -- somthing well under a |
---|
264 | millisecond. Unfortunately, adding 2 usleep(1)'s to qc_command slowed |
---|
265 | it down by a factor of over 1000 over the same loop with 2 |
---|
266 | usleep(0)'s, and that's too slow -- qc_start was taking over a second |
---|
267 | to run. This seems to help, but if anyone has a good |
---|
268 | speed-independent pause routine, please tell me. -- Scott */ |
---|
269 | |
---|
270 | void |
---|
271 | qc_wait (int val) |
---|
272 | { |
---|
273 | int i; |
---|
274 | |
---|
275 | while (val--) |
---|
276 | for (i = 0; i < 50000; i++); |
---|
277 | } |
---|