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