Rev 9 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
9 | f9daq | 1 | //**************************************************************************** |
2 | // Copyright (C) 2000-2004 ARW Elektronik Germany |
||
3 | // |
||
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., 675 Mass Ave, Cambridge, MA 02139, USA. |
||
18 | // |
||
19 | // This product is not authorized for use as critical component in |
||
20 | // life support systems without the express written approval of |
||
21 | // ARW Elektronik Germany. |
||
22 | // |
||
23 | // Please announce changes and hints to ARW Elektronik |
||
24 | // |
||
25 | // Maintainer(s): Klaus Hitschler (klaus.hitschler@gmx.de) |
||
26 | // |
||
27 | //**************************************************************************** |
||
28 | |||
29 | //**************************************************************************** |
||
30 | // |
||
31 | // fops.c -- the file operations module for the PCIVME PCI to VME Interface |
||
32 | // |
||
33 | // $Log: fops.c,v $ |
||
34 | // Revision 1.11 2005/03/01 10:56:12 klaus |
||
35 | // removed warnings with gcc 3.3.3 |
||
36 | // |
||
37 | // Revision 1.10 2004/08/13 19:23:26 klaus |
||
38 | // conversion to kernel-version 2.6, released version 3.0 |
||
39 | // |
||
40 | // Revision 1.9 2003/06/27 17:25:52 klaus |
||
41 | // incomplete try to get mmap() with nopage() running for automatic page switch |
||
42 | // |
||
43 | // Revision 1.8 2002/10/20 18:06:51 klaus |
||
44 | // changed error handling |
||
45 | // |
||
46 | // Revision 1.7 2002/10/18 21:56:28 klaus |
||
47 | // completed functional features, untested |
||
48 | // |
||
49 | // Revision 1.6 2002/10/18 21:56:28 klaus |
||
50 | // completed functional features, untested |
||
51 | // |
||
52 | // Revision 1.5 2002/10/17 19:05:03 klaus |
||
53 | // VME access is working through test to lib to driver |
||
54 | // |
||
55 | //**************************************************************************** |
||
56 | |||
57 | /*--- INCLUDES -----------------------------------------------------------------------------------*/ |
||
58 | #include "common.h" /* must be the first include */ |
||
59 | |||
60 | #include <linux/kernel.h> /* printk() */ |
||
61 | #include <linux/module.h> /* only here ?cause of MAJOR ... */ |
||
62 | #include <linux/pci.h> |
||
63 | #include <linux/list.h> |
||
64 | #include <asm/errno.h> |
||
65 | #include <asm/types.h> |
||
66 | #include <asm/uaccess.h> |
||
67 | |||
68 | #include <linux/sched.h> |
||
69 | #include <linux/fs.h> |
||
70 | #if HAVE_UNLOCKED_IOCTL |
||
71 | #include <linux/mutex.h> |
||
72 | #else |
||
73 | #include <linux/smp_lock.h> |
||
74 | #endif |
||
75 | |||
76 | |||
77 | #include "fops.h" |
||
78 | #include "plx9050.h" |
||
79 | #include "pcivme.h" /* the common ioctl commands and structures between driver and application */ |
||
80 | #include "main.h" |
||
81 | #include "askpci.h" |
||
82 | #include "pciif.h" |
||
83 | #include "vic.h" |
||
84 | #include "vme.h" |
||
85 | |||
86 | /*--- DEFINES ------------------------------------------------------------------------------------*/ |
||
87 | |||
88 | #ifndef MINOR |
||
89 | #define MINOR(x) minor(x) // since 2.5.? |
||
90 | #endif |
||
91 | |||
92 | static PCIVME_INIT_ELEMENT init_element[] = |
||
93 | {{LCR, WORD_ACCESS, PLX9050_INTCSR, DISABLE_PCIADA_IRQS}, // disable interrupts |
||
94 | {LCR, WORD_ACCESS, PLX9050_CNTRL, RELEASE_VMEMM}, // enable interface |
||
95 | |||
96 | {VIC, BYTE_ACCESS, VIICR, 0xf8+1}, // VIICR |
||
97 | |||
98 | {VIC, BYTE_ACCESS, VICR1, 0x78+1}, // VICR1 |
||
99 | {VIC, BYTE_ACCESS, VICR2, 0x78+2}, |
||
100 | {VIC, BYTE_ACCESS, VICR3, 0x78+3}, |
||
101 | {VIC, BYTE_ACCESS, VICR4, 0x78+4}, |
||
102 | {VIC, BYTE_ACCESS, VICR5, 0x78+5}, |
||
103 | {VIC, BYTE_ACCESS, VICR6, 0x78+6}, |
||
104 | {VIC, BYTE_ACCESS, VICR7, 0x78+7}, // VICR7 |
||
105 | |||
106 | {VIC, BYTE_ACCESS, DSICR, 0xf8+0}, // DSICR |
||
107 | |||
108 | {VIC, BYTE_ACCESS, LICR1, 0xf8+1}, // LICR1 |
||
109 | {VIC, BYTE_ACCESS, LICR2, 0xf8+2}, |
||
110 | {VIC, BYTE_ACCESS, LICR3, 0xf8+3}, |
||
111 | {VIC, BYTE_ACCESS, LICR4, 0xf8+4}, |
||
112 | {VIC, BYTE_ACCESS, LICR5, 0xf8+5}, |
||
113 | {VIC, BYTE_ACCESS, LICR6, 0x38+6}, |
||
114 | {VIC, BYTE_ACCESS, LICR7, 0x38+7}, // LICR7 |
||
115 | |||
116 | {VIC, BYTE_ACCESS, ICGSICR, 0xf8+2}, // ICGS |
||
117 | {VIC, BYTE_ACCESS, ICMSICR, 0xf8+3}, // ICMS |
||
118 | |||
119 | {VIC, BYTE_ACCESS, EGICR, 0xf8+6}, // EGICR |
||
120 | |||
121 | {VIC, BYTE_ACCESS, ICGSVBR, 0x08}, // ICGS-IVBR (!) |
||
122 | {VIC, BYTE_ACCESS, ICMSVBR, 0x0c}, // ICMS-IVBR (!) |
||
123 | |||
124 | {VIC, BYTE_ACCESS, LIVBR, 0x00}, // LIVBR (!) |
||
125 | |||
126 | {VIC, BYTE_ACCESS, EGIVBR, 0x10}, // EGIVBR (!) |
||
127 | |||
128 | {VIC, BYTE_ACCESS, ICSR, 0x00}, // ICSR |
||
129 | |||
130 | {VIC, BYTE_ACCESS, ICR0, 0x00}, // ICR0 |
||
131 | {VIC, BYTE_ACCESS, ICR1, 0x00}, |
||
132 | {VIC, BYTE_ACCESS, ICR2, 0x00}, |
||
133 | {VIC, BYTE_ACCESS, ICR3, 0x00}, |
||
134 | {VIC, BYTE_ACCESS, ICR4, 0x00}, // ICR4 |
||
135 | |||
136 | {VIC, BYTE_ACCESS, VIRSR, 0xfe}, // VIRSR |
||
137 | |||
138 | {VIC, BYTE_ACCESS, VIVR1, 0x0f}, // VIVR1 |
||
139 | {VIC, BYTE_ACCESS, VIVR2, 0x0f}, |
||
140 | {VIC, BYTE_ACCESS, VIVR3, 0x0f}, |
||
141 | {VIC, BYTE_ACCESS, VIVR4, 0x0f}, |
||
142 | {VIC, BYTE_ACCESS, VIVR5, 0x0f}, |
||
143 | {VIC, BYTE_ACCESS, VIVR6, 0x0f}, |
||
144 | {VIC, BYTE_ACCESS, VIVR7, 0x0f}, // VIVR7 |
||
145 | |||
146 | {VIC, BYTE_ACCESS, TTR, 0x3c}, // TTR |
||
147 | |||
148 | {VIC, BYTE_ACCESS, ARCR, 0x40}, // ARCR |
||
149 | {VIC, BYTE_ACCESS, AMSR, 0x29}, // AMSR |
||
150 | {VIC, BYTE_ACCESS, RCR, 0x00}, // RCR |
||
151 | |||
152 | {IFR, LONG_ACCESS, (u16)ADRHL, 0xF0F0F0F0}, // ADR-H, ADR-L |
||
153 | {IFR, WORD_ACCESS, (u16)CSR , 0x0000}, // Contr-Reg |
||
154 | |||
155 | {VIC, BYTE_ACCESS, ICR7, 0x80}, // ICR7 |
||
156 | |||
157 | {LCR, WORD_ACCESS, PLX9050_INTCSR, DISABLE_PCIADA_IRQS}, // disable interrupts |
||
158 | |||
159 | {STOP, WORD_ACCESS, 0, 0}}; |
||
160 | |||
161 | static PCIVME_INIT_ELEMENT deinit_element_pre[] = |
||
162 | {{VIC, BYTE_ACCESS, ICR7, 0x00}, // ICR7 - sysfail |
||
163 | {LCR, WORD_ACCESS, PLX9050_INTCSR, DISABLE_PCIADA_IRQS}, // disable interrupts |
||
164 | {STOP, WORD_ACCESS, 0, 0}}; |
||
165 | |||
166 | static PCIVME_INIT_ELEMENT deinit_element_post[] = |
||
167 | {{LCR, WORD_ACCESS, PLX9050_CNTRL, INHIBIT_VMEMM}, // disable interface |
||
168 | {STOP, WORD_ACCESS, 0, 0}}; |
||
169 | |||
170 | |||
171 | /*--- EXTERNALS ----------------------------------------------------------------------------------*/ |
||
172 | |||
173 | /*--- TYPEDEFS -----------------------------------------------------------------------------------*/ |
||
174 | |||
175 | /*--- FUNCTIONS ----------------------------------------------------------------------------------*/ |
||
176 | static inline void switch_VMEMM_on(DEVICE_OBJ *pd) |
||
177 | { |
||
178 | writew(RELEASE_VMEMM, (volatile void *) (pd->pLCR + PLX9050_CNTRL)); /* enable access */ |
||
179 | } |
||
180 | |||
181 | static inline void switch_VMEMM_off(DEVICE_OBJ *pd) |
||
182 | { |
||
183 | writew(INHIBIT_VMEMM, (volatile void *) (pd->pLCR + PLX9050_CNTRL)); /* enable access */ |
||
184 | } |
||
185 | |||
186 | static inline void setPageAddress(DEVICE_OBJ *pd, u32 newPageAddress) |
||
187 | { |
||
188 | PRINTK(KERN_DEBUG "%s : setPageAddress(0x%08x)\n", DEVICE_NAME, newPageAddress); |
||
189 | |||
190 | writel(newPageAddress, (volatile void *) pd->pAdrReg); |
||
191 | pd->dwCurrentPageAddress = newPageAddress; |
||
192 | } |
||
193 | |||
194 | static inline void setModifier(DEVICE_OBJ *pd, u8 newModifier) |
||
195 | { |
||
196 | PRINTK(KERN_DEBUG "%s : setModifier(0x%02x)\n", DEVICE_NAME, newModifier); |
||
197 | |||
198 | writeb(newModifier, (volatile void *) pd->pAdrMod); |
||
199 | pd->bCurrentModifier = newModifier; |
||
200 | } |
||
201 | |||
202 | /* read and write functions -----------------------------------------------------------------------*/ |
||
203 | static inline u8 *increment8(void **pvBuffer) |
||
204 | { |
||
205 | u8 *tmp = (u8*)*pvBuffer; |
||
206 | |||
207 | *pvBuffer += sizeof(u8); |
||
208 | |||
209 | return tmp; |
||
210 | } |
||
211 | |||
212 | static inline u16 *increment16(void **pvBuffer) |
||
213 | { |
||
214 | u16 *tmp = (u16*)*pvBuffer; |
||
215 | |||
216 | *pvBuffer += sizeof(u16); |
||
217 | |||
218 | return tmp; |
||
219 | } |
||
220 | |||
221 | static inline u32 *increment32(void **pvBuffer) |
||
222 | { |
||
223 | u32 *tmp = (u32*)*pvBuffer; |
||
224 | |||
225 | *pvBuffer += sizeof(u32); |
||
226 | |||
227 | return tmp; |
||
228 | } |
||
229 | |||
230 | static void readByte(DEVICE_OBJ *pd, void **pvBuffer, u32 dwLocalAddressInPage) |
||
231 | { |
||
232 | u8 tmp; |
||
233 | |||
234 | tmp = readb((const volatile void *) (pd->pVME + dwLocalAddressInPage)); |
||
235 | __put_user(tmp, increment8(pvBuffer)); |
||
236 | } |
||
237 | |||
238 | static void writeByte(DEVICE_OBJ *pd, u32 dwLocalAddressInPage, void **pvBuffer) |
||
239 | { |
||
240 | u8 tmp; |
||
241 | |||
242 | __get_user(tmp, increment8(pvBuffer)); |
||
243 | writeb(tmp, (volatile void *) (pd->pVME + dwLocalAddressInPage )); |
||
244 | } |
||
245 | |||
246 | static void readWord(DEVICE_OBJ *pd, void **pvBuffer, u32 dwLocalAddressInPage) |
||
247 | { |
||
248 | u16 tmp; |
||
249 | |||
250 | tmp = readw((const volatile void *) (pd->pVME + dwLocalAddressInPage)); |
||
251 | __put_user(tmp, increment16(pvBuffer)); |
||
252 | } |
||
253 | |||
254 | static void writeWord(DEVICE_OBJ *pd, u32 dwLocalAddressInPage, void **pvBuffer) |
||
255 | { |
||
256 | u16 tmp; |
||
257 | |||
258 | __get_user(tmp, increment16(pvBuffer)); |
||
259 | writew(tmp, (volatile void *) ( pd->pVME + dwLocalAddressInPage )); |
||
260 | } |
||
261 | |||
262 | static void readLong(DEVICE_OBJ *pd, void **pvBuffer, u32 dwLocalAddressInPage) |
||
263 | { |
||
264 | u32 tmp; |
||
265 | |||
266 | tmp = readl((const volatile void *) (pd->pVME + dwLocalAddressInPage)); |
||
267 | __put_user(tmp, increment32(pvBuffer)); |
||
268 | } |
||
269 | |||
270 | static void writeLong(DEVICE_OBJ *pd, u32 dwLocalAddressInPage, void **pvBuffer) |
||
271 | { |
||
272 | u32 tmp; |
||
273 | |||
274 | __get_user(tmp, increment32(pvBuffer)); |
||
275 | writel(tmp, (volatile void *) (pd->pVME + dwLocalAddressInPage)); |
||
276 | } |
||
277 | |||
278 | /* test alignment functions -----------------------------------------------------------------------*/ |
||
279 | static int MisalignmentForByteAccess(loff_t offset) |
||
280 | { |
||
281 | return 0; |
||
282 | } |
||
283 | |||
284 | static int MisalignmentForWordAccess(loff_t offset) |
||
285 | { |
||
286 | return(offset & 1); |
||
287 | } |
||
288 | |||
289 | static int MisalignmentForLongAccess(loff_t offset) |
||
290 | { |
||
291 | return(offset & 3); |
||
292 | } |
||
293 | |||
294 | // helper functions -------------------------------------------------------------------------------- |
||
295 | int check_command(const PCIVME_INIT_ELEMENT *psInitElement) |
||
296 | { |
||
297 | u16 range; |
||
298 | u16 access_size; |
||
299 | |||
300 | // PRINTK(KERN_DEBUG "%s : check_command()\n", DEVICE_NAME); |
||
301 | |||
302 | switch (psInitElement->bDestination) |
||
303 | { |
||
304 | case LCR: |
||
305 | range = 0x54; |
||
306 | break; |
||
307 | case IFR: |
||
308 | range = 0x0c; |
||
309 | break; |
||
310 | case VIC: |
||
311 | range = 0xe4; |
||
312 | if ((psInitElement->wOffset & 3) != 3) |
||
313 | return -EINVAL; |
||
314 | break; |
||
315 | default: |
||
316 | return -EINVAL; |
||
317 | break; |
||
318 | } |
||
319 | |||
320 | // check alignment and allowed address range |
||
321 | switch (psInitElement->bAccessType) |
||
322 | { |
||
323 | case LONG_ACCESS: |
||
324 | if (psInitElement->wOffset & 3) |
||
325 | return -EINVAL; |
||
326 | access_size = sizeof(u32); |
||
327 | break; |
||
328 | case WORD_ACCESS: |
||
329 | if (psInitElement->wOffset & 1) |
||
330 | return -EINVAL; |
||
331 | access_size = sizeof(u16); |
||
332 | break; |
||
333 | case BYTE_ACCESS: |
||
334 | access_size = sizeof(u8); |
||
335 | break; |
||
336 | default : |
||
337 | return -EINVAL; |
||
338 | break; |
||
339 | } |
||
340 | |||
341 | if ((psInitElement->wOffset + access_size) > range) |
||
342 | return -EINVAL; // ignore it |
||
343 | |||
344 | return 0; |
||
345 | } |
||
346 | |||
347 | static int CmdMachine(DEVICE_OBJ *pd, const PCIVME_INIT_ELEMENT *psInitElement) |
||
348 | { |
||
349 | u32 adr; |
||
350 | int err; |
||
351 | |||
11 | f9daq | 352 | //PRINTK(KERN_DEBUG "%s : CmdMachine()\n", DEVICE_NAME); |
9 | f9daq | 353 | |
354 | // loop through the init (or deinit) list |
||
355 | while (psInitElement->bDestination != STOP) |
||
356 | { |
||
357 | err = check_command(psInitElement); |
||
358 | if (!err) |
||
359 | { |
||
360 | switch (psInitElement->bDestination) |
||
361 | { |
||
362 | case LCR: |
||
363 | adr = pd->pLCR; |
||
364 | break; |
||
365 | case VIC: |
||
366 | adr = pd->pCtl + VICBASE; |
||
367 | break; |
||
368 | case IFR: |
||
369 | adr = pd->pCtl + CSR; |
||
370 | break; |
||
371 | default: |
||
372 | return -EINVAL; |
||
373 | } |
||
374 | |||
375 | switch (psInitElement->bAccessType) |
||
376 | { |
||
377 | case LONG_ACCESS: |
||
378 | writel(psInitElement->dwValue, (volatile void *) (adr + psInitElement->wOffset)); |
||
379 | break; |
||
380 | case WORD_ACCESS: |
||
381 | writew((u16)psInitElement->dwValue, (volatile void *) (adr + psInitElement->wOffset)); |
||
382 | break; |
||
383 | case BYTE_ACCESS: |
||
384 | writeb((u8)psInitElement->dwValue, (volatile void *) (adr + psInitElement->wOffset)); |
||
385 | break; |
||
386 | default: |
||
387 | return -EINVAL; |
||
388 | } |
||
389 | } |
||
390 | else |
||
391 | return err; |
||
392 | |||
393 | psInitElement++; |
||
394 | } |
||
395 | |||
396 | return 0; |
||
397 | } |
||
398 | |||
399 | // all ioctls -------------------------------------------------------------------------------------- |
||
400 | static int init_hardware(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_INIT_COMMAND *init) |
||
401 | { |
||
402 | int err; |
||
403 | PCIVME_INIT_ELEMENT *element = init->sVie; |
||
404 | |||
11 | f9daq | 405 | PRINTK(KERN_INFO "%s : init_hardware()\n", DEVICE_NAME); |
9 | f9daq | 406 | |
407 | err = CmdMachine(pd, element); |
||
408 | if (err) |
||
409 | { |
||
410 | PRINTK(KERN_DEBUG "%s : init failed with err = %d!\n", DEVICE_NAME, err); |
||
411 | return err; |
||
412 | } |
||
413 | |||
414 | // sync storage with hardware |
||
415 | pd->bCurrentModifier = readb((const volatile void *) pd->pAdrMod) & 0x3f; |
||
416 | pd->dwCurrentPageAddress = readl((const volatile void *) pd->pAdrReg) & HI_ADDRESS_MASK; |
||
417 | |||
418 | return 0; |
||
419 | } |
||
420 | |||
421 | static int deinit_hardware(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_INIT_COMMAND *deinit) |
||
422 | { |
||
423 | int err; |
||
424 | PCIVME_INIT_ELEMENT *element = deinit->sVie; |
||
425 | |||
426 | PRINTK(KERN_DEBUG "%s : deinit_hardware()\n", DEVICE_NAME); |
||
427 | |||
428 | err = CmdMachine(pd, deinit_element_pre); |
||
429 | if (err) |
||
430 | goto fail; |
||
431 | |||
432 | err = CmdMachine(pd, element); |
||
433 | if (err) |
||
434 | goto fail; |
||
435 | |||
436 | err = CmdMachine(pd, deinit_element_post); |
||
437 | if (err) |
||
438 | goto fail; |
||
439 | |||
440 | return 0; |
||
441 | |||
442 | fail: |
||
443 | return err; |
||
444 | } |
||
445 | |||
446 | static int access_command(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_ACCESS_COMMAND *cmd) |
||
447 | { |
||
448 | PRINTK(KERN_DEBUG "%s : access_command()\n", DEVICE_NAME); |
||
449 | |||
450 | pp->bModifier = cmd->bModifier; |
||
451 | pp->bAccessType = cmd->bAccessType; |
||
452 | pp->bIncrement = cmd->bIncrement; |
||
453 | |||
454 | switch (pp->bAccessType) |
||
455 | { |
||
456 | case BYTE_ACCESS: |
||
457 | pp->read = readByte; |
||
458 | pp->write = writeByte; |
||
459 | pp->AlignmentCheck = MisalignmentForByteAccess; |
||
460 | break; |
||
461 | case WORD_ACCESS: |
||
462 | pp->read = readWord; |
||
463 | pp->write = writeWord; |
||
464 | pp->AlignmentCheck = MisalignmentForWordAccess; |
||
465 | break; |
||
466 | case LONG_ACCESS: |
||
467 | pp->read = readLong; |
||
468 | pp->write = writeLong; |
||
469 | pp->AlignmentCheck = MisalignmentForLongAccess; |
||
470 | break; |
||
471 | default: |
||
472 | return -EINVAL; |
||
473 | } |
||
474 | |||
475 | return 0; |
||
476 | } |
||
477 | |||
478 | static int get_static_status(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_STATIC_STATUS *static_status) |
||
479 | { |
||
480 | PRINTK(KERN_DEBUG "%s : get_static_status()\n", DEVICE_NAME); |
||
481 | |||
482 | static_status->bConnected = pd->bConnected; |
||
483 | static_status->cModuleNumber = pd->cModuleNumber; |
||
484 | static_status->cFPGAVersion = pd->cFPGAVersion; |
||
485 | static_status->cSystemController = pd->cSystemController; |
||
486 | static_status->cWordMode = pd->cWordMode; |
||
487 | |||
488 | return 0; |
||
489 | } |
||
490 | |||
491 | static int get_dynamic_status(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_DYNAMIC_STATUS *dynamic_status) |
||
492 | { |
||
493 | u16 cntrl = readw((const volatile void *) pd->pPCIADACntrl); |
||
494 | u16 intCSR = readw((const volatile void *) pd->pPCIADAIntCSR); |
||
495 | |||
496 | PRINTK(KERN_DEBUG "%s : get_dynamic_status()\n", DEVICE_NAME); |
||
497 | |||
498 | dynamic_status->bConnected = (cntrl & 0x0800) ? 1 : 0; |
||
499 | dynamic_status->bPCIADAIrq = (intCSR & 0x0020) ? 1 : 0; |
||
500 | dynamic_status->bVMEMMIrq = (intCSR & 0x0004) ? 1 : 0; |
||
501 | |||
502 | return 0; |
||
503 | } |
||
504 | |||
505 | static int read_vector_polling(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_VECTOR_LEVEL *vector) |
||
506 | { |
||
507 | u16 cntrl = readw((const volatile void *) pd->pPCIADACntrl); |
||
508 | u16 intCSR = readw((const volatile void *) pd->pPCIADAIntCSR); |
||
509 | |||
510 | PRINTK(KERN_DEBUG "%s : read_vector()\n", DEVICE_NAME); |
||
511 | |||
512 | vector->dwStatusID = 0; |
||
513 | vector->bLevel = 0; |
||
514 | vector->bPCIADAIrq = 0; |
||
515 | |||
516 | if (intCSR & 0x20) // check for PCIADA interrupt |
||
517 | { |
||
518 | vector->bPCIADAIrq = 1; |
||
519 | vector->dwStatusID = 1; // force for PCIADA irqs |
||
520 | |||
521 | writew(cntrl & ~0x0100, (volatile void *) pd->pPCIADACntrl); // clear pending PCIADA irq |
||
522 | writew(cntrl, (volatile void *) pd->pPCIADACntrl); |
||
523 | } |
||
524 | else |
||
525 | { |
||
526 | if ((cntrl & 0x0980) == 0x0980) // check if VMEMM is connected and ready |
||
527 | { |
||
528 | vector->bLevel = (u8)readw((const volatile void *) ( pd->pCtl + VICRES )); |
||
529 | if (vector->bLevel & 1) |
||
530 | { |
||
531 | if (vector->bLevel != 1) |
||
532 | vector->dwStatusID = (u32)readb((const volatile void *) (pd->pCtl + VECBASE + vector->bLevel)); |
||
533 | |||
534 | vector->bLevel >>= 1; |
||
535 | } |
||
536 | } |
||
537 | } |
||
538 | return 0; |
||
539 | } |
||
540 | |||
541 | static int read_vector_blocking(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_VECTOR_LEVEL *vector, struct file *pFile) |
||
542 | { |
||
543 | int error; |
||
544 | |||
545 | vector->dwStatusID = 0; |
||
546 | vector->bLevel = 0; |
||
547 | vector->bPCIADAIrq = 0; |
||
548 | |||
549 | // support nonblocking read if requested |
||
550 | if ((pFile->f_flags & O_NONBLOCK) && (!pd->wIrqStatus)) |
||
551 | return -EAGAIN; |
||
552 | |||
553 | // sleep until data are available |
||
554 | if ((error = wait_event_interruptible(pd->event_queue, (pd->wIrqStatus)))) |
||
555 | return error; |
||
556 | |||
557 | error = read_vector_polling(pp, pd, vector); |
||
558 | |||
559 | pd->wIrqStatus = 0; // clear the status since it is read |
||
560 | |||
561 | return error; |
||
562 | } |
||
563 | |||
564 | |||
565 | static int control_interrupts(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_IRQ_CONTROL *irq_control) |
||
566 | { |
||
567 | u16 intCSR = readw((const volatile void *) pd->pPCIADAIntCSR); |
||
568 | u8 ret = (intCSR & 0x40) ? 1 : 0; |
||
569 | |||
570 | PRINTK(KERN_DEBUG "%s : control_interrupts()\n", DEVICE_NAME); |
||
571 | |||
572 | if (irq_control->bEnable) |
||
573 | writew(intCSR | 0x40, (volatile void *) pd->pPCIADAIntCSR); |
||
574 | else |
||
575 | writew(intCSR & ~0x40, (volatile void *) pd->pPCIADAIntCSR); |
||
576 | |||
577 | // return the switch before set |
||
578 | irq_control->bEnable = ret; |
||
579 | |||
580 | return 0; |
||
581 | } |
||
582 | |||
583 | static int VME_TAS(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_TAS_STRUCT *tas_cmd) |
||
584 | { |
||
585 | u32 access_adr = pd->pVME + (tas_cmd->dwAddress & LO_ADDRESS_MASK); // make low part of address |
||
586 | u8 data; |
||
587 | |||
588 | // save old contents |
||
589 | u32 old_address = readl((const volatile void *) pd->pAdrReg); |
||
590 | u16 old_CSR = readw((const volatile void *) pd->pCSR); |
||
591 | u16 intCSR = readw((const volatile void *) pd->pPCIADAIntCSR); |
||
592 | pd->bCurrentModifier = readb((const volatile void *) pd->pAdrMod) & 0x3f; |
||
593 | |||
594 | PRINTK(KERN_DEBUG "%s : VME_TAS()\n", DEVICE_NAME); |
||
595 | |||
596 | // set new contents |
||
597 | writew(DISABLE_PCIADA_IRQS, (volatile void *) pd->pPCIADAIntCSR); |
||
598 | writeb((u8)tas_cmd->bModifier & 0x3f, (volatile void *) pd->pAdrMod); |
||
599 | writel(tas_cmd->dwAddress, (volatile void *) pd->pAdrReg); |
||
600 | writew(old_CSR | FLAG_RMC, (volatile void *) pd->pCSR); |
||
601 | |||
602 | // do the read - modify - write |
||
603 | data = readb((const volatile void *) access_adr); |
||
604 | writeb(tas_cmd->bContent, (volatile void *) access_adr); |
||
605 | |||
606 | // restore old contents |
||
607 | writeb(pd->bCurrentModifier, (volatile void *) pd->pAdrMod); |
||
608 | writew(old_CSR, (volatile void *) pd->pCSR); |
||
609 | writel(old_address, (volatile void *) pd->pAdrReg); |
||
610 | writew(intCSR, (volatile void *) pd->pPCIADAIntCSR); |
||
611 | |||
612 | // get back read data |
||
613 | tas_cmd->bContent = data; |
||
614 | |||
615 | return 0; |
||
616 | } |
||
617 | |||
618 | static int VMEMM_RESET(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_RESET_COMMAND *reset_cmd) |
||
619 | { |
||
620 | u16 cntrl = readw((const volatile void *) pd->pPCIADACntrl); |
||
621 | u16 intCSR = readw((const volatile void *) pd->pPCIADAIntCSR); |
||
622 | int status = 0; |
||
623 | |||
11 | f9daq | 624 | PRINTK(KERN_INFO "%s : VMEMM_RESET()\n", DEVICE_NAME); |
9 | f9daq | 625 | |
626 | // am I connected and switched on?? |
||
627 | if ((cntrl & 0x0980) == 0x0980) |
||
628 | { |
||
629 | // do command |
||
630 | switch (reset_cmd->bCommand) |
||
631 | { |
||
632 | case POLL_RESET_CMD: |
||
633 | break; |
||
634 | case VME_RESET_CMD: |
||
635 | writeb(0, (volatile void *) pd->pAdrMod); |
||
636 | writeb(0xf0, (volatile void *) (pd->pCtl + VICBASE + SRR)); // make VME reset |
||
637 | break; |
||
638 | case LOCAL_RESET_CMD: |
||
639 | writeb(0, (volatile void *) pd->pAdrMod); |
||
640 | writew(LOCAL_RESET, (volatile void *) (pd->pCtl + VICRES)); |
||
641 | break; |
||
642 | case GLOBAL_RESET_CMD: |
||
643 | writeb(0, (volatile void *) pd->pAdrMod); |
||
644 | writew(GLOBAL_RESET, (volatile void *) (pd->pCtl + VICRES)); |
||
645 | break; |
||
646 | |||
647 | default: status = -EINVAL; |
||
648 | } |
||
649 | |||
650 | // inhibit PCIADA generated irqs |
||
651 | writew(DISABLE_PCIADA_IRQS, (volatile void *) pd->pPCIADAIntCSR); |
||
652 | |||
653 | // always poll reset status - access will sometimes generate PCIADA #2 interrupt |
||
654 | reset_cmd->bResult = readb((const volatile void *) pd->pAdrMod); |
||
655 | |||
656 | // reset any pending PCIADA interrupt #2 |
||
657 | writew(cntrl & ~0x0100, (volatile void *) pd->pPCIADACntrl); |
||
658 | writew(cntrl , (volatile void *) pd->pPCIADACntrl); |
||
659 | |||
660 | // restore IRQStatus |
||
661 | writew(intCSR , (volatile void *) pd->pPCIADAIntCSR); |
||
662 | } |
||
663 | else |
||
664 | status = -EBUSY; |
||
665 | |||
666 | // sync storage with hardware |
||
667 | pd->bCurrentModifier = readb((const volatile void *) pd->pAdrMod) & 0x3f; |
||
668 | |||
669 | return status; |
||
670 | } |
||
671 | |||
672 | static int access_VIC68A(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_VIC68A_ACTION *action) |
||
673 | { |
||
674 | int nStatus = 0; |
||
675 | |||
676 | PRINTK(KERN_DEBUG "%s : access_VIC68A()\n", DEVICE_NAME); |
||
677 | |||
678 | if ((action->wRegisterAddress <= SRR) && ((action->wRegisterAddress & 0x03) == 3)) |
||
679 | { |
||
680 | u32 dwAddress; |
||
681 | u8 bByte = 0; |
||
682 | |||
683 | dwAddress = (pd->pCtl + VICBASE + action->wRegisterAddress); |
||
684 | |||
685 | switch (action->bAccessMode) |
||
686 | { |
||
687 | case VIC68A_WRITE_ONLY: |
||
688 | writeb(action->bContent, (volatile void *) dwAddress); |
||
689 | break; |
||
690 | case VIC68A_WRITE: |
||
691 | writeb(action->bContent, (volatile void *) dwAddress); |
||
692 | action->bContent = readb((const volatile void *) dwAddress); |
||
693 | break; |
||
694 | case VIC68A_OR: |
||
695 | bByte = readb((const volatile void *) dwAddress); |
||
696 | bByte |= action->bContent; |
||
697 | writeb(bByte, (volatile void *) dwAddress); |
||
698 | action->bContent = readb((const volatile void *) dwAddress); |
||
699 | break; |
||
700 | case VIC68A_AND: |
||
701 | bByte = readb((const volatile void *) dwAddress); |
||
702 | bByte &= action->bContent; |
||
703 | writeb(bByte, (volatile void *) dwAddress); |
||
704 | action->bContent = readb((const volatile void *) dwAddress); |
||
705 | break; |
||
706 | case VIC68A_READ: |
||
707 | action->bContent = readb((const volatile void *) dwAddress); |
||
708 | break; |
||
709 | default: |
||
710 | nStatus = -EINVAL; |
||
711 | } |
||
712 | } |
||
713 | else |
||
714 | nStatus = -EINVAL; |
||
715 | |||
716 | return nStatus; |
||
717 | } |
||
718 | |||
719 | // the dispatcher ---------------------------------------------------------------------------------- |
||
720 | int pcivme_ioctl(struct inode *pInode, struct file *pFile, unsigned int cmd, unsigned long arg) |
||
721 | { |
||
722 | PATH_OBJ *pp = (PATH_OBJ *)pFile->private_data; |
||
723 | DEVICE_OBJ *pd = pp->pDo; |
||
724 | int err = 1; |
||
725 | |||
726 | PRINTK(KERN_DEBUG "%s : pcivme_ioctl(0x%08x), size = %d\n", DEVICE_NAME, cmd, _IOC_SIZE(cmd)); |
||
727 | |||
728 | if (_IOC_TYPE(cmd) != PCIVME_MAGIC) |
||
729 | return -EINVAL; |
||
730 | |||
731 | // check for accessible user buffer |
||
732 | if (_IOC_DIR(cmd) & _IOC_READ) |
||
733 | err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)); |
||
734 | if (_IOC_DIR(cmd) & _IOC_WRITE) |
||
735 | err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)); |
||
736 | if (err) |
||
737 | return -EFAULT; |
||
738 | |||
739 | switch (_IOC_NR(cmd)) |
||
740 | { |
||
741 | case _IOC_NR(PCIVME_READ_VECTOR_BLOCK): |
||
742 | if (_IOC_SIZE(cmd) < sizeof(PCIVME_VECTOR_LEVEL)) |
||
743 | return -EINVAL; |
||
744 | return read_vector_blocking(pp, pd, (PCIVME_VECTOR_LEVEL *)arg, pFile); |
||
745 | |||
746 | case _IOC_NR(PCIVME_READ_VECTOR_POLL): |
||
747 | if (_IOC_SIZE(cmd) < sizeof(PCIVME_VECTOR_LEVEL)) |
||
748 | return -EINVAL; |
||
749 | return read_vector_polling(pp, pd, (PCIVME_VECTOR_LEVEL *)arg); |
||
750 | |||
751 | case _IOC_NR(PCIVME_CONTROL_INTERRUPTS): |
||
752 | if (_IOC_SIZE(cmd) < sizeof(PCIVME_IRQ_CONTROL)) |
||
753 | return -EINVAL; |
||
754 | return control_interrupts(pp, pd, (PCIVME_IRQ_CONTROL *)arg); |
||
755 | |||
756 | case _IOC_NR(PCIVME_TAS): |
||
757 | if (_IOC_SIZE(cmd) < sizeof(PCIVME_TAS_STRUCT)) |
||
758 | return -EINVAL; |
||
759 | return VME_TAS(pp, pd, (PCIVME_TAS_STRUCT *)arg); |
||
760 | |||
761 | case _IOC_NR(PCIVME_ACCESS_VIC68A): |
||
762 | if (_IOC_SIZE(cmd) < sizeof(PCIVME_VIC68A_ACTION)) |
||
763 | return -EINVAL; |
||
764 | return access_VIC68A(pp, pd, (PCIVME_VIC68A_ACTION *)arg); |
||
765 | |||
766 | case _IOC_NR(PCIVME_GET_DYNAMIC_STATUS): |
||
767 | if (_IOC_SIZE(cmd) < sizeof(PCIVME_DYNAMIC_STATUS)) |
||
768 | return -EINVAL; |
||
769 | return get_dynamic_status(pp, pd, (PCIVME_DYNAMIC_STATUS *)arg); |
||
770 | |||
771 | case _IOC_NR(PCIVME_RESET): |
||
772 | if (_IOC_SIZE(cmd) < sizeof(PCIVME_RESET_COMMAND)) |
||
773 | return -EINVAL; |
||
774 | return VMEMM_RESET(pp, pd, (PCIVME_RESET_COMMAND *)arg); |
||
775 | |||
776 | case _IOC_NR(PCIVME_SET_ACCESS_PARA): |
||
777 | if (_IOC_SIZE(cmd) < sizeof(PCIVME_ACCESS_COMMAND)) |
||
778 | return -EINVAL; |
||
779 | return access_command(pp, pd, (PCIVME_ACCESS_COMMAND *)arg); |
||
780 | |||
781 | case _IOC_NR(PCIVME_GET_STATIC_STATUS): |
||
782 | if (_IOC_SIZE(cmd) < sizeof(PCIVME_STATIC_STATUS)) |
||
783 | return -EINVAL; |
||
784 | return get_static_status(pp, pd, (PCIVME_STATIC_STATUS *)arg); |
||
785 | |||
786 | case _IOC_NR(PCIVME_INIT_HARDWARE): |
||
787 | if (_IOC_SIZE(cmd) < sizeof(PCIVME_INIT_COMMAND)) |
||
788 | return -EINVAL; |
||
789 | return init_hardware(pp, pd, (PCIVME_INIT_COMMAND *)arg); |
||
790 | |||
791 | case _IOC_NR(PCIVME_DEINIT_HARDWARE): |
||
792 | if (_IOC_SIZE(cmd) < sizeof(PCIVME_INIT_COMMAND)) |
||
793 | return -EINVAL; |
||
794 | return deinit_hardware(pp, pd, (PCIVME_INIT_COMMAND *)arg); |
||
795 | |||
796 | default: |
||
797 | PRINTK(KERN_DEBUG "%s : pcivme_ioctl(0x%08x) is illegal\n", DEVICE_NAME, cmd); |
||
798 | return -EINVAL; |
||
799 | } |
||
800 | |||
801 | return 0; |
||
802 | } |
||
803 | |||
11 | f9daq | 804 | /* |
805 | static long pcivme_compat_ioctl(struct file *pFile, unsigned int cmd, unsigned long arg){ |
||
806 | PRINTK(KERN_DEBUG "%s : pcivme_compat_ioctl(0x%08x), size = %d\n", DEVICE_NAME, cmd, _IOC_SIZE(cmd)); |
||
807 | return pcivme_ioctl(NULL, pFile, cmd,arg); |
||
808 | } |
||
809 | */ |
||
810 | |||
9 | f9daq | 811 | static long pcivme_unlocked_ioctl(struct file *pFile, unsigned int cmd, unsigned long arg){ |
812 | long retval=0; |
||
11 | f9daq | 813 | |
814 | |||
9 | f9daq | 815 | #if HAVE_UNLOCKED_IOCTL |
816 | struct mutex fs_mutex; |
||
817 | mutex_init(&fs_mutex); |
||
818 | mutex_lock(&fs_mutex); |
||
819 | #else |
||
820 | lock_kernel(); |
||
821 | #endif |
||
822 | |||
11 | f9daq | 823 | PRINTK(KERN_DEBUG "%s : pcivme_unlocked_ioctl(0x%08x), size = %d\n", DEVICE_NAME, cmd, _IOC_SIZE(cmd)); |
9 | f9daq | 824 | retval = pcivme_ioctl(NULL, pFile, cmd,arg); |
825 | |||
826 | #if HAVE_UNLOCKED_IOCTL |
||
827 | mutex_unlock(&fs_mutex); |
||
828 | #else |
||
829 | unlock_kernel(); |
||
830 | #endif |
||
11 | f9daq | 831 | |
9 | f9daq | 832 | return retval; |
833 | } |
||
834 | |||
835 | int pcivme_open(struct inode *pInode, struct file *pFile) |
||
836 | { |
||
837 | DEVICE_OBJ *pd = 0; |
||
838 | DEVICE_OBJ *desc = 0; |
||
11 | f9daq | 839 | int nMinor = MINOR(pInode->i_rdev); |
9 | f9daq | 840 | struct list_head *ptr; |
841 | |||
11 | f9daq | 842 | PRINTK(KERN_DEBUG "%s : pcivme_open(), %d, scanning %d devices\n", DEVICE_NAME, nMinor, drv.count); |
9 | f9daq | 843 | |
844 | /* search for device */ |
||
845 | for (ptr = drv.devList.next; ptr != &drv.devList; ptr = ptr->next) |
||
846 | { |
||
847 | pd = list_entry(ptr, DEVICE_OBJ, list); |
||
848 | pd->bConnected = get_module_info(pd); |
||
849 | if (pd->bConnected) |
||
850 | { |
||
851 | if (test_connection(pd)) |
||
852 | { |
||
11 | f9daq | 853 | printk(KERN_ERR "%s : pcivme_open() connection test for module %d failed!\n", DEVICE_NAME, pd->cModuleNumber); |
9 | f9daq | 854 | pd->bConnected = 0; |
855 | } |
||
856 | else |
||
857 | if (pd->cModuleNumber == nMinor) |
||
858 | { |
||
859 | desc = pd; |
||
860 | break; |
||
861 | } |
||
862 | } |
||
863 | else |
||
11 | f9daq | 864 | PRINTK(KERN_DEBUG "%s pcivme_open(): module %d not connected!\n", DEVICE_NAME, nMinor); |
9 | f9daq | 865 | } |
866 | |||
867 | if (desc) |
||
868 | { |
||
869 | int err; |
||
870 | PATH_OBJ *pp; |
||
871 | |||
872 | pp = (PATH_OBJ *)kmalloc(sizeof(PATH_OBJ), GFP_ATOMIC); |
||
873 | if (!pp) |
||
874 | return -ENOMEM; |
||
875 | |||
876 | // file PATH_OBJ structure with initialisation data |
||
877 | pp->pDo = pd; |
||
878 | pp->bAccessType = pp->bIncrement = BYTE_ACCESS; |
||
879 | pp->bModifier = Short_NoPriv; |
||
880 | pp->read = readByte; |
||
881 | pp->write = writeByte; |
||
882 | pp->AlignmentCheck = MisalignmentForByteAccess; |
||
883 | pFile->private_data = (void *)pp; |
||
884 | |||
11 | f9daq | 885 | PRINTK(KERN_DEBUG "%s : pcivme_open() found VMEMM module with number %d.\n", DEVICE_NAME, nMinor); |
9 | f9daq | 886 | |
887 | if (!pd->nOpenCounter) |
||
888 | { |
||
889 | err = CmdMachine(pd, init_element); |
||
890 | if (err) |
||
891 | { |
||
11 | f9daq | 892 | printk(KERN_ERR "%s : pcivme_open() default init failed with err = %d!\n", DEVICE_NAME, err); |
9 | f9daq | 893 | kfree_s(pp, sizeof(*pp)); // FREE(pFile->private_data); |
894 | return err; |
||
895 | } |
||
896 | } |
||
897 | |||
898 | pd->nOpenCounter++; |
||
899 | } |
||
900 | else |
||
901 | { |
||
11 | f9daq | 902 | printk(KERN_ERR "%s pcivme_open(): No VMEMM module found.\n", DEVICE_NAME); |
9 | f9daq | 903 | return -ENODEV; |
904 | } |
||
905 | |||
906 | __MOD_INC_USE_COUNT__; |
||
907 | return 0; |
||
908 | } |
||
909 | |||
910 | int pcivme_release(struct inode *pInode, struct file *pFile) |
||
911 | { |
||
912 | PATH_OBJ *pp; |
||
913 | |||
11 | f9daq | 914 | PRINTK(KERN_DEBUG "%s : pcivme_release()\n", DEVICE_NAME); |
9 | f9daq | 915 | |
916 | if (pFile->private_data) |
||
917 | { |
||
918 | pp = (PATH_OBJ *)pFile->private_data; |
||
919 | if (pp && pp->pDo ) |
||
920 | { |
||
921 | DEVICE_OBJ *pd = pp->pDo; |
||
922 | |||
923 | pd->nOpenCounter--; |
||
924 | |||
925 | // the last one closes the door |
||
926 | if (pd->nOpenCounter <= 0) |
||
927 | { |
||
928 | CmdMachine(pd, deinit_element_pre); |
||
929 | CmdMachine(pd, deinit_element_post); |
||
930 | |||
931 | // Vorsicht ist die Mutter der Porzelankiste! |
||
932 | pd->nOpenCounter = 0; |
||
933 | } |
||
934 | |||
935 | pp->pDo = 0; |
||
936 | } |
||
937 | |||
938 | kfree_s(pp, sizeof(*pp)); // FREE(pFile->private_data); |
||
939 | } |
||
940 | |||
941 | __MOD_DEC_USE_COUNT__; |
||
942 | return 0; |
||
943 | } |
||
944 | |||
945 | static ssize_t pcivme_read(struct file *pFile, char *pcBuffer, size_t count, loff_t *offp) |
||
946 | { |
||
947 | PATH_OBJ *pp = (PATH_OBJ *)pFile->private_data; |
||
948 | DEVICE_OBJ *pd = pp->pDo; |
||
949 | u32 dwLocalCount = count; |
||
950 | register u32 dwLocalPageAddress; |
||
951 | u32 dwLocalAddressInPage; |
||
952 | |||
953 | PRINTK(KERN_DEBUG "%s : pcivme_read(0x%08x, %d)\n", DEVICE_NAME, (u32)*offp, dwLocalCount); |
||
954 | |||
955 | // inhibit misaligned accesses |
||
956 | if (pp->AlignmentCheck(*offp)) |
||
957 | return -EFAULT; |
||
958 | |||
959 | // check for free access to user buffer |
||
960 | if (!access_ok(VERIFY_WRITE, pcBuffer, count)) |
||
961 | return -EFAULT; |
||
962 | |||
963 | // do I still have the same modifier? |
||
964 | if (pp->bModifier != pd->bCurrentModifier) |
||
965 | setModifier(pd, pp->bModifier); |
||
966 | |||
967 | while (count >= pp->bAccessType) |
||
968 | { |
||
969 | dwLocalPageAddress = *offp & HI_ADDRESS_MASK; |
||
970 | dwLocalAddressInPage = *offp & LO_ADDRESS_MASK; |
||
971 | |||
972 | // do I still work in the same page? |
||
973 | if (dwLocalPageAddress != pd->dwCurrentPageAddress) |
||
974 | setPageAddress(pd, dwLocalPageAddress); |
||
975 | |||
976 | // standard access method |
||
977 | pp->read(pd, (void **)&pcBuffer, dwLocalAddressInPage); |
||
978 | |||
979 | // decrement count and update pointer to next access address |
||
980 | count -= pp->bAccessType; |
||
981 | *offp += pp->bIncrement; |
||
982 | } |
||
983 | |||
984 | return dwLocalCount - count; |
||
985 | } |
||
986 | |||
987 | static ssize_t pcivme_write(struct file *pFile, const char *pcBuffer, size_t count, loff_t *offp) |
||
988 | { |
||
989 | PATH_OBJ *pp = (PATH_OBJ *)pFile->private_data; |
||
990 | DEVICE_OBJ *pd = pp->pDo; |
||
991 | u32 dwLocalCount = count; |
||
992 | register u32 dwLocalPageAddress; |
||
993 | u32 dwLocalAddressInPage; |
||
994 | |||
995 | PRINTK(KERN_DEBUG "%s : pcivme_write(0x%08x, %d)\n", DEVICE_NAME, (u32)*offp, dwLocalCount); |
||
996 | |||
997 | // inhibit misaligned accesses |
||
998 | if (pp->AlignmentCheck(*offp)) |
||
999 | return -EFAULT; |
||
1000 | |||
1001 | // check for free access to user buffer |
||
1002 | if (!access_ok(VERIFY_READ, pcBuffer, count)) |
||
1003 | return -EFAULT; |
||
1004 | |||
1005 | // do I still have the same modifier? |
||
1006 | if (pp->bModifier != pd->bCurrentModifier) |
||
1007 | setModifier(pd, pp->bModifier); |
||
1008 | |||
1009 | while (count >= pp->bAccessType) |
||
1010 | { |
||
1011 | dwLocalPageAddress = *offp & HI_ADDRESS_MASK; |
||
1012 | dwLocalAddressInPage = *offp & LO_ADDRESS_MASK; |
||
1013 | |||
1014 | // do I still work in the same page? |
||
1015 | if (dwLocalPageAddress != pd->dwCurrentPageAddress) |
||
1016 | setPageAddress(pd, dwLocalPageAddress); |
||
1017 | |||
1018 | // standard access method |
||
1019 | pp->write(pd, dwLocalAddressInPage, (void **)&pcBuffer); |
||
1020 | |||
1021 | // decrement count and update pointer to next access address |
||
1022 | count -= pp->bAccessType; |
||
1023 | *offp += pp->bIncrement; |
||
1024 | } |
||
1025 | |||
1026 | return dwLocalCount - count; |
||
1027 | } |
||
1028 | |||
1029 | |||
11 | f9daq | 1030 | // http://learninglinuxkernel.in/writing-char-driver-for-linux-kernel-2-6/ |
1031 | // http://appusajeev.wordpress.com/2011/06/18/writing-a-linux-character-device-driver/ |
||
1032 | loff_t pcivme_lseek(struct file* filep, loff_t offset, int whence) |
||
9 | f9daq | 1033 | { |
11 | f9daq | 1034 | |
1035 | PRINTK(KERN_DEBUG "%s : pcivme_lseek(0x%08x, %d)\n", DEVICE_NAME, (u32) offset, whence); |
||
1036 | switch (whence) { |
||
1037 | case 0: /* SEEK_SET */ |
||
1038 | filep->f_pos = offset; |
||
1039 | break; |
||
1040 | case 1: /* SEEK_CUR */ |
||
1041 | filep->f_pos += offset; |
||
1042 | break; |
||
1043 | case 2: /* SEEK_END */ |
||
1044 | return -EINVAL; |
||
1045 | default: |
||
1046 | return -EINVAL; |
||
1047 | }; |
||
1048 | |||
1049 | return filep->f_pos; |
||
1050 | } |
||
1051 | |||
1052 | |||
1053 | |||
9 | f9daq | 1054 | struct file_operations pcivme_fops = |
1055 | { |
||
11 | f9daq | 1056 | .llseek = pcivme_lseek, /* lseek */ |
9 | f9daq | 1057 | .read = pcivme_read, /* read */ |
1058 | .write = pcivme_write, /* write */ |
||
11 | f9daq | 1059 | // .compat_ioctl = pcivme_compat_ioctl, /* ioctl */ |
9 | f9daq | 1060 | .unlocked_ioctl = pcivme_unlocked_ioctl, /* ioctl */ |
1061 | .open = pcivme_open, /* open */ |
||
1062 | .release = pcivme_release, /* release */ |
||
1063 | }; |
||
1064 | |||
1065 | |||
11 | f9daq | 1066 | |
9 | f9daq | 1067 | |
1068 | |||
1069 |