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 |