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 |