Commit | Line | Data |
---|---|---|
aa337ef1 SS |
1 | /* |
2 | ||
3 | Copyright 1996,2002,2005 Gregory D. Hager, Alfred A. Rizzi, Noah J. Cowan, | |
4 | Jason Lapenta, Scott Smedley, Greg Sharp | |
5 | ||
6 | This file is part of the DT3155 Device Driver. | |
7 | ||
8 | The DT3155 Device Driver is free software; you can redistribute it | |
9 | and/or modify it under the terms of the GNU General Public License as | |
10 | published by the Free Software Foundation; either version 2 of the | |
11 | License, or (at your option) any later version. | |
12 | ||
13 | The DT3155 Device Driver is distributed in the hope that it will be | |
14 | useful, but WITHOUT ANY WARRANTY; without even the implied warranty | |
15 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with the DT3155 Device Driver; if not, write to the Free | |
20 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
21 | MA 02111-1307 USA | |
22 | ||
23 | -- Changes -- | |
24 | ||
25 | Date Programmer Description of changes made | |
26 | ------------------------------------------------------------------- | |
27 | 03-Jul-2000 JML n/a | |
28 | 10-Oct-2001 SS port to 2.4 kernel | |
29 | 02-Apr-2002 SS Mods to use allocator as a standalone module; | |
30 | Merged John Roll's changes (john@cfa.harvard.edu) | |
31 | to make work with multiple boards. | |
32 | 02-Jul-2002 SS Merged James Rose's chages (rosejr@purdue.edu) to: | |
33 | * fix successive interrupt-driven captures | |
34 | * add select/poll support. | |
35 | 10-Jul-2002 GCS Add error check when ndevices > MAXBOARDS. | |
36 | 02-Aug-2002 GCS Fix field mode so that odd (lower) field is stored | |
37 | in lower half of buffer. | |
38 | 05-Aug-2005 SS port to 2.6 kernel. | |
39 | 26-Oct-2009 SS port to 2.6.30 kernel. | |
40 | ||
41 | -- Notes -- | |
42 | ||
43 | ** appended "mem=124" in lilo.conf to allow for 4megs free on my 128meg system. | |
44 | * using allocator.c and allocator.h from o'reilly book (alessandro rubini) | |
45 | ftp://ftp.systemy.it/pub/develop (see README.allocator) | |
46 | ||
47 | + might want to get rid of MAXboards for allocating initial buffer. | |
48 | confusing and not necessary | |
49 | ||
50 | + in cleanup_module the MOD_IN_USE looks like it is check after it should | |
51 | ||
52 | * GFP_DMA should not be set with a PCI system (pg 291) | |
53 | ||
54 | - NJC why are only two buffers allowed? (see isr, approx line 358) | |
55 | ||
56 | */ | |
57 | ||
58 | extern void printques(int); | |
59 | ||
aa337ef1 | 60 | #include <linux/module.h> |
aa337ef1 | 61 | #include <linux/interrupt.h> |
aa337ef1 SS |
62 | #include <linux/pci.h> |
63 | #include <linux/types.h> | |
64 | #include <linux/poll.h> | |
ae7fd7b8 | 65 | #include <linux/sched.h> |
b1f2ac07 | 66 | #include <linux/smp_lock.h> |
aa337ef1 SS |
67 | |
68 | #include <asm/io.h> | |
69 | #include <asm/uaccess.h> | |
70 | ||
71 | #include "dt3155.h" | |
72 | #include "dt3155_drv.h" | |
73 | #include "dt3155_isr.h" | |
74 | #include "dt3155_io.h" | |
75 | #include "allocator.h" | |
76 | ||
74a92013 RD |
77 | |
78 | MODULE_LICENSE("GPL"); | |
79 | ||
aa337ef1 SS |
80 | /* Error variable. Zero means no error. */ |
81 | int dt3155_errno = 0; | |
82 | ||
83 | #ifndef PCI_DEVICE_ID_INTEL_7116 | |
84 | #define PCI_DEVICE_ID_INTEL_7116 0x1223 | |
85 | #endif | |
86 | ||
87 | #define DT3155_VENDORID PCI_VENDOR_ID_INTEL | |
88 | #define DT3155_DEVICEID PCI_DEVICE_ID_INTEL_7116 | |
89 | #define MAXPCI 16 | |
90 | ||
91 | #ifdef DT_DEBUG | |
92 | #define DT_3155_DEBUG_MSG(x,y) printk(x,y) | |
93 | #else | |
94 | #define DT_3155_DEBUG_MSG(x,y) | |
95 | #endif | |
96 | ||
97 | /* wait queue for interrupts */ | |
d241fd58 | 98 | wait_queue_head_t dt3155_read_wait_queue[MAXBOARDS]; |
aa337ef1 SS |
99 | |
100 | #define DT_3155_SUCCESS 0 | |
101 | #define DT_3155_FAILURE -EIO | |
102 | ||
103 | /* set to dynamicaly allocate, but it is tunable: */ | |
104 | /* insmod DT_3155 dt3155 dt3155_major=XX */ | |
105 | int dt3155_major = 0; | |
106 | ||
107 | /* The minor numbers are 0 and 1 ... they are not tunable. | |
108 | * They are used as the indices for the structure vectors, | |
109 | * and register address vectors | |
110 | */ | |
111 | ||
112 | /* Global structures and variables */ | |
113 | ||
114 | /* Status of each device */ | |
923c1244 | 115 | struct dt3155_status dt3155_status[MAXBOARDS]; |
aa337ef1 SS |
116 | |
117 | /* kernel logical address of the board */ | |
d241fd58 | 118 | u8 *dt3155_lbase[MAXBOARDS] = { NULL |
aa337ef1 SS |
119 | #if MAXBOARDS == 2 |
120 | , NULL | |
121 | #endif | |
122 | }; | |
123 | /* DT3155 registers */ | |
5d392111 | 124 | u8 *dt3155_bbase = NULL; /* kernel logical address of the * |
aa337ef1 | 125 | * buffer region */ |
d241fd58 | 126 | u32 dt3155_dev_open[MAXBOARDS] = {0 |
aa337ef1 SS |
127 | #if MAXBOARDS == 2 |
128 | , 0 | |
129 | #endif | |
130 | }; | |
131 | ||
dcff74ce | 132 | u32 ndevices = 0; |
3a8954e8 | 133 | u32 unique_tag = 0;; |
aa337ef1 SS |
134 | |
135 | ||
136 | /* | |
137 | * Stops interrupt generation right away and resets the status | |
138 | * to idle. I don't know why this works and the other way doesn't. | |
139 | * (James Rose) | |
140 | */ | |
141 | static void quick_stop (int minor) | |
142 | { | |
143 | // TODO: scott was here | |
144 | #if 1 | |
d241fd58 | 145 | ReadMReg((dt3155_lbase[minor] + INT_CSR), int_csr_r.reg); |
aa337ef1 SS |
146 | /* disable interrupts */ |
147 | int_csr_r.fld.FLD_END_EVE_EN = 0; | |
148 | int_csr_r.fld.FLD_END_ODD_EN = 0; | |
d241fd58 | 149 | WriteMReg((dt3155_lbase[minor] + INT_CSR), int_csr_r.reg); |
aa337ef1 | 150 | |
d241fd58 | 151 | dt3155_status[minor].state &= ~(DT3155_STATE_STOP|0xff); |
aa337ef1 | 152 | /* mark the system stopped: */ |
d241fd58 JB |
153 | dt3155_status[minor].state |= DT3155_STATE_IDLE; |
154 | dt3155_fbuffer[minor]->stop_acquire = 0; | |
155 | dt3155_fbuffer[minor]->even_stopped = 0; | |
aa337ef1 SS |
156 | #else |
157 | dt3155_status[minor].state |= DT3155_STATE_STOP; | |
158 | dt3155_status[minor].fbuffer.stop_acquire = 1; | |
159 | #endif | |
160 | ||
161 | } | |
162 | ||
163 | ||
164 | /***************************************************** | |
165 | * dt3155_isr() Interrupt service routien | |
166 | * | |
167 | * - looks like this isr supports IRQ sharing (or could) JML | |
168 | * - Assumes irq's are disabled, via SA_INTERRUPT flag | |
169 | * being set in request_irq() call from init_module() | |
170 | *****************************************************/ | |
e8afd402 | 171 | static void dt3155_isr(int irq, void *dev_id, struct pt_regs *regs) |
aa337ef1 SS |
172 | { |
173 | int minor = -1; | |
174 | int index; | |
aa337ef1 | 175 | unsigned long flags; |
3a8954e8 | 176 | u32 buffer_addr; |
aa337ef1 SS |
177 | |
178 | /* find out who issued the interrupt */ | |
d241fd58 JB |
179 | for (index = 0; index < ndevices; index++) { |
180 | if(dev_id == (void*) &dt3155_status[index]) | |
aa337ef1 SS |
181 | { |
182 | minor = index; | |
183 | break; | |
184 | } | |
185 | } | |
186 | ||
187 | /* hopefully we should not get here */ | |
d241fd58 | 188 | if (minor < 0 || minor >= MAXBOARDS) { |
aa337ef1 SS |
189 | printk(KERN_ERR "dt3155_isr called with invalid dev_id\n"); |
190 | return; | |
191 | } | |
192 | ||
193 | /* Check for corruption and set a flag if so */ | |
d241fd58 | 194 | ReadMReg((dt3155_lbase[minor] + CSR1), csr1_r.reg); |
aa337ef1 | 195 | |
d241fd58 | 196 | if ((csr1_r.fld.FLD_CRPT_EVE) || (csr1_r.fld.FLD_CRPT_ODD)) |
aa337ef1 SS |
197 | { |
198 | /* TODO: this should probably stop acquisition */ | |
199 | /* and set some flags so that dt3155_read */ | |
200 | /* returns an error next time it is called */ | |
201 | dt3155_errno = DT_ERR_CORRUPT; | |
202 | printk("dt3155: corrupt field\n"); | |
203 | return; | |
204 | } | |
205 | ||
d241fd58 | 206 | ReadMReg((dt3155_lbase[minor] + INT_CSR), int_csr_r.reg); |
aa337ef1 SS |
207 | |
208 | /* Handle the even field ... */ | |
209 | if (int_csr_r.fld.FLD_END_EVE) | |
210 | { | |
d241fd58 JB |
211 | if ((dt3155_status[minor].state & DT3155_STATE_MODE) == |
212 | DT3155_STATE_FLD) | |
aa337ef1 | 213 | { |
d241fd58 | 214 | dt3155_fbuffer[minor]->frame_count++; |
aa337ef1 SS |
215 | } |
216 | ||
d241fd58 | 217 | ReadI2C(dt3155_lbase[minor], EVEN_CSR, &i2c_even_csr.reg); |
aa337ef1 SS |
218 | |
219 | /* Clear the interrupt? */ | |
220 | int_csr_r.fld.FLD_END_EVE = 1; | |
221 | ||
222 | /* disable the interrupt if last field */ | |
d241fd58 | 223 | if (dt3155_fbuffer[minor]->stop_acquire) |
aa337ef1 SS |
224 | { |
225 | printk("dt3155: even stopped.\n"); | |
d241fd58 | 226 | dt3155_fbuffer[minor]->even_stopped = 1; |
aa337ef1 SS |
227 | if (i2c_even_csr.fld.SNGL_EVE) |
228 | { | |
229 | int_csr_r.fld.FLD_END_EVE_EN = 0; | |
230 | } | |
231 | else | |
232 | { | |
233 | i2c_even_csr.fld.SNGL_EVE = 1; | |
234 | } | |
235 | } | |
236 | ||
d241fd58 | 237 | WriteMReg((dt3155_lbase[minor] + INT_CSR), int_csr_r.reg); |
aa337ef1 SS |
238 | |
239 | /* Set up next DMA if we are doing FIELDS */ | |
d241fd58 | 240 | if ((dt3155_status[minor].state & DT3155_STATE_MODE) == |
aa337ef1 SS |
241 | DT3155_STATE_FLD) |
242 | { | |
243 | /* GCS (Aug 2, 2002) -- In field mode, dma the odd field | |
244 | into the lower half of the buffer */ | |
d241fd58 JB |
245 | const u32 stride = dt3155_status[minor].config.cols; |
246 | buffer_addr = dt3155_fbuffer[minor]-> | |
247 | frame_info[dt3155_fbuffer[minor]->active_buf].addr | |
aa337ef1 | 248 | + (DT3155_MAX_ROWS / 2) * stride; |
aa337ef1 SS |
249 | local_save_flags(flags); |
250 | local_irq_disable(); | |
d241fd58 | 251 | wake_up_interruptible(&dt3155_read_wait_queue[minor]); |
aa337ef1 SS |
252 | |
253 | /* Set up the DMA address for the next field */ | |
aa337ef1 | 254 | local_irq_restore(flags); |
d241fd58 | 255 | WriteMReg((dt3155_lbase[minor] + ODD_DMA_START), buffer_addr); |
aa337ef1 SS |
256 | } |
257 | ||
258 | /* Check for errors. */ | |
259 | i2c_even_csr.fld.DONE_EVE = 1; | |
d241fd58 | 260 | if (i2c_even_csr.fld.ERROR_EVE) |
aa337ef1 SS |
261 | dt3155_errno = DT_ERR_OVERRUN; |
262 | ||
d241fd58 | 263 | WriteI2C(dt3155_lbase[minor], EVEN_CSR, i2c_even_csr.reg); |
aa337ef1 SS |
264 | |
265 | /* Note that we actually saw an even field meaning */ | |
266 | /* that subsequent odd field complete the frame */ | |
d241fd58 | 267 | dt3155_fbuffer[minor]->even_happened = 1; |
aa337ef1 SS |
268 | |
269 | /* recording the time that the even field finished, this should be */ | |
270 | /* about time in the middle of the frame */ | |
d241fd58 JB |
271 | do_gettimeofday(&(dt3155_fbuffer[minor]-> |
272 | frame_info[dt3155_fbuffer[minor]-> | |
273 | active_buf].time)); | |
aa337ef1 SS |
274 | return; |
275 | } | |
276 | ||
277 | /* ... now handle the odd field */ | |
d241fd58 | 278 | if (int_csr_r.fld.FLD_END_ODD) |
aa337ef1 | 279 | { |
d241fd58 | 280 | ReadI2C(dt3155_lbase[minor], ODD_CSR, &i2c_odd_csr.reg); |
aa337ef1 SS |
281 | |
282 | /* Clear the interrupt? */ | |
283 | int_csr_r.fld.FLD_END_ODD = 1; | |
284 | ||
d241fd58 JB |
285 | if (dt3155_fbuffer[minor]->even_happened || |
286 | (dt3155_status[minor].state & DT3155_STATE_MODE) == | |
aa337ef1 SS |
287 | DT3155_STATE_FLD) |
288 | { | |
d241fd58 | 289 | dt3155_fbuffer[minor]->frame_count++; |
aa337ef1 SS |
290 | } |
291 | ||
d241fd58 JB |
292 | if (dt3155_fbuffer[minor]->stop_acquire && |
293 | dt3155_fbuffer[minor]->even_stopped) | |
aa337ef1 SS |
294 | { |
295 | printk(KERN_DEBUG "dt3155: stopping odd..\n"); | |
d241fd58 | 296 | if (i2c_odd_csr.fld.SNGL_ODD) |
aa337ef1 SS |
297 | { |
298 | /* disable interrupts */ | |
299 | int_csr_r.fld.FLD_END_ODD_EN = 0; | |
d241fd58 | 300 | dt3155_status[minor].state &= ~(DT3155_STATE_STOP|0xff); |
aa337ef1 SS |
301 | |
302 | /* mark the system stopped: */ | |
d241fd58 JB |
303 | dt3155_status[minor].state |= DT3155_STATE_IDLE; |
304 | dt3155_fbuffer[minor]->stop_acquire = 0; | |
305 | dt3155_fbuffer[minor]->even_stopped = 0; | |
aa337ef1 | 306 | |
dcff74ce | 307 | printk(KERN_DEBUG "dt3155: state is now %x\n", |
3a8954e8 | 308 | dt3155_status[minor].state); |
aa337ef1 SS |
309 | } |
310 | else | |
311 | { | |
312 | i2c_odd_csr.fld.SNGL_ODD = 1; | |
313 | } | |
314 | } | |
315 | ||
d241fd58 | 316 | WriteMReg((dt3155_lbase[minor] + INT_CSR), int_csr_r.reg); |
aa337ef1 SS |
317 | |
318 | /* if the odd field has been acquired, then */ | |
319 | /* change the next dma location for both fields */ | |
320 | /* and wake up the process if sleeping */ | |
d241fd58 JB |
321 | if (dt3155_fbuffer[minor]->even_happened || |
322 | (dt3155_status[minor].state & DT3155_STATE_MODE) == | |
323 | DT3155_STATE_FLD) | |
aa337ef1 SS |
324 | { |
325 | ||
aa337ef1 SS |
326 | local_save_flags(flags); |
327 | local_irq_disable(); | |
aa337ef1 SS |
328 | |
329 | #ifdef DEBUG_QUES_B | |
d241fd58 | 330 | printques(minor); |
aa337ef1 | 331 | #endif |
d241fd58 | 332 | if (dt3155_fbuffer[minor]->nbuffers > 2) |
aa337ef1 | 333 | { |
d241fd58 | 334 | if (!are_empty_buffers(minor)) |
aa337ef1 SS |
335 | { |
336 | /* The number of active + locked buffers is | |
337 | * at most 2, and since there are none empty, there | |
338 | * must be at least nbuffers-2 ready buffers. | |
339 | * This is where we 'drop frames', oldest first. */ | |
d241fd58 | 340 | push_empty(pop_ready(minor), minor); |
aa337ef1 SS |
341 | } |
342 | ||
343 | /* The ready_que can't be full, since we know | |
344 | * there is one active buffer right now, so it's safe | |
345 | * to push the active buf on the ready_que. */ | |
d241fd58 | 346 | push_ready(minor, dt3155_fbuffer[minor]->active_buf); |
aa337ef1 | 347 | /* There's at least 1 empty -- make it active */ |
d241fd58 JB |
348 | dt3155_fbuffer[minor]->active_buf = pop_empty(minor); |
349 | dt3155_fbuffer[minor]-> | |
350 | frame_info[dt3155_fbuffer[minor]-> | |
351 | active_buf].tag = ++unique_tag; | |
aa337ef1 SS |
352 | } |
353 | else /* nbuffers == 2, special case */ | |
354 | { /* There is 1 active buffer. | |
355 | * If there is a locked buffer, keep the active buffer | |
356 | * the same -- that means we drop a frame. | |
357 | */ | |
d241fd58 | 358 | if (dt3155_fbuffer[minor]->locked_buf < 0) |
aa337ef1 | 359 | { |
d241fd58 JB |
360 | push_ready(minor, |
361 | dt3155_fbuffer[minor]->active_buf); | |
362 | if (are_empty_buffers(minor)) | |
aa337ef1 | 363 | { |
d241fd58 JB |
364 | dt3155_fbuffer[minor]->active_buf = |
365 | pop_empty(minor); | |
aa337ef1 SS |
366 | } |
367 | else | |
368 | { /* no empty or locked buffers, so use a readybuf */ | |
d241fd58 JB |
369 | dt3155_fbuffer[minor]->active_buf = |
370 | pop_ready(minor); | |
aa337ef1 SS |
371 | } |
372 | } | |
373 | } | |
374 | ||
375 | #ifdef DEBUG_QUES_B | |
d241fd58 | 376 | printques(minor); |
aa337ef1 SS |
377 | #endif |
378 | ||
d241fd58 | 379 | dt3155_fbuffer[minor]->even_happened = 0; |
aa337ef1 | 380 | |
d241fd58 | 381 | wake_up_interruptible(&dt3155_read_wait_queue[minor]); |
aa337ef1 | 382 | |
aa337ef1 | 383 | local_irq_restore(flags); |
aa337ef1 SS |
384 | } |
385 | ||
386 | ||
387 | /* Set up the DMA address for the next frame/field */ | |
d241fd58 JB |
388 | buffer_addr = dt3155_fbuffer[minor]-> |
389 | frame_info[dt3155_fbuffer[minor]->active_buf].addr; | |
390 | if ((dt3155_status[minor].state & DT3155_STATE_MODE) == | |
391 | DT3155_STATE_FLD) | |
aa337ef1 | 392 | { |
d241fd58 | 393 | WriteMReg((dt3155_lbase[minor] + EVEN_DMA_START), buffer_addr); |
aa337ef1 SS |
394 | } |
395 | else | |
396 | { | |
d241fd58 | 397 | WriteMReg((dt3155_lbase[minor] + EVEN_DMA_START), buffer_addr); |
aa337ef1 | 398 | |
d241fd58 JB |
399 | WriteMReg((dt3155_lbase[minor] + ODD_DMA_START), buffer_addr |
400 | + dt3155_status[minor].config.cols); | |
aa337ef1 SS |
401 | } |
402 | ||
403 | /* Do error checking */ | |
404 | i2c_odd_csr.fld.DONE_ODD = 1; | |
d241fd58 | 405 | if (i2c_odd_csr.fld.ERROR_ODD) |
aa337ef1 SS |
406 | dt3155_errno = DT_ERR_OVERRUN; |
407 | ||
d241fd58 | 408 | WriteI2C(dt3155_lbase[minor], ODD_CSR, i2c_odd_csr.reg); |
aa337ef1 SS |
409 | |
410 | return; | |
411 | } | |
412 | /* If we get here, the Odd Field wasn't it either... */ | |
d241fd58 | 413 | printk("neither even nor odd. shared perhaps?\n"); |
aa337ef1 SS |
414 | } |
415 | ||
416 | /***************************************************** | |
417 | * init_isr(int minor) | |
418 | * turns on interupt generation for the card | |
419 | * designated by "minor". | |
420 | * It is called *only* from inside ioctl(). | |
421 | *****************************************************/ | |
422 | static void dt3155_init_isr(int minor) | |
423 | { | |
d241fd58 | 424 | const u32 stride = dt3155_status[minor].config.cols; |
aa337ef1 | 425 | |
d241fd58 | 426 | switch (dt3155_status[minor].state & DT3155_STATE_MODE) |
aa337ef1 SS |
427 | { |
428 | case DT3155_STATE_FLD: | |
429 | { | |
d241fd58 JB |
430 | even_dma_start_r = dt3155_status[minor]. |
431 | fbuffer.frame_info[dt3155_status[minor].fbuffer.active_buf].addr; | |
aa337ef1 SS |
432 | even_dma_stride_r = 0; |
433 | odd_dma_stride_r = 0; | |
434 | ||
d241fd58 | 435 | WriteMReg((dt3155_lbase[minor] + EVEN_DMA_START), |
aa337ef1 | 436 | even_dma_start_r); |
d241fd58 | 437 | WriteMReg((dt3155_lbase[minor] + EVEN_DMA_STRIDE), |
aa337ef1 | 438 | even_dma_stride_r); |
d241fd58 | 439 | WriteMReg((dt3155_lbase[minor] + ODD_DMA_STRIDE), |
aa337ef1 SS |
440 | odd_dma_stride_r); |
441 | break; | |
442 | } | |
443 | ||
444 | case DT3155_STATE_FRAME: | |
445 | default: | |
446 | { | |
d241fd58 JB |
447 | even_dma_start_r = dt3155_status[minor]. |
448 | fbuffer.frame_info[dt3155_status[minor].fbuffer.active_buf].addr; | |
aa337ef1 SS |
449 | odd_dma_start_r = even_dma_start_r + stride; |
450 | even_dma_stride_r = stride; | |
451 | odd_dma_stride_r = stride; | |
452 | ||
d241fd58 | 453 | WriteMReg((dt3155_lbase[minor] + EVEN_DMA_START), |
aa337ef1 | 454 | even_dma_start_r); |
d241fd58 | 455 | WriteMReg((dt3155_lbase[minor] + ODD_DMA_START), |
aa337ef1 | 456 | odd_dma_start_r); |
d241fd58 | 457 | WriteMReg((dt3155_lbase[minor] + EVEN_DMA_STRIDE), |
aa337ef1 | 458 | even_dma_stride_r); |
d241fd58 | 459 | WriteMReg((dt3155_lbase[minor] + ODD_DMA_STRIDE), |
aa337ef1 SS |
460 | odd_dma_stride_r); |
461 | break; | |
462 | } | |
463 | } | |
464 | ||
465 | /* 50/60 Hz should be set before this point but let's make sure it is */ | |
466 | /* right anyway */ | |
467 | ||
c8d1a126 | 468 | ReadI2C(dt3155_lbase[minor], CSR2, &i2c_csr2.reg); |
aa337ef1 | 469 | i2c_csr2.fld.HZ50 = FORMAT50HZ; |
c8d1a126 | 470 | WriteI2C(dt3155_lbase[minor], CSR2, i2c_csr2.reg); |
aa337ef1 SS |
471 | |
472 | /* enable busmaster chip, clear flags */ | |
473 | ||
474 | /* | |
475 | * TODO: | |
476 | * shouldn't we be concered with continuous values of | |
477 | * DT3155_SNAP & DT3155_ACQ here? (SS) | |
478 | */ | |
479 | ||
480 | csr1_r.reg = 0; | |
481 | csr1_r.fld.CAP_CONT_EVE = 1; /* use continuous capture bits to */ | |
482 | csr1_r.fld.CAP_CONT_ODD = 1; /* enable */ | |
483 | csr1_r.fld.FLD_DN_EVE = 1; /* writing a 1 clears flags */ | |
484 | csr1_r.fld.FLD_DN_ODD = 1; | |
485 | csr1_r.fld.SRST = 1; /* reset - must be 1 */ | |
486 | csr1_r.fld.FIFO_EN = 1; /* fifo control - must be 1 */ | |
487 | csr1_r.fld.FLD_CRPT_EVE = 1; /* writing a 1 clears flags */ | |
488 | csr1_r.fld.FLD_CRPT_ODD = 1; | |
489 | ||
d241fd58 | 490 | WriteMReg((dt3155_lbase[minor] + CSR1),csr1_r.reg); |
aa337ef1 SS |
491 | |
492 | /* Enable interrupts at the end of each field */ | |
493 | ||
494 | int_csr_r.reg = 0; | |
495 | int_csr_r.fld.FLD_END_EVE_EN = 1; | |
496 | int_csr_r.fld.FLD_END_ODD_EN = 1; | |
497 | int_csr_r.fld.FLD_START_EN = 0; | |
498 | ||
d241fd58 | 499 | WriteMReg((dt3155_lbase[minor] + INT_CSR), int_csr_r.reg); |
aa337ef1 SS |
500 | |
501 | /* start internal BUSY bits */ | |
502 | ||
d241fd58 | 503 | ReadI2C(dt3155_lbase[minor], CSR2, &i2c_csr2.reg); |
aa337ef1 SS |
504 | i2c_csr2.fld.BUSY_ODD = 1; |
505 | i2c_csr2.fld.BUSY_EVE = 1; | |
d241fd58 | 506 | WriteI2C(dt3155_lbase[minor], CSR2, i2c_csr2.reg); |
aa337ef1 SS |
507 | |
508 | /* Now its up to the interrupt routine!! */ | |
509 | ||
510 | return; | |
511 | } | |
512 | ||
513 | ||
514 | /***************************************************** | |
515 | * ioctl() | |
516 | * | |
517 | *****************************************************/ | |
dcff74ce GKH |
518 | static int dt3155_ioctl(struct inode *inode, |
519 | struct file *file, | |
520 | unsigned int cmd, | |
521 | unsigned long arg) | |
aa337ef1 SS |
522 | { |
523 | int minor = MINOR(inode->i_rdev); /* What device are we ioctl()'ing? */ | |
524 | ||
d241fd58 | 525 | if (minor >= MAXBOARDS || minor < 0) |
aa337ef1 SS |
526 | return -ENODEV; |
527 | ||
528 | /* make sure it is valid command */ | |
529 | if (_IOC_NR(cmd) > DT3155_IOC_MAXNR) | |
530 | { | |
531 | printk("DT3155: invalid IOCTL(0x%x)\n",cmd); | |
532 | printk("DT3155: Valid commands (0x%x), (0x%x), (0x%x), (0x%x), (0x%x)\n", | |
f721ad7a GKH |
533 | (unsigned int)DT3155_GET_CONFIG, |
534 | (unsigned int)DT3155_SET_CONFIG, | |
535 | (unsigned int)DT3155_START, | |
536 | (unsigned int)DT3155_STOP, | |
537 | (unsigned int)DT3155_FLUSH); | |
aa337ef1 SS |
538 | return -EINVAL; |
539 | } | |
540 | ||
541 | switch (cmd) | |
542 | { | |
543 | case DT3155_SET_CONFIG: | |
544 | { | |
545 | if (dt3155_status[minor].state != DT3155_STATE_IDLE) | |
546 | return -EBUSY; | |
547 | ||
548 | { | |
8b692e69 | 549 | struct dt3155_config tmp; |
aa337ef1 SS |
550 | if (copy_from_user((void *)&tmp, (void *) arg, sizeof(tmp))) |
551 | return -EFAULT; | |
552 | /* check for valid settings */ | |
553 | if (tmp.rows > DT3155_MAX_ROWS || | |
554 | tmp.cols > DT3155_MAX_COLS || | |
555 | (tmp.acq_mode != DT3155_MODE_FRAME && | |
556 | tmp.acq_mode != DT3155_MODE_FIELD) || | |
557 | (tmp.continuous != DT3155_SNAP && | |
558 | tmp.continuous != DT3155_ACQ)) | |
559 | { | |
560 | return -EINVAL; | |
561 | } | |
562 | dt3155_status[minor].config = tmp; | |
563 | } | |
564 | return 0; | |
565 | } | |
566 | case DT3155_GET_CONFIG: | |
567 | { | |
568 | if (copy_to_user((void *) arg, (void *) &dt3155_status[minor], | |
923c1244 | 569 | sizeof(struct dt3155_status))) |
aa337ef1 SS |
570 | return -EFAULT; |
571 | return 0; | |
572 | } | |
573 | case DT3155_FLUSH: /* Flushes the buffers -- ensures fresh data */ | |
574 | { | |
575 | if (dt3155_status[minor].state != DT3155_STATE_IDLE) | |
576 | return -EBUSY; | |
577 | return dt3155_flush(minor); | |
578 | } | |
579 | case DT3155_STOP: | |
580 | { | |
581 | if (dt3155_status[minor].state & DT3155_STATE_STOP || | |
582 | dt3155_status[minor].fbuffer.stop_acquire) | |
583 | return -EBUSY; | |
584 | ||
585 | if (dt3155_status[minor].state == DT3155_STATE_IDLE) | |
586 | return 0; | |
587 | ||
588 | quick_stop(minor); | |
589 | if (copy_to_user((void *) arg, (void *) &dt3155_status[minor], | |
923c1244 | 590 | sizeof(struct dt3155_status))) |
aa337ef1 SS |
591 | return -EFAULT; |
592 | return 0; | |
593 | } | |
594 | case DT3155_START: | |
595 | { | |
596 | if (dt3155_status[minor].state != DT3155_STATE_IDLE) | |
597 | return -EBUSY; | |
598 | ||
599 | dt3155_status[minor].fbuffer.stop_acquire = 0; | |
600 | dt3155_status[minor].fbuffer.frame_count = 0; | |
601 | ||
602 | /* Set the MODE in the status -- we default to FRAME */ | |
603 | if (dt3155_status[minor].config.acq_mode == DT3155_MODE_FIELD) | |
604 | { | |
605 | dt3155_status[minor].state = DT3155_STATE_FLD; | |
606 | } | |
607 | else | |
608 | { | |
609 | dt3155_status[minor].state = DT3155_STATE_FRAME; | |
610 | } | |
611 | ||
612 | dt3155_init_isr(minor); | |
d241fd58 | 613 | if (copy_to_user((void *) arg, (void *) &dt3155_status[minor], |
923c1244 | 614 | sizeof(struct dt3155_status))) |
aa337ef1 SS |
615 | return -EFAULT; |
616 | return 0; | |
617 | } | |
618 | default: | |
619 | { | |
620 | printk("DT3155: invalid IOCTL(0x%x)\n",cmd); | |
621 | printk("DT3155: Valid commands (0x%x), (0x%x), (0x%x), (0x%x), (0x%x)\n", | |
f721ad7a GKH |
622 | (unsigned int)DT3155_GET_CONFIG, |
623 | (unsigned int)DT3155_SET_CONFIG, | |
aa337ef1 SS |
624 | DT3155_START, DT3155_STOP, DT3155_FLUSH); |
625 | return -ENOSYS; | |
626 | } | |
627 | } | |
628 | return -ENOSYS; | |
629 | } | |
630 | ||
631 | /***************************************************** | |
632 | * mmap() | |
633 | * | |
634 | * only allow the user to mmap the registers and buffer | |
635 | * It is quite possible that this is broken, since the | |
636 | * addition of of the capacity for two cards!!!!!!!! | |
637 | * It *looks* like it should work but since I'm not | |
638 | * sure how to use it, I'm not actually sure. (NJC? ditto by SS) | |
639 | *****************************************************/ | |
640 | static int dt3155_mmap (struct file * file, struct vm_area_struct * vma) | |
641 | { | |
642 | /* which device are we mmapping? */ | |
643 | int minor = MINOR(file->f_dentry->d_inode->i_rdev); | |
644 | unsigned long offset; | |
aa337ef1 SS |
645 | offset = vma->vm_pgoff << PAGE_SHIFT; |
646 | ||
647 | if (offset >= __pa(high_memory) || (file->f_flags & O_SYNC)) | |
648 | vma->vm_flags |= VM_IO; | |
649 | ||
650 | /* Don't try to swap out physical pages.. */ | |
651 | vma->vm_flags |= VM_RESERVED; | |
652 | ||
aa337ef1 SS |
653 | /* they are mapping the registers or the buffer */ |
654 | if ((offset == dt3155_status[minor].reg_addr && | |
655 | vma->vm_end - vma->vm_start == PCI_PAGE_SIZE) || | |
656 | (offset == dt3155_status[minor].mem_addr && | |
657 | vma->vm_end - vma->vm_start == dt3155_status[minor].mem_size)) | |
658 | { | |
aa337ef1 SS |
659 | if (remap_pfn_range(vma, |
660 | vma->vm_start, | |
661 | offset >> PAGE_SHIFT, | |
662 | vma->vm_end - vma->vm_start, | |
2141ec62 | 663 | vma->vm_page_prot)) { |
aa337ef1 SS |
664 | printk("DT3155: remap_page_range() failed.\n"); |
665 | return -EAGAIN; | |
666 | } | |
667 | } | |
668 | else | |
669 | { | |
670 | printk("DT3155: dt3155_mmap() bad call.\n"); | |
671 | return -ENXIO; | |
672 | } | |
673 | ||
674 | return 0; | |
675 | } | |
676 | ||
677 | ||
678 | /***************************************************** | |
679 | * open() | |
680 | * | |
681 | * Our special open code. | |
682 | * MOD_INC_USE_COUNT make sure that the driver memory is not freed | |
683 | * while the device is in use. | |
684 | *****************************************************/ | |
d241fd58 | 685 | static int dt3155_open(struct inode* inode, struct file* filep) |
aa337ef1 SS |
686 | { |
687 | int minor = MINOR(inode->i_rdev); /* what device are we opening? */ | |
d241fd58 | 688 | if (dt3155_dev_open[minor]) { |
aa337ef1 SS |
689 | printk ("DT3155: Already opened by another process.\n"); |
690 | return -EBUSY; | |
691 | } | |
692 | ||
d241fd58 | 693 | if (dt3155_status[minor].device_installed==0) |
aa337ef1 SS |
694 | { |
695 | printk("DT3155 Open Error: No such device dt3155 minor number %d\n", | |
696 | minor); | |
697 | return -EIO; | |
698 | } | |
699 | ||
d241fd58 | 700 | if (dt3155_status[minor].state != DT3155_STATE_IDLE) { |
dcff74ce | 701 | printk ("DT3155: Not in idle state (state = %x)\n", |
d241fd58 | 702 | dt3155_status[minor].state); |
aa337ef1 SS |
703 | return -EBUSY; |
704 | } | |
705 | ||
706 | printk("DT3155: Device opened.\n"); | |
707 | ||
d241fd58 | 708 | dt3155_dev_open[minor] = 1 ; |
aa337ef1 | 709 | |
d241fd58 | 710 | dt3155_flush(minor); |
aa337ef1 SS |
711 | |
712 | /* Disable ALL interrupts */ | |
713 | int_csr_r.reg = 0; | |
d241fd58 | 714 | WriteMReg((dt3155_lbase[minor] + INT_CSR), int_csr_r.reg); |
aa337ef1 | 715 | |
aa337ef1 | 716 | init_waitqueue_head(&(dt3155_read_wait_queue[minor])); |
aa337ef1 SS |
717 | |
718 | return 0; | |
719 | } | |
720 | ||
721 | ||
722 | /***************************************************** | |
723 | * close() | |
724 | * | |
725 | * Now decrement the use count. | |
726 | * | |
727 | *****************************************************/ | |
d241fd58 | 728 | static int dt3155_close(struct inode *inode, struct file *filep) |
aa337ef1 SS |
729 | { |
730 | int minor; | |
731 | ||
732 | minor = MINOR(inode->i_rdev); /* which device are we closing */ | |
d241fd58 | 733 | if (!dt3155_dev_open[minor]) |
aa337ef1 SS |
734 | { |
735 | printk("DT3155: attempt to CLOSE a not OPEN device\n"); | |
736 | } | |
737 | else | |
738 | { | |
d241fd58 | 739 | dt3155_dev_open[minor] = 0; |
aa337ef1 | 740 | |
d241fd58 | 741 | if (dt3155_status[minor].state != DT3155_STATE_IDLE) |
aa337ef1 SS |
742 | { |
743 | quick_stop(minor); | |
744 | } | |
745 | } | |
746 | return 0; | |
747 | } | |
748 | ||
749 | /***************************************************** | |
750 | * read() | |
751 | * | |
752 | *****************************************************/ | |
f721ad7a GKH |
753 | static ssize_t dt3155_read(struct file *filep, char __user *buf, |
754 | size_t count, loff_t *ppos) | |
aa337ef1 SS |
755 | { |
756 | /* which device are we reading from? */ | |
757 | int minor = MINOR(filep->f_dentry->d_inode->i_rdev); | |
3a8954e8 | 758 | u32 offset; |
aa337ef1 | 759 | int frame_index; |
7f76c52f | 760 | struct frame_info *frame_info; |
aa337ef1 SS |
761 | |
762 | /* TODO: this should check the error flag and */ | |
763 | /* return an error on hardware failures */ | |
5019d284 | 764 | if (count != sizeof(struct dt3155_read)) |
aa337ef1 SS |
765 | { |
766 | printk("DT3155 ERROR (NJC): count is not right\n"); | |
767 | return -EINVAL; | |
768 | } | |
769 | ||
770 | ||
771 | /* Hack here -- I'm going to allow reading even when idle. | |
772 | * this is so that the frames can be read after STOP has | |
773 | * been called. Leaving it here, commented out, as a reminder | |
774 | * for a short while to make sure there are no problems. | |
775 | * Note that if the driver is not opened in non_blocking mode, | |
776 | * and the device is idle, then it could sit here forever! */ | |
777 | ||
778 | /* if (dt3155_status[minor].state == DT3155_STATE_IDLE)*/ | |
779 | /* return -EBUSY;*/ | |
780 | ||
aa337ef1 SS |
781 | /* non-blocking reads should return if no data */ |
782 | if (filep->f_flags & O_NDELAY) | |
783 | { | |
784 | if ((frame_index = dt3155_get_ready_buffer(minor)) < 0) { | |
d241fd58 | 785 | /*printk("dt3155: no buffers available (?)\n");*/ |
aa337ef1 SS |
786 | /* printques(minor); */ |
787 | return -EAGAIN; | |
788 | } | |
789 | } | |
790 | else | |
791 | { | |
792 | /* | |
793 | * sleep till data arrives , or we get interrupted. | |
794 | * Note that wait_event_interruptible() does not actually | |
795 | * sleep/wait if it's condition evaluates to true upon entry. | |
796 | */ | |
797 | wait_event_interruptible(dt3155_read_wait_queue[minor], | |
798 | (frame_index = dt3155_get_ready_buffer(minor)) | |
799 | >= 0); | |
800 | ||
801 | if (frame_index < 0) | |
802 | { | |
803 | printk ("DT3155: read: interrupted\n"); | |
804 | quick_stop (minor); | |
805 | printques(minor); | |
806 | return -EINTR; | |
807 | } | |
808 | } | |
809 | ||
7f76c52f | 810 | frame_info = &dt3155_status[minor].fbuffer.frame_info[frame_index]; |
aa337ef1 SS |
811 | |
812 | /* make this an offset */ | |
7f76c52f | 813 | offset = frame_info->addr - dt3155_status[minor].mem_addr; |
aa337ef1 SS |
814 | |
815 | put_user(offset, (unsigned int *) buf); | |
3a8954e8 | 816 | buf += sizeof(u32); |
d241fd58 | 817 | put_user(dt3155_status[minor].fbuffer.frame_count, (unsigned int *) buf); |
3a8954e8 | 818 | buf += sizeof(u32); |
aa337ef1 | 819 | put_user(dt3155_status[minor].state, (unsigned int *) buf); |
3a8954e8 | 820 | buf += sizeof(u32); |
7f76c52f | 821 | if (copy_to_user(buf, frame_info, sizeof(*frame_info))) |
aa337ef1 SS |
822 | return -EFAULT; |
823 | ||
5019d284 | 824 | return sizeof(struct dt3155_read); |
aa337ef1 SS |
825 | } |
826 | ||
827 | static unsigned int dt3155_poll (struct file * filp, poll_table *wait) | |
828 | { | |
829 | int minor = MINOR(filp->f_dentry->d_inode->i_rdev); | |
830 | ||
831 | if (!is_ready_buf_empty(minor)) | |
832 | return POLLIN | POLLRDNORM; | |
833 | ||
834 | poll_wait (filp, &dt3155_read_wait_queue[minor], wait); | |
835 | ||
836 | return 0; | |
837 | } | |
838 | ||
b1f2ac07 AB |
839 | static long |
840 | dt3155_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |
841 | { | |
842 | int ret; | |
843 | ||
844 | lock_kernel(); | |
845 | ret = dt3155_ioctl(file->f_path.dentry->d_inode, file, cmd, arg); | |
846 | unlock_kernel(); | |
847 | ||
848 | return ret; | |
849 | } | |
aa337ef1 SS |
850 | |
851 | /***************************************************** | |
852 | * file operations supported by DT3155 driver | |
853 | * needed by init_module | |
854 | * register_chrdev | |
855 | *****************************************************/ | |
856 | static struct file_operations dt3155_fops = { | |
b1f2ac07 AB |
857 | .read = dt3155_read, |
858 | .unlocked_ioctl = dt3155_unlocked_ioctl, | |
859 | .mmap = dt3155_mmap, | |
860 | .poll = dt3155_poll, | |
861 | .open = dt3155_open, | |
862 | .release = dt3155_close | |
aa337ef1 SS |
863 | }; |
864 | ||
865 | ||
866 | /***************************************************** | |
867 | * find_PCI(); | |
868 | * | |
869 | * PCI has been totally reworked in 2.1.. | |
870 | *****************************************************/ | |
871 | static int find_PCI (void) | |
872 | { | |
873 | struct pci_dev *pci_dev = NULL; | |
874 | int error, pci_index = 0; | |
875 | unsigned short rev_device; | |
876 | unsigned long base; | |
877 | unsigned char irq; | |
878 | ||
6910dadf | 879 | while ((pci_dev = pci_get_device |
aa337ef1 SS |
880 | (DT3155_VENDORID, DT3155_DEVICEID, pci_dev)) != NULL) |
881 | { | |
882 | pci_index ++; | |
883 | ||
884 | /* Is it really there? */ | |
885 | if ((error = | |
886 | pci_read_config_word(pci_dev, PCI_CLASS_DEVICE, &rev_device))) | |
887 | continue; | |
888 | ||
889 | /* Found a board */ | |
890 | DT_3155_DEBUG_MSG("DT3155: Device number %d \n", pci_index); | |
891 | ||
892 | /* Make sure the driver was compiled with enough buffers to handle | |
893 | this many boards */ | |
894 | if (pci_index > MAXBOARDS) { | |
895 | printk("DT3155: ERROR - found %d devices, but driver only configured " | |
896 | "for %d devices\n" | |
897 | "DT3155: Please change MAXBOARDS in dt3155.h\n", | |
898 | pci_index, MAXBOARDS); | |
6910dadf | 899 | goto err; |
aa337ef1 SS |
900 | } |
901 | ||
902 | /* Now, just go out and make sure that this/these device(s) is/are | |
903 | actually mapped into the kernel address space */ | |
d241fd58 | 904 | if ((error = pci_read_config_dword(pci_dev, PCI_BASE_ADDRESS_0, |
dcff74ce | 905 | (u32 *) &base))) |
aa337ef1 SS |
906 | { |
907 | printk("DT3155: Was not able to find device \n"); | |
6910dadf | 908 | goto err; |
aa337ef1 SS |
909 | } |
910 | ||
911 | DT_3155_DEBUG_MSG("DT3155: Base address 0 for device is %lx \n", base); | |
912 | dt3155_status[pci_index-1].reg_addr = base; | |
913 | ||
914 | /* Remap the base address to a logical address through which we | |
915 | * can access it. */ | |
d241fd58 JB |
916 | dt3155_lbase[pci_index - 1] = ioremap(base,PCI_PAGE_SIZE); |
917 | dt3155_status[pci_index - 1].reg_addr = base; | |
f721ad7a GKH |
918 | DT_3155_DEBUG_MSG("DT3155: New logical address is %p \n", |
919 | dt3155_lbase[pci_index-1]); | |
d241fd58 | 920 | if (!dt3155_lbase[pci_index-1]) |
aa337ef1 SS |
921 | { |
922 | printk("DT3155: Unable to remap control registers\n"); | |
6910dadf | 923 | goto err; |
aa337ef1 SS |
924 | } |
925 | ||
d241fd58 | 926 | if ((error = pci_read_config_byte(pci_dev, PCI_INTERRUPT_LINE, &irq))) |
aa337ef1 SS |
927 | { |
928 | printk("DT3155: Was not able to find device \n"); | |
6910dadf | 929 | goto err; |
aa337ef1 SS |
930 | } |
931 | ||
932 | DT_3155_DEBUG_MSG("DT3155: IRQ is %d \n",irq); | |
d241fd58 | 933 | dt3155_status[pci_index-1].irq = irq; |
aa337ef1 | 934 | /* Set flag: kth device found! */ |
d241fd58 | 935 | dt3155_status[pci_index-1].device_installed = 1; |
f721ad7a | 936 | printk("DT3155: Installing device %d w/irq %d and address %p\n", |
aa337ef1 | 937 | pci_index, |
3a8954e8 | 938 | dt3155_status[pci_index-1].irq, |
f721ad7a | 939 | dt3155_lbase[pci_index-1]); |
aa337ef1 SS |
940 | |
941 | } | |
942 | ndevices = pci_index; | |
943 | ||
944 | return DT_3155_SUCCESS; | |
6910dadf SH |
945 | |
946 | err: | |
947 | pci_dev_put(pci_dev); | |
948 | return DT_3155_FAILURE; | |
aa337ef1 SS |
949 | } |
950 | ||
3a8954e8 | 951 | u32 allocatorAddr = 0; |
aa337ef1 SS |
952 | |
953 | /***************************************************** | |
954 | * init_module() | |
955 | *****************************************************/ | |
956 | int init_module(void) | |
957 | { | |
958 | int index; | |
959 | int rcode = 0; | |
d241fd58 | 960 | char *devname[MAXBOARDS]; |
aa337ef1 | 961 | |
d241fd58 | 962 | devname[0] = "dt3155a"; |
aa337ef1 | 963 | #if MAXBOARDS == 2 |
d241fd58 | 964 | devname[1] = "dt3155b"; |
aa337ef1 SS |
965 | #endif |
966 | ||
967 | printk("DT3155: Loading module...\n"); | |
968 | ||
969 | /* Register the device driver */ | |
d241fd58 JB |
970 | rcode = register_chrdev(dt3155_major, "dt3155", &dt3155_fops); |
971 | if(rcode < 0) | |
aa337ef1 | 972 | { |
d241fd58 | 973 | printk(KERN_INFO "DT3155: register_chrdev failed \n"); |
aa337ef1 SS |
974 | return rcode; |
975 | } | |
976 | ||
d241fd58 | 977 | if(dt3155_major == 0) |
aa337ef1 SS |
978 | dt3155_major = rcode; /* dynamic */ |
979 | ||
980 | ||
981 | /* init the status variables. */ | |
982 | /* DMA memory is taken care of in setup_buffers() */ | |
d241fd58 | 983 | for (index = 0; index < MAXBOARDS; index++) |
aa337ef1 | 984 | { |
d241fd58 JB |
985 | dt3155_status[index].config.acq_mode = DT3155_MODE_FRAME; |
986 | dt3155_status[index].config.continuous = DT3155_ACQ; | |
987 | dt3155_status[index].config.cols = DT3155_MAX_COLS; | |
988 | dt3155_status[index].config.rows = DT3155_MAX_ROWS; | |
989 | dt3155_status[index].state = DT3155_STATE_IDLE; | |
aa337ef1 SS |
990 | |
991 | /* find_PCI() will check if devices are installed; */ | |
992 | /* first assume they're not: */ | |
d241fd58 JB |
993 | dt3155_status[index].mem_addr = 0; |
994 | dt3155_status[index].mem_size = 0; | |
995 | dt3155_status[index].state = DT3155_STATE_IDLE; | |
996 | dt3155_status[index].device_installed = 0; | |
aa337ef1 SS |
997 | } |
998 | ||
999 | /* Now let's find the hardware. find_PCI() will set ndevices to the | |
1000 | * number of cards found in this machine. */ | |
aa337ef1 | 1001 | { |
d241fd58 | 1002 | if ((rcode = find_PCI()) != DT_3155_SUCCESS) |
aa337ef1 SS |
1003 | { |
1004 | printk("DT3155 error: find_PCI() failed to find dt3155 board(s)\n"); | |
d241fd58 | 1005 | unregister_chrdev(dt3155_major, "dt3155"); |
aa337ef1 SS |
1006 | return rcode; |
1007 | } | |
1008 | } | |
1009 | ||
1010 | /* Ok, time to setup the frame buffers */ | |
d241fd58 | 1011 | if((rcode = dt3155_setup_buffers(&allocatorAddr)) < 0) |
aa337ef1 SS |
1012 | { |
1013 | printk("DT3155: Error: setting up buffer not large enough."); | |
d241fd58 | 1014 | unregister_chrdev(dt3155_major, "dt3155"); |
aa337ef1 SS |
1015 | return rcode; |
1016 | } | |
1017 | ||
1018 | /* If we are this far, then there is enough RAM */ | |
1019 | /* for the buffers: Print the configuration. */ | |
d241fd58 | 1020 | for( index = 0; index < ndevices; index++) |
aa337ef1 SS |
1021 | { |
1022 | printk("DT3155: Device = %d; acq_mode = %d; " | |
1023 | "continuous = %d; cols = %d; rows = %d;\n", | |
1024 | index , | |
d241fd58 JB |
1025 | dt3155_status[index].config.acq_mode, |
1026 | dt3155_status[index].config.continuous, | |
1027 | dt3155_status[index].config.cols, | |
1028 | dt3155_status[index].config.rows); | |
aa337ef1 | 1029 | printk("DT3155: m_addr = 0x%x; m_size = %ld; " |
dcff74ce | 1030 | "state = %d; device_installed = %d\n", |
d241fd58 JB |
1031 | dt3155_status[index].mem_addr, |
1032 | (long int)dt3155_status[index].mem_size, | |
1033 | dt3155_status[index].state, | |
1034 | dt3155_status[index].device_installed); | |
aa337ef1 SS |
1035 | } |
1036 | ||
1037 | /* Disable ALL interrupts */ | |
1038 | int_csr_r.reg = 0; | |
d241fd58 | 1039 | for( index = 0; index < ndevices; index++) |
aa337ef1 | 1040 | { |
d241fd58 JB |
1041 | WriteMReg((dt3155_lbase[index] + INT_CSR), int_csr_r.reg); |
1042 | if(dt3155_status[index].device_installed) | |
aa337ef1 SS |
1043 | { |
1044 | /* | |
1045 | * This driver *looks* like it can handle sharing interrupts, | |
1046 | * but I can't actually test myself. I've had reports that it | |
1047 | * DOES work so I'll enable it for now. This comment will remain | |
1048 | * as a reminder in case any problems arise. (SS) | |
1049 | */ | |
1050 | /* in older kernels flags are: SA_SHIRQ | SA_INTERRUPT */ | |
d241fd58 JB |
1051 | rcode = request_irq(dt3155_status[index].irq, (void *)dt3155_isr, |
1052 | IRQF_SHARED | IRQF_DISABLED, devname[index], | |
aa337ef1 | 1053 | (void*) &dt3155_status[index]); |
d241fd58 | 1054 | if(rcode < 0) |
aa337ef1 SS |
1055 | { |
1056 | printk("DT3155: minor %d request_irq failed for IRQ %d\n", | |
1057 | index, dt3155_status[index].irq); | |
d241fd58 | 1058 | unregister_chrdev(dt3155_major, "dt3155"); |
aa337ef1 SS |
1059 | return rcode; |
1060 | } | |
1061 | } | |
1062 | } | |
1063 | ||
1064 | printk("DT3155: finished loading\n"); | |
1065 | ||
1066 | return 0; | |
1067 | } | |
1068 | ||
1069 | /***************************************************** | |
1070 | * cleanup_module(void) | |
1071 | * | |
1072 | *****************************************************/ | |
1073 | void cleanup_module(void) | |
1074 | { | |
1075 | int index; | |
1076 | ||
1077 | printk("DT3155: cleanup_module called\n"); | |
1078 | ||
1079 | /* removed DMA allocated with the allocator */ | |
1080 | #ifdef STANDALONE_ALLOCATOR | |
1081 | if (allocatorAddr != 0) | |
1082 | allocator_free_dma(allocatorAddr); | |
1083 | #else | |
1084 | allocator_cleanup(); | |
1085 | #endif | |
1086 | ||
d241fd58 | 1087 | unregister_chrdev(dt3155_major, "dt3155"); |
aa337ef1 | 1088 | |
d241fd58 | 1089 | for(index = 0; index < ndevices; index++) |
aa337ef1 | 1090 | { |
d241fd58 | 1091 | if(dt3155_status[index].device_installed == 1) |
aa337ef1 | 1092 | { |
d241fd58 JB |
1093 | printk("DT3155: Freeing irq %d for device %d\n", |
1094 | dt3155_status[index].irq, index); | |
1095 | free_irq(dt3155_status[index].irq, (void*)&dt3155_status[index]); | |
aa337ef1 SS |
1096 | } |
1097 | } | |
aa337ef1 SS |
1098 | } |
1099 |