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